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 coords: Vec<f64>,
67}
68
69impl Root {
70 pub fn new(coords: Vec<f64>) -> Self {
72 Self { coords }
73 }
74
75 pub fn rank(&self) -> usize {
77 self.coords.len()
78 }
79
80 pub fn inner_product(&self, other: &Root) -> f64 {
82 assert_eq!(self.rank(), other.rank());
83 self.coords
84 .iter()
85 .zip(&other.coords)
86 .map(|(a, b)| a * b)
87 .sum()
88 }
89
90 #[must_use]
92 pub fn norm_squared(&self) -> f64 {
93 self.inner_product(self)
94 }
95
96 #[must_use]
98 pub fn reflect(&self, beta: &Root) -> Root {
99 let factor = 2.0 * self.inner_product(beta) / self.norm_squared();
100 Root::new(
101 beta.coords
102 .iter()
103 .zip(&self.coords)
104 .map(|(b, a)| b - factor * a)
105 .collect(),
106 )
107 }
108
109 #[must_use]
111 pub fn cartan_integer(&self, beta: &Root) -> i32 {
112 let value = 2.0 * self.inner_product(beta) / self.norm_squared();
113 value.round() as i32
114 }
115
116 #[must_use]
118 pub fn is_positive(&self) -> bool {
119 for &c in &self.coords {
120 if c.abs() > 1e-10 {
121 return c > 0.0;
122 }
123 }
124 false }
126
127 #[must_use]
129 pub fn negate(&self) -> Root {
130 Root::new(self.coords.iter().map(|c| -c).collect())
131 }
132}
133
134impl fmt::Display for Root {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 write!(f, "(")?;
137 for (i, c) in self.coords.iter().enumerate() {
138 if i > 0 {
139 write!(f, ", ")?;
140 }
141 write!(f, "{:.2}", c)?;
142 }
143 write!(f, ")")
144 }
145}
146
147#[derive(Debug, Clone)]
163pub struct RootSystem {
164 rank: usize,
166
167 roots: Vec<Root>,
169
170 simple_roots: Vec<Root>,
172
173 cartan_matrix: Vec<Vec<i32>>,
175}
176
177impl RootSystem {
178 pub fn type_a(n: usize) -> Self {
199 assert!(n >= 1, "Type A_n requires n >= 1");
200
201 let rank = n;
202
203 let mut simple_roots = Vec::new();
206 for i in 0..n {
207 let mut coords = vec![0.0; n + 1];
208 coords[i] = 1.0;
209 coords[i + 1] = -1.0;
210 simple_roots.push(Root::new(coords));
211 }
212
213 let mut positive_roots = Vec::new();
215 for i in 0..=n {
216 for j in (i + 1)..=n {
217 let mut coords = vec![0.0; n + 1];
218 coords[i] = 1.0;
219 coords[j] = -1.0;
220 positive_roots.push(Root::new(coords));
221 }
222 }
223
224 let mut roots = positive_roots.clone();
226 for root in &positive_roots {
227 roots.push(root.negate());
228 }
229
230 let cartan_matrix = simple_roots
232 .iter()
233 .map(|alpha_i| {
234 simple_roots
235 .iter()
236 .map(|alpha_j| alpha_i.cartan_integer(alpha_j))
237 .collect()
238 })
239 .collect();
240
241 Self {
242 rank,
243 roots,
244 simple_roots,
245 cartan_matrix,
246 }
247 }
248
249 pub fn rank(&self) -> usize {
251 self.rank
252 }
253
254 pub fn roots(&self) -> &[Root] {
256 &self.roots
257 }
258
259 pub fn simple_roots(&self) -> &[Root] {
261 &self.simple_roots
262 }
263
264 pub fn cartan_matrix(&self) -> &[Vec<i32>] {
266 &self.cartan_matrix
267 }
268
269 pub fn num_roots(&self) -> usize {
271 self.roots.len()
272 }
273
274 pub fn num_positive_roots(&self) -> usize {
276 self.roots.iter().filter(|r| r.is_positive()).count()
277 }
278
279 pub fn positive_roots(&self) -> Vec<Root> {
281 self.roots
282 .iter()
283 .filter(|r| r.is_positive())
284 .cloned()
285 .collect()
286 }
287
288 pub fn highest_root(&self) -> Root {
307 let n = self.rank;
311
312 let mut coords = vec![0.0; n + 1];
314
315 for simple_root in &self.simple_roots {
316 for (i, &coord) in simple_root.coords.iter().enumerate() {
317 coords[i] += coord;
318 }
319 }
320
321 Root::new(coords)
322 }
323
324 pub fn contains_root(&self, root: &Root) -> bool {
326 self.roots.iter().any(|r| {
327 r.coords.len() == root.coords.len()
328 && r.coords
329 .iter()
330 .zip(root.coords.iter())
331 .all(|(a, b)| (a - b).abs() < 1e-10)
332 })
333 }
334
335 pub fn weyl_reflection(&self, alpha: &Root, beta: &Root) -> Root {
337 alpha.reflect(beta)
338 }
339
340 pub fn weyl_orbit(&self, weight: &Root) -> Vec<Root> {
345 let mut orbit = vec![weight.clone()];
346 let mut seen = HashSet::new();
347 seen.insert(format!("{:?}", weight.coords));
348
349 let mut queue = vec![weight.clone()];
350
351 while let Some(current) = queue.pop() {
352 for simple_root in &self.simple_roots {
353 let reflected = simple_root.reflect(¤t);
354 let key = format!("{:?}", reflected.coords);
355
356 if !seen.contains(&key) {
357 seen.insert(key);
358 orbit.push(reflected.clone());
359 queue.push(reflected);
360 }
361 }
362 }
363
364 orbit
365 }
366
367 #[must_use]
371 pub fn dimension(&self) -> usize {
372 self.rank + self.num_roots()
373 }
374
375 #[must_use]
377 pub fn is_dominant_weight(&self, weight: &Root) -> bool {
378 self.simple_roots
379 .iter()
380 .all(|alpha| weight.inner_product(alpha) >= -1e-10)
381 }
382
383 pub fn simple_root_expansion(&self, root: &Root) -> Option<Vec<i32>> {
398 if !self.contains_root(root) {
399 return None;
400 }
401
402 let coords = &root.coords;
408 let mut pos_idx: Option<usize> = None;
409 let mut neg_idx: Option<usize> = None;
410
411 for (idx, &val) in coords.iter().enumerate() {
412 if (val - 1.0).abs() < 1e-10 {
413 if pos_idx.is_some() {
414 return None;
416 }
417 pos_idx = Some(idx);
418 } else if (val + 1.0).abs() < 1e-10 {
419 if neg_idx.is_some() {
420 return None;
421 }
422 neg_idx = Some(idx);
423 } else if val.abs() > 1e-10 {
424 return None;
426 }
427 }
428
429 let (Some(i), Some(j)) = (pos_idx, neg_idx) else {
430 return None;
431 };
432
433 let mut coeffs = vec![0i32; self.rank];
437 let (min_idx, max_idx) = if i < j { (i, j) } else { (j, i) };
438 let sign = if i < j { 1 } else { -1 };
439
440 for k in min_idx..max_idx {
441 if k < self.rank {
442 coeffs[k] = sign;
443 }
444 }
445
446 Some(coeffs)
447 }
448}
449
450pub struct WeightLattice {
464 root_system: RootSystem,
465 fundamental_weights: Vec<Root>,
466}
467
468impl WeightLattice {
469 pub fn from_root_system(root_system: RootSystem) -> Self {
471 let fundamental_weights = Self::compute_fundamental_weights(&root_system);
472 Self {
473 root_system,
474 fundamental_weights,
475 }
476 }
477
478 fn compute_fundamental_weights(rs: &RootSystem) -> Vec<Root> {
480 let n = rs.rank();
487 let dim = n + 1; let mut weights = Vec::new();
489
490 for i in 0..n {
491 let mut coords = vec![0.0; dim];
493 for j in 0..=i {
494 coords[j] = 1.0;
495 }
496
497 let sum: f64 = coords.iter().sum();
499 let mean = sum / (dim as f64);
500 for coord in &mut coords {
501 *coord -= mean;
502 }
503
504 weights.push(Root::new(coords));
505 }
506
507 weights
508 }
509
510 pub fn fundamental_weights(&self) -> &[Root] {
512 &self.fundamental_weights
513 }
514
515 pub fn dynkin_to_weight(&self, dynkin_labels: &[usize]) -> Root {
517 assert_eq!(dynkin_labels.len(), self.root_system.rank());
518
519 let mut weight_coords = vec![0.0; self.root_system.rank() + 1];
520 for (i, &m) in dynkin_labels.iter().enumerate() {
521 for (j, &w) in self.fundamental_weights[i].coords.iter().enumerate() {
522 weight_coords[j] += (m as f64) * w;
523 }
524 }
525
526 Root::new(weight_coords)
527 }
528
529 pub fn root_system(&self) -> &RootSystem {
531 &self.root_system
532 }
533
534 pub fn rho(&self) -> Root {
538 let mut rho_coords = vec![0.0; self.root_system.rank() + 1];
539 for omega in &self.fundamental_weights {
540 for (i, &coord) in omega.coords.iter().enumerate() {
541 rho_coords[i] += coord;
542 }
543 }
544 Root::new(rho_coords)
545 }
546
547 fn kostant_partition_function(&self, gamma: &Root) -> usize {
566 use std::collections::HashMap;
567
568 if gamma.coords.iter().all(|&x| x.abs() < 1e-10) {
570 return 1;
571 }
572
573 let mut memo: HashMap<(String, usize), usize> = HashMap::new();
576 let positive_roots: Vec<Root> = self.root_system.positive_roots();
577
578 Self::partition_helper_ordered(gamma, &positive_roots, 0, &mut memo)
579 }
580
581 fn partition_helper_ordered(
586 gamma: &Root,
587 positive_roots: &[Root],
588 start_idx: usize,
589 memo: &mut std::collections::HashMap<(String, usize), usize>,
590 ) -> usize {
591 if gamma.coords.iter().all(|&x| x.abs() < 1e-10) {
593 return 1;
594 }
595
596 if start_idx >= positive_roots.len() {
598 return 0;
599 }
600
601 let key = (WeightLattice::weight_key(&gamma.coords), start_idx);
603 if let Some(&result) = memo.get(&key) {
604 return result;
605 }
606
607 let mut has_large_negative = false;
609 for &coord in &gamma.coords {
610 if coord < -100.0 {
611 has_large_negative = true;
612 break;
613 }
614 }
615
616 if has_large_negative {
617 memo.insert(key, 0);
618 return 0;
619 }
620
621 let mut count = 0;
625 let alpha = &positive_roots[start_idx];
626
627 if memo.len() < 100_000 {
629 count += Self::partition_helper_ordered(gamma, positive_roots, start_idx + 1, memo);
630 }
631
632 let mut gamma_minus_k_alpha = gamma.clone();
634 for (i, &a) in alpha.coords.iter().enumerate() {
635 gamma_minus_k_alpha.coords[i] -= a;
636 }
637
638 let can_use = gamma_minus_k_alpha.coords.iter().all(|&x| x > -100.0);
640
641 if can_use && memo.len() < 100_000 {
642 count += Self::partition_helper_ordered(
644 &gamma_minus_k_alpha,
645 positive_roots,
646 start_idx,
647 memo,
648 );
649 }
650
651 memo.insert(key, count);
652 count
653 }
654
655 fn weyl_group_type_a(&self) -> Vec<(Vec<usize>, i32)> {
670 let n_plus_1 = self.root_system.rank() + 1;
671 let mut perms = Vec::new();
672
673 let mut current: Vec<usize> = (0..n_plus_1).collect();
675 Self::generate_permutations(&mut current, n_plus_1, &mut perms);
676
677 perms
678 }
679
680 fn generate_permutations(arr: &mut [usize], size: usize, result: &mut Vec<(Vec<usize>, i32)>) {
682 if size == 1 {
683 let perm = arr.to_vec();
684 let sign = Self::permutation_sign(&perm);
685 result.push((perm, sign));
686 return;
687 }
688
689 for i in 0..size {
690 Self::generate_permutations(arr, size - 1, result);
691
692 if size % 2 == 0 {
693 arr.swap(i, size - 1);
694 } else {
695 arr.swap(0, size - 1);
696 }
697 }
698 }
699
700 fn permutation_sign(perm: &[usize]) -> i32 {
702 let n = perm.len();
703 let mut sign = 1i32;
704
705 for i in 0..n {
706 for j in (i + 1)..n {
707 if perm[i] > perm[j] {
708 sign *= -1;
709 }
710 }
711 }
712
713 sign
714 }
715
716 fn weyl_action(&self, weight: &Root, permutation: &[usize]) -> Root {
718 let mut new_coords = vec![0.0; weight.coords.len()];
719 for (i, &pi) in permutation.iter().enumerate() {
720 new_coords[i] = weight.coords[pi];
721 }
722 Root::new(new_coords)
723 }
724
725 pub fn kostant_multiplicity(&self, highest_weight: &Root, weight: &Root) -> usize {
754 let rho = self.rho();
755
756 let mut lambda_plus_rho = highest_weight.clone();
758 for (i, &r) in rho.coords.iter().enumerate() {
759 lambda_plus_rho.coords[i] += r;
760 }
761
762 let mut mu_plus_rho = weight.clone();
764 for (i, &r) in rho.coords.iter().enumerate() {
765 mu_plus_rho.coords[i] += r;
766 }
767
768 let weyl_group = self.weyl_group_type_a();
770 let mut multiplicity = 0i32; for (perm, sign) in weyl_group {
773 let w_lambda_plus_rho = self.weyl_action(&lambda_plus_rho, &perm);
775
776 let mut gamma = w_lambda_plus_rho.clone();
778 for (i, &mu) in mu_plus_rho.coords.iter().enumerate() {
779 gamma.coords[i] -= mu;
780 }
781
782 let p_gamma = self.kostant_partition_function(&gamma);
784
785 multiplicity += sign * (p_gamma as i32);
787 }
788
789 assert!(
791 multiplicity >= 0,
792 "Kostant multiplicity must be non-negative, got {}",
793 multiplicity
794 );
795 multiplicity as usize
796 }
797
798 pub fn weyl_dimension(&self, highest_weight: &Root) -> usize {
807 let rho = self.rho();
808
809 let mut lambda_plus_rho = highest_weight.clone();
811 for (i, &r) in rho.coords.iter().enumerate() {
812 lambda_plus_rho.coords[i] += r;
813 }
814
815 let mut numerator = 1.0;
816 let mut denominator = 1.0;
817
818 for alpha in self.root_system.positive_roots() {
819 let num = lambda_plus_rho.inner_product(&alpha);
820 let denom = rho.inner_product(&alpha);
821
822 numerator *= num;
823 denominator *= denom;
824 }
825
826 (numerator / denominator).round() as usize
827 }
828
829 fn weight_key(coords: &[f64]) -> String {
834 let rounded: Vec<String> = coords.iter().map(|&x| format!("{:.10}", x)).collect();
835 format!("[{}]", rounded.join(", "))
836 }
837
838 pub fn weights_of_irrep(&self, highest_weight: &Root) -> Vec<(Root, usize)> {
848 use std::collections::{HashMap, VecDeque};
849
850 let mut candidates: HashMap<String, Root> = HashMap::new();
851 let mut queue: VecDeque<Root> = VecDeque::new();
852
853 let hw_key = Self::weight_key(&highest_weight.coords);
855 candidates.insert(hw_key, highest_weight.clone());
856 queue.push_back(highest_weight.clone());
857
858 while let Some(weight) = queue.pop_front() {
861 for pos_root in self.root_system.positive_roots() {
862 let mut new_weight = weight.clone();
863 for (i, &a) in pos_root.coords.iter().enumerate() {
864 new_weight.coords[i] -= a;
865 }
866
867 let new_key = Self::weight_key(&new_weight.coords);
868
869 if candidates.contains_key(&new_key) {
871 continue;
872 }
873
874 let norm = new_weight.norm_squared();
876 let hw_norm = highest_weight.norm_squared();
877 if norm > 3.0 * hw_norm + 10.0 {
878 continue;
879 }
880
881 candidates.insert(new_key, new_weight.clone());
882 queue.push_back(new_weight);
883
884 if candidates.len() > 1000 {
886 break;
887 }
888 }
889
890 if candidates.len() > 1000 {
891 break;
892 }
893 }
894
895 let weyl_orbit = self.root_system.weyl_orbit(highest_weight);
898 for w in weyl_orbit {
899 let key = Self::weight_key(&w.coords);
900 candidates.insert(key, w);
901 }
902
903 let candidate_list: Vec<Root> = candidates.into_values().collect();
905 let mut result = Vec::new();
906
907 for weight in candidate_list.iter() {
908 let mult = self.kostant_multiplicity(highest_weight, weight);
909 if mult > 0 {
910 result.push((weight.clone(), mult));
911 }
912 }
913
914 result
915 }
916
917 pub fn weight_diagram_string(&self, highest_weight: &Root) -> String {
935 if self.root_system.rank() != 2 {
936 return "Weight diagrams only implemented for rank 2".to_string();
937 }
938
939 let weights = self.weights_of_irrep(highest_weight);
940
941 let mut output = String::new();
942 writeln!(
943 &mut output,
944 "Weight diagram for highest weight {:?}",
945 highest_weight.coords
946 )
947 .expect("String formatting should not fail");
948 writeln!(&mut output, "Dimension: {}", weights.len())
949 .expect("String formatting should not fail");
950 output.push_str("Weights (multiplicity):\n");
951
952 for (weight, mult) in &weights {
953 writeln!(&mut output, " {} (×{})", weight, mult)
954 .expect("String formatting should not fail");
955 }
956
957 output
958 }
959}
960
961pub struct CartanSubalgebra {
987 dimension: usize,
989
990 basis_matrices: Vec<Array2<Complex64>>,
993
994 matrix_size: usize,
996}
997
998impl CartanSubalgebra {
999 pub fn type_a(n: usize) -> Self {
1019 assert!(n >= 1, "Type A_n requires n >= 1");
1020
1021 let matrix_size = n + 1;
1022 let dimension = n;
1023
1024 let mut basis_matrices = Vec::new();
1025
1026 for i in 0..n {
1028 let mut h = Array2::<Complex64>::zeros((matrix_size, matrix_size));
1029 h[(i, i)] = Complex64::new(1.0, 0.0);
1030 h[(i + 1, i + 1)] = Complex64::new(-1.0, 0.0);
1031 basis_matrices.push(h);
1032 }
1033
1034 Self {
1035 dimension,
1036 basis_matrices,
1037 matrix_size,
1038 }
1039 }
1040
1041 pub fn dimension(&self) -> usize {
1043 self.dimension
1044 }
1045
1046 pub fn basis_matrices(&self) -> &[Array2<Complex64>] {
1048 &self.basis_matrices
1049 }
1050
1051 pub fn matrix_size(&self) -> usize {
1053 self.matrix_size
1054 }
1055
1056 pub fn evaluate_root(&self, root: &Root, coefficients: &[f64]) -> Complex64 {
1069 assert_eq!(
1070 coefficients.len(),
1071 self.dimension,
1072 "Coefficient vector must match Cartan dimension"
1073 );
1074 assert_eq!(
1075 root.rank(),
1076 self.matrix_size,
1077 "Root dimension must match matrix size"
1078 );
1079
1080 let mut h_diagonal = vec![Complex64::new(0.0, 0.0); self.matrix_size];
1086 for (i, &c) in coefficients.iter().enumerate() {
1087 h_diagonal[i] += Complex64::new(c, 0.0);
1088 h_diagonal[i + 1] -= Complex64::new(c, 0.0);
1089 }
1090
1091 root.coords
1093 .iter()
1094 .zip(&h_diagonal)
1095 .map(|(alpha_i, h_i)| h_i * alpha_i)
1096 .sum()
1097 }
1098
1099 pub fn project_matrix(&self, matrix: &Array2<Complex64>) -> Option<Vec<f64>> {
1121 assert_eq!(
1122 matrix.shape(),
1123 &[self.matrix_size, self.matrix_size],
1124 "Matrix must be {}×{}",
1125 self.matrix_size,
1126 self.matrix_size
1127 );
1128
1129 let n = self.dimension;
1131 let mut gram = vec![vec![0.0; n]; n];
1132 let mut rhs = vec![0.0; n];
1133
1134 for i in 0..n {
1135 for j in 0..n {
1136 let inner: Complex64 = self.basis_matrices[i]
1138 .iter()
1139 .zip(self.basis_matrices[j].iter())
1140 .map(|(h_i, h_j)| h_i.conj() * h_j)
1141 .sum();
1142 gram[i][j] = inner.re;
1143 }
1144
1145 let inner: Complex64 = matrix
1147 .iter()
1148 .zip(self.basis_matrices[i].iter())
1149 .map(|(m_ij, h_ij)| m_ij.conj() * h_ij)
1150 .sum();
1151 rhs[i] = inner.re;
1152 }
1153
1154 let mut coefficients = vec![0.0; n];
1156
1157 match n {
1158 1 => {
1159 coefficients[0] = rhs[0] / gram[0][0];
1160 Some(coefficients)
1161 }
1162 2 => {
1163 let det = gram[0][0] * gram[1][1] - gram[0][1] * gram[1][0];
1165 coefficients[0] = (rhs[0] * gram[1][1] - rhs[1] * gram[0][1]) / det;
1166 coefficients[1] = (gram[0][0] * rhs[1] - gram[1][0] * rhs[0]) / det;
1167 Some(coefficients)
1168 }
1169 _ => {
1170 None
1172 }
1173 }
1174 }
1175
1176 pub fn from_coefficients(&self, coefficients: &[f64]) -> Array2<Complex64> {
1180 assert_eq!(
1181 coefficients.len(),
1182 self.dimension,
1183 "Must provide {} coefficients",
1184 self.dimension
1185 );
1186
1187 let mut result = Array2::<Complex64>::zeros((self.matrix_size, self.matrix_size));
1188
1189 for (&c_i, h_i) in coefficients.iter().zip(&self.basis_matrices) {
1190 result = result + h_i * c_i;
1191 }
1192
1193 result
1194 }
1195
1196 pub fn contains(&self, matrix: &Array2<Complex64>, tolerance: f64) -> bool {
1200 assert_eq!(
1201 matrix.shape(),
1202 &[self.matrix_size, self.matrix_size],
1203 "Matrix must be {}×{}",
1204 self.matrix_size,
1205 self.matrix_size
1206 );
1207
1208 for i in 0..self.matrix_size {
1210 for j in 0..self.matrix_size {
1211 if i != j && matrix[(i, j)].norm() > tolerance {
1212 return false;
1213 }
1214 }
1215 }
1216
1217 let trace: Complex64 = (0..self.matrix_size).map(|i| matrix[(i, i)]).sum();
1219
1220 trace.norm() < tolerance
1221 }
1222
1223 pub fn killing_form(&self, coeffs1: &[f64], coeffs2: &[f64]) -> f64 {
1237 assert_eq!(coeffs1.len(), self.dimension);
1238 assert_eq!(coeffs2.len(), self.dimension);
1239
1240 let n_plus_1 = self.matrix_size as f64;
1247
1248 coeffs1
1249 .iter()
1250 .zip(coeffs2)
1251 .map(|(c1, c2)| 2.0 * n_plus_1 * 2.0 * c1 * c2)
1252 .sum()
1253 }
1254
1255 pub fn dual_basis(&self) -> Vec<Root> {
1260 let mut dual_roots = Vec::new();
1261
1262 for i in 0..self.dimension {
1263 let mut coords = vec![0.0; self.matrix_size];
1265 coords[i] = 1.0;
1266 coords[i + 1] = -1.0;
1267 dual_roots.push(Root::new(coords));
1268 }
1269
1270 dual_roots
1271 }
1272}
1273
1274#[derive(Debug, Clone)]
1314pub struct WeylChamber {
1315 root_system: RootSystem,
1317
1318 simple_roots: Vec<Root>,
1320}
1321
1322impl WeylChamber {
1323 pub fn fundamental(root_system: &RootSystem) -> Self {
1332 Self {
1333 root_system: root_system.clone(),
1334 simple_roots: root_system.simple_roots.clone(),
1335 }
1336 }
1337
1338 pub fn contains(&self, weight: &Root, strict: bool) -> bool {
1349 assert_eq!(weight.rank(), self.root_system.rank + 1);
1351
1352 for alpha in &self.simple_roots {
1353 let pairing = weight.inner_product(alpha);
1354
1355 if strict {
1356 if pairing <= 1e-10 {
1357 return false;
1358 }
1359 } else if pairing < -1e-10 {
1360 return false;
1361 }
1362 }
1363
1364 true
1365 }
1366
1367 pub fn project(&self, weight: &Root) -> Root {
1377 let mut current = weight.clone();
1378
1379 let max_iterations = 100;
1381 let mut iterations = 0;
1382
1383 while iterations < max_iterations {
1384 let mut found_violation = false;
1385
1386 for alpha in &self.simple_roots {
1387 let pairing = current.inner_product(alpha);
1388
1389 if pairing < -1e-10 {
1390 current = alpha.reflect(¤t);
1392 found_violation = true;
1393 }
1394 }
1395
1396 if !found_violation {
1397 break;
1398 }
1399
1400 iterations += 1;
1401 }
1402
1403 if iterations >= max_iterations {
1404 eprintln!(
1405 "Warning: WeylChamber::project did not converge in {} iterations",
1406 max_iterations
1407 );
1408 }
1409
1410 current
1411 }
1412
1413 pub fn simple_roots(&self) -> &[Root] {
1415 &self.simple_roots
1416 }
1417}
1418
1419#[derive(Debug, Clone)]
1456pub struct Alcove {
1457 root_system: RootSystem,
1459
1460 simple_roots: Vec<Root>,
1462
1463 highest_root: Root,
1465
1466 level: f64,
1468}
1469
1470impl Alcove {
1471 pub fn fundamental(root_system: &RootSystem) -> Self {
1478 let simple_roots = root_system.simple_roots.clone();
1479 let highest_root = root_system.highest_root();
1480
1481 Self {
1482 root_system: root_system.clone(),
1483 simple_roots,
1484 highest_root,
1485 level: 1.0,
1486 }
1487 }
1488
1489 pub fn at_level(root_system: &RootSystem, level: f64) -> Self {
1496 assert!(level > 0.0, "Level must be positive");
1497
1498 let simple_roots = root_system.simple_roots.clone();
1499 let highest_root = root_system.highest_root();
1500
1501 Self {
1502 root_system: root_system.clone(),
1503 simple_roots,
1504 highest_root,
1505 level,
1506 }
1507 }
1508
1509 pub fn contains(&self, weight: &Root, strict: bool) -> bool {
1520 assert_eq!(weight.rank(), self.root_system.rank + 1);
1522
1523 for alpha in &self.simple_roots {
1525 let pairing = weight.inner_product(alpha);
1526
1527 if strict {
1528 if pairing <= 1e-10 {
1529 return false;
1530 }
1531 } else if pairing < -1e-10 {
1532 return false;
1533 }
1534 }
1535
1536 let pairing_highest = weight.inner_product(&self.highest_root);
1538
1539 if strict {
1540 if pairing_highest >= self.level - 1e-10 {
1541 return false;
1542 }
1543 } else if pairing_highest > self.level + 1e-10 {
1544 return false;
1545 }
1546
1547 true
1548 }
1549
1550 pub fn level(&self) -> f64 {
1552 self.level
1553 }
1554
1555 pub fn highest_root(&self) -> &Root {
1557 &self.highest_root
1558 }
1559
1560 pub fn vertices(&self) -> Vec<Root> {
1569 if self.level != 1.0 {
1570 return vec![];
1574 }
1575
1576 let mut vertices = vec![Root::new(vec![0.0; self.root_system.rank + 1])];
1580
1581 let n = self.root_system.rank;
1586
1587 for i in 1..=n {
1588 let mut coords = vec![0.0; n + 1];
1589
1590 for j in 0..i {
1592 coords[j] = (n + 1 - i) as f64 / (n + 1) as f64;
1593 }
1594 for j in i..=n {
1595 coords[j] = -(i as f64) / (n + 1) as f64;
1596 }
1597
1598 vertices.push(Root::new(coords));
1599 }
1600
1601 vertices
1602 }
1603}
1604
1605#[cfg(test)]
1606mod tests {
1607 use super::*;
1608
1609 #[test]
1610 fn test_root_operations() {
1611 let alpha = Root::new(vec![1.0, -1.0, 0.0]);
1612 let beta = Root::new(vec![0.0, 1.0, -1.0]);
1613
1614 assert_eq!(alpha.rank(), 3);
1615 assert!((alpha.norm_squared() - 2.0).abs() < 1e-10);
1616 assert!((alpha.inner_product(&beta) + 1.0).abs() < 1e-10);
1617 assert!(alpha.is_positive());
1618 assert!(!alpha.negate().is_positive());
1619 }
1620
1621 #[test]
1622 fn test_weyl_reflection() {
1623 let alpha = Root::new(vec![1.0, -1.0]);
1624 let beta = Root::new(vec![0.5, 0.5]);
1625
1626 let reflected = alpha.reflect(&beta);
1627 assert!((reflected.coords[0] - 0.5).abs() < 1e-10);
1630 assert!((reflected.coords[1] - 0.5).abs() < 1e-10);
1631 }
1632
1633 #[test]
1634 fn test_cartan_integer() {
1635 let alpha = Root::new(vec![1.0, -1.0, 0.0]);
1636 let beta = Root::new(vec![0.0, 1.0, -1.0]);
1637
1638 assert_eq!(alpha.cartan_integer(&beta), -1);
1640 assert_eq!(beta.cartan_integer(&alpha), -1);
1641 }
1642
1643 #[test]
1644 fn test_type_a1_su2() {
1645 let rs = RootSystem::type_a(1);
1646
1647 assert_eq!(rs.rank(), 1);
1648 assert_eq!(rs.num_roots(), 2); assert_eq!(rs.num_positive_roots(), 1);
1650 assert_eq!(rs.dimension(), 3); let cartan = rs.cartan_matrix();
1653 assert_eq!(cartan.len(), 1);
1654 assert_eq!(cartan[0][0], 2); }
1656
1657 #[test]
1658 fn test_type_a2_su3() {
1659 let rs = RootSystem::type_a(2);
1660
1661 assert_eq!(rs.rank(), 2);
1662 assert_eq!(rs.num_roots(), 6); assert_eq!(rs.num_positive_roots(), 3);
1664 assert_eq!(rs.dimension(), 8); let cartan = rs.cartan_matrix();
1667 assert_eq!(cartan.len(), 2);
1668 assert_eq!(cartan[0][0], 2);
1669 assert_eq!(cartan[1][1], 2);
1670 assert_eq!(cartan[0][1], -1); assert_eq!(cartan[1][0], -1);
1672 }
1673
1674 #[test]
1675 fn test_simple_roots_su3() {
1676 let rs = RootSystem::type_a(2);
1677 let simple = rs.simple_roots();
1678
1679 assert_eq!(simple.len(), 2);
1680
1681 assert!((simple[0].coords[0] - 1.0).abs() < 1e-10);
1683 assert!((simple[0].coords[1] + 1.0).abs() < 1e-10);
1684 assert!((simple[0].coords[2]).abs() < 1e-10);
1685
1686 assert!((simple[1].coords[0]).abs() < 1e-10);
1688 assert!((simple[1].coords[1] - 1.0).abs() < 1e-10);
1689 assert!((simple[1].coords[2] + 1.0).abs() < 1e-10);
1690 }
1691
1692 #[test]
1693 fn test_positive_roots_su3() {
1694 let rs = RootSystem::type_a(2);
1695 let positive = rs.positive_roots();
1696
1697 assert_eq!(positive.len(), 3);
1698 }
1700
1701 #[test]
1702 fn test_dominant_weight() {
1703 let rs = RootSystem::type_a(2);
1704
1705 let weight1 = Root::new(vec![1.0, 0.0, 0.0]);
1707 assert!(rs.is_dominant_weight(&weight1));
1708
1709 let weight2 = Root::new(vec![-1.0, 0.0, 0.0]);
1711 assert!(!rs.is_dominant_weight(&weight2));
1712 }
1713
1714 #[test]
1715 fn test_weight_lattice_su2() {
1716 let rs = RootSystem::type_a(1);
1717 let wl = WeightLattice::from_root_system(rs);
1718
1719 assert_eq!(wl.fundamental_weights().len(), 1);
1720
1721 let weight = wl.dynkin_to_weight(&[2]);
1723 assert_eq!(weight.rank(), 2);
1724 }
1725
1726 #[test]
1727 fn test_weight_lattice_su3() {
1728 let rs = RootSystem::type_a(2);
1729 let wl = WeightLattice::from_root_system(rs);
1730
1731 assert_eq!(wl.fundamental_weights().len(), 2);
1732
1733 let fund = wl.dynkin_to_weight(&[1, 0]);
1735 assert_eq!(fund.rank(), 3);
1736
1737 let adj = wl.dynkin_to_weight(&[1, 1]);
1739 assert_eq!(adj.rank(), 3);
1740 }
1741
1742 #[test]
1743 fn test_weyl_orbit_su2() {
1744 let rs = RootSystem::type_a(1);
1745 let weight = Root::new(vec![1.0, 0.0]);
1746
1747 let orbit = rs.weyl_orbit(&weight);
1748 assert_eq!(orbit.len(), 2); }
1750
1751 #[test]
1752 fn test_weyl_dimension_formula() {
1753 let rs = RootSystem::type_a(2); let wl = WeightLattice::from_root_system(rs);
1755
1756 let fund = wl.dynkin_to_weight(&[1, 0]);
1759 assert_eq!(wl.weyl_dimension(&fund), 3);
1760
1761 let antifund = wl.dynkin_to_weight(&[0, 1]);
1763 assert_eq!(wl.weyl_dimension(&antifund), 3);
1764
1765 let adj = wl.dynkin_to_weight(&[1, 1]);
1767 assert_eq!(wl.weyl_dimension(&adj), 8);
1768
1769 let sym2 = wl.dynkin_to_weight(&[2, 0]);
1771 assert_eq!(wl.weyl_dimension(&sym2), 6);
1772
1773 let sym2_bar = wl.dynkin_to_weight(&[0, 2]);
1775 assert_eq!(wl.weyl_dimension(&sym2_bar), 6);
1776
1777 let rs2 = RootSystem::type_a(1); let wl2 = WeightLattice::from_root_system(rs2);
1780 let trivial = wl2.dynkin_to_weight(&[0]);
1781 assert_eq!(wl2.weyl_dimension(&trivial), 1);
1782
1783 let spin1 = wl2.dynkin_to_weight(&[2]);
1785 assert_eq!(wl2.weyl_dimension(&spin1), 3);
1786 }
1787
1788 #[test]
1789 fn test_weights_of_irrep_su3_fundamental() {
1790 let rs = RootSystem::type_a(2);
1791 let wl = WeightLattice::from_root_system(rs);
1792
1793 let highest = wl.dynkin_to_weight(&[1, 0]);
1795 let weights = wl.weights_of_irrep(&highest);
1796
1797 let expected_dim = wl.weyl_dimension(&highest);
1799 assert_eq!(expected_dim, 3, "Fundamental rep has dimension 3");
1800 assert_eq!(
1801 weights.len(),
1802 3,
1803 "Should find exactly 3 weights, found {}",
1804 weights.len()
1805 );
1806 }
1807
1808 #[test]
1809 fn test_weights_of_irrep_su3_adjoint() {
1810 let rs = RootSystem::type_a(2);
1811 let wl = WeightLattice::from_root_system(rs);
1812
1813 let highest = wl.dynkin_to_weight(&[1, 1]);
1815 let weights = wl.weights_of_irrep(&highest);
1816
1817 let dim = wl.weyl_dimension(&highest);
1819 assert_eq!(dim, 8, "Adjoint rep has dimension 8");
1820
1821 let total_dim: usize = weights.iter().map(|(_, m)| m).sum();
1824 assert_eq!(
1825 total_dim, 8,
1826 "Total dimension (sum of multiplicities) should be 8"
1827 );
1828 }
1829
1830 #[test]
1831 fn test_partition_function_basics() {
1832 let rs = RootSystem::type_a(1); let wl = WeightLattice::from_root_system(rs);
1835
1836 let zero = Root::new(vec![0.0, 0.0]);
1839 assert_eq!(wl.kostant_partition_function(&zero), 1, "P(0) = 1");
1840
1841 let alpha = Root::new(vec![1.0, -1.0]);
1843 assert_eq!(wl.kostant_partition_function(&alpha), 1, "P(α) = 1");
1844
1845 let two_alpha = Root::new(vec![2.0, -2.0]);
1847 assert_eq!(wl.kostant_partition_function(&two_alpha), 1, "P(2α) = 1");
1848
1849 let neg_alpha = Root::new(vec![-1.0, 1.0]);
1851 assert_eq!(wl.kostant_partition_function(&neg_alpha), 0, "P(-α) = 0");
1852 }
1853
1854 #[test]
1855 fn test_partition_function_su3() {
1856 let rs = RootSystem::type_a(2);
1858 let wl = WeightLattice::from_root_system(rs);
1859
1860 let zero = Root::new(vec![0.0, 0.0, 0.0]);
1862 assert_eq!(wl.kostant_partition_function(&zero), 1, "P(0) = 1");
1863
1864 let alpha1 = Root::new(vec![1.0, -1.0, 0.0]);
1866 let p_alpha1 = wl.kostant_partition_function(&alpha1);
1867 eprintln!("P(α₁) = {}", p_alpha1);
1868 assert_eq!(p_alpha1, 1, "P(α₁) = 1");
1869
1870 let alpha1_plus_alpha2 = Root::new(vec![1.0, 0.0, -1.0]);
1873 let p_sum = wl.kostant_partition_function(&alpha1_plus_alpha2);
1874 eprintln!("P(α₁+α₂) = {}", p_sum);
1875 }
1877
1878 #[test]
1879 fn test_kostant_dimension_invariant() {
1880 let rs = RootSystem::type_a(2); let wl = WeightLattice::from_root_system(rs);
1884
1885 let fund = wl.dynkin_to_weight(&[1, 0]);
1887 let fund_weights = wl.weights_of_irrep(&fund);
1888 eprintln!("Fundamental [1,0] weights:");
1889 for (w, m) in &fund_weights {
1890 eprintln!(" {:?} mult {}", w.coords, m);
1891 }
1892 let fund_dim: usize = fund_weights.iter().map(|(_, m)| m).sum();
1893 eprintln!("Total dimension: {} (expected 3)", fund_dim);
1894 assert_eq!(fund_dim, 3, "Fundamental rep dimension invariant");
1895
1896 let adj = wl.dynkin_to_weight(&[1, 1]);
1898 let adj_weights = wl.weights_of_irrep(&adj);
1899 eprintln!("\nAdjoint [1,1] weights:");
1900 for (w, m) in &adj_weights {
1901 eprintln!(" {:?} mult {}", w.coords, m);
1902 }
1903 let adj_dim: usize = adj_weights.iter().map(|(_, m)| m).sum();
1904 eprintln!("Total dimension: {} (expected 8)", adj_dim);
1905 assert_eq!(adj_dim, 8, "Adjoint rep dimension invariant");
1906
1907 let anti = wl.dynkin_to_weight(&[0, 1]);
1909 let anti_weights = wl.weights_of_irrep(&anti);
1910 let anti_dim: usize = anti_weights.iter().map(|(_, m)| m).sum();
1911 assert_eq!(anti_dim, 3, "Antifundamental rep dimension invariant");
1912 }
1913
1914 #[test]
1915 fn test_weight_diagram_string_su3() {
1916 let rs = RootSystem::type_a(2);
1917 let wl = WeightLattice::from_root_system(rs);
1918
1919 let highest = wl.dynkin_to_weight(&[1, 0]);
1920 let diagram = wl.weight_diagram_string(&highest);
1921
1922 assert!(diagram.contains("Weight diagram"));
1924 assert!(diagram.contains("Dimension"));
1925 assert!(diagram.contains("Weights"));
1926 }
1927
1928 #[test]
1929 fn test_weight_diagram_rank1_not_supported() {
1930 let rs = RootSystem::type_a(1);
1931 let wl = WeightLattice::from_root_system(rs);
1932
1933 let highest = wl.dynkin_to_weight(&[2]);
1934 let diagram = wl.weight_diagram_string(&highest);
1935
1936 assert!(diagram.contains("only implemented for rank 2"));
1937 }
1938
1939 #[test]
1942 fn test_cartan_subalgebra_su2() {
1943 let cartan = CartanSubalgebra::type_a(1);
1944
1945 assert_eq!(cartan.dimension(), 1);
1946 assert_eq!(cartan.matrix_size(), 2);
1947 assert_eq!(cartan.basis_matrices().len(), 1);
1948
1949 let h1 = &cartan.basis_matrices()[0];
1951 assert!((h1[(0, 0)].re - 1.0).abs() < 1e-10);
1952 assert!((h1[(1, 1)].re + 1.0).abs() < 1e-10);
1953 assert!(h1[(0, 1)].norm() < 1e-10);
1954 assert!(h1[(1, 0)].norm() < 1e-10);
1955 }
1956
1957 #[test]
1958 fn test_cartan_subalgebra_su3() {
1959 let cartan = CartanSubalgebra::type_a(2);
1960
1961 assert_eq!(cartan.dimension(), 2);
1962 assert_eq!(cartan.matrix_size(), 3);
1963 assert_eq!(cartan.basis_matrices().len(), 2);
1964
1965 let h1 = &cartan.basis_matrices()[0];
1967 assert!((h1[(0, 0)].re - 1.0).abs() < 1e-10);
1968 assert!((h1[(1, 1)].re + 1.0).abs() < 1e-10);
1969 assert!(h1[(2, 2)].norm() < 1e-10);
1970
1971 let h2 = &cartan.basis_matrices()[1];
1973 assert!(h2[(0, 0)].norm() < 1e-10);
1974 assert!((h2[(1, 1)].re - 1.0).abs() < 1e-10);
1975 assert!((h2[(2, 2)].re + 1.0).abs() < 1e-10);
1976 }
1977
1978 #[test]
1979 fn test_cartan_basis_commutes() {
1980 let cartan = CartanSubalgebra::type_a(2);
1981
1982 let h1 = &cartan.basis_matrices()[0];
1984 let h2 = &cartan.basis_matrices()[1];
1985
1986 let commutator = h1.dot(h2) - h2.dot(h1);
1987
1988 for val in commutator.iter() {
1989 assert!(val.norm() < 1e-10, "Cartan basis elements must commute");
1990 }
1991 }
1992
1993 #[test]
1994 fn test_cartan_dual_basis() {
1995 let cartan = CartanSubalgebra::type_a(2);
1996 let dual = cartan.dual_basis();
1997
1998 assert_eq!(dual.len(), 2);
1999
2000 assert!((dual[0].coords[0] - 1.0).abs() < 1e-10);
2002 assert!((dual[0].coords[1] + 1.0).abs() < 1e-10);
2003 assert!(dual[0].coords[2].abs() < 1e-10);
2004
2005 assert!(dual[1].coords[0].abs() < 1e-10);
2007 assert!((dual[1].coords[1] - 1.0).abs() < 1e-10);
2008 assert!((dual[1].coords[2] + 1.0).abs() < 1e-10);
2009
2010 let rs = RootSystem::type_a(2);
2012 let simple_roots = rs.simple_roots();
2013
2014 for (d, s) in dual.iter().zip(simple_roots) {
2015 for (dc, sc) in d.coords.iter().zip(&s.coords) {
2016 assert!((dc - sc).abs() < 1e-10);
2017 }
2018 }
2019 }
2020
2021 #[test]
2022 fn test_cartan_evaluate_root() {
2023 let cartan = CartanSubalgebra::type_a(1);
2024
2025 let root = Root::new(vec![1.0, -1.0]);
2028
2029 let result = cartan.evaluate_root(&root, &[1.0]);
2031 assert!((result.re - 2.0).abs() < 1e-10);
2032 assert!(result.im.abs() < 1e-10);
2033
2034 let result2 = cartan.evaluate_root(&root, &[0.5]);
2035 assert!((result2.re - 1.0).abs() < 1e-10);
2036 }
2037
2038 #[test]
2039 fn test_cartan_from_coefficients() {
2040 let cartan = CartanSubalgebra::type_a(2);
2041
2042 let h = cartan.from_coefficients(&[2.0, 3.0]);
2044
2045 assert!((h[(0, 0)].re - 2.0).abs() < 1e-10);
2047 assert!((h[(1, 1)].re - 1.0).abs() < 1e-10);
2048 assert!((h[(2, 2)].re + 3.0).abs() < 1e-10);
2049
2050 let trace: Complex64 = (0..3).map(|i| h[(i, i)]).sum();
2052 assert!(trace.norm() < 1e-10);
2053 }
2054
2055 #[test]
2056 fn test_cartan_project_matrix() {
2057 let cartan = CartanSubalgebra::type_a(2);
2058
2059 let mut mat = Array2::<Complex64>::zeros((3, 3));
2061 mat[(0, 0)] = Complex64::new(1.0, 0.0);
2062 mat[(1, 1)] = Complex64::new(-0.5, 0.0);
2063 mat[(2, 2)] = Complex64::new(-0.5, 0.0);
2064
2065 let coeffs = cartan
2066 .project_matrix(&mat)
2067 .expect("Rank 2 should be supported");
2068 assert_eq!(coeffs.len(), 2);
2069
2070 let reconstructed = cartan.from_coefficients(&coeffs);
2072
2073 for i in 0..3 {
2075 let diff = (mat[(i, i)] - reconstructed[(i, i)]).norm();
2076 assert!(
2077 diff < 1e-10,
2078 "Diagonal projection should be exact: diff at ({},{}) = {}",
2079 i,
2080 i,
2081 diff
2082 );
2083 }
2084 }
2085
2086 #[test]
2087 fn test_cartan_contains() {
2088 let cartan = CartanSubalgebra::type_a(2);
2089
2090 let mut h = Array2::<Complex64>::zeros((3, 3));
2092 h[(0, 0)] = Complex64::new(1.0, 0.0);
2093 h[(1, 1)] = Complex64::new(-0.5, 0.0);
2094 h[(2, 2)] = Complex64::new(-0.5, 0.0);
2095
2096 assert!(cartan.contains(&h, 1e-6));
2097
2098 let mut not_h = Array2::<Complex64>::zeros((3, 3));
2100 not_h[(0, 1)] = Complex64::new(1.0, 0.0);
2101
2102 assert!(!cartan.contains(¬_h, 1e-6));
2103
2104 let mut not_traceless = Array2::<Complex64>::zeros((3, 3));
2106 not_traceless[(0, 0)] = Complex64::new(1.0, 0.0);
2107
2108 assert!(!cartan.contains(¬_traceless, 1e-6));
2109 }
2110
2111 #[test]
2112 fn test_cartan_killing_form() {
2113 let cartan = CartanSubalgebra::type_a(1);
2114
2115 let killing = cartan.killing_form(&[1.0], &[1.0]);
2117 assert!((killing - 8.0).abs() < 1e-10);
2118
2119 let killing_zero = cartan.killing_form(&[1.0], &[0.0]);
2121 assert!(killing_zero.abs() < 1e-10);
2122
2123 let k12 = cartan.killing_form(&[1.0], &[2.0]);
2125 let k21 = cartan.killing_form(&[2.0], &[1.0]);
2126 assert!((k12 - k21).abs() < 1e-10);
2127 }
2128
2129 #[test]
2130 fn test_cartan_killing_form_su3() {
2131 let cartan = CartanSubalgebra::type_a(2);
2132
2133 let k11 = cartan.killing_form(&[1.0, 0.0], &[1.0, 0.0]);
2135 assert!((k11 - 12.0).abs() < 1e-10);
2136
2137 let k22 = cartan.killing_form(&[0.0, 1.0], &[0.0, 1.0]);
2138 assert!((k22 - 12.0).abs() < 1e-10);
2139
2140 let k12 = cartan.killing_form(&[1.0, 0.0], &[0.0, 1.0]);
2142 assert!(k12.abs() < 1e-10);
2143 }
2144
2145 #[test]
2146 fn test_weyl_chamber_contains_su2() {
2147 let rs = RootSystem::type_a(1); let chamber = WeylChamber::fundamental(&rs);
2149
2150 let dominant = Root::new(vec![1.0, 0.0]);
2155 assert!(chamber.contains(&dominant, false));
2156 assert!(chamber.contains(&dominant, true));
2157
2158 let non_dominant = Root::new(vec![-1.0, 0.0]);
2160 assert!(!chamber.contains(&non_dominant, false));
2161
2162 let boundary = Root::new(vec![0.0, 0.0]);
2164 assert!(chamber.contains(&boundary, false)); assert!(!chamber.contains(&boundary, true)); }
2167
2168 #[test]
2169 fn test_weyl_chamber_contains_su3() {
2170 let rs = RootSystem::type_a(2); let chamber = WeylChamber::fundamental(&rs);
2172
2173 let omega1 = Root::new(vec![2.0 / 3.0, -1.0 / 3.0, -1.0 / 3.0]);
2178 assert!(chamber.contains(&omega1, false));
2179
2180 let neg = Root::new(vec![-1.0, 0.0, 1.0]);
2182 assert!(!chamber.contains(&neg, false));
2183
2184 let origin = Root::new(vec![0.0, 0.0, 0.0]);
2186 assert!(chamber.contains(&origin, false));
2187 assert!(!chamber.contains(&origin, true));
2188 }
2189
2190 #[test]
2191 fn test_weyl_chamber_project() {
2192 let rs = RootSystem::type_a(1); let chamber = WeylChamber::fundamental(&rs);
2194
2195 let dominant = Root::new(vec![1.0, -1.0]);
2197 let projected = chamber.project(&dominant);
2198 assert!((projected.coords[0] - dominant.coords[0]).abs() < 1e-10);
2199 assert!((projected.coords[1] - dominant.coords[1]).abs() < 1e-10);
2200
2201 let non_dominant = Root::new(vec![-1.0, 1.0]);
2203 let projected = chamber.project(&non_dominant);
2204
2205 assert!(chamber.contains(&projected, false));
2207 assert!((projected.coords[0] - 1.0).abs() < 1e-10);
2208 assert!((projected.coords[1] + 1.0).abs() < 1e-10);
2209 }
2210
2211 #[test]
2212 fn test_weyl_chamber_project_su3() {
2213 let rs = RootSystem::type_a(2); let chamber = WeylChamber::fundamental(&rs);
2215
2216 let lambda = Root::new(vec![-1.0, 2.0, -1.0]);
2218 let projected = chamber.project(&lambda);
2219
2220 assert!(chamber.contains(&projected, false));
2222
2223 let norm_before = lambda.norm_squared();
2225 let norm_after = projected.norm_squared();
2226 assert!((norm_before - norm_after).abs() < 1e-8);
2227 }
2228
2229 #[test]
2230 fn test_alcove_contains_su2() {
2231 let rs = RootSystem::type_a(1); let alcove = Alcove::fundamental(&rs);
2233
2234 let inside = Root::new(vec![0.4, -0.4]);
2239 assert!(alcove.contains(&inside, false));
2242
2243 let outside = Root::new(vec![1.0, -1.0]);
2245 assert!(!alcove.contains(&outside, false));
2247
2248 let origin = Root::new(vec![0.0, 0.0]);
2250 assert!(alcove.contains(&origin, false));
2251 assert!(!alcove.contains(&origin, true)); }
2253
2254 #[test]
2255 fn test_alcove_vertices_su2() {
2256 let rs = RootSystem::type_a(1); let alcove = Alcove::fundamental(&rs);
2258
2259 let vertices = alcove.vertices();
2260
2261 assert_eq!(vertices.len(), 2);
2263
2264 assert!(vertices[0].coords[0].abs() < 1e-10);
2266 assert!(vertices[0].coords[1].abs() < 1e-10);
2267
2268 assert!((vertices[1].coords[0] - 0.5).abs() < 1e-10);
2270 assert!((vertices[1].coords[1] + 0.5).abs() < 1e-10);
2271 }
2272
2273 #[test]
2274 fn test_alcove_vertices_su3() {
2275 let rs = RootSystem::type_a(2); let alcove = Alcove::fundamental(&rs);
2277
2278 let vertices = alcove.vertices();
2279
2280 assert_eq!(vertices.len(), 3);
2282
2283 for &coord in &vertices[0].coords {
2285 assert!(coord.abs() < 1e-10);
2286 }
2287
2288 for vertex in &vertices {
2290 assert!(alcove.contains(vertex, false));
2291 }
2292 }
2293
2294 #[test]
2295 fn test_alcove_at_level() {
2296 let rs = RootSystem::type_a(1); let alcove_k2 = Alcove::at_level(&rs, 2.0);
2298
2299 assert_eq!(alcove_k2.level(), 2.0);
2300
2301 let inside = Root::new(vec![0.8, -0.8]);
2303 assert!(alcove_k2.contains(&inside, false));
2305
2306 let outside = Root::new(vec![1.5, -1.5]);
2307 assert!(!alcove_k2.contains(&outside, false));
2309 }
2310
2311 #[test]
2312 fn test_alcove_highest_root() {
2313 let rs = RootSystem::type_a(2); let alcove = Alcove::fundamental(&rs);
2315
2316 let theta = alcove.highest_root();
2317
2318 assert!((theta.coords[0] - 1.0).abs() < 1e-10);
2320 assert!(theta.coords[1].abs() < 1e-10);
2321 assert!((theta.coords[2] + 1.0).abs() < 1e-10);
2322
2323 let theta_norm = theta.norm_squared();
2325 for root in rs.positive_roots() {
2326 assert!(root.norm_squared() <= theta_norm + 1e-10);
2327 }
2328 }
2329
2330 #[test]
2333 fn test_simple_root_expansion_su2() {
2334 let rs = RootSystem::type_a(1); let alpha1 = Root::new(vec![1.0, -1.0]);
2341 let coeffs = rs.simple_root_expansion(&alpha1);
2342 assert_eq!(coeffs, Some(vec![1]));
2343
2344 let neg_alpha1 = Root::new(vec![-1.0, 1.0]);
2346 let coeffs_neg = rs.simple_root_expansion(&neg_alpha1);
2347 assert_eq!(coeffs_neg, Some(vec![-1]));
2348 }
2349
2350 #[test]
2351 fn test_simple_root_expansion_su3() {
2352 let rs = RootSystem::type_a(2); let alpha1 = Root::new(vec![1.0, -1.0, 0.0]);
2361 assert_eq!(rs.simple_root_expansion(&alpha1), Some(vec![1, 0]));
2362
2363 let alpha2 = Root::new(vec![0.0, 1.0, -1.0]);
2365 assert_eq!(rs.simple_root_expansion(&alpha2), Some(vec![0, 1]));
2366
2367 let alpha1_plus_alpha2 = Root::new(vec![1.0, 0.0, -1.0]);
2369 assert_eq!(
2370 rs.simple_root_expansion(&alpha1_plus_alpha2),
2371 Some(vec![1, 1])
2372 );
2373
2374 let neg_alpha1 = Root::new(vec![-1.0, 1.0, 0.0]);
2376 assert_eq!(rs.simple_root_expansion(&neg_alpha1), Some(vec![-1, 0]));
2377
2378 let neg_highest = Root::new(vec![-1.0, 0.0, 1.0]);
2379 assert_eq!(rs.simple_root_expansion(&neg_highest), Some(vec![-1, -1]));
2380 }
2381
2382 #[test]
2383 fn test_simple_root_expansion_su4() {
2384 let rs = RootSystem::type_a(3); let alpha1 = Root::new(vec![1.0, -1.0, 0.0, 0.0]);
2393 assert_eq!(rs.simple_root_expansion(&alpha1), Some(vec![1, 0, 0]));
2394
2395 let alpha2 = Root::new(vec![0.0, 1.0, -1.0, 0.0]);
2396 assert_eq!(rs.simple_root_expansion(&alpha2), Some(vec![0, 1, 0]));
2397
2398 let alpha3 = Root::new(vec![0.0, 0.0, 1.0, -1.0]);
2399 assert_eq!(rs.simple_root_expansion(&alpha3), Some(vec![0, 0, 1]));
2400
2401 let e0_minus_e2 = Root::new(vec![1.0, 0.0, -1.0, 0.0]);
2403 assert_eq!(rs.simple_root_expansion(&e0_minus_e2), Some(vec![1, 1, 0]));
2404
2405 let e1_minus_e3 = Root::new(vec![0.0, 1.0, 0.0, -1.0]);
2407 assert_eq!(rs.simple_root_expansion(&e1_minus_e3), Some(vec![0, 1, 1]));
2408
2409 let highest = Root::new(vec![1.0, 0.0, 0.0, -1.0]);
2411 assert_eq!(rs.simple_root_expansion(&highest), Some(vec![1, 1, 1]));
2412
2413 let neg_highest = Root::new(vec![-1.0, 0.0, 0.0, 1.0]);
2415 assert_eq!(
2416 rs.simple_root_expansion(&neg_highest),
2417 Some(vec![-1, -1, -1])
2418 );
2419 }
2420
2421 #[test]
2422 fn test_simple_root_expansion_not_a_root() {
2423 let rs = RootSystem::type_a(2); let not_a_root = Root::new(vec![1.0, 1.0, -2.0]);
2427 assert_eq!(rs.simple_root_expansion(¬_a_root), None);
2428
2429 let zero = Root::new(vec![0.0, 0.0, 0.0]);
2431 assert_eq!(rs.simple_root_expansion(&zero), None);
2432 }
2433
2434 #[test]
2435 fn test_simple_root_expansion_roundtrip() {
2436 let rs = RootSystem::type_a(2); for root in rs.roots() {
2440 let coeffs = rs.simple_root_expansion(root);
2441 assert!(coeffs.is_some(), "All roots should have expansions");
2442
2443 let coeffs = coeffs.unwrap();
2444 let simple = rs.simple_roots();
2445
2446 let mut reconstructed = vec![0.0; root.coords.len()];
2448 for (i, &c) in coeffs.iter().enumerate() {
2449 for (j, &coord) in simple[i].coords.iter().enumerate() {
2450 reconstructed[j] += (c as f64) * coord;
2451 }
2452 }
2453
2454 for (orig, recon) in root.coords.iter().zip(&reconstructed) {
2456 assert!(
2457 (orig - recon).abs() < 1e-10,
2458 "Reconstruction failed for root {:?}: expected {:?}, got {:?}",
2459 root.coords,
2460 root.coords,
2461 reconstructed
2462 );
2463 }
2464 }
2465 }
2466}