qsym2/group/
mod.rs

1//! Abstract group structures.
2
3use 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
25// =================
26// Trait definitions
27// =================
28
29/// Trait for order finiteness of group elements.
30pub trait FiniteOrder {
31    /// The integer type for the order of the element.
32    type Int: Integer;
33
34    /// Returns the finite order of the element.
35    fn order(&self) -> Self::Int;
36}
37
38/// Trait for purely group-theoretic properties.
39pub 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    /// The type of the elements in the group.
51    type GroupElement;
52    type ElementCollection: Clone + IntoIterator<Item = Self::GroupElement>;
53
54    /// Returns the name of the group.
55    fn name(&self) -> String;
56
57    /// Returns the finite subgroup name of this group, if any.
58    fn finite_subgroup_name(&self) -> Option<&String>;
59
60    /// Returns an iterable collection of the elements in the group.
61    fn elements(&self) -> &Self::ElementCollection;
62
63    /// Given an index, returns the element associated with it, or `None` if the index is out of
64    /// range.
65    fn get_index(&self, index: usize) -> Option<Self::GroupElement>;
66
67    /// Given an element, returns its index in the group, or `None` if the element does not exist
68    /// in the group.
69    fn get_index_of(&self, g: &Self::GroupElement) -> Option<usize>;
70
71    /// Checks if an element is a member of the group.
72    fn contains(&self, g: &Self::GroupElement) -> bool;
73
74    /// Checks if this group is abelian.
75    fn is_abelian(&self) -> bool;
76
77    /// Returns the order of the group.
78    fn order(&self) -> usize;
79
80    /// Returns the Cayley table of the group, if any.
81    fn cayley_table(&self) -> Option<&Array2<usize>>;
82}
83
84/// Trait for indicating that a group can be partitioned into a unitary halving subgroup and and
85/// antiunitary coset.
86pub trait HasUnitarySubgroup: GroupProperties
87where
88    Self::UnitarySubgroup: GroupProperties<GroupElement = Self::GroupElement> + CharacterProperties,
89{
90    /// The type of the unitary halving subgroup.
91    type UnitarySubgroup;
92
93    /// Returns a shared reference to the unitary subgroup associated with this group.
94    fn unitary_subgroup(&self) -> &Self::UnitarySubgroup;
95
96    /// Checks if an element in the group belongs to the antiunitary coset.
97    ///
98    /// # Arguments
99    ///
100    /// * `element` - A group element.
101    ///
102    /// # Returns
103    ///
104    /// Returns `true` if `element` is in the antiunitary coset of the group.
105    ///
106    /// # Panics
107    ///
108    /// Panics if `element` is not a member of the group.
109    fn check_elem_antiunitary(&self, element: &Self::GroupElement) -> bool;
110}
111
112// ====================================
113// Enum definitions and implementations
114// ====================================
115
116/// Enumerated type for the type of a group.
117#[derive(Clone, Hash, PartialEq, Eq, Debug, Serialize, Deserialize)]
118pub enum GroupType {
119    /// Variant for an ordinary group which contains no time-reversed operations.
120    ///
121    /// The associated boolean indicates whether this group is a double group or not.
122    Ordinary(bool),
123
124    /// Variant for a magnetic grey group which contains the time-reversal operation.
125    ///
126    /// The associated boolean indicates whether this group is a double group or not.
127    MagneticGrey(bool),
128
129    /// Variant for a magnetic black and white group which contains time-reversed operations, but
130    /// not the time-reversal operation itself.
131    ///
132    /// The associated boolean indicates whether this group is a double group or not.
133    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
149/// Ordinary group.
150pub const ORGRP: GroupType = GroupType::Ordinary(false);
151
152/// Ordinary double group.
153pub const ORGRP2: GroupType = GroupType::Ordinary(true);
154
155/// Black-and-white magnetic group.
156pub const BWGRP: GroupType = GroupType::MagneticBlackWhite(false);
157
158/// Black-and-white magnetic double group.
159pub const BWGRP2: GroupType = GroupType::MagneticBlackWhite(true);
160
161/// Grey magnetic group.
162pub const GRGRP: GroupType = GroupType::MagneticGrey(false);
163
164/// Grey magnetic double group.
165pub const GRGRP2: GroupType = GroupType::MagneticGrey(true);
166
167// ======================================
168// Struct definitions and implementations
169// ======================================
170
171// --------------
172// Abstract group
173// --------------
174
175/// Structure for managing abstract groups eagerly, *i.e.* all group elements are stored.
176#[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    /// A name for the abstract group.
182    name: String,
183
184    /// An ordered hashset containing the elements of the group.
185    #[builder(setter(custom))]
186    elements: IndexSet<T>,
187
188    /// The Cayley table for this group w.r.t. the elements in [`Self::elements`].
189    ///
190    /// Elements are multiplied left-to-right, with functions written on the right.
191    /// Row elements are on the left, column elements on the right.  So column
192    /// elements act on the function first, followed by row elements.
193    ///
194    /// Each element in this array contains the index of the resultant operation
195    /// from the composition, w.r.t. the ordered hashset [`Self::elements`].
196    #[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    /// Returns a builder to construct a new abstract group.
222    ///
223    /// # Returns
224    ///
225    /// A builder to construct a new abstract group.
226    ///
227    /// # Panics
228    ///
229    /// Panics if the group fails to be constructed.
230    fn builder() -> EagerGroupBuilder<T> {
231        EagerGroupBuilder::<T>::default()
232    }
233
234    /// Constructs an abstract group from its elements and calculate its Cayley table.
235    ///
236    /// # Arguments
237    ///
238    /// * `name` - A name to be given to the abstract group.
239    /// * `elements` - A vector of *all* group elements.
240    ///
241    /// # Returns
242    ///
243    /// An abstract group with its Cayley table constructed.
244    ///
245    /// # Panics
246    ///
247    /// Panics if the group fails to be constructed.
248    ///
249    /// # Errors
250    ///
251    /// Errors if the Cayley table fails to be constructed.
252    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    /// Constructs an abstract group from its elements but without calculating its Cayley table.
263    ///
264    /// # Arguments
265    ///
266    /// * `name` - A name to be given to the abstract group.
267    /// * `elements` - A vector of *all* group elements.
268    ///
269    /// # Returns
270    ///
271    /// An abstract group without its Cayley table constructed.
272    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    /// Constructs an abstract group from an iterator of its elements and calculate its Cayley
281    /// table.
282    ///
283    /// # Arguments
284    ///
285    /// * `name` - A name to be given to the abstract group.
286    /// * `elements_iter` - An iterator yielding group elements.
287    ///
288    /// # Returns
289    ///
290    /// An abstract group with its Cayley table constructed.
291    ///
292    /// # Panics
293    ///
294    /// Panics if the group fails to be constructed.
295    ///
296    /// # Errors
297    ///
298    /// Errors if the Cayley table fails to be constructed.
299    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    /// Constructs an abstract group from an iterator of its elements but without calculating its
313    /// Cayley table.
314    ///
315    /// # Arguments
316    ///
317    /// * `name` - A name to be given to the abstract group.
318    /// * `elements_iter` - An iterator yielding group elements.
319    ///
320    /// # Panics
321    ///
322    /// Panics if the group fails to be constructed.
323    ///
324    /// # Returns
325    ///
326    /// An abstract group without its Cayley table constructed.
327    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    /// Constructs the Cayley table for the abstract group.
336    ///
337    /// # Errors
338    ///
339    /// Errors if the Cayley table fails to be constructed.
340    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// -------------------------
425// Unitary-represented group
426// -------------------------
427
428/// Structure for managing groups with unitary representations.
429#[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    /// A name for the unitary-represented group.
437    name: String,
438
439    /// An optional name if this unitary group is actually a finite subgroup of an infinite
440    /// group [`Self::name`].
441    #[builder(setter(custom), default = "None")]
442    finite_subgroup_name: Option<String>,
443
444    /// The underlying abstract group of this unitary-represented group.
445    abstract_group: EagerGroup<T>,
446
447    /// The class structure of this unitary-represented group that is induced by the following
448    /// equivalence relation:
449    ///
450    /// ```math
451    ///     g \sim h \Leftrightarrow \exists u : h = u g u ^{-1}.
452    /// ```
453    #[builder(setter(skip), default = "None")]
454    class_structure: Option<EagerClassStructure<T, ColSymbol>>,
455
456    /// The character table for the irreducible representations of this group.
457    #[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    /// Sets the finite subgroup name of this group.
495    ///
496    /// # Arguments
497    ///
498    /// * `name` - A name to be set as the finite subgroup name of this group.
499    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    /// Returns a builder to construct a new unitary-represented group.
512    ///
513    /// # Returns
514    ///
515    /// A builder to construct a new unitary-represented group.
516    fn builder() -> UnitaryRepresentedGroupBuilder<T, RowSymbol, ColSymbol> {
517        UnitaryRepresentedGroupBuilder::<T, RowSymbol, ColSymbol>::default()
518    }
519
520    /// Constructs a unitary-represented group from its elements.
521    ///
522    /// # Arguments
523    ///
524    /// * `name` - A name to be given to the unitary-represented group.
525    /// * `elements` - A vector of *all* group elements.
526    ///
527    /// # Returns
528    ///
529    /// A unitary-represented group with its Cayley table constructed and conjugacy classes
530    /// determined.
531    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// --------------------------
591// Magnetic-represented group
592// --------------------------
593
594/// Structure for managing groups with magnetic corepresentations.
595///
596/// Such a group consists of two types of elements in equal numbers: those that are unitary
597/// represented and those that are antiunitary represented. This division of elements affects the
598/// class structure of the group via an equivalence relation defined in
599/// Newmarch, J. D. & Golding, R. M. The character table for the corepresentations of magnetic
600/// groups. *Journal of Mathematical Physics* **23**, 695–704 (1982),
601/// [DOI](http://aip.scitation.org/doi/10.1063/1.525423).
602#[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    /// A name for the magnetic-represented group.
613    name: String,
614
615    /// An optional name if this unitary group is actually a finite subgroup of an infinite
616    /// group [`Self::name`].
617    #[builder(setter(custom), default = "None")]
618    finite_subgroup_name: Option<String>,
619
620    /// The underlying abstract group of this magnetic-represented group.
621    abstract_group: EagerGroup<T>,
622
623    /// The subgroup consisting of the unitary-represented elements in the full group.
624    #[builder(setter(custom))]
625    unitary_subgroup: UG,
626
627    /// The class structure of this magnetic-represented group that is induced by the following
628    /// equivalence relation:
629    ///
630    /// ```math
631    ///     g \sim h \Leftrightarrow \exists u : h = u g u^{-1} \quad \textrm{or} \quad \exists a : h = a
632    ///     g^{-1} a^{-1},
633    /// ```
634    ///
635    /// where $`u`$ is unitary-represented (*i.e.* $`u`$ is in [`Self::unitary_subgroup`]) and
636    /// $`a`$ is antiunitary-represented (*i.e.* $`a`$ is not in [`Self::unitary_subgroup`]).
637    #[builder(setter(skip), default = "None")]
638    class_structure: Option<
639        EagerClassStructure<T, <<UG as CharacterProperties>::CharTab as CharacterTable>::ColSymbol>,
640    >,
641
642    /// The character table for the irreducible corepresentations of this group.
643    #[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    /// Sets the finite subgroup name of this group.
700    ///
701    /// # Arguments
702    ///
703    /// * `name` - A name to be set as the finite subgroup name of this group.
704    pub(crate) fn set_finite_subgroup_name(&mut self, name: Option<String>) {
705        self.finite_subgroup_name = name;
706    }
707
708    /// Returns a shared reference to the unitary subgroup of this group.
709    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    /// Returns a builder to construct a new magnetic-represented group.
726    ///
727    /// # Returns
728    ///
729    /// A builder to construct a new magnetic-represented group.
730    fn builder() -> MagneticRepresentedGroupBuilder<T, UG, RowSymbol> {
731        MagneticRepresentedGroupBuilder::<T, UG, RowSymbol>::default()
732    }
733
734    /// Constructs a magnetic-represented group from its elements and the unitary subgroup.
735    ///
736    /// # Arguments
737    ///
738    /// * `name` - A name to be given to the magnetic-reprented group.
739    /// * `elements` - A vector of *all* group elements.
740    /// * `unitary_subgroup` - The unitary subgroup of the magnetic-represented group. All elements
741    /// of this must be present in `elements`.
742    ///
743    /// # Returns
744    ///
745    /// A magnetic-represented group with its Cayley table constructed and conjugacy classes
746    /// determined.
747    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    /// Checks if a given element is antiunitary-represented in this group.
827    ///
828    /// # Arguments
829    ///
830    /// `element` - A reference to an element to be checked.
831    ///
832    /// # Returns
833    ///
834    /// Returns `true` if `element` is antiunitary-represented in this group.
835    ///
836    /// # Panics
837    ///
838    /// Panics if `element` is not in the group.
839    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}