1use std::fmt;
4use std::hash::Hash;
5use std::ops::Mul;
6
7use anyhow::{self, format_err};
8use derive_builder::Builder;
9use indexmap::IndexSet;
10use log;
11use ndarray::{Array2, Zip};
12use num::Integer;
13use num_traits::Inv;
14use serde::{de::DeserializeOwned, Deserialize, Serialize};
15
16use crate::chartab::chartab_group::CharacterProperties;
17use crate::chartab::chartab_symbols::{
18 CollectionSymbol, LinearSpaceSymbol, ReducibleLinearSpaceSymbol,
19};
20use crate::chartab::{CharacterTable, CorepCharacterTable, RepCharacterTable};
21use crate::group::class::{ClassProperties, EagerClassStructure};
22
23pub mod class;
24
25pub trait FiniteOrder {
31 type Int: Integer;
33
34 fn order(&self) -> Self::Int;
36}
37
38pub trait GroupProperties
40where
41 Self::GroupElement: Mul<Output = Self::GroupElement>
42 + Inv<Output = Self::GroupElement>
43 + Hash
44 + Eq
45 + Clone
46 + Sync
47 + fmt::Debug
48 + FiniteOrder,
49{
50 type GroupElement;
52 type ElementCollection: Clone + IntoIterator<Item = Self::GroupElement>;
53
54 fn name(&self) -> String;
56
57 fn finite_subgroup_name(&self) -> Option<&String>;
59
60 fn elements(&self) -> &Self::ElementCollection;
62
63 fn get_index(&self, index: usize) -> Option<Self::GroupElement>;
66
67 fn get_index_of(&self, g: &Self::GroupElement) -> Option<usize>;
70
71 fn contains(&self, g: &Self::GroupElement) -> bool;
73
74 fn is_abelian(&self) -> bool;
76
77 fn order(&self) -> usize;
79
80 fn cayley_table(&self) -> Option<&Array2<usize>>;
82}
83
84pub trait HasUnitarySubgroup: GroupProperties
87where
88 Self::UnitarySubgroup: GroupProperties<GroupElement = Self::GroupElement> + CharacterProperties,
89{
90 type UnitarySubgroup;
92
93 fn unitary_subgroup(&self) -> &Self::UnitarySubgroup;
95
96 fn check_elem_antiunitary(&self, element: &Self::GroupElement) -> bool;
110}
111
112#[derive(Clone, Hash, PartialEq, Eq, Debug, Serialize, Deserialize)]
118pub enum GroupType {
119 Ordinary(bool),
123
124 MagneticGrey(bool),
128
129 MagneticBlackWhite(bool),
134}
135
136impl fmt::Display for GroupType {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 match self {
139 Self::Ordinary(true) => write!(f, "Double ordinary group"),
140 Self::Ordinary(false) => write!(f, "Ordinary group"),
141 Self::MagneticGrey(true) => write!(f, "Double magnetic grey group"),
142 Self::MagneticGrey(false) => write!(f, "Magnetic grey group"),
143 Self::MagneticBlackWhite(true) => write!(f, "Double magnetic black-and-white group"),
144 Self::MagneticBlackWhite(false) => write!(f, "Magnetic black-and-white group"),
145 }
146 }
147}
148
149pub const ORGRP: GroupType = GroupType::Ordinary(false);
151
152pub const ORGRP2: GroupType = GroupType::Ordinary(true);
154
155pub const BWGRP: GroupType = GroupType::MagneticBlackWhite(false);
157
158pub const BWGRP2: GroupType = GroupType::MagneticBlackWhite(true);
160
161pub const GRGRP: GroupType = GroupType::MagneticGrey(false);
163
164pub const GRGRP2: GroupType = GroupType::MagneticGrey(true);
166
167#[derive(Builder, Clone, Serialize, Deserialize)]
177pub struct EagerGroup<T>
178where
179 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
180{
181 name: String,
183
184 #[builder(setter(custom))]
186 elements: IndexSet<T>,
187
188 #[builder(setter(skip), default = "None")]
197 cayley_table: Option<Array2<usize>>,
198}
199
200impl<T> EagerGroupBuilder<T>
201where
202 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
203 for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
204{
205 fn elements(&mut self, elems: Vec<T>) -> &mut Self {
206 self.elements = Some(elems.into_iter().collect());
207 self
208 }
209
210 fn elements_iter(&mut self, elems_iter: impl Iterator<Item = T>) -> &mut Self {
211 self.elements = Some(elems_iter.collect());
212 self
213 }
214}
215
216impl<T> EagerGroup<T>
217where
218 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
219 for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
220{
221 fn builder() -> EagerGroupBuilder<T> {
231 EagerGroupBuilder::<T>::default()
232 }
233
234 pub fn new(name: &str, elements: Vec<T>) -> Result<Self, anyhow::Error> {
253 let mut group = Self::builder()
254 .name(name.to_string())
255 .elements(elements)
256 .build()
257 .expect("Unable to construct a group.");
258 group.compute_cayley_table()?;
259 Ok(group)
260 }
261
262 pub fn new_no_ctb(name: &str, elements: Vec<T>) -> Self {
273 Self::builder()
274 .name(name.to_string())
275 .elements(elements)
276 .build()
277 .expect("Unable to construct a group.")
278 }
279
280 pub fn from_iter(
300 name: &str,
301 elements_iter: impl Iterator<Item = T>,
302 ) -> Result<Self, anyhow::Error> {
303 let mut group = Self::builder()
304 .name(name.to_string())
305 .elements_iter(elements_iter)
306 .build()
307 .expect("Unable to construct a group.");
308 group.compute_cayley_table()?;
309 Ok(group)
310 }
311
312 pub fn from_iter_no_ctb(name: &str, elements_iter: impl Iterator<Item = T>) -> Self {
328 Self::builder()
329 .name(name.to_string())
330 .elements_iter(elements_iter)
331 .build()
332 .expect("Unable to construct a group.")
333 }
334
335 fn compute_cayley_table(&mut self) -> Result<(), anyhow::Error> {
341 log::debug!("Constructing Cayley table in parallel...");
342 let mut ctb = Array2::<usize>::zeros((self.order(), self.order()));
343 Zip::indexed(&mut ctb).par_map_collect(|(i, j), k| {
344 let op_i_ref = self.elements
345 .get_index(i)
346 .ok_or_else(|| format_err!("Element with index {i} cannot be retrieved."))?;
347 let op_j_ref = self.elements
348 .get_index(j)
349 .ok_or_else(|| format_err!("Element with index {j} cannot be retrieved."))?;
350 let op_k = op_i_ref * op_j_ref;
351 *k = self.elements
352 .get_index_of(&op_k)
353 .ok_or_else(||
354 format_err!("Group closure not fulfilled. The composition {:?} * {:?} = {:?} is not contained in the group. Try relaxing distance thresholds.",
355 op_i_ref,
356 op_j_ref,
357 &op_k)
358 )?;
359 Ok::<(), anyhow::Error>(())
360 }).into_iter().collect::<Result<(), anyhow::Error>>()?;
361 self.cayley_table = Some(ctb);
362 log::debug!("Constructing Cayley table in parallel... Done.");
363 Ok(())
364 }
365}
366
367impl<T> GroupProperties for EagerGroup<T>
368where
369 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
370 for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
371{
372 type GroupElement = T;
373 type ElementCollection = IndexSet<T>;
374
375 fn name(&self) -> String {
376 self.name.to_string()
377 }
378
379 fn finite_subgroup_name(&self) -> Option<&String> {
380 None
381 }
382
383 fn get_index(&self, index: usize) -> Option<Self::GroupElement> {
384 self.elements.get_index(index).cloned()
385 }
386
387 fn get_index_of(&self, g: &Self::GroupElement) -> Option<usize> {
388 self.elements.get_index_of(g)
389 }
390
391 fn contains(&self, g: &Self::GroupElement) -> bool {
392 self.elements.contains(g)
393 }
394
395 fn elements(&self) -> &Self::ElementCollection {
396 &self.elements
397 }
398
399 fn is_abelian(&self) -> bool {
400 if let Some(ctb) = &self.cayley_table {
401 ctb == ctb.t()
402 } else {
403 self.elements.iter().enumerate().all(|(i, gi)| {
404 (0..i).all(|j| {
405 let gj = self
406 .elements
407 .get_index(j)
408 .unwrap_or_else(|| panic!("Element with index `{j}` not found."));
409 gi * gj == gj * gi
410 })
411 })
412 }
413 }
414
415 fn order(&self) -> usize {
416 self.elements.len()
417 }
418
419 fn cayley_table(&self) -> Option<&Array2<usize>> {
420 self.cayley_table.as_ref()
421 }
422}
423
424#[derive(Clone, Builder, Serialize, Deserialize)]
430pub struct UnitaryRepresentedGroup<T, RowSymbol, ColSymbol>
431where
432 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
433 RowSymbol: LinearSpaceSymbol,
434 ColSymbol: CollectionSymbol<CollectionElement = T>,
435{
436 name: String,
438
439 #[builder(setter(custom), default = "None")]
442 finite_subgroup_name: Option<String>,
443
444 abstract_group: EagerGroup<T>,
446
447 #[builder(setter(skip), default = "None")]
454 class_structure: Option<EagerClassStructure<T, ColSymbol>>,
455
456 #[builder(setter(skip), default = "None")]
458 pub(crate) irrep_character_table: Option<RepCharacterTable<RowSymbol, ColSymbol>>,
459}
460
461impl<T, RowSymbol, ColSymbol> UnitaryRepresentedGroupBuilder<T, RowSymbol, ColSymbol>
462where
463 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
464 for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
465 RowSymbol: LinearSpaceSymbol,
466 ColSymbol: CollectionSymbol<CollectionElement = T>,
467{
468 fn finite_subgroup_name(&mut self, name_opt: Option<String>) -> &mut Self {
469 if name_opt.is_some() {
470 if self.name.as_ref().expect("Group name not found.").clone() == *"O(3)"
471 || self
472 .name
473 .as_ref()
474 .expect("Group name not found.")
475 .contains('∞')
476 {
477 self.finite_subgroup_name = Some(name_opt);
478 } else {
479 panic!(
480 "Setting a finite subgroup name for a non-infinite group is not supported yet."
481 )
482 }
483 }
484 self
485 }
486}
487
488impl<T, RowSymbol, ColSymbol> UnitaryRepresentedGroup<T, RowSymbol, ColSymbol>
489where
490 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
491 RowSymbol: LinearSpaceSymbol,
492 ColSymbol: CollectionSymbol<CollectionElement = T>,
493{
494 pub(crate) fn set_finite_subgroup_name(&mut self, name: Option<String>) {
500 self.finite_subgroup_name = name;
501 }
502}
503
504impl<T, RowSymbol, ColSymbol> UnitaryRepresentedGroup<T, RowSymbol, ColSymbol>
505where
506 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
507 for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
508 RowSymbol: LinearSpaceSymbol,
509 ColSymbol: CollectionSymbol<CollectionElement = T>,
510{
511 fn builder() -> UnitaryRepresentedGroupBuilder<T, RowSymbol, ColSymbol> {
517 UnitaryRepresentedGroupBuilder::<T, RowSymbol, ColSymbol>::default()
518 }
519
520 pub fn new(name: &str, elements: Vec<T>) -> Result<Self, anyhow::Error> {
532 let abstract_group = EagerGroup::<T>::new(name, elements);
533 let mut unitary_group = UnitaryRepresentedGroup::<T, RowSymbol, ColSymbol>::builder()
534 .name(name.to_string())
535 .abstract_group(abstract_group?)
536 .build()
537 .expect("Unable to construct a unitary group.");
538 unitary_group.compute_class_structure()?;
539 Ok(unitary_group)
540 }
541}
542
543impl<T, RowSymbol, ColSymbol> GroupProperties for UnitaryRepresentedGroup<T, RowSymbol, ColSymbol>
544where
545 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
546 for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
547 RowSymbol: LinearSpaceSymbol,
548 ColSymbol: CollectionSymbol<CollectionElement = T>,
549{
550 type GroupElement = T;
551 type ElementCollection = IndexSet<Self::GroupElement>;
552
553 fn name(&self) -> String {
554 self.name.to_string()
555 }
556
557 fn finite_subgroup_name(&self) -> Option<&String> {
558 self.finite_subgroup_name.as_ref()
559 }
560
561 fn get_index(&self, index: usize) -> Option<Self::GroupElement> {
562 self.abstract_group.get_index(index)
563 }
564
565 fn get_index_of(&self, g: &Self::GroupElement) -> Option<usize> {
566 self.abstract_group.get_index_of(g)
567 }
568
569 fn contains(&self, g: &Self::GroupElement) -> bool {
570 self.abstract_group.contains(g)
571 }
572
573 fn elements(&self) -> &Self::ElementCollection {
574 self.abstract_group.elements()
575 }
576
577 fn is_abelian(&self) -> bool {
578 self.abstract_group.is_abelian()
579 }
580
581 fn order(&self) -> usize {
582 self.abstract_group.order()
583 }
584
585 fn cayley_table(&self) -> Option<&Array2<usize>> {
586 self.abstract_group.cayley_table()
587 }
588}
589
590#[derive(Clone, Builder, Serialize, Deserialize)]
603pub struct MagneticRepresentedGroup<T, UG, RowSymbol>
604where
605 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
606 RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol>,
607 UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
608 <UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
609 CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
610 Serialize + DeserializeOwned,
611{
612 name: String,
614
615 #[builder(setter(custom), default = "None")]
618 finite_subgroup_name: Option<String>,
619
620 abstract_group: EagerGroup<T>,
622
623 #[builder(setter(custom))]
625 unitary_subgroup: UG,
626
627 #[builder(setter(skip), default = "None")]
638 class_structure: Option<
639 EagerClassStructure<T, <<UG as CharacterProperties>::CharTab as CharacterTable>::ColSymbol>,
640 >,
641
642 #[builder(setter(skip), default = "None")]
644 pub(crate) ircorep_character_table: Option<CorepCharacterTable<RowSymbol, UG::CharTab>>,
645}
646
647impl<T, UG, RowSymbol> MagneticRepresentedGroupBuilder<T, UG, RowSymbol>
648where
649 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
650 for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
651 RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol>,
652 UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
653 <UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
654 CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
655 Serialize + DeserializeOwned,
656{
657 fn finite_subgroup_name(&mut self, name_opt: Option<String>) -> &mut Self {
658 if name_opt.is_some() {
659 if self.name.as_ref().expect("Group name not found.").clone() == *"O(3)"
660 || self
661 .name
662 .as_ref()
663 .expect("Group name not found.")
664 .contains('∞')
665 {
666 self.finite_subgroup_name = Some(name_opt);
667 } else {
668 panic!(
669 "Setting a finite subgroup name for a non-infinite group is not supported yet."
670 )
671 }
672 }
673 self
674 }
675
676 fn unitary_subgroup(&mut self, uni_subgrp: UG) -> &mut Self {
677 assert!(
678 uni_subgrp.elements().clone().into_iter().all(|op| self
679 .abstract_group
680 .as_ref()
681 .expect("Abstract group not yet set for this magnetic-represented group.")
682 .contains(&op)),
683 "Some unitary operations cannot be found in the magnetic group."
684 );
685 self.unitary_subgroup = Some(uni_subgrp);
686 self
687 }
688}
689
690impl<T, UG, RowSymbol> MagneticRepresentedGroup<T, UG, RowSymbol>
691where
692 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
693 RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol>,
694 UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
695 <UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
696 CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
697 Serialize + DeserializeOwned,
698{
699 pub(crate) fn set_finite_subgroup_name(&mut self, name: Option<String>) {
705 self.finite_subgroup_name = name;
706 }
707
708 pub fn unitary_subgroup(&self) -> &UG {
710 &self.unitary_subgroup
711 }
712}
713
714impl<T, UG, RowSymbol> MagneticRepresentedGroup<T, UG, RowSymbol>
715where
716 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
717 for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
718 RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol> + Serialize + DeserializeOwned,
719 UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
720 <UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
721 <UG as CharacterProperties>::CharTab: Serialize + DeserializeOwned,
722 CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
723 Serialize + DeserializeOwned,
724{
725 fn builder() -> MagneticRepresentedGroupBuilder<T, UG, RowSymbol> {
731 MagneticRepresentedGroupBuilder::<T, UG, RowSymbol>::default()
732 }
733
734 pub fn new(name: &str, elements: Vec<T>, unitary_subgroup: UG) -> Result<Self, anyhow::Error> {
748 let abstract_group = EagerGroup::<T>::new(name, elements);
749 let mut magnetic_group = MagneticRepresentedGroup::<T, UG, RowSymbol>::builder()
750 .name(name.to_string())
751 .abstract_group(abstract_group?)
752 .unitary_subgroup(unitary_subgroup)
753 .build()
754 .expect("Unable to construct a magnetic group.");
755 magnetic_group.compute_class_structure()?;
756 Ok(magnetic_group)
757 }
758}
759
760impl<T, UG, RowSymbol> GroupProperties for MagneticRepresentedGroup<T, UG, RowSymbol>
761where
762 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
763 for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
764 RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol>,
765 UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
766 <UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
767 CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
768 Serialize + DeserializeOwned,
769{
770 type GroupElement = T;
771 type ElementCollection = IndexSet<T>;
772
773 fn name(&self) -> String {
774 self.name.to_string()
775 }
776
777 fn finite_subgroup_name(&self) -> Option<&String> {
778 self.finite_subgroup_name.as_ref()
779 }
780
781 fn get_index(&self, index: usize) -> Option<Self::GroupElement> {
782 self.abstract_group.get_index(index)
783 }
784
785 fn get_index_of(&self, g: &Self::GroupElement) -> Option<usize> {
786 self.abstract_group.get_index_of(g)
787 }
788
789 fn contains(&self, g: &Self::GroupElement) -> bool {
790 self.abstract_group.contains(g)
791 }
792
793 fn elements(&self) -> &Self::ElementCollection {
794 self.abstract_group.elements()
795 }
796
797 fn is_abelian(&self) -> bool {
798 self.abstract_group.is_abelian()
799 }
800
801 fn order(&self) -> usize {
802 self.abstract_group.order()
803 }
804
805 fn cayley_table(&self) -> Option<&Array2<usize>> {
806 self.abstract_group.cayley_table()
807 }
808}
809
810impl<T, UG, RowSymbol> HasUnitarySubgroup for MagneticRepresentedGroup<T, UG, RowSymbol>
811where
812 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
813 for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
814 RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol>,
815 UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
816 <UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
817 CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
818 Serialize + DeserializeOwned,
819{
820 type UnitarySubgroup = UG;
821
822 fn unitary_subgroup(&self) -> &Self::UnitarySubgroup {
823 &self.unitary_subgroup
824 }
825
826 fn check_elem_antiunitary(&self, element: &T) -> bool {
840 if self.abstract_group.contains(element) {
841 !self.unitary_subgroup.contains(element)
842 } else {
843 panic!("`{element:?}` is not an element of the group.")
844 }
845 }
846}