use std::fmt;
use std::hash::Hash;
use std::ops::Mul;
use anyhow::{self, format_err};
use derive_builder::Builder;
use indexmap::IndexSet;
use log;
use ndarray::{Array2, Zip};
use num::Integer;
use num_traits::Inv;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use crate::chartab::chartab_group::CharacterProperties;
use crate::chartab::chartab_symbols::{
CollectionSymbol, LinearSpaceSymbol, ReducibleLinearSpaceSymbol,
};
use crate::chartab::{CharacterTable, CorepCharacterTable, RepCharacterTable};
use crate::group::class::{ClassProperties, EagerClassStructure};
pub mod class;
pub trait FiniteOrder {
type Int: Integer;
fn order(&self) -> Self::Int;
}
pub trait GroupProperties
where
Self::GroupElement: Mul<Output = Self::GroupElement>
+ Inv<Output = Self::GroupElement>
+ Hash
+ Eq
+ Clone
+ Sync
+ fmt::Debug
+ FiniteOrder,
{
type GroupElement;
type ElementCollection: Clone + IntoIterator<Item = Self::GroupElement>;
fn name(&self) -> String;
fn finite_subgroup_name(&self) -> Option<&String>;
fn elements(&self) -> &Self::ElementCollection;
fn get_index(&self, index: usize) -> Option<Self::GroupElement>;
fn get_index_of(&self, g: &Self::GroupElement) -> Option<usize>;
fn contains(&self, g: &Self::GroupElement) -> bool;
fn is_abelian(&self) -> bool;
fn order(&self) -> usize;
fn cayley_table(&self) -> Option<&Array2<usize>>;
}
pub trait HasUnitarySubgroup: GroupProperties
where
Self::UnitarySubgroup: GroupProperties<GroupElement = Self::GroupElement> + CharacterProperties,
{
type UnitarySubgroup;
fn unitary_subgroup(&self) -> &Self::UnitarySubgroup;
fn check_elem_antiunitary(&self, element: &Self::GroupElement) -> bool;
}
#[derive(Clone, Hash, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub enum GroupType {
Ordinary(bool),
MagneticGrey(bool),
MagneticBlackWhite(bool),
}
impl fmt::Display for GroupType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Ordinary(true) => write!(f, "Double ordinary group"),
Self::Ordinary(false) => write!(f, "Ordinary group"),
Self::MagneticGrey(true) => write!(f, "Double magnetic grey group"),
Self::MagneticGrey(false) => write!(f, "Magnetic grey group"),
Self::MagneticBlackWhite(true) => write!(f, "Double magnetic black-and-white group"),
Self::MagneticBlackWhite(false) => write!(f, "Magnetic black-and-white group"),
}
}
}
pub const ORGRP: GroupType = GroupType::Ordinary(false);
pub const ORGRP2: GroupType = GroupType::Ordinary(true);
pub const BWGRP: GroupType = GroupType::MagneticBlackWhite(false);
pub const BWGRP2: GroupType = GroupType::MagneticBlackWhite(true);
pub const GRGRP: GroupType = GroupType::MagneticGrey(false);
pub const GRGRP2: GroupType = GroupType::MagneticGrey(true);
#[derive(Builder, Clone, Serialize, Deserialize)]
pub struct EagerGroup<T>
where
T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
{
name: String,
#[builder(setter(custom))]
elements: IndexSet<T>,
#[builder(setter(skip), default = "None")]
cayley_table: Option<Array2<usize>>,
}
impl<T> EagerGroupBuilder<T>
where
T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
{
fn elements(&mut self, elems: Vec<T>) -> &mut Self {
self.elements = Some(elems.into_iter().collect());
self
}
fn elements_iter(&mut self, elems_iter: impl Iterator<Item = T>) -> &mut Self {
self.elements = Some(elems_iter.collect());
self
}
}
impl<T> EagerGroup<T>
where
T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
{
fn builder() -> EagerGroupBuilder<T> {
EagerGroupBuilder::<T>::default()
}
pub fn new(name: &str, elements: Vec<T>) -> Result<Self, anyhow::Error> {
let mut group = Self::builder()
.name(name.to_string())
.elements(elements)
.build()
.expect("Unable to construct a group.");
group.compute_cayley_table()?;
Ok(group)
}
pub fn new_no_ctb(name: &str, elements: Vec<T>) -> Self {
Self::builder()
.name(name.to_string())
.elements(elements)
.build()
.expect("Unable to construct a group.")
}
pub fn from_iter(
name: &str,
elements_iter: impl Iterator<Item = T>,
) -> Result<Self, anyhow::Error> {
let mut group = Self::builder()
.name(name.to_string())
.elements_iter(elements_iter)
.build()
.expect("Unable to construct a group.");
group.compute_cayley_table()?;
Ok(group)
}
pub fn from_iter_no_ctb(name: &str, elements_iter: impl Iterator<Item = T>) -> Self {
Self::builder()
.name(name.to_string())
.elements_iter(elements_iter)
.build()
.expect("Unable to construct a group.")
}
fn compute_cayley_table(&mut self) -> Result<(), anyhow::Error> {
log::debug!("Constructing Cayley table in parallel...");
let mut ctb = Array2::<usize>::zeros((self.order(), self.order()));
Zip::indexed(&mut ctb).par_map_collect(|(i, j), k| {
let op_i_ref = self.elements
.get_index(i)
.ok_or_else(|| format_err!("Element with index {i} cannot be retrieved."))?;
let op_j_ref = self.elements
.get_index(j)
.ok_or_else(|| format_err!("Element with index {j} cannot be retrieved."))?;
let op_k = op_i_ref * op_j_ref;
*k = self.elements
.get_index_of(&op_k)
.ok_or_else(||
format_err!("Group closure not fulfilled. The composition {:?} * {:?} = {:?} is not contained in the group. Try relaxing distance thresholds.",
op_i_ref,
op_j_ref,
&op_k)
)?;
Ok::<(), anyhow::Error>(())
}).into_iter().collect::<Result<(), anyhow::Error>>()?;
self.cayley_table = Some(ctb);
log::debug!("Constructing Cayley table in parallel... Done.");
Ok(())
}
}
impl<T> GroupProperties for EagerGroup<T>
where
T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
{
type GroupElement = T;
type ElementCollection = IndexSet<T>;
fn name(&self) -> String {
self.name.to_string()
}
fn finite_subgroup_name(&self) -> Option<&String> {
None
}
fn get_index(&self, index: usize) -> Option<Self::GroupElement> {
self.elements.get_index(index).cloned()
}
fn get_index_of(&self, g: &Self::GroupElement) -> Option<usize> {
self.elements.get_index_of(g)
}
fn contains(&self, g: &Self::GroupElement) -> bool {
self.elements.contains(g)
}
fn elements(&self) -> &Self::ElementCollection {
&self.elements
}
fn is_abelian(&self) -> bool {
if let Some(ctb) = &self.cayley_table {
ctb == ctb.t()
} else {
self.elements.iter().enumerate().all(|(i, gi)| {
(0..i).all(|j| {
let gj = self
.elements
.get_index(j)
.unwrap_or_else(|| panic!("Element with index `{j}` not found."));
gi * gj == gj * gi
})
})
}
}
fn order(&self) -> usize {
self.elements.len()
}
fn cayley_table(&self) -> Option<&Array2<usize>> {
self.cayley_table.as_ref()
}
}
#[derive(Clone, Builder, Serialize, Deserialize)]
pub struct UnitaryRepresentedGroup<T, RowSymbol, ColSymbol>
where
T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
RowSymbol: LinearSpaceSymbol,
ColSymbol: CollectionSymbol<CollectionElement = T>,
{
name: String,
#[builder(setter(custom), default = "None")]
finite_subgroup_name: Option<String>,
abstract_group: EagerGroup<T>,
#[builder(setter(skip), default = "None")]
class_structure: Option<EagerClassStructure<T, ColSymbol>>,
#[builder(setter(skip), default = "None")]
pub(crate) irrep_character_table: Option<RepCharacterTable<RowSymbol, ColSymbol>>,
}
impl<T, RowSymbol, ColSymbol> UnitaryRepresentedGroupBuilder<T, RowSymbol, ColSymbol>
where
T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
RowSymbol: LinearSpaceSymbol,
ColSymbol: CollectionSymbol<CollectionElement = T>,
{
fn finite_subgroup_name(&mut self, name_opt: Option<String>) -> &mut Self {
if name_opt.is_some() {
if self.name.as_ref().expect("Group name not found.").clone() == *"O(3)"
|| self
.name
.as_ref()
.expect("Group name not found.")
.contains('∞')
{
self.finite_subgroup_name = Some(name_opt);
} else {
panic!(
"Setting a finite subgroup name for a non-infinite group is not supported yet."
)
}
}
self
}
}
impl<T, RowSymbol, ColSymbol> UnitaryRepresentedGroup<T, RowSymbol, ColSymbol>
where
T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
RowSymbol: LinearSpaceSymbol,
ColSymbol: CollectionSymbol<CollectionElement = T>,
{
pub(crate) fn set_finite_subgroup_name(&mut self, name: Option<String>) {
self.finite_subgroup_name = name;
}
}
impl<T, RowSymbol, ColSymbol> UnitaryRepresentedGroup<T, RowSymbol, ColSymbol>
where
T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
RowSymbol: LinearSpaceSymbol,
ColSymbol: CollectionSymbol<CollectionElement = T>,
{
fn builder() -> UnitaryRepresentedGroupBuilder<T, RowSymbol, ColSymbol> {
UnitaryRepresentedGroupBuilder::<T, RowSymbol, ColSymbol>::default()
}
pub fn new(name: &str, elements: Vec<T>) -> Result<Self, anyhow::Error> {
let abstract_group = EagerGroup::<T>::new(name, elements);
let mut unitary_group = UnitaryRepresentedGroup::<T, RowSymbol, ColSymbol>::builder()
.name(name.to_string())
.abstract_group(abstract_group?)
.build()
.expect("Unable to construct a unitary group.");
unitary_group.compute_class_structure()?;
Ok(unitary_group)
}
}
impl<T, RowSymbol, ColSymbol> GroupProperties for UnitaryRepresentedGroup<T, RowSymbol, ColSymbol>
where
T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
RowSymbol: LinearSpaceSymbol,
ColSymbol: CollectionSymbol<CollectionElement = T>,
{
type GroupElement = T;
type ElementCollection = IndexSet<Self::GroupElement>;
fn name(&self) -> String {
self.name.to_string()
}
fn finite_subgroup_name(&self) -> Option<&String> {
self.finite_subgroup_name.as_ref()
}
fn get_index(&self, index: usize) -> Option<Self::GroupElement> {
self.abstract_group.get_index(index)
}
fn get_index_of(&self, g: &Self::GroupElement) -> Option<usize> {
self.abstract_group.get_index_of(g)
}
fn contains(&self, g: &Self::GroupElement) -> bool {
self.abstract_group.contains(g)
}
fn elements(&self) -> &Self::ElementCollection {
self.abstract_group.elements()
}
fn is_abelian(&self) -> bool {
self.abstract_group.is_abelian()
}
fn order(&self) -> usize {
self.abstract_group.order()
}
fn cayley_table(&self) -> Option<&Array2<usize>> {
self.abstract_group.cayley_table()
}
}
#[derive(Clone, Builder, Serialize, Deserialize)]
pub struct MagneticRepresentedGroup<T, UG, RowSymbol>
where
T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol>,
UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
<UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
Serialize + DeserializeOwned,
{
name: String,
#[builder(setter(custom), default = "None")]
finite_subgroup_name: Option<String>,
abstract_group: EagerGroup<T>,
#[builder(setter(custom))]
unitary_subgroup: UG,
#[builder(setter(skip), default = "None")]
class_structure: Option<
EagerClassStructure<T, <<UG as CharacterProperties>::CharTab as CharacterTable>::ColSymbol>,
>,
#[builder(setter(skip), default = "None")]
pub(crate) ircorep_character_table: Option<CorepCharacterTable<RowSymbol, UG::CharTab>>,
}
impl<T, UG, RowSymbol> MagneticRepresentedGroupBuilder<T, UG, RowSymbol>
where
T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol>,
UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
<UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
Serialize + DeserializeOwned,
{
fn finite_subgroup_name(&mut self, name_opt: Option<String>) -> &mut Self {
if name_opt.is_some() {
if self.name.as_ref().expect("Group name not found.").clone() == *"O(3)"
|| self
.name
.as_ref()
.expect("Group name not found.")
.contains('∞')
{
self.finite_subgroup_name = Some(name_opt);
} else {
panic!(
"Setting a finite subgroup name for a non-infinite group is not supported yet."
)
}
}
self
}
fn unitary_subgroup(&mut self, uni_subgrp: UG) -> &mut Self {
assert!(
uni_subgrp.elements().clone().into_iter().all(|op| self
.abstract_group
.as_ref()
.expect("Abstract group not yet set for this magnetic-represented group.")
.contains(&op)),
"Some unitary operations cannot be found in the magnetic group."
);
self.unitary_subgroup = Some(uni_subgrp);
self
}
}
impl<T, UG, RowSymbol> MagneticRepresentedGroup<T, UG, RowSymbol>
where
T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol>,
UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
<UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
Serialize + DeserializeOwned,
{
pub(crate) fn set_finite_subgroup_name(&mut self, name: Option<String>) {
self.finite_subgroup_name = name;
}
pub fn unitary_subgroup(&self) -> &UG {
&self.unitary_subgroup
}
}
impl<T, UG, RowSymbol> MagneticRepresentedGroup<T, UG, RowSymbol>
where
T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol> + Serialize + DeserializeOwned,
UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
<UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
<UG as CharacterProperties>::CharTab: Serialize + DeserializeOwned,
CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
Serialize + DeserializeOwned,
{
fn builder() -> MagneticRepresentedGroupBuilder<T, UG, RowSymbol> {
MagneticRepresentedGroupBuilder::<T, UG, RowSymbol>::default()
}
pub fn new(name: &str, elements: Vec<T>, unitary_subgroup: UG) -> Result<Self, anyhow::Error> {
let abstract_group = EagerGroup::<T>::new(name, elements);
let mut magnetic_group = MagneticRepresentedGroup::<T, UG, RowSymbol>::builder()
.name(name.to_string())
.abstract_group(abstract_group?)
.unitary_subgroup(unitary_subgroup)
.build()
.expect("Unable to construct a magnetic group.");
magnetic_group.compute_class_structure()?;
Ok(magnetic_group)
}
}
impl<T, UG, RowSymbol> GroupProperties for MagneticRepresentedGroup<T, UG, RowSymbol>
where
T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol>,
UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
<UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
Serialize + DeserializeOwned,
{
type GroupElement = T;
type ElementCollection = IndexSet<T>;
fn name(&self) -> String {
self.name.to_string()
}
fn finite_subgroup_name(&self) -> Option<&String> {
self.finite_subgroup_name.as_ref()
}
fn get_index(&self, index: usize) -> Option<Self::GroupElement> {
self.abstract_group.get_index(index)
}
fn get_index_of(&self, g: &Self::GroupElement) -> Option<usize> {
self.abstract_group.get_index_of(g)
}
fn contains(&self, g: &Self::GroupElement) -> bool {
self.abstract_group.contains(g)
}
fn elements(&self) -> &Self::ElementCollection {
self.abstract_group.elements()
}
fn is_abelian(&self) -> bool {
self.abstract_group.is_abelian()
}
fn order(&self) -> usize {
self.abstract_group.order()
}
fn cayley_table(&self) -> Option<&Array2<usize>> {
self.abstract_group.cayley_table()
}
}
impl<T, UG, RowSymbol> HasUnitarySubgroup for MagneticRepresentedGroup<T, UG, RowSymbol>
where
T: Mul<Output = T> + Inv<Output = T> + Hash + Eq + Clone + Sync + fmt::Debug + FiniteOrder,
for<'a, 'b> &'b T: Mul<&'a T, Output = T>,
RowSymbol: ReducibleLinearSpaceSymbol<Subspace = UG::RowSymbol>,
UG: Clone + GroupProperties<GroupElement = T> + CharacterProperties,
<UG as ClassProperties>::ClassSymbol: Serialize + DeserializeOwned,
CorepCharacterTable<RowSymbol, <UG as CharacterProperties>::CharTab>:
Serialize + DeserializeOwned,
{
type UnitarySubgroup = UG;
fn unitary_subgroup(&self) -> &Self::UnitarySubgroup {
&self.unitary_subgroup
}
fn check_elem_antiunitary(&self, element: &T) -> bool {
if self.abstract_group.contains(element) {
!self.unitary_subgroup.contains(element)
} else {
panic!("`{element:?}` is not an element of the group.")
}
}
}