1use ndarray::Array2;
44use num_complex::Complex64;
45use std::collections::HashSet;
46use std::fmt::{self, Write};
47
48#[derive(Debug, Clone, PartialEq)]
64pub struct Root {
65 pub(crate) coords: Vec<f64>,
67}
68
69impl Root {
70 #[must_use]
72 pub fn new(coords: Vec<f64>) -> Self {
73 Self { coords }
74 }
75
76 #[must_use]
78 pub fn coords(&self) -> &[f64] {
79 &self.coords
80 }
81
82 #[must_use]
84 pub fn rank(&self) -> usize {
85 self.coords.len()
86 }
87
88 #[must_use]
90 pub fn inner_product(&self, other: &Root) -> f64 {
91 assert_eq!(self.rank(), other.rank());
92 self.coords
93 .iter()
94 .zip(&other.coords)
95 .map(|(a, b)| a * b)
96 .sum()
97 }
98
99 #[must_use]
101 pub fn norm_squared(&self) -> f64 {
102 self.inner_product(self)
103 }
104
105 #[must_use]
107 pub fn reflect(&self, beta: &Root) -> Root {
108 let factor = 2.0 * self.inner_product(beta) / self.norm_squared();
109 Root::new(
110 beta.coords
111 .iter()
112 .zip(&self.coords)
113 .map(|(b, a)| b - factor * a)
114 .collect(),
115 )
116 }
117
118 #[inline]
120 #[must_use]
121 pub fn cartan_integer(&self, beta: &Root) -> i32 {
122 let value = 2.0 * self.inner_product(beta) / self.norm_squared();
123 value.round() as i32
124 }
125
126 #[must_use]
128 pub fn is_positive(&self) -> bool {
129 for &c in &self.coords {
130 if c.abs() > 1e-10 {
131 return c > 0.0;
132 }
133 }
134 false }
136
137 #[must_use]
139 pub fn negate(&self) -> Root {
140 Root::new(self.coords.iter().map(|c| -c).collect())
141 }
142}
143
144impl fmt::Display for Root {
145 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146 write!(f, "(")?;
147 for (i, c) in self.coords.iter().enumerate() {
148 if i > 0 {
149 write!(f, ", ")?;
150 }
151 write!(f, "{:.2}", c)?;
152 }
153 write!(f, ")")
154 }
155}
156
157#[derive(Debug, Clone)]
173pub struct RootSystem {
174 rank: usize,
176
177 roots: Vec<Root>,
179
180 simple_roots: Vec<Root>,
182
183 cartan_matrix: Vec<Vec<i32>>,
185}
186
187impl RootSystem {
188 #[must_use]
209 pub fn type_a(n: usize) -> Self {
210 assert!(n >= 1, "Type A_n requires n >= 1");
211
212 let rank = n;
213
214 let mut simple_roots = Vec::new();
217 for i in 0..n {
218 let mut coords = vec![0.0; n + 1];
219 coords[i] = 1.0;
220 coords[i + 1] = -1.0;
221 simple_roots.push(Root::new(coords));
222 }
223
224 let mut positive_roots = Vec::new();
226 for i in 0..=n {
227 for j in (i + 1)..=n {
228 let mut coords = vec![0.0; n + 1];
229 coords[i] = 1.0;
230 coords[j] = -1.0;
231 positive_roots.push(Root::new(coords));
232 }
233 }
234
235 let mut roots = positive_roots.clone();
237 for root in &positive_roots {
238 roots.push(root.negate());
239 }
240
241 let cartan_matrix = simple_roots
243 .iter()
244 .map(|alpha_i| {
245 simple_roots
246 .iter()
247 .map(|alpha_j| alpha_i.cartan_integer(alpha_j))
248 .collect()
249 })
250 .collect();
251
252 Self {
253 rank,
254 roots,
255 simple_roots,
256 cartan_matrix,
257 }
258 }
259
260 #[must_use]
262 pub fn rank(&self) -> usize {
263 self.rank
264 }
265
266 #[must_use]
268 pub fn roots(&self) -> &[Root] {
269 &self.roots
270 }
271
272 #[must_use]
274 pub fn simple_roots(&self) -> &[Root] {
275 &self.simple_roots
276 }
277
278 #[must_use]
280 pub fn cartan_matrix(&self) -> &[Vec<i32>] {
281 &self.cartan_matrix
282 }
283
284 #[must_use]
286 pub fn num_roots(&self) -> usize {
287 self.roots.len()
288 }
289
290 #[must_use]
292 pub fn num_positive_roots(&self) -> usize {
293 self.roots.iter().filter(|r| r.is_positive()).count()
294 }
295
296 #[must_use]
298 pub fn positive_roots(&self) -> Vec<Root> {
299 self.roots
300 .iter()
301 .filter(|r| r.is_positive())
302 .cloned()
303 .collect()
304 }
305
306 #[must_use]
325 pub fn highest_root(&self) -> Root {
326 let n = self.rank;
330
331 let mut coords = vec![0.0; n + 1];
333
334 for simple_root in &self.simple_roots {
335 for (i, &coord) in simple_root.coords.iter().enumerate() {
336 coords[i] += coord;
337 }
338 }
339
340 Root::new(coords)
341 }
342
343 #[must_use]
345 pub fn contains_root(&self, root: &Root) -> bool {
346 self.roots.iter().any(|r| {
347 r.coords.len() == root.coords.len()
348 && r.coords
349 .iter()
350 .zip(root.coords.iter())
351 .all(|(a, b)| (a - b).abs() < 1e-10)
352 })
353 }
354
355 #[must_use]
357 pub fn weyl_reflection(&self, alpha: &Root, beta: &Root) -> Root {
358 alpha.reflect(beta)
359 }
360
361 #[must_use]
366 pub fn weyl_orbit(&self, weight: &Root) -> Vec<Root> {
367 let mut orbit = vec![weight.clone()];
368 let mut seen = HashSet::new();
369 seen.insert(format!("{:?}", weight.coords));
370
371 let mut queue = vec![weight.clone()];
372
373 while let Some(current) = queue.pop() {
374 for simple_root in &self.simple_roots {
375 let reflected = simple_root.reflect(¤t);
376 let key = format!("{:?}", reflected.coords);
377
378 if !seen.contains(&key) {
379 seen.insert(key);
380 orbit.push(reflected.clone());
381 queue.push(reflected);
382 }
383 }
384 }
385
386 orbit
387 }
388
389 #[must_use]
393 pub fn dimension(&self) -> usize {
394 self.rank + self.num_roots()
395 }
396
397 #[must_use]
399 pub fn is_dominant_weight(&self, weight: &Root) -> bool {
400 self.simple_roots
401 .iter()
402 .all(|alpha| weight.inner_product(alpha) >= -1e-10)
403 }
404
405 pub fn simple_root_expansion(&self, root: &Root) -> Option<Vec<i32>> {
420 if !self.contains_root(root) {
421 return None;
422 }
423
424 let coords = &root.coords;
430 let mut pos_idx: Option<usize> = None;
431 let mut neg_idx: Option<usize> = None;
432
433 for (idx, &val) in coords.iter().enumerate() {
434 if (val - 1.0).abs() < 1e-10 {
435 if pos_idx.is_some() {
436 return None;
438 }
439 pos_idx = Some(idx);
440 } else if (val + 1.0).abs() < 1e-10 {
441 if neg_idx.is_some() {
442 return None;
443 }
444 neg_idx = Some(idx);
445 } else if val.abs() > 1e-10 {
446 return None;
448 }
449 }
450
451 let (Some(i), Some(j)) = (pos_idx, neg_idx) else {
452 return None;
453 };
454
455 let mut coeffs = vec![0i32; self.rank];
459 let (min_idx, max_idx) = if i < j { (i, j) } else { (j, i) };
460 let sign = if i < j { 1 } else { -1 };
461
462 for k in min_idx..max_idx {
463 if k < self.rank {
464 coeffs[k] = sign;
465 }
466 }
467
468 Some(coeffs)
469 }
470}
471
472pub struct WeightLattice {
486 root_system: RootSystem,
487 fundamental_weights: Vec<Root>,
488}
489
490impl WeightLattice {
491 #[must_use]
493 pub fn from_root_system(root_system: RootSystem) -> Self {
494 let fundamental_weights = Self::compute_fundamental_weights(&root_system);
495 Self {
496 root_system,
497 fundamental_weights,
498 }
499 }
500
501 fn compute_fundamental_weights(rs: &RootSystem) -> Vec<Root> {
503 let n = rs.rank();
510 let dim = n + 1; let mut weights = Vec::new();
512
513 for i in 0..n {
514 let mut coords = vec![0.0; dim];
516 for j in 0..=i {
517 coords[j] = 1.0;
518 }
519
520 let sum: f64 = coords.iter().sum();
522 let mean = sum / (dim as f64);
523 for coord in &mut coords {
524 *coord -= mean;
525 }
526
527 weights.push(Root::new(coords));
528 }
529
530 weights
531 }
532
533 #[must_use]
535 pub fn fundamental_weights(&self) -> &[Root] {
536 &self.fundamental_weights
537 }
538
539 #[must_use]
541 pub fn dynkin_to_weight(&self, dynkin_labels: &[usize]) -> Root {
542 assert_eq!(dynkin_labels.len(), self.root_system.rank());
543
544 let mut weight_coords = vec![0.0; self.root_system.rank() + 1];
545 for (i, &m) in dynkin_labels.iter().enumerate() {
546 for (j, &w) in self.fundamental_weights[i].coords.iter().enumerate() {
547 weight_coords[j] += (m as f64) * w;
548 }
549 }
550
551 Root::new(weight_coords)
552 }
553
554 #[must_use]
556 pub fn root_system(&self) -> &RootSystem {
557 &self.root_system
558 }
559
560 #[must_use]
564 pub fn rho(&self) -> Root {
565 let mut rho_coords = vec![0.0; self.root_system.rank() + 1];
566 for omega in &self.fundamental_weights {
567 for (i, &coord) in omega.coords.iter().enumerate() {
568 rho_coords[i] += coord;
569 }
570 }
571 Root::new(rho_coords)
572 }
573
574 fn kostant_partition_function(&self, gamma: &Root) -> usize {
593 use std::collections::HashMap;
594
595 if gamma.coords.iter().all(|&x| x.abs() < 1e-10) {
597 return 1;
598 }
599
600 let mut memo: HashMap<(String, usize), usize> = HashMap::new();
603 let positive_roots: Vec<Root> = self.root_system.positive_roots();
604
605 Self::partition_helper_ordered(gamma, &positive_roots, 0, &mut memo)
606 }
607
608 fn partition_helper_ordered(
613 gamma: &Root,
614 positive_roots: &[Root],
615 start_idx: usize,
616 memo: &mut std::collections::HashMap<(String, usize), usize>,
617 ) -> usize {
618 if gamma.coords.iter().all(|&x| x.abs() < 1e-10) {
620 return 1;
621 }
622
623 if start_idx >= positive_roots.len() {
625 return 0;
626 }
627
628 let key = (WeightLattice::weight_key(&gamma.coords), start_idx);
630 if let Some(&result) = memo.get(&key) {
631 return result;
632 }
633
634 let mut has_large_negative = false;
636 for &coord in &gamma.coords {
637 if coord < -100.0 {
638 has_large_negative = true;
639 break;
640 }
641 }
642
643 if has_large_negative {
644 memo.insert(key, 0);
645 return 0;
646 }
647
648 let mut count = 0;
652 let alpha = &positive_roots[start_idx];
653
654 if memo.len() < 100_000 {
656 count += Self::partition_helper_ordered(gamma, positive_roots, start_idx + 1, memo);
657 }
658
659 let mut gamma_minus_k_alpha = gamma.clone();
661 for (i, &a) in alpha.coords.iter().enumerate() {
662 gamma_minus_k_alpha.coords[i] -= a;
663 }
664
665 let can_use = gamma_minus_k_alpha.coords.iter().all(|&x| x > -100.0);
667
668 if can_use && memo.len() < 100_000 {
669 count += Self::partition_helper_ordered(
671 &gamma_minus_k_alpha,
672 positive_roots,
673 start_idx,
674 memo,
675 );
676 }
677
678 memo.insert(key, count);
679 count
680 }
681
682 fn weyl_group_type_a(&self) -> Vec<(Vec<usize>, i32)> {
697 let n_plus_1 = self.root_system.rank() + 1;
698 let mut perms = Vec::new();
699
700 let mut current: Vec<usize> = (0..n_plus_1).collect();
702 Self::generate_permutations(&mut current, n_plus_1, &mut perms);
703
704 perms
705 }
706
707 fn generate_permutations(arr: &mut [usize], size: usize, result: &mut Vec<(Vec<usize>, i32)>) {
709 if size == 1 {
710 let perm = arr.to_vec();
711 let sign = Self::permutation_sign(&perm);
712 result.push((perm, sign));
713 return;
714 }
715
716 for i in 0..size {
717 Self::generate_permutations(arr, size - 1, result);
718
719 if size % 2 == 0 {
720 arr.swap(i, size - 1);
721 } else {
722 arr.swap(0, size - 1);
723 }
724 }
725 }
726
727 fn permutation_sign(perm: &[usize]) -> i32 {
729 let n = perm.len();
730 let mut sign = 1i32;
731
732 for i in 0..n {
733 for j in (i + 1)..n {
734 if perm[i] > perm[j] {
735 sign *= -1;
736 }
737 }
738 }
739
740 sign
741 }
742
743 fn weyl_action(&self, weight: &Root, permutation: &[usize]) -> Root {
745 let mut new_coords = vec![0.0; weight.coords.len()];
746 for (i, &pi) in permutation.iter().enumerate() {
747 new_coords[i] = weight.coords[pi];
748 }
749 Root::new(new_coords)
750 }
751
752 #[must_use]
781 pub fn kostant_multiplicity(&self, highest_weight: &Root, weight: &Root) -> usize {
782 let rho = self.rho();
783
784 let mut lambda_plus_rho = highest_weight.clone();
786 for (i, &r) in rho.coords.iter().enumerate() {
787 lambda_plus_rho.coords[i] += r;
788 }
789
790 let mut mu_plus_rho = weight.clone();
792 for (i, &r) in rho.coords.iter().enumerate() {
793 mu_plus_rho.coords[i] += r;
794 }
795
796 let weyl_group = self.weyl_group_type_a();
798 let mut multiplicity = 0i32; for (perm, sign) in weyl_group {
801 let w_lambda_plus_rho = self.weyl_action(&lambda_plus_rho, &perm);
803
804 let mut gamma = w_lambda_plus_rho.clone();
806 for (i, &mu) in mu_plus_rho.coords.iter().enumerate() {
807 gamma.coords[i] -= mu;
808 }
809
810 let p_gamma = self.kostant_partition_function(&gamma);
812
813 multiplicity += sign * (p_gamma as i32);
815 }
816
817 assert!(
819 multiplicity >= 0,
820 "Kostant multiplicity must be non-negative, got {}",
821 multiplicity
822 );
823 multiplicity as usize
824 }
825
826 #[must_use]
835 pub fn weyl_dimension(&self, highest_weight: &Root) -> usize {
836 let rho = self.rho();
837
838 let mut lambda_plus_rho = highest_weight.clone();
840 for (i, &r) in rho.coords.iter().enumerate() {
841 lambda_plus_rho.coords[i] += r;
842 }
843
844 let mut numerator = 1.0;
845 let mut denominator = 1.0;
846
847 for alpha in self.root_system.positive_roots() {
848 let num = lambda_plus_rho.inner_product(&alpha);
849 let denom = rho.inner_product(&alpha);
850
851 numerator *= num;
852 denominator *= denom;
853 }
854
855 (numerator / denominator).round() as usize
856 }
857
858 fn weight_key(coords: &[f64]) -> String {
863 let rounded: Vec<String> = coords.iter().map(|&x| format!("{:.10}", x)).collect();
864 format!("[{}]", rounded.join(", "))
865 }
866
867 #[must_use]
877 pub fn weights_of_irrep(&self, highest_weight: &Root) -> Vec<(Root, usize)> {
878 use std::collections::{HashMap, VecDeque};
879
880 let mut candidates: HashMap<String, Root> = HashMap::new();
881 let mut queue: VecDeque<Root> = VecDeque::new();
882
883 let hw_key = Self::weight_key(&highest_weight.coords);
885 candidates.insert(hw_key, highest_weight.clone());
886 queue.push_back(highest_weight.clone());
887
888 while let Some(weight) = queue.pop_front() {
891 for pos_root in self.root_system.positive_roots() {
892 let mut new_weight = weight.clone();
893 for (i, &a) in pos_root.coords.iter().enumerate() {
894 new_weight.coords[i] -= a;
895 }
896
897 let new_key = Self::weight_key(&new_weight.coords);
898
899 if candidates.contains_key(&new_key) {
901 continue;
902 }
903
904 let norm = new_weight.norm_squared();
906 let hw_norm = highest_weight.norm_squared();
907 if norm > 3.0 * hw_norm + 10.0 {
908 continue;
909 }
910
911 candidates.insert(new_key, new_weight.clone());
912 queue.push_back(new_weight);
913
914 if candidates.len() > 1000 {
916 break;
917 }
918 }
919
920 if candidates.len() > 1000 {
921 break;
922 }
923 }
924
925 let weyl_orbit = self.root_system.weyl_orbit(highest_weight);
928 for w in weyl_orbit {
929 let key = Self::weight_key(&w.coords);
930 candidates.insert(key, w);
931 }
932
933 let candidate_list: Vec<Root> = candidates.into_values().collect();
935 let mut result = Vec::new();
936
937 for weight in candidate_list.iter() {
938 let mult = self.kostant_multiplicity(highest_weight, weight);
939 if mult > 0 {
940 result.push((weight.clone(), mult));
941 }
942 }
943
944 result
945 }
946
947 #[must_use]
965 pub fn weight_diagram_string(&self, highest_weight: &Root) -> String {
966 if self.root_system.rank() != 2 {
967 return "Weight diagrams only implemented for rank 2".to_string();
968 }
969
970 let weights = self.weights_of_irrep(highest_weight);
971
972 let mut output = String::new();
973 writeln!(
974 &mut output,
975 "Weight diagram for highest weight {:?}",
976 highest_weight.coords
977 )
978 .expect("String formatting should not fail");
979 writeln!(&mut output, "Dimension: {}", weights.len())
980 .expect("String formatting should not fail");
981 output.push_str("Weights (multiplicity):\n");
982
983 for (weight, mult) in &weights {
984 writeln!(&mut output, " {} (×{})", weight, mult)
985 .expect("String formatting should not fail");
986 }
987
988 output
989 }
990}
991
992pub struct CartanSubalgebra {
1018 dimension: usize,
1020
1021 basis_matrices: Vec<Array2<Complex64>>,
1024
1025 matrix_size: usize,
1027}
1028
1029impl CartanSubalgebra {
1030 #[must_use]
1050 pub fn type_a(n: usize) -> Self {
1051 assert!(n >= 1, "Type A_n requires n >= 1");
1052
1053 let matrix_size = n + 1;
1054 let dimension = n;
1055
1056 let mut basis_matrices = Vec::new();
1057
1058 for i in 0..n {
1060 let mut h = Array2::<Complex64>::zeros((matrix_size, matrix_size));
1061 h[(i, i)] = Complex64::new(1.0, 0.0);
1062 h[(i + 1, i + 1)] = Complex64::new(-1.0, 0.0);
1063 basis_matrices.push(h);
1064 }
1065
1066 Self {
1067 dimension,
1068 basis_matrices,
1069 matrix_size,
1070 }
1071 }
1072
1073 #[must_use]
1075 pub fn dimension(&self) -> usize {
1076 self.dimension
1077 }
1078
1079 #[must_use]
1081 pub fn basis_matrices(&self) -> &[Array2<Complex64>] {
1082 &self.basis_matrices
1083 }
1084
1085 #[must_use]
1087 pub fn matrix_size(&self) -> usize {
1088 self.matrix_size
1089 }
1090
1091 #[must_use]
1104 pub fn evaluate_root(&self, root: &Root, coefficients: &[f64]) -> Complex64 {
1105 assert_eq!(
1106 coefficients.len(),
1107 self.dimension,
1108 "Coefficient vector must match Cartan dimension"
1109 );
1110 assert_eq!(
1111 root.rank(),
1112 self.matrix_size,
1113 "Root dimension must match matrix size"
1114 );
1115
1116 let mut h_diagonal = vec![Complex64::new(0.0, 0.0); self.matrix_size];
1122 for (i, &c) in coefficients.iter().enumerate() {
1123 h_diagonal[i] += Complex64::new(c, 0.0);
1124 h_diagonal[i + 1] -= Complex64::new(c, 0.0);
1125 }
1126
1127 root.coords
1129 .iter()
1130 .zip(&h_diagonal)
1131 .map(|(alpha_i, h_i)| h_i * alpha_i)
1132 .sum()
1133 }
1134
1135 #[must_use]
1157 pub fn project_matrix(&self, matrix: &Array2<Complex64>) -> Option<Vec<f64>> {
1158 assert_eq!(
1159 matrix.shape(),
1160 &[self.matrix_size, self.matrix_size],
1161 "Matrix must be {}×{}",
1162 self.matrix_size,
1163 self.matrix_size
1164 );
1165
1166 let n = self.dimension;
1168 let mut gram = vec![vec![0.0; n]; n];
1169 let mut rhs = vec![0.0; n];
1170
1171 for i in 0..n {
1172 for j in 0..n {
1173 let inner: Complex64 = self.basis_matrices[i]
1175 .iter()
1176 .zip(self.basis_matrices[j].iter())
1177 .map(|(h_i, h_j)| h_i.conj() * h_j)
1178 .sum();
1179 gram[i][j] = inner.re;
1180 }
1181
1182 let inner: Complex64 = matrix
1184 .iter()
1185 .zip(self.basis_matrices[i].iter())
1186 .map(|(m_ij, h_ij)| m_ij.conj() * h_ij)
1187 .sum();
1188 rhs[i] = inner.re;
1189 }
1190
1191 let mut coefficients = vec![0.0; n];
1193
1194 match n {
1195 1 => {
1196 coefficients[0] = rhs[0] / gram[0][0];
1197 Some(coefficients)
1198 }
1199 2 => {
1200 let det = gram[0][0] * gram[1][1] - gram[0][1] * gram[1][0];
1202 coefficients[0] = (rhs[0] * gram[1][1] - rhs[1] * gram[0][1]) / det;
1203 coefficients[1] = (gram[0][0] * rhs[1] - gram[1][0] * rhs[0]) / det;
1204 Some(coefficients)
1205 }
1206 _ => {
1207 None
1209 }
1210 }
1211 }
1212
1213 #[must_use]
1217 pub fn from_coefficients(&self, coefficients: &[f64]) -> Array2<Complex64> {
1218 assert_eq!(
1219 coefficients.len(),
1220 self.dimension,
1221 "Must provide {} coefficients",
1222 self.dimension
1223 );
1224
1225 let mut result = Array2::<Complex64>::zeros((self.matrix_size, self.matrix_size));
1226
1227 for (&c_i, h_i) in coefficients.iter().zip(&self.basis_matrices) {
1228 result = result + h_i * c_i;
1229 }
1230
1231 result
1232 }
1233
1234 #[must_use]
1238 pub fn contains(&self, matrix: &Array2<Complex64>, tolerance: f64) -> bool {
1239 assert_eq!(
1240 matrix.shape(),
1241 &[self.matrix_size, self.matrix_size],
1242 "Matrix must be {}×{}",
1243 self.matrix_size,
1244 self.matrix_size
1245 );
1246
1247 for i in 0..self.matrix_size {
1249 for j in 0..self.matrix_size {
1250 if i != j && matrix[(i, j)].norm() > tolerance {
1251 return false;
1252 }
1253 }
1254 }
1255
1256 let trace: Complex64 = (0..self.matrix_size).map(|i| matrix[(i, i)]).sum();
1258
1259 trace.norm() < tolerance
1260 }
1261
1262 #[must_use]
1276 pub fn killing_form(&self, coeffs1: &[f64], coeffs2: &[f64]) -> f64 {
1277 assert_eq!(coeffs1.len(), self.dimension);
1278 assert_eq!(coeffs2.len(), self.dimension);
1279
1280 let n_plus_1 = self.matrix_size as f64;
1287
1288 coeffs1
1289 .iter()
1290 .zip(coeffs2)
1291 .map(|(c1, c2)| 2.0 * n_plus_1 * 2.0 * c1 * c2)
1292 .sum()
1293 }
1294
1295 #[must_use]
1300 pub fn dual_basis(&self) -> Vec<Root> {
1301 let mut dual_roots = Vec::new();
1302
1303 for i in 0..self.dimension {
1304 let mut coords = vec![0.0; self.matrix_size];
1306 coords[i] = 1.0;
1307 coords[i + 1] = -1.0;
1308 dual_roots.push(Root::new(coords));
1309 }
1310
1311 dual_roots
1312 }
1313}
1314
1315#[derive(Debug, Clone)]
1355pub struct WeylChamber {
1356 root_system: RootSystem,
1358
1359 simple_roots: Vec<Root>,
1361}
1362
1363impl WeylChamber {
1364 #[must_use]
1373 pub fn fundamental(root_system: &RootSystem) -> Self {
1374 Self {
1375 root_system: root_system.clone(),
1376 simple_roots: root_system.simple_roots.clone(),
1377 }
1378 }
1379
1380 #[must_use]
1391 pub fn contains(&self, weight: &Root, strict: bool) -> bool {
1392 assert_eq!(weight.rank(), self.root_system.rank + 1);
1394
1395 for alpha in &self.simple_roots {
1396 let pairing = weight.inner_product(alpha);
1397
1398 if strict {
1399 if pairing <= 1e-10 {
1400 return false;
1401 }
1402 } else if pairing < -1e-10 {
1403 return false;
1404 }
1405 }
1406
1407 true
1408 }
1409
1410 #[must_use]
1420 pub fn project(&self, weight: &Root) -> Root {
1421 let mut current = weight.clone();
1422
1423 let max_iterations = 100;
1425 let mut iterations = 0;
1426
1427 while iterations < max_iterations {
1428 let mut found_violation = false;
1429
1430 for alpha in &self.simple_roots {
1431 let pairing = current.inner_product(alpha);
1432
1433 if pairing < -1e-10 {
1434 current = alpha.reflect(¤t);
1436 found_violation = true;
1437 }
1438 }
1439
1440 if !found_violation {
1441 break;
1442 }
1443
1444 iterations += 1;
1445 }
1446
1447 if iterations >= max_iterations {
1448 eprintln!(
1449 "Warning: WeylChamber::project did not converge in {} iterations",
1450 max_iterations
1451 );
1452 }
1453
1454 current
1455 }
1456
1457 #[must_use]
1459 pub fn simple_roots(&self) -> &[Root] {
1460 &self.simple_roots
1461 }
1462}
1463
1464#[derive(Debug, Clone)]
1501pub struct Alcove {
1502 root_system: RootSystem,
1504
1505 simple_roots: Vec<Root>,
1507
1508 highest_root: Root,
1510
1511 level: f64,
1513}
1514
1515impl Alcove {
1516 #[must_use]
1523 pub fn fundamental(root_system: &RootSystem) -> Self {
1524 let simple_roots = root_system.simple_roots.clone();
1525 let highest_root = root_system.highest_root();
1526
1527 Self {
1528 root_system: root_system.clone(),
1529 simple_roots,
1530 highest_root,
1531 level: 1.0,
1532 }
1533 }
1534
1535 #[must_use]
1542 pub fn at_level(root_system: &RootSystem, level: f64) -> Self {
1543 assert!(level > 0.0, "Level must be positive");
1544
1545 let simple_roots = root_system.simple_roots.clone();
1546 let highest_root = root_system.highest_root();
1547
1548 Self {
1549 root_system: root_system.clone(),
1550 simple_roots,
1551 highest_root,
1552 level,
1553 }
1554 }
1555
1556 #[must_use]
1567 pub fn contains(&self, weight: &Root, strict: bool) -> bool {
1568 assert_eq!(weight.rank(), self.root_system.rank + 1);
1570
1571 for alpha in &self.simple_roots {
1573 let pairing = weight.inner_product(alpha);
1574
1575 if strict {
1576 if pairing <= 1e-10 {
1577 return false;
1578 }
1579 } else if pairing < -1e-10 {
1580 return false;
1581 }
1582 }
1583
1584 let pairing_highest = weight.inner_product(&self.highest_root);
1586
1587 if strict {
1588 if pairing_highest >= self.level - 1e-10 {
1589 return false;
1590 }
1591 } else if pairing_highest > self.level + 1e-10 {
1592 return false;
1593 }
1594
1595 true
1596 }
1597
1598 #[must_use]
1600 pub fn level(&self) -> f64 {
1601 self.level
1602 }
1603
1604 #[must_use]
1606 pub fn highest_root(&self) -> &Root {
1607 &self.highest_root
1608 }
1609
1610 #[must_use]
1619 pub fn vertices(&self) -> Vec<Root> {
1620 if self.level != 1.0 {
1621 return vec![];
1625 }
1626
1627 let mut vertices = vec![Root::new(vec![0.0; self.root_system.rank + 1])];
1631
1632 let n = self.root_system.rank;
1637
1638 for i in 1..=n {
1639 let mut coords = vec![0.0; n + 1];
1640
1641 for j in 0..i {
1643 coords[j] = (n + 1 - i) as f64 / (n + 1) as f64;
1644 }
1645 for j in i..=n {
1646 coords[j] = -(i as f64) / (n + 1) as f64;
1647 }
1648
1649 vertices.push(Root::new(coords));
1650 }
1651
1652 vertices
1653 }
1654}
1655
1656#[cfg(test)]
1657mod tests {
1658 use super::*;
1659
1660 #[test]
1661 fn test_root_operations() {
1662 let alpha = Root::new(vec![1.0, -1.0, 0.0]);
1663 let beta = Root::new(vec![0.0, 1.0, -1.0]);
1664
1665 assert_eq!(alpha.rank(), 3);
1666 assert!((alpha.norm_squared() - 2.0).abs() < 1e-10);
1667 assert!((alpha.inner_product(&beta) + 1.0).abs() < 1e-10);
1668 assert!(alpha.is_positive());
1669 assert!(!alpha.negate().is_positive());
1670 }
1671
1672 #[test]
1673 fn test_weyl_reflection() {
1674 let alpha = Root::new(vec![1.0, -1.0]);
1675 let beta = Root::new(vec![0.5, 0.5]);
1676
1677 let reflected = alpha.reflect(&beta);
1678 assert!((reflected.coords[0] - 0.5).abs() < 1e-10);
1681 assert!((reflected.coords[1] - 0.5).abs() < 1e-10);
1682 }
1683
1684 #[test]
1685 fn test_cartan_integer() {
1686 let alpha = Root::new(vec![1.0, -1.0, 0.0]);
1687 let beta = Root::new(vec![0.0, 1.0, -1.0]);
1688
1689 assert_eq!(alpha.cartan_integer(&beta), -1);
1691 assert_eq!(beta.cartan_integer(&alpha), -1);
1692 }
1693
1694 #[test]
1695 fn test_type_a1_su2() {
1696 let rs = RootSystem::type_a(1);
1697
1698 assert_eq!(rs.rank(), 1);
1699 assert_eq!(rs.num_roots(), 2); assert_eq!(rs.num_positive_roots(), 1);
1701 assert_eq!(rs.dimension(), 3); let cartan = rs.cartan_matrix();
1704 assert_eq!(cartan.len(), 1);
1705 assert_eq!(cartan[0][0], 2); }
1707
1708 #[test]
1709 fn test_type_a2_su3() {
1710 let rs = RootSystem::type_a(2);
1711
1712 assert_eq!(rs.rank(), 2);
1713 assert_eq!(rs.num_roots(), 6); assert_eq!(rs.num_positive_roots(), 3);
1715 assert_eq!(rs.dimension(), 8); let cartan = rs.cartan_matrix();
1718 assert_eq!(cartan.len(), 2);
1719 assert_eq!(cartan[0][0], 2);
1720 assert_eq!(cartan[1][1], 2);
1721 assert_eq!(cartan[0][1], -1); assert_eq!(cartan[1][0], -1);
1723 }
1724
1725 #[test]
1726 fn test_simple_roots_su3() {
1727 let rs = RootSystem::type_a(2);
1728 let simple = rs.simple_roots();
1729
1730 assert_eq!(simple.len(), 2);
1731
1732 assert!((simple[0].coords[0] - 1.0).abs() < 1e-10);
1734 assert!((simple[0].coords[1] + 1.0).abs() < 1e-10);
1735 assert!((simple[0].coords[2]).abs() < 1e-10);
1736
1737 assert!((simple[1].coords[0]).abs() < 1e-10);
1739 assert!((simple[1].coords[1] - 1.0).abs() < 1e-10);
1740 assert!((simple[1].coords[2] + 1.0).abs() < 1e-10);
1741 }
1742
1743 #[test]
1744 fn test_positive_roots_su3() {
1745 let rs = RootSystem::type_a(2);
1746 let positive = rs.positive_roots();
1747
1748 assert_eq!(positive.len(), 3);
1749 }
1751
1752 #[test]
1753 fn test_dominant_weight() {
1754 let rs = RootSystem::type_a(2);
1755
1756 let weight1 = Root::new(vec![1.0, 0.0, 0.0]);
1758 assert!(rs.is_dominant_weight(&weight1));
1759
1760 let weight2 = Root::new(vec![-1.0, 0.0, 0.0]);
1762 assert!(!rs.is_dominant_weight(&weight2));
1763 }
1764
1765 #[test]
1766 fn test_weight_lattice_su2() {
1767 let rs = RootSystem::type_a(1);
1768 let wl = WeightLattice::from_root_system(rs);
1769
1770 assert_eq!(wl.fundamental_weights().len(), 1);
1771
1772 let weight = wl.dynkin_to_weight(&[2]);
1774 assert_eq!(weight.rank(), 2);
1775 }
1776
1777 #[test]
1778 fn test_weight_lattice_su3() {
1779 let rs = RootSystem::type_a(2);
1780 let wl = WeightLattice::from_root_system(rs);
1781
1782 assert_eq!(wl.fundamental_weights().len(), 2);
1783
1784 let fund = wl.dynkin_to_weight(&[1, 0]);
1786 assert_eq!(fund.rank(), 3);
1787
1788 let adj = wl.dynkin_to_weight(&[1, 1]);
1790 assert_eq!(adj.rank(), 3);
1791 }
1792
1793 #[test]
1794 fn test_weyl_orbit_su2() {
1795 let rs = RootSystem::type_a(1);
1796 let weight = Root::new(vec![1.0, 0.0]);
1797
1798 let orbit = rs.weyl_orbit(&weight);
1799 assert_eq!(orbit.len(), 2); }
1801
1802 #[test]
1803 fn test_weyl_dimension_formula() {
1804 let rs = RootSystem::type_a(2); let wl = WeightLattice::from_root_system(rs);
1806
1807 let fund = wl.dynkin_to_weight(&[1, 0]);
1810 assert_eq!(wl.weyl_dimension(&fund), 3);
1811
1812 let antifund = wl.dynkin_to_weight(&[0, 1]);
1814 assert_eq!(wl.weyl_dimension(&antifund), 3);
1815
1816 let adj = wl.dynkin_to_weight(&[1, 1]);
1818 assert_eq!(wl.weyl_dimension(&adj), 8);
1819
1820 let sym2 = wl.dynkin_to_weight(&[2, 0]);
1822 assert_eq!(wl.weyl_dimension(&sym2), 6);
1823
1824 let sym2_bar = wl.dynkin_to_weight(&[0, 2]);
1826 assert_eq!(wl.weyl_dimension(&sym2_bar), 6);
1827
1828 let rs2 = RootSystem::type_a(1); let wl2 = WeightLattice::from_root_system(rs2);
1831 let trivial = wl2.dynkin_to_weight(&[0]);
1832 assert_eq!(wl2.weyl_dimension(&trivial), 1);
1833
1834 let spin1 = wl2.dynkin_to_weight(&[2]);
1836 assert_eq!(wl2.weyl_dimension(&spin1), 3);
1837 }
1838
1839 #[test]
1840 fn test_weights_of_irrep_su3_fundamental() {
1841 let rs = RootSystem::type_a(2);
1842 let wl = WeightLattice::from_root_system(rs);
1843
1844 let highest = wl.dynkin_to_weight(&[1, 0]);
1846 let weights = wl.weights_of_irrep(&highest);
1847
1848 let expected_dim = wl.weyl_dimension(&highest);
1850 assert_eq!(expected_dim, 3, "Fundamental rep has dimension 3");
1851 assert_eq!(
1852 weights.len(),
1853 3,
1854 "Should find exactly 3 weights, found {}",
1855 weights.len()
1856 );
1857 }
1858
1859 #[test]
1860 fn test_weights_of_irrep_su3_adjoint() {
1861 let rs = RootSystem::type_a(2);
1862 let wl = WeightLattice::from_root_system(rs);
1863
1864 let highest = wl.dynkin_to_weight(&[1, 1]);
1866 let weights = wl.weights_of_irrep(&highest);
1867
1868 let dim = wl.weyl_dimension(&highest);
1870 assert_eq!(dim, 8, "Adjoint rep has dimension 8");
1871
1872 let total_dim: usize = weights.iter().map(|(_, m)| m).sum();
1875 assert_eq!(
1876 total_dim, 8,
1877 "Total dimension (sum of multiplicities) should be 8"
1878 );
1879 }
1880
1881 #[test]
1882 fn test_partition_function_basics() {
1883 let rs = RootSystem::type_a(1); let wl = WeightLattice::from_root_system(rs);
1886
1887 let zero = Root::new(vec![0.0, 0.0]);
1890 assert_eq!(wl.kostant_partition_function(&zero), 1, "P(0) = 1");
1891
1892 let alpha = Root::new(vec![1.0, -1.0]);
1894 assert_eq!(wl.kostant_partition_function(&alpha), 1, "P(α) = 1");
1895
1896 let two_alpha = Root::new(vec![2.0, -2.0]);
1898 assert_eq!(wl.kostant_partition_function(&two_alpha), 1, "P(2α) = 1");
1899
1900 let neg_alpha = Root::new(vec![-1.0, 1.0]);
1902 assert_eq!(wl.kostant_partition_function(&neg_alpha), 0, "P(-α) = 0");
1903 }
1904
1905 #[test]
1906 fn test_partition_function_su3() {
1907 let rs = RootSystem::type_a(2);
1909 let wl = WeightLattice::from_root_system(rs);
1910
1911 let zero = Root::new(vec![0.0, 0.0, 0.0]);
1913 assert_eq!(wl.kostant_partition_function(&zero), 1, "P(0) = 1");
1914
1915 let alpha1 = Root::new(vec![1.0, -1.0, 0.0]);
1917 let p_alpha1 = wl.kostant_partition_function(&alpha1);
1918 eprintln!("P(α₁) = {}", p_alpha1);
1919 assert_eq!(p_alpha1, 1, "P(α₁) = 1");
1920
1921 let alpha1_plus_alpha2 = Root::new(vec![1.0, 0.0, -1.0]);
1924 let p_sum = wl.kostant_partition_function(&alpha1_plus_alpha2);
1925 eprintln!("P(α₁+α₂) = {}", p_sum);
1926 }
1928
1929 #[test]
1930 fn test_kostant_dimension_invariant() {
1931 let rs = RootSystem::type_a(2); let wl = WeightLattice::from_root_system(rs);
1935
1936 let fund = wl.dynkin_to_weight(&[1, 0]);
1938 let fund_weights = wl.weights_of_irrep(&fund);
1939 eprintln!("Fundamental [1,0] weights:");
1940 for (w, m) in &fund_weights {
1941 eprintln!(" {:?} mult {}", w.coords, m);
1942 }
1943 let fund_dim: usize = fund_weights.iter().map(|(_, m)| m).sum();
1944 eprintln!("Total dimension: {} (expected 3)", fund_dim);
1945 assert_eq!(fund_dim, 3, "Fundamental rep dimension invariant");
1946
1947 let adj = wl.dynkin_to_weight(&[1, 1]);
1949 let adj_weights = wl.weights_of_irrep(&adj);
1950 eprintln!("\nAdjoint [1,1] weights:");
1951 for (w, m) in &adj_weights {
1952 eprintln!(" {:?} mult {}", w.coords, m);
1953 }
1954 let adj_dim: usize = adj_weights.iter().map(|(_, m)| m).sum();
1955 eprintln!("Total dimension: {} (expected 8)", adj_dim);
1956 assert_eq!(adj_dim, 8, "Adjoint rep dimension invariant");
1957
1958 let anti = wl.dynkin_to_weight(&[0, 1]);
1960 let anti_weights = wl.weights_of_irrep(&anti);
1961 let anti_dim: usize = anti_weights.iter().map(|(_, m)| m).sum();
1962 assert_eq!(anti_dim, 3, "Antifundamental rep dimension invariant");
1963 }
1964
1965 #[test]
1966 fn test_weight_diagram_string_su3() {
1967 let rs = RootSystem::type_a(2);
1968 let wl = WeightLattice::from_root_system(rs);
1969
1970 let highest = wl.dynkin_to_weight(&[1, 0]);
1971 let diagram = wl.weight_diagram_string(&highest);
1972
1973 assert!(diagram.contains("Weight diagram"));
1975 assert!(diagram.contains("Dimension"));
1976 assert!(diagram.contains("Weights"));
1977 }
1978
1979 #[test]
1980 fn test_weight_diagram_rank1_not_supported() {
1981 let rs = RootSystem::type_a(1);
1982 let wl = WeightLattice::from_root_system(rs);
1983
1984 let highest = wl.dynkin_to_weight(&[2]);
1985 let diagram = wl.weight_diagram_string(&highest);
1986
1987 assert!(diagram.contains("only implemented for rank 2"));
1988 }
1989
1990 #[test]
1993 fn test_cartan_subalgebra_su2() {
1994 let cartan = CartanSubalgebra::type_a(1);
1995
1996 assert_eq!(cartan.dimension(), 1);
1997 assert_eq!(cartan.matrix_size(), 2);
1998 assert_eq!(cartan.basis_matrices().len(), 1);
1999
2000 let h1 = &cartan.basis_matrices()[0];
2002 assert!((h1[(0, 0)].re - 1.0).abs() < 1e-10);
2003 assert!((h1[(1, 1)].re + 1.0).abs() < 1e-10);
2004 assert!(h1[(0, 1)].norm() < 1e-10);
2005 assert!(h1[(1, 0)].norm() < 1e-10);
2006 }
2007
2008 #[test]
2009 fn test_cartan_subalgebra_su3() {
2010 let cartan = CartanSubalgebra::type_a(2);
2011
2012 assert_eq!(cartan.dimension(), 2);
2013 assert_eq!(cartan.matrix_size(), 3);
2014 assert_eq!(cartan.basis_matrices().len(), 2);
2015
2016 let h1 = &cartan.basis_matrices()[0];
2018 assert!((h1[(0, 0)].re - 1.0).abs() < 1e-10);
2019 assert!((h1[(1, 1)].re + 1.0).abs() < 1e-10);
2020 assert!(h1[(2, 2)].norm() < 1e-10);
2021
2022 let h2 = &cartan.basis_matrices()[1];
2024 assert!(h2[(0, 0)].norm() < 1e-10);
2025 assert!((h2[(1, 1)].re - 1.0).abs() < 1e-10);
2026 assert!((h2[(2, 2)].re + 1.0).abs() < 1e-10);
2027 }
2028
2029 #[test]
2030 fn test_cartan_basis_commutes() {
2031 let cartan = CartanSubalgebra::type_a(2);
2032
2033 let h1 = &cartan.basis_matrices()[0];
2035 let h2 = &cartan.basis_matrices()[1];
2036
2037 let commutator = h1.dot(h2) - h2.dot(h1);
2038
2039 for val in commutator.iter() {
2040 assert!(val.norm() < 1e-10, "Cartan basis elements must commute");
2041 }
2042 }
2043
2044 #[test]
2045 fn test_cartan_dual_basis() {
2046 let cartan = CartanSubalgebra::type_a(2);
2047 let dual = cartan.dual_basis();
2048
2049 assert_eq!(dual.len(), 2);
2050
2051 assert!((dual[0].coords[0] - 1.0).abs() < 1e-10);
2053 assert!((dual[0].coords[1] + 1.0).abs() < 1e-10);
2054 assert!(dual[0].coords[2].abs() < 1e-10);
2055
2056 assert!(dual[1].coords[0].abs() < 1e-10);
2058 assert!((dual[1].coords[1] - 1.0).abs() < 1e-10);
2059 assert!((dual[1].coords[2] + 1.0).abs() < 1e-10);
2060
2061 let rs = RootSystem::type_a(2);
2063 let simple_roots = rs.simple_roots();
2064
2065 for (d, s) in dual.iter().zip(simple_roots) {
2066 for (dc, sc) in d.coords.iter().zip(&s.coords) {
2067 assert!((dc - sc).abs() < 1e-10);
2068 }
2069 }
2070 }
2071
2072 #[test]
2073 fn test_cartan_evaluate_root() {
2074 let cartan = CartanSubalgebra::type_a(1);
2075
2076 let root = Root::new(vec![1.0, -1.0]);
2079
2080 let result = cartan.evaluate_root(&root, &[1.0]);
2082 assert!((result.re - 2.0).abs() < 1e-10);
2083 assert!(result.im.abs() < 1e-10);
2084
2085 let result2 = cartan.evaluate_root(&root, &[0.5]);
2086 assert!((result2.re - 1.0).abs() < 1e-10);
2087 }
2088
2089 #[test]
2090 fn test_cartan_from_coefficients() {
2091 let cartan = CartanSubalgebra::type_a(2);
2092
2093 let h = cartan.from_coefficients(&[2.0, 3.0]);
2095
2096 assert!((h[(0, 0)].re - 2.0).abs() < 1e-10);
2098 assert!((h[(1, 1)].re - 1.0).abs() < 1e-10);
2099 assert!((h[(2, 2)].re + 3.0).abs() < 1e-10);
2100
2101 let trace: Complex64 = (0..3).map(|i| h[(i, i)]).sum();
2103 assert!(trace.norm() < 1e-10);
2104 }
2105
2106 #[test]
2107 fn test_cartan_project_matrix() {
2108 let cartan = CartanSubalgebra::type_a(2);
2109
2110 let mut mat = Array2::<Complex64>::zeros((3, 3));
2112 mat[(0, 0)] = Complex64::new(1.0, 0.0);
2113 mat[(1, 1)] = Complex64::new(-0.5, 0.0);
2114 mat[(2, 2)] = Complex64::new(-0.5, 0.0);
2115
2116 let coeffs = cartan
2117 .project_matrix(&mat)
2118 .expect("Rank 2 should be supported");
2119 assert_eq!(coeffs.len(), 2);
2120
2121 let reconstructed = cartan.from_coefficients(&coeffs);
2123
2124 for i in 0..3 {
2126 let diff = (mat[(i, i)] - reconstructed[(i, i)]).norm();
2127 assert!(
2128 diff < 1e-10,
2129 "Diagonal projection should be exact: diff at ({},{}) = {}",
2130 i,
2131 i,
2132 diff
2133 );
2134 }
2135 }
2136
2137 #[test]
2138 fn test_cartan_contains() {
2139 let cartan = CartanSubalgebra::type_a(2);
2140
2141 let mut h = Array2::<Complex64>::zeros((3, 3));
2143 h[(0, 0)] = Complex64::new(1.0, 0.0);
2144 h[(1, 1)] = Complex64::new(-0.5, 0.0);
2145 h[(2, 2)] = Complex64::new(-0.5, 0.0);
2146
2147 assert!(cartan.contains(&h, 1e-6));
2148
2149 let mut not_h = Array2::<Complex64>::zeros((3, 3));
2151 not_h[(0, 1)] = Complex64::new(1.0, 0.0);
2152
2153 assert!(!cartan.contains(¬_h, 1e-6));
2154
2155 let mut not_traceless = Array2::<Complex64>::zeros((3, 3));
2157 not_traceless[(0, 0)] = Complex64::new(1.0, 0.0);
2158
2159 assert!(!cartan.contains(¬_traceless, 1e-6));
2160 }
2161
2162 #[test]
2163 fn test_cartan_killing_form() {
2164 let cartan = CartanSubalgebra::type_a(1);
2165
2166 let killing = cartan.killing_form(&[1.0], &[1.0]);
2168 assert!((killing - 8.0).abs() < 1e-10);
2169
2170 let killing_zero = cartan.killing_form(&[1.0], &[0.0]);
2172 assert!(killing_zero.abs() < 1e-10);
2173
2174 let k12 = cartan.killing_form(&[1.0], &[2.0]);
2176 let k21 = cartan.killing_form(&[2.0], &[1.0]);
2177 assert!((k12 - k21).abs() < 1e-10);
2178 }
2179
2180 #[test]
2181 fn test_cartan_killing_form_su3() {
2182 let cartan = CartanSubalgebra::type_a(2);
2183
2184 let k11 = cartan.killing_form(&[1.0, 0.0], &[1.0, 0.0]);
2186 assert!((k11 - 12.0).abs() < 1e-10);
2187
2188 let k22 = cartan.killing_form(&[0.0, 1.0], &[0.0, 1.0]);
2189 assert!((k22 - 12.0).abs() < 1e-10);
2190
2191 let k12 = cartan.killing_form(&[1.0, 0.0], &[0.0, 1.0]);
2193 assert!(k12.abs() < 1e-10);
2194 }
2195
2196 #[test]
2197 fn test_weyl_chamber_contains_su2() {
2198 let rs = RootSystem::type_a(1); let chamber = WeylChamber::fundamental(&rs);
2200
2201 let dominant = Root::new(vec![1.0, 0.0]);
2206 assert!(chamber.contains(&dominant, false));
2207 assert!(chamber.contains(&dominant, true));
2208
2209 let non_dominant = Root::new(vec![-1.0, 0.0]);
2211 assert!(!chamber.contains(&non_dominant, false));
2212
2213 let boundary = Root::new(vec![0.0, 0.0]);
2215 assert!(chamber.contains(&boundary, false)); assert!(!chamber.contains(&boundary, true)); }
2218
2219 #[test]
2220 fn test_weyl_chamber_contains_su3() {
2221 let rs = RootSystem::type_a(2); let chamber = WeylChamber::fundamental(&rs);
2223
2224 let omega1 = Root::new(vec![2.0 / 3.0, -1.0 / 3.0, -1.0 / 3.0]);
2229 assert!(chamber.contains(&omega1, false));
2230
2231 let neg = Root::new(vec![-1.0, 0.0, 1.0]);
2233 assert!(!chamber.contains(&neg, false));
2234
2235 let origin = Root::new(vec![0.0, 0.0, 0.0]);
2237 assert!(chamber.contains(&origin, false));
2238 assert!(!chamber.contains(&origin, true));
2239 }
2240
2241 #[test]
2242 fn test_weyl_chamber_project() {
2243 let rs = RootSystem::type_a(1); let chamber = WeylChamber::fundamental(&rs);
2245
2246 let dominant = Root::new(vec![1.0, -1.0]);
2248 let projected = chamber.project(&dominant);
2249 assert!((projected.coords[0] - dominant.coords[0]).abs() < 1e-10);
2250 assert!((projected.coords[1] - dominant.coords[1]).abs() < 1e-10);
2251
2252 let non_dominant = Root::new(vec![-1.0, 1.0]);
2254 let projected = chamber.project(&non_dominant);
2255
2256 assert!(chamber.contains(&projected, false));
2258 assert!((projected.coords[0] - 1.0).abs() < 1e-10);
2259 assert!((projected.coords[1] + 1.0).abs() < 1e-10);
2260 }
2261
2262 #[test]
2263 fn test_weyl_chamber_project_su3() {
2264 let rs = RootSystem::type_a(2); let chamber = WeylChamber::fundamental(&rs);
2266
2267 let lambda = Root::new(vec![-1.0, 2.0, -1.0]);
2269 let projected = chamber.project(&lambda);
2270
2271 assert!(chamber.contains(&projected, false));
2273
2274 let norm_before = lambda.norm_squared();
2276 let norm_after = projected.norm_squared();
2277 assert!((norm_before - norm_after).abs() < 1e-8);
2278 }
2279
2280 #[test]
2281 fn test_alcove_contains_su2() {
2282 let rs = RootSystem::type_a(1); let alcove = Alcove::fundamental(&rs);
2284
2285 let inside = Root::new(vec![0.4, -0.4]);
2290 assert!(alcove.contains(&inside, false));
2293
2294 let outside = Root::new(vec![1.0, -1.0]);
2296 assert!(!alcove.contains(&outside, false));
2298
2299 let origin = Root::new(vec![0.0, 0.0]);
2301 assert!(alcove.contains(&origin, false));
2302 assert!(!alcove.contains(&origin, true)); }
2304
2305 #[test]
2306 fn test_alcove_vertices_su2() {
2307 let rs = RootSystem::type_a(1); let alcove = Alcove::fundamental(&rs);
2309
2310 let vertices = alcove.vertices();
2311
2312 assert_eq!(vertices.len(), 2);
2314
2315 assert!(vertices[0].coords[0].abs() < 1e-10);
2317 assert!(vertices[0].coords[1].abs() < 1e-10);
2318
2319 assert!((vertices[1].coords[0] - 0.5).abs() < 1e-10);
2321 assert!((vertices[1].coords[1] + 0.5).abs() < 1e-10);
2322 }
2323
2324 #[test]
2325 fn test_alcove_vertices_su3() {
2326 let rs = RootSystem::type_a(2); let alcove = Alcove::fundamental(&rs);
2328
2329 let vertices = alcove.vertices();
2330
2331 assert_eq!(vertices.len(), 3);
2333
2334 for &coord in &vertices[0].coords {
2336 assert!(coord.abs() < 1e-10);
2337 }
2338
2339 for vertex in &vertices {
2341 assert!(alcove.contains(vertex, false));
2342 }
2343 }
2344
2345 #[test]
2346 fn test_alcove_at_level() {
2347 let rs = RootSystem::type_a(1); let alcove_k2 = Alcove::at_level(&rs, 2.0);
2349
2350 assert_eq!(alcove_k2.level(), 2.0);
2351
2352 let inside = Root::new(vec![0.8, -0.8]);
2354 assert!(alcove_k2.contains(&inside, false));
2356
2357 let outside = Root::new(vec![1.5, -1.5]);
2358 assert!(!alcove_k2.contains(&outside, false));
2360 }
2361
2362 #[test]
2363 fn test_alcove_highest_root() {
2364 let rs = RootSystem::type_a(2); let alcove = Alcove::fundamental(&rs);
2366
2367 let theta = alcove.highest_root();
2368
2369 assert!((theta.coords[0] - 1.0).abs() < 1e-10);
2371 assert!(theta.coords[1].abs() < 1e-10);
2372 assert!((theta.coords[2] + 1.0).abs() < 1e-10);
2373
2374 let theta_norm = theta.norm_squared();
2376 for root in rs.positive_roots() {
2377 assert!(root.norm_squared() <= theta_norm + 1e-10);
2378 }
2379 }
2380
2381 #[test]
2384 fn test_simple_root_expansion_su2() {
2385 let rs = RootSystem::type_a(1); let alpha1 = Root::new(vec![1.0, -1.0]);
2392 let coeffs = rs.simple_root_expansion(&alpha1);
2393 assert_eq!(coeffs, Some(vec![1]));
2394
2395 let neg_alpha1 = Root::new(vec![-1.0, 1.0]);
2397 let coeffs_neg = rs.simple_root_expansion(&neg_alpha1);
2398 assert_eq!(coeffs_neg, Some(vec![-1]));
2399 }
2400
2401 #[test]
2402 fn test_simple_root_expansion_su3() {
2403 let rs = RootSystem::type_a(2); let alpha1 = Root::new(vec![1.0, -1.0, 0.0]);
2412 assert_eq!(rs.simple_root_expansion(&alpha1), Some(vec![1, 0]));
2413
2414 let alpha2 = Root::new(vec![0.0, 1.0, -1.0]);
2416 assert_eq!(rs.simple_root_expansion(&alpha2), Some(vec![0, 1]));
2417
2418 let alpha1_plus_alpha2 = Root::new(vec![1.0, 0.0, -1.0]);
2420 assert_eq!(
2421 rs.simple_root_expansion(&alpha1_plus_alpha2),
2422 Some(vec![1, 1])
2423 );
2424
2425 let neg_alpha1 = Root::new(vec![-1.0, 1.0, 0.0]);
2427 assert_eq!(rs.simple_root_expansion(&neg_alpha1), Some(vec![-1, 0]));
2428
2429 let neg_highest = Root::new(vec![-1.0, 0.0, 1.0]);
2430 assert_eq!(rs.simple_root_expansion(&neg_highest), Some(vec![-1, -1]));
2431 }
2432
2433 #[test]
2434 fn test_simple_root_expansion_su4() {
2435 let rs = RootSystem::type_a(3); let alpha1 = Root::new(vec![1.0, -1.0, 0.0, 0.0]);
2444 assert_eq!(rs.simple_root_expansion(&alpha1), Some(vec![1, 0, 0]));
2445
2446 let alpha2 = Root::new(vec![0.0, 1.0, -1.0, 0.0]);
2447 assert_eq!(rs.simple_root_expansion(&alpha2), Some(vec![0, 1, 0]));
2448
2449 let alpha3 = Root::new(vec![0.0, 0.0, 1.0, -1.0]);
2450 assert_eq!(rs.simple_root_expansion(&alpha3), Some(vec![0, 0, 1]));
2451
2452 let e0_minus_e2 = Root::new(vec![1.0, 0.0, -1.0, 0.0]);
2454 assert_eq!(rs.simple_root_expansion(&e0_minus_e2), Some(vec![1, 1, 0]));
2455
2456 let e1_minus_e3 = Root::new(vec![0.0, 1.0, 0.0, -1.0]);
2458 assert_eq!(rs.simple_root_expansion(&e1_minus_e3), Some(vec![0, 1, 1]));
2459
2460 let highest = Root::new(vec![1.0, 0.0, 0.0, -1.0]);
2462 assert_eq!(rs.simple_root_expansion(&highest), Some(vec![1, 1, 1]));
2463
2464 let neg_highest = Root::new(vec![-1.0, 0.0, 0.0, 1.0]);
2466 assert_eq!(
2467 rs.simple_root_expansion(&neg_highest),
2468 Some(vec![-1, -1, -1])
2469 );
2470 }
2471
2472 #[test]
2473 fn test_simple_root_expansion_not_a_root() {
2474 let rs = RootSystem::type_a(2); let not_a_root = Root::new(vec![1.0, 1.0, -2.0]);
2478 assert_eq!(rs.simple_root_expansion(¬_a_root), None);
2479
2480 let zero = Root::new(vec![0.0, 0.0, 0.0]);
2482 assert_eq!(rs.simple_root_expansion(&zero), None);
2483 }
2484
2485 #[test]
2486 fn test_simple_root_expansion_roundtrip() {
2487 let rs = RootSystem::type_a(2); for root in rs.roots() {
2491 let coeffs = rs.simple_root_expansion(root);
2492 assert!(coeffs.is_some(), "All roots should have expansions");
2493
2494 let coeffs = coeffs.unwrap();
2495 let simple = rs.simple_roots();
2496
2497 let mut reconstructed = vec![0.0; root.coords.len()];
2499 for (i, &c) in coeffs.iter().enumerate() {
2500 for (j, &coord) in simple[i].coords.iter().enumerate() {
2501 reconstructed[j] += (c as f64) * coord;
2502 }
2503 }
2504
2505 for (orig, recon) in root.coords.iter().zip(&reconstructed) {
2507 assert!(
2508 (orig - recon).abs() < 1e-10,
2509 "Reconstruction failed for root {:?}: expected {:?}, got {:?}",
2510 root.coords,
2511 root.coords,
2512 reconstructed
2513 );
2514 }
2515 }
2516 }
2517}