1use std::collections::{HashSet, VecDeque};
4use std::fmt;
5use std::ops::Range;
6
7use anyhow::{self, format_err};
8use derive_builder::Builder;
9use factorial::Factorial;
10use indexmap::IndexSet;
11use itertools::Itertools;
12use ndarray::Array2;
13use rayon::prelude::*;
14
15use crate::chartab::chartab_group::{CharacterProperties, IrrepCharTabConstruction};
16use crate::chartab::{CharacterTable, RepCharacterTable};
17use crate::group::class::ClassProperties;
18use crate::group::{GroupProperties, UnitaryRepresentedGroup};
19use crate::permutation::permutation_symbols::{
20 deduce_permutation_irrep_symbols, sort_perm_irreps, PermutationClassSymbol,
21 PermutationIrrepSymbol,
22};
23use crate::permutation::Permutation;
24use crate::permutation::PermutationRank;
25
26#[cfg(test)]
27#[path = "permutation_group_tests.rs"]
28mod permutation_group_tests;
29
30#[derive(Clone)]
37pub struct PermutationIterator<T: PermutationRank> {
38 rank: T,
39 raw_perm_indices: Range<usize>,
40}
41
42impl<T: PermutationRank> Iterator for PermutationIterator<T>
43where
44 std::ops::Range<T>: Iterator + DoubleEndedIterator,
45 Vec<T>: FromIterator<<std::ops::Range<T> as Iterator>::Item>,
46 VecDeque<T>: FromIterator<<std::ops::Range<T> as Iterator>::Item>,
47 <T as TryFrom<usize>>::Error: fmt::Debug,
48 IndexSet<T>: FromIterator<<std::ops::Range<T> as Iterator>::Item>,
49{
50 type Item = Permutation<T>;
51
52 fn next(&mut self) -> Option<Self::Item> {
53 self.raw_perm_indices
54 .next()
55 .and_then(|index| Permutation::<T>::from_lehmer_index(index, self.rank).ok())
56 }
57}
58
59#[derive(Clone, Builder)]
63pub struct PermutationGroup {
64 rank: u8,
66
67 perms_iter: PermutationIterator<u8>,
70
71 #[builder(setter(skip), default = "None")]
74 cycle_patterns: Option<IndexSet<Vec<u8>>>,
75
76 #[builder(setter(skip), default = "None")]
80 conjugacy_classes: Option<Vec<HashSet<usize>>>,
81
82 #[builder(setter(skip), default = "None")]
84 conjugacy_class_symbols: Option<IndexSet<PermutationClassSymbol<u8>>>,
85
86 #[builder(setter(skip), default = "None")]
88 irrep_character_table:
89 Option<RepCharacterTable<PermutationIrrepSymbol, PermutationClassSymbol<u8>>>,
90}
91
92impl PermutationGroup {
93 fn builder() -> PermutationGroupBuilder {
94 PermutationGroupBuilder::default()
95 }
96}
97
98pub trait PermutationGroupProperties:
106 ClassProperties<GroupElement = Permutation<u8>, ClassSymbol = PermutationClassSymbol<u8>>
107 + CharacterProperties
108 + Sized
109{
110 fn from_rank(rank: u8) -> Result<Self, anyhow::Error>;
121
122 fn set_class_symbols_from_cycle_patterns(&mut self) {
127 log::debug!("Assigning class symbols from cycle patterns...");
128 let class_symbols = (0..self.class_number())
129 .map(|cc_i| {
130 let rep_ele = self.get_cc_transversal(cc_i).unwrap_or_else(|| {
131 panic!("No representative element found for conjugacy class index `{cc_i}`.")
132 });
133 let cycle_pattern = rep_ele.cycle_pattern();
134 let mut cycle_pattern_count: Vec<(u8, u8)> =
135 Vec::with_capacity(cycle_pattern.len());
136 let mut i = 0u8;
137 while i < u8::try_from(cycle_pattern.len()).unwrap() {
138 let mut j = i + 1;
139 while j < u8::try_from(cycle_pattern.len()).unwrap()
140 && cycle_pattern[usize::from(j)] == cycle_pattern[usize::from(i)]
141 {
142 j += 1;
143 }
144 cycle_pattern_count.push((cycle_pattern[usize::from(i)], j - i));
145 i = j;
146 }
147 let cycle_pattern_str = cycle_pattern_count
148 .iter()
149 .map(|(length, count)| {
150 if *count > 1 {
151 format!("{length}^{count}")
152 } else {
153 length.to_string()
154 }
155 })
156 .collect_vec()
157 .join("ยท");
158 let size = self
159 .class_size(cc_i)
160 .unwrap_or_else(|| panic!("Unknown size for conjugacy class index `{i}`."));
161 PermutationClassSymbol::new(
162 format!("{size}||{cycle_pattern_str}||").as_str(),
163 Some(vec![rep_ele]),
164 )
165 .unwrap_or_else(|_| {
166 panic!(
167 "Unable to construct a class symbol from `{size}||{cycle_pattern_str}||`"
168 )
169 })
170 })
171 .collect_vec();
172 self.set_class_symbols(&class_symbols);
173 log::debug!("Assigning class symbols from cycle patterns... Done.");
174 }
175
176 fn canonicalise_character_table(&mut self);
179}
180
181impl PermutationGroupProperties
190 for UnitaryRepresentedGroup<Permutation<u8>, PermutationIrrepSymbol, PermutationClassSymbol<u8>>
191{
192 fn from_rank(rank: u8) -> Result<Self, anyhow::Error> {
193 assert!(rank > 0, "A permutation rank must be a positive integer.");
194 assert!(
195 rank <= 20,
196 "Permutations of rank more than 20 will not be representable."
197 );
198 log::debug!("Generating all permutations of rank {rank}...");
199 let perms = (0..rank)
200 .permutations(usize::from(rank))
201 .map(Permutation::from_image)
202 .collect::<Result<Vec<_>, _>>()?;
203 log::debug!("Generating all permutations of rank {rank}... Done.");
204 log::debug!("Collecting all permutations into a unitary-represented group...");
205 let mut group = UnitaryRepresentedGroup::<
206 Permutation<u8>,
207 PermutationIrrepSymbol,
208 PermutationClassSymbol<u8>,
209 >::new(format!("Sym({rank})").as_str(), perms)?;
210 log::debug!("Collecting all permutations into a unitary-represented group... Done.");
211 group.set_class_symbols_from_cycle_patterns();
212 group.construct_irrep_character_table();
213 group.canonicalise_character_table();
214 Ok(group)
215 }
216
217 fn canonicalise_character_table(&mut self) {
218 let old_chartab = self.character_table();
219 let class_symbols = (0..self.class_number())
220 .map(|i| {
221 self.get_cc_symbol_of_index(i)
222 .expect("Unable to retrieve all class symbols.")
223 })
224 .collect_vec();
225 let (char_arr, sorted_fs) = sort_perm_irreps(
226 &old_chartab.array().view(),
227 &old_chartab.frobenius_schurs.values().copied().collect_vec(),
228 );
229 let ordered_irreps = deduce_permutation_irrep_symbols(&char_arr.view());
230 self.set_irrep_character_table(RepCharacterTable::new(
231 &old_chartab.name,
232 &ordered_irreps,
233 &class_symbols,
234 &[],
235 char_arr,
236 &sorted_fs,
237 ));
238 }
239}
240
241impl GroupProperties for PermutationGroup {
246 type GroupElement = Permutation<u8>;
247 type ElementCollection = PermutationIterator<u8>;
248
249 fn name(&self) -> String {
250 format!("Sym({})", self.rank)
251 }
252
253 fn finite_subgroup_name(&self) -> Option<&String> {
254 None
255 }
256
257 fn get_index(&self, index: usize) -> Option<Self::GroupElement> {
258 Permutation::from_lehmer_index(index, self.rank).ok()
259 }
260
261 fn get_index_of(&self, g: &Self::GroupElement) -> Option<usize> {
262 if g.rank() != self.rank {
263 None
264 } else {
265 Some(g.lehmer_index(None))
266 }
267 }
268
269 fn contains(&self, g: &Self::GroupElement) -> bool {
270 g.rank() == self.rank
271 }
272
273 fn elements(&self) -> &Self::ElementCollection {
274 &self.perms_iter
275 }
276
277 fn is_abelian(&self) -> bool {
278 self.perms_iter.clone().enumerate().all(|(i, gi)| {
279 (0..i).all(|j| {
280 let gj = self
281 .get_index(j)
282 .unwrap_or_else(|| panic!("Element with index `{j}` not found."));
283 (&gi) * (&gj) == (&gj) * (&gi)
284 })
285 })
286 }
287
288 fn order(&self) -> usize {
289 usize::from(self.rank).checked_factorial().unwrap()
290 }
291
292 fn cayley_table(&self) -> Option<&Array2<usize>> {
293 None
294 }
295}
296
297fn partitions(n: u8) -> IndexSet<Vec<u8>> {
312 if n == 0 {
313 IndexSet::from([vec![0]])
314 } else {
315 let mut res: IndexSet<Vec<u8>> = IndexSet::new();
316 let mut a = vec![0; usize::from(n) + 1];
317 let mut k = 1;
318 let mut y = n - 1;
319 while k != 0 {
320 let mut x = a[k - 1] + 1;
321 k -= 1;
322 while 2 * x <= y {
323 a[k] = x;
324 y -= x;
325 k += 1;
326 }
327 let l = k + 1;
328 while x <= y {
329 a[k] = x;
330 a[l] = y;
331 let mut cycle = a[0..k + 2].to_vec();
332 cycle.reverse();
333 res.insert(cycle);
334 x += 1;
335 y -= 1;
336 }
337 a[k] = x + y;
338 y = x + y - 1;
339 let mut cycle = a[0..k + 1].to_vec();
340 cycle.reverse();
341 res.insert(cycle);
342 }
343 res
344 }
345}
346
347impl ClassProperties for PermutationGroup {
348 type ClassSymbol = PermutationClassSymbol<u8>;
349
350 fn compute_class_structure(&mut self) -> Result<(), anyhow::Error> {
352 log::debug!("Finding all partitions of {}...", self.rank);
353 self.cycle_patterns = Some(partitions(self.rank));
354 log::debug!("Finding all partitions of {}... Done.", self.rank);
355
356 log::debug!("Finding conjugacy classes based on cycle patterns in parallel...");
357 let mut conjugacy_classes = vec![HashSet::<usize>::new(); self.class_number()];
358
359 let e2ccs: Vec<(usize, u16)> = (0..self.order())
362 .into_par_iter()
363 .map(|i| {
364 let p_i = Permutation::from_lehmer_index(i, self.rank).map_err(|_| {
365 format_err!(
366 "Unable to construct a permutation of rank {} with Lehmer index {i}.",
367 self.rank
368 )
369 })?;
370 let cycle_pattern = p_i.cycle_pattern();
371 let c_i = self
372 .cycle_patterns
373 .as_ref()
374 .expect("Cycle patterns not found.")
375 .get_index_of(&cycle_pattern)
376 .ok_or_else(|| {
377 format_err!("Cycle pattern {cycle_pattern:?} is not valid in this group.")
378 })
379 .and_then(|index| u16::try_from(index).map_err(|err| format_err!(err)))?;
380 Ok::<_, anyhow::Error>((i, c_i))
381 })
382 .collect::<Result<Vec<_>, _>>()?;
383 e2ccs.into_iter().for_each(|(i, c_i)| {
384 conjugacy_classes[usize::from(c_i)].insert(i);
385 });
386 self.conjugacy_classes = Some(conjugacy_classes);
387 log::debug!("Finding conjugacy classes based on cycle patterns in parallel... Done.");
388 Ok(())
389 }
390
391 fn get_cc_index(&self, cc_idx: usize) -> Option<&HashSet<usize>> {
392 self.conjugacy_classes
393 .as_ref()
394 .map(|conjugacy_classes| &conjugacy_classes[cc_idx])
395 }
396
397 fn filter_cc_symbols<P: FnMut(&Self::ClassSymbol) -> bool>(
398 &self,
399 predicate: P,
400 ) -> Vec<Self::ClassSymbol> {
401 self.conjugacy_class_symbols
402 .as_ref()
403 .expect("No class structure found.")
404 .iter()
405 .cloned()
406 .filter(predicate)
407 .collect::<Vec<_>>()
408 }
409
410 fn get_cc_of_element_index(&self, e_idx: usize) -> Option<usize> {
411 let perm = Permutation::from_lehmer_index(e_idx, self.rank).ok()?;
412 self.cycle_patterns
413 .as_ref()
414 .expect("Cycle patterns not found.")
415 .get_index_of(&perm.cycle_pattern())
416 }
417
418 fn get_cc_transversal(&self, cc_idx: usize) -> Option<Self::GroupElement> {
419 let cycle_pattern = self
420 .cycle_patterns
421 .as_ref()
422 .expect("Cycle patterns not found.")
423 .get_index(cc_idx)?;
424 let cycles = cycle_pattern
425 .iter()
426 .scan(0u8, |start, &l| {
427 let cycle = (*start..*start + l).collect::<Vec<u8>>();
428 *start += l;
429 Some(cycle)
430 })
431 .collect_vec();
432 Permutation::from_cycles(&cycles).ok()
433 }
434
435 fn get_index_of_cc_symbol(&self, cc_sym: &Self::ClassSymbol) -> Option<usize> {
436 self.conjugacy_class_symbols
437 .as_ref()
438 .expect("Conjugacy class symbols not found.")
439 .get_index_of(cc_sym)
440 }
441
442 fn get_cc_symbol_of_index(&self, cc_idx: usize) -> Option<Self::ClassSymbol> {
443 self.conjugacy_class_symbols
444 .as_ref()
445 .expect("Conjugacy class symbols not found.")
446 .get_index(cc_idx)
447 .cloned()
448 }
449
450 fn set_class_symbols(&mut self, cc_symbols: &[Self::ClassSymbol]) {
451 self.conjugacy_class_symbols = Some(cc_symbols.iter().cloned().collect());
452 }
453
454 fn get_inverse_cc(&self, cc_idx: usize) -> Option<usize> {
455 Some(cc_idx)
456 }
457
458 fn class_number(&self) -> usize {
459 self.cycle_patterns
460 .as_ref()
461 .expect("Cycle patterns not found.")
462 .len()
463 }
464
465 fn class_size(&self, cc_idx: usize) -> Option<usize> {
470 let cycle_pattern = self
471 .cycle_patterns
472 .as_ref()
473 .expect("Cycle patterns not found.")
474 .get_index(cc_idx)?;
475 let mut cycle_pattern_count: Vec<(u8, u8)> = Vec::with_capacity(cycle_pattern.len());
476 let mut i = 0u8;
477 while i < u8::try_from(cycle_pattern.len()).unwrap() {
478 let mut j = i + 1;
479 while j < u8::try_from(cycle_pattern.len()).unwrap()
480 && cycle_pattern[usize::from(j)] == cycle_pattern[usize::from(i)]
481 {
482 j += 1;
483 }
484 cycle_pattern_count.push((cycle_pattern[usize::from(i)], j - i));
485 i = j;
486 }
487 let denom = cycle_pattern_count
488 .into_iter()
489 .map(|(l, m)| {
490 usize::from(l).pow(u32::from(m)) * usize::from(m).checked_factorial().unwrap()
491 })
492 .product();
493 Some(
494 usize::from(self.rank)
495 .checked_factorial()
496 .unwrap()
497 .div_euclid(denom),
498 )
499 }
500}
501
502impl CharacterProperties for PermutationGroup {
503 type RowSymbol = PermutationIrrepSymbol;
504 type CharTab = RepCharacterTable<PermutationIrrepSymbol, PermutationClassSymbol<u8>>;
505
506 fn character_table(&self) -> &Self::CharTab {
507 self.irrep_character_table
508 .as_ref()
509 .expect("Irrep character table not found for this group.")
510 }
511
512 fn unitary_represented(&self) -> bool {
513 true
514 }
515}
516
517impl IrrepCharTabConstruction for PermutationGroup {
518 fn set_irrep_character_table(&mut self, chartab: Self::CharTab) {
519 self.irrep_character_table = Some(chartab)
520 }
521}
522
523impl PermutationGroupProperties for PermutationGroup {
524 fn from_rank(rank: u8) -> Result<Self, anyhow::Error> {
525 assert!(rank > 0, "A permutation rank must be a positive integer.");
526 assert!(
527 rank <= 20,
528 "Permutations of rank more than 20 will not be representable."
529 );
530 log::debug!("Initialising lazy iterator for permutations of rank {rank}...");
531 let perms_iter = PermutationIterator {
532 rank,
533 raw_perm_indices: (0..usize::from(rank).checked_factorial().unwrap()),
534 };
535 let mut group = PermutationGroup::builder()
536 .rank(rank)
537 .perms_iter(perms_iter)
538 .build()
539 .expect("Unable to construct a `PermutationGroup`.");
540 log::debug!("Initialising lazy iterator for permutations of rank {rank}... Done.");
541 group.compute_class_structure()?;
542 group.set_class_symbols_from_cycle_patterns();
543 group.construct_irrep_character_table();
544 group.canonicalise_character_table();
545 Ok(group)
546 }
547
548 fn canonicalise_character_table(&mut self) {
549 let old_chartab = self.character_table();
550 let class_symbols = (0..self.class_number())
551 .map(|i| {
552 self.get_cc_symbol_of_index(i)
553 .expect("Unable to retrieve all class symbols.")
554 })
555 .collect_vec();
556 let (char_arr, sorted_fs) = sort_perm_irreps(
557 &old_chartab.array().view(),
558 &old_chartab.frobenius_schurs.values().copied().collect_vec(),
559 );
560 let ordered_irreps = deduce_permutation_irrep_symbols(&char_arr.view());
561 self.set_irrep_character_table(RepCharacterTable::new(
562 &old_chartab.name,
563 &ordered_irreps,
564 &class_symbols,
565 &[],
566 char_arr,
567 &sorted_fs,
568 ));
569 }
570}