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::{Deserialize, Serialize, de::DeserializeOwned};
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 pub 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 RowSymbol: LinearSpaceSymbol,
465 ColSymbol: CollectionSymbol<CollectionElement = T>,
466{
467 #[allow(dead_code)]
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 pub fn builder() -> UnitaryRepresentedGroupBuilder<T, RowSymbol, ColSymbol> {
517 UnitaryRepresentedGroupBuilder::<T, RowSymbol, ColSymbol>::default()
518 }
519
520 pub fn new(
532 name: &str,
533 elements: Vec<T>,
534 ) -> Result<Self, anyhow::Error> {
535 let abstract_group = EagerGroup::<T>::new(name, elements);
536 let mut unitary_group = UnitaryRepresentedGroup::<T, RowSymbol, ColSymbol>::builder()
537 .name(name.to_string())
538 .abstract_group(abstract_group?)
539 .build()
540 .map_err(|err| format_err!(err))?;
541 unitary_group.compute_class_structure()?;
542 Ok(unitary_group)
543 }
544}
545
546impl<T, RowSymbol, ColSymbol> GroupProperties for UnitaryRepresentedGroup<T, RowSymbol, ColSymbol>
547where
548 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
549 for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
550 RowSymbol: LinearSpaceSymbol,
551 ColSymbol: CollectionSymbol<CollectionElement = T>,
552{
553 type GroupElement = T;
554 type ElementCollection = IndexSet<Self::GroupElement>;
555
556 fn name(&self) -> String {
557 self.name.to_string()
558 }
559
560 fn finite_subgroup_name(&self) -> Option<&String> {
561 self.finite_subgroup_name.as_ref()
562 }
563
564 fn get_index(&self, index: usize) -> Option<Self::GroupElement> {
565 self.abstract_group.get_index(index)
566 }
567
568 fn get_index_of(&self, g: &Self::GroupElement) -> Option<usize> {
569 self.abstract_group.get_index_of(g)
570 }
571
572 fn contains(&self, g: &Self::GroupElement) -> bool {
573 self.abstract_group.contains(g)
574 }
575
576 fn elements(&self) -> &Self::ElementCollection {
577 self.abstract_group.elements()
578 }
579
580 fn is_abelian(&self) -> bool {
581 self.abstract_group.is_abelian()
582 }
583
584 fn order(&self) -> usize {
585 self.abstract_group.order()
586 }
587
588 fn cayley_table(&self) -> Option<&Array2<usize>> {
589 self.abstract_group.cayley_table()
590 }
591}
592
593#[derive(Clone, Builder, Serialize, Deserialize)]
606pub struct MagneticRepresentedGroup<T, UG, RowSymbol>
607where
608 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
609 RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol>,
610 UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
611 <UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
612 CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
613 Serialize + DeserializeOwned,
614{
615 name: String,
617
618 #[builder(setter(custom), default = "None")]
621 finite_subgroup_name: Option<String>,
622
623 abstract_group: EagerGroup<T>,
625
626 #[builder(setter(custom))]
628 unitary_subgroup: UG,
629
630 #[builder(setter(skip), default = "None")]
641 class_structure: Option<
642 EagerClassStructure<T, <<UG as CharacterProperties>::CharTab as CharacterTable>::ColSymbol>,
643 >,
644
645 #[builder(setter(skip), default = "None")]
647 pub(crate) ircorep_character_table: Option<CorepCharacterTable<RowSymbol, UG::CharTab>>,
648}
649
650impl<T, UG, RowSymbol> MagneticRepresentedGroupBuilder<T, UG, RowSymbol>
651where
652 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
653 for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
654 RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol>,
655 UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
656 <UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
657 CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
658 Serialize + DeserializeOwned,
659{
660 #[allow(dead_code)]
661 fn finite_subgroup_name(&mut self, name_opt: Option<String>) -> &mut Self {
662 if name_opt.is_some() {
663 if self.name.as_ref().expect("Group name not found.").clone() == *"O(3)"
664 || self
665 .name
666 .as_ref()
667 .expect("Group name not found.")
668 .contains('∞')
669 {
670 self.finite_subgroup_name = Some(name_opt);
671 } else {
672 panic!(
673 "Setting a finite subgroup name for a non-infinite group is not supported yet."
674 )
675 }
676 }
677 self
678 }
679
680 fn unitary_subgroup(&mut self, uni_subgrp: UG) -> &mut Self {
681 assert!(
682 uni_subgrp.elements().clone().into_iter().all(|op| self
683 .abstract_group
684 .as_ref()
685 .expect("Abstract group not yet set for this magnetic-represented group.")
686 .contains(&op)),
687 "Some unitary operations cannot be found in the magnetic group."
688 );
689 self.unitary_subgroup = Some(uni_subgrp);
690 self
691 }
692}
693
694impl<T, UG, RowSymbol> MagneticRepresentedGroup<T, UG, RowSymbol>
695where
696 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
697 RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol>,
698 UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
699 <UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
700 CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
701 Serialize + DeserializeOwned,
702{
703 pub(crate) fn set_finite_subgroup_name(&mut self, name: Option<String>) {
709 self.finite_subgroup_name = name;
710 }
711
712 pub fn unitary_subgroup(&self) -> &UG {
714 &self.unitary_subgroup
715 }
716}
717
718impl<T, UG, RowSymbol> MagneticRepresentedGroup<T, UG, RowSymbol>
719where
720 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
721 for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
722 RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol> + Serialize + DeserializeOwned,
723 UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
724 <UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
725 <UG as CharacterProperties>::CharTab: Serialize + DeserializeOwned,
726 CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
727 Serialize + DeserializeOwned,
728{
729 pub fn builder() -> MagneticRepresentedGroupBuilder<T, UG, RowSymbol> {
735 MagneticRepresentedGroupBuilder::<T, UG, RowSymbol>::default()
736 }
737
738 pub fn new(name: &str, elements: Vec<T>, unitary_subgroup: UG) -> Result<Self, anyhow::Error> {
752 let abstract_group = EagerGroup::<T>::new(name, elements);
753 let mut magnetic_group = MagneticRepresentedGroup::<T, UG, RowSymbol>::builder()
754 .name(name.to_string())
755 .abstract_group(abstract_group?)
756 .unitary_subgroup(unitary_subgroup)
757 .build()
758 .expect("Unable to construct a magnetic group.");
759 magnetic_group.compute_class_structure()?;
760 Ok(magnetic_group)
761 }
762}
763
764impl<T, UG, RowSymbol> GroupProperties for MagneticRepresentedGroup<T, UG, RowSymbol>
765where
766 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
767 for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
768 RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol>,
769 UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
770 <UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
771 CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
772 Serialize + DeserializeOwned,
773{
774 type GroupElement = T;
775 type ElementCollection = IndexSet<T>;
776
777 fn name(&self) -> String {
778 self.name.to_string()
779 }
780
781 fn finite_subgroup_name(&self) -> Option<&String> {
782 self.finite_subgroup_name.as_ref()
783 }
784
785 fn get_index(&self, index: usize) -> Option<Self::GroupElement> {
786 self.abstract_group.get_index(index)
787 }
788
789 fn get_index_of(&self, g: &Self::GroupElement) -> Option<usize> {
790 self.abstract_group.get_index_of(g)
791 }
792
793 fn contains(&self, g: &Self::GroupElement) -> bool {
794 self.abstract_group.contains(g)
795 }
796
797 fn elements(&self) -> &Self::ElementCollection {
798 self.abstract_group.elements()
799 }
800
801 fn is_abelian(&self) -> bool {
802 self.abstract_group.is_abelian()
803 }
804
805 fn order(&self) -> usize {
806 self.abstract_group.order()
807 }
808
809 fn cayley_table(&self) -> Option<&Array2<usize>> {
810 self.abstract_group.cayley_table()
811 }
812}
813
814impl<T, UG, RowSymbol> HasUnitarySubgroup for MagneticRepresentedGroup<T, UG, RowSymbol>
815where
816 T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
817 for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
818 RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol>,
819 UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
820 <UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
821 CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
822 Serialize + DeserializeOwned,
823{
824 type UnitarySubgroup = UG;
825
826 fn unitary_subgroup(&self) -> &Self::UnitarySubgroup {
827 &self.unitary_subgroup
828 }
829
830 fn check_elem_antiunitary(&self, element: &T) -> bool {
844 if self.abstract_group.contains(element) {
845 !self.unitary_subgroup.contains(element)
846 } else {
847 panic!("`{element:?}` is not an element of the group.")
848 }
849 }
850}