qsym2/permutation/
permutation_symbols.rs1use std::fmt;
4use std::hash::{Hash, Hasher};
5use std::str::FromStr;
6
7use derive_builder::Builder;
8use itertools::Itertools;
9use ndarray::{Array2, ArrayView2, Axis};
10use num_traits::ToPrimitive;
11use serde::{Deserialize, Serialize};
12
13use crate::chartab::character::Character;
14use crate::chartab::chartab_symbols::{
15 disambiguate_linspace_symbols, CollectionSymbol, GenericSymbol, GenericSymbolParsingError,
16 LinearSpaceSymbol, MathematicalSymbol,
17};
18use crate::chartab::unityroot::UnityRoot;
19use crate::permutation::{Permutation, PermutationRank};
20
21#[derive(Builder, Debug, Clone, Serialize, Deserialize)]
31pub struct PermutationClassSymbol<T: PermutationRank> {
32 generic_symbol: GenericSymbol,
34
35 representatives: Option<Vec<Permutation<T>>>,
37}
38
39impl<T: PermutationRank> PartialEq for PermutationClassSymbol<T> {
40 fn eq(&self, other: &Self) -> bool {
41 self.generic_symbol == other.generic_symbol
42 }
43}
44
45impl<T: PermutationRank> Eq for PermutationClassSymbol<T> {}
46
47impl<T: PermutationRank> Hash for PermutationClassSymbol<T> {
48 fn hash<H: Hasher>(&self, state: &mut H) {
49 self.generic_symbol.hash(state);
50 }
51}
52
53impl<T: PermutationRank> PermutationClassSymbol<T> {
54 fn builder() -> PermutationClassSymbolBuilder<T> {
55 PermutationClassSymbolBuilder::default()
56 }
57
58 pub fn new(
88 symstr: &str,
89 reps: Option<Vec<Permutation<T>>>,
90 ) -> Result<Self, GenericSymbolParsingError> {
91 let generic_symbol = GenericSymbol::from_str(symstr)?;
92 if generic_symbol.multiplicity().is_none() {
93 Err(GenericSymbolParsingError(format!(
94 "{symstr} contains no class size prefactor."
95 )))
96 } else {
97 Ok(Self::builder()
98 .generic_symbol(generic_symbol)
99 .representatives(reps)
100 .build()
101 .unwrap_or_else(|_| panic!("Unable to construct a class symbol from `{symstr}`.")))
102 }
103 }
104}
105
106impl<T: PermutationRank> MathematicalSymbol for PermutationClassSymbol<T> {
107 fn main(&self) -> String {
109 self.generic_symbol.main()
110 }
111
112 fn presuper(&self) -> String {
114 String::new()
115 }
116
117 fn presub(&self) -> String {
119 String::new()
120 }
121
122 fn postsuper(&self) -> String {
124 String::new()
125 }
126
127 fn postsub(&self) -> String {
129 String::new()
130 }
131
132 fn prefactor(&self) -> String {
134 self.generic_symbol.prefactor()
135 }
136
137 fn postfactor(&self) -> String {
139 String::new()
140 }
141
142 fn multiplicity(&self) -> Option<usize> {
145 self.generic_symbol.multiplicity()
146 }
147}
148
149impl<T: PermutationRank> CollectionSymbol for PermutationClassSymbol<T> {
150 type CollectionElement = Permutation<T>;
151
152 fn from_reps(
153 symstr: &str,
154 reps: Option<Vec<Self::CollectionElement>>,
155 ) -> Result<Self, GenericSymbolParsingError> {
156 Self::new(symstr, reps)
157 }
158
159 fn representative(&self) -> Option<&Self::CollectionElement> {
160 self.representatives.as_ref().map(|reps| &reps[0])
161 }
162
163 fn representatives(&self) -> Option<&Vec<Self::CollectionElement>> {
164 self.representatives.as_ref()
165 }
166}
167
168impl<T: PermutationRank> fmt::Display for PermutationClassSymbol<T> {
169 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170 write!(f, "{}", self.generic_symbol)
171 }
172}
173
174#[derive(Builder, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Serialize, Deserialize)]
181pub struct PermutationIrrepSymbol {
182 #[builder(setter(custom))]
184 generic_symbol: GenericSymbol,
185
186 #[builder(setter(custom), default = "None")]
188 dim: Option<usize>,
189}
190
191impl PermutationIrrepSymbolBuilder {
192 fn generic_symbol(&mut self, sym: GenericSymbol) -> &mut Self {
193 assert!(
194 sym.main() == "Sym" || sym.main() == "Alt" || sym.main() == "Λ",
195 "The main part of a permutation irrep symbol can only be `Sym`, `Alt`, or `Λ`.",
196 );
197 self.generic_symbol = Some(sym);
198 self
199 }
200
201 fn dim(&mut self, dim: usize) -> &mut Self {
202 let main = self
203 .generic_symbol
204 .as_ref()
205 .expect("The generic symbol has not been set for this permutation symbol.")
206 .main();
207 if main == "Sym" || main == "Alt" {
208 assert_eq!(
209 dim, 1,
210 "A `{main}` permutation irrep must be one-dimensional."
211 );
212 }
213 self.dim = Some(Some(dim));
214 self
215 }
216}
217
218impl PermutationIrrepSymbol {
219 fn builder() -> PermutationIrrepSymbolBuilder {
220 PermutationIrrepSymbolBuilder::default()
221 }
222
223 pub fn new(symstr: &str, dim: usize) -> Result<Self, PermutationIrrepSymbolBuilderError> {
241 let generic_symbol = GenericSymbol::from_str(symstr)
242 .unwrap_or_else(|_| panic!("Unable to parse {symstr} as a generic symbol."));
243 Self::builder()
244 .generic_symbol(generic_symbol)
245 .dim(dim)
246 .build()
247 }
248}
249
250impl MathematicalSymbol for PermutationIrrepSymbol {
251 fn main(&self) -> String {
253 self.generic_symbol.main()
254 }
255
256 fn presuper(&self) -> String {
259 self.generic_symbol.presuper()
260 }
261
262 fn presub(&self) -> String {
263 self.generic_symbol.presub()
264 }
265
266 fn postsuper(&self) -> String {
268 self.generic_symbol.postsuper()
269 }
270
271 fn postsub(&self) -> String {
274 self.generic_symbol.postsub()
275 }
276
277 fn prefactor(&self) -> String {
279 String::new()
280 }
281
282 fn postfactor(&self) -> String {
284 String::new()
285 }
286
287 fn multiplicity(&self) -> Option<usize> {
289 self.dim
290 }
291}
292
293impl FromStr for PermutationIrrepSymbol {
294 type Err = PermutationIrrepSymbolBuilderError;
295
296 fn from_str(symstr: &str) -> Result<Self, Self::Err> {
314 let generic_symbol = GenericSymbol::from_str(symstr)
315 .unwrap_or_else(|_| panic!("Unable to parse {symstr} as a generic symbol."));
316 Self::builder().generic_symbol(generic_symbol).build()
317 }
318}
319
320impl LinearSpaceSymbol for PermutationIrrepSymbol {
321 fn dimensionality(&self) -> usize {
322 self.dim
323 .unwrap_or_else(|| panic!("Unknown dimensionality for permutation irrep `{self}`."))
324 }
325
326 fn set_dimensionality(&mut self, dim: usize) -> bool {
327 if dim == 1 {
328 if self.main() == "Sym" || self.main() == "Alt" {
329 self.dim = Some(dim);
330 true
331 } else {
332 false
333 }
334 } else if self.main() != "Sym" && self.main() != "Alt" {
335 self.dim = Some(dim);
336 true
337 } else {
338 false
339 }
340 }
341}
342
343impl fmt::Display for PermutationIrrepSymbol {
344 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
345 write!(
346 f,
347 "|{}{}|{}",
348 self.main(),
349 if self.main() == "Λ" {
350 self.dim
351 .map(|dim| dim.to_string())
352 .unwrap_or_else(|| "?".to_string())
353 } else {
354 String::new()
355 },
356 if self.postsub().is_empty() {
357 String::new()
358 } else {
359 format!("_({})", self.postsub())
360 }
361 )
362 }
363}
364
365pub(super) fn sort_perm_irreps(
381 char_arr: &ArrayView2<Character>,
382 frobenius_schur_indicators: &[i8],
383) -> (Array2<Character>, Vec<i8>) {
384 log::debug!("Sorting permutation irreducible representations...");
385 let n_rows = char_arr.nrows();
386 let col_idxs = (0..n_rows).collect_vec();
387 let sort_arr = char_arr.select(Axis(1), &col_idxs);
388
389 let sort_row_indices: Vec<_> = (0..n_rows)
390 .sorted_by(|&i, &j| {
391 let keys_i = sort_arr.row(i).iter().cloned().collect_vec();
392 let keys_j = sort_arr.row(j).iter().cloned().collect_vec();
393 keys_i
394 .partial_cmp(&keys_j)
395 .unwrap_or_else(|| panic!("`{keys_i:?}` and `{keys_j:?}` cannot be compared."))
396 })
397 .collect();
398 let char_arr = char_arr.select(Axis(0), &sort_row_indices);
399 let old_fs = frobenius_schur_indicators.iter().collect::<Vec<_>>();
400 let sorted_fs = sort_row_indices.iter().map(|&i| *old_fs[i]).collect_vec();
401 log::debug!("Sorting permutation irreducible representations... Done.");
402 (char_arr, sorted_fs)
403}
404
405pub(super) fn deduce_permutation_irrep_symbols(
419 char_arr: &ArrayView2<Character>,
420) -> Vec<PermutationIrrepSymbol> {
421 log::debug!("Generating permutation irreducible representation symbols...");
422
423 log::debug!("First pass: assign symbols from rules");
425
426 let one = Character::new(&[(UnityRoot::new(0u32, 1u32), 1)]);
427 let raw_irrep_symbols = char_arr.rows().into_iter().map(|irrep| {
428 let dim = irrep[0].clone();
429 if dim == one {
430 if irrep.iter().all(|chr| chr.clone() == one) {
431 PermutationIrrepSymbol::new("||Sym||", 1).unwrap_or_else(|_| {
432 panic!("Unable to construct permutation irrep symbol `||Sym||`")
433 })
434 } else {
435 PermutationIrrepSymbol::new("||Alt||", 1).unwrap_or_else(|_| {
436 panic!("Unable to construct permutation irrep symbol `||Alt||`")
437 })
438 }
439 } else {
440 let dim_c = dim.complex_value();
441 assert!(
442 approx::relative_eq!(dim_c.im, 0.0)
443 && approx::relative_eq!(dim_c.re.round(), dim_c.re)
444 && dim_c.re.round() > 0.0
445 );
446 let dim_u = dim_c.re
447 .round()
448 .to_usize()
449 .expect("Unable to convert the dimensionality of an irrep to `usize`.");
450 PermutationIrrepSymbol::new("||Λ||", dim_u).unwrap_or_else(|_| {
451 panic!("Unable to construct permutation irrep symbol `||Λ||` with dimensionality `{dim_u}`.")
452 })
453 }
454 });
455
456 log::debug!("Second pass: disambiguate identical cases not distinguishable by rules");
458 let irrep_symbols = disambiguate_linspace_symbols(raw_irrep_symbols);
459
460 log::debug!("Generating permutation irreducible representation symbols... Done.");
461 irrep_symbols
462}