use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum RootSystem {
A(usize),
B(usize),
C(usize),
D(usize),
G2,
F4,
E6,
E7,
E8,
}
impl RootSystem {
pub fn rank(self) -> usize {
match self {
RootSystem::A(r) | RootSystem::B(r) | RootSystem::C(r) | RootSystem::D(r) => r,
RootSystem::G2 => 2,
RootSystem::F4 => 4,
RootSystem::E6 => 6,
RootSystem::E7 => 7,
RootSystem::E8 => 8,
}
}
}
impl fmt::Display for RootSystem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RootSystem::A(r) => write!(f, "A{r}"),
RootSystem::B(r) => write!(f, "B{r}"),
RootSystem::C(r) => write!(f, "C{r}"),
RootSystem::D(r) => write!(f, "D{r}"),
RootSystem::G2 => write!(f, "G2"),
RootSystem::F4 => write!(f, "F4"),
RootSystem::E6 => write!(f, "E6"),
RootSystem::E7 => write!(f, "E7"),
RootSystem::E8 => write!(f, "E8"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum CenterSubgroup {
Zk(usize),
Z2,
Z4,
DVector,
DHalfSpinPlus,
DHalfSpinMinus,
DFull,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum GlobalForm {
SimplyConnected,
Quotient(CenterSubgroup),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct GroupId {
pub root_system: RootSystem,
pub form: GlobalForm,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum GroupError {
UnsupportedRank {
family: &'static str,
value: usize,
},
NotACenterSubgroup {
n: usize,
k: usize,
},
}
impl fmt::Display for GroupError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
GroupError::UnsupportedRank { family, value } => write!(
f,
"{family}({value}) is not a supported rank for this family"
),
GroupError::NotACenterSubgroup { n, k } => write!(
f,
"Z{k} is not a subgroup of the SU({n}) center Z{n}: {k} does not divide {n}"
),
}
}
}
impl std::error::Error for GroupError {}
impl GroupId {
pub fn su(n: usize) -> Result<Self, GroupError> {
if n < 2 {
return Err(GroupError::UnsupportedRank {
family: "su",
value: n,
});
}
Ok(GroupId {
root_system: RootSystem::A(n - 1),
form: GlobalForm::SimplyConnected,
})
}
pub fn su_quotient(n: usize, k: usize) -> Result<Self, GroupError> {
if n < 2 {
return Err(GroupError::UnsupportedRank {
family: "su",
value: n,
});
}
if k == 0 || !n.is_multiple_of(k) {
return Err(GroupError::NotACenterSubgroup { n, k });
}
Ok(GroupId {
root_system: RootSystem::A(n - 1),
form: GlobalForm::Quotient(CenterSubgroup::Zk(k)),
})
}
pub fn psu(n: usize) -> Result<Self, GroupError> {
Self::su_quotient(n, n)
}
pub fn sp(m: usize) -> Result<Self, GroupError> {
Ok(GroupId {
root_system: RootSystem::C(sp_rank(m)?),
form: GlobalForm::SimplyConnected,
})
}
pub fn psp(m: usize) -> Result<Self, GroupError> {
Ok(GroupId {
root_system: RootSystem::C(sp_rank(m)?),
form: GlobalForm::Quotient(CenterSubgroup::Z2),
})
}
pub fn spin(n: usize) -> Result<Self, GroupError> {
Ok(GroupId {
root_system: orthogonal(n, "spin")?,
form: GlobalForm::SimplyConnected,
})
}
pub fn so(n: usize) -> Result<Self, GroupError> {
let root_system = orthogonal(n, "so")?;
let sub = match root_system {
RootSystem::D(_) => CenterSubgroup::DVector,
_ => CenterSubgroup::Z2,
};
Ok(GroupId {
root_system,
form: GlobalForm::Quotient(sub),
})
}
pub fn pso(n: usize) -> Result<Self, GroupError> {
let root_system = orthogonal(n, "pso")?;
let sub = match root_system {
RootSystem::B(_) => CenterSubgroup::Z2,
RootSystem::D(r) if r % 2 == 0 => CenterSubgroup::DFull,
_ => CenterSubgroup::Z4,
};
Ok(GroupId {
root_system,
form: GlobalForm::Quotient(sub),
})
}
pub fn half_spin_plus(n: usize) -> Result<Self, GroupError> {
Ok(GroupId {
root_system: half_spin_root_system(n)?,
form: GlobalForm::Quotient(CenterSubgroup::DHalfSpinPlus),
})
}
pub fn half_spin_minus(n: usize) -> Result<Self, GroupError> {
Ok(GroupId {
root_system: half_spin_root_system(n)?,
form: GlobalForm::Quotient(CenterSubgroup::DHalfSpinMinus),
})
}
pub fn admits(&self, dynkin: &[i64]) -> bool {
let r = self.root_system.rank();
if dynkin.len() != r || dynkin.iter().any(|&a| a < 0) {
return false;
}
let sub = match self.form {
GlobalForm::SimplyConnected => return true,
GlobalForm::Quotient(sub) => sub,
};
match (self.root_system, sub) {
(RootSystem::A(_), CenterSubgroup::Zk(k)) => {
if k == 0 || !(r + 1).is_multiple_of(k) {
return false;
}
let k = k as i64;
let nality: i64 = dynkin
.iter()
.enumerate()
.map(|(i, &a)| (i as i64 + 1) * a)
.sum();
nality.rem_euclid(k) == 0
}
(RootSystem::B(_), CenterSubgroup::Z2) => dynkin[r - 1] % 2 == 0,
(RootSystem::C(_), CenterSubgroup::Z2) => odd_index_sum(dynkin) % 2 == 0,
(RootSystem::D(_), CenterSubgroup::Z2 | CenterSubgroup::DVector) if r >= 2 => {
(dynkin[r - 2] + dynkin[r - 1]) % 2 == 0
}
(RootSystem::D(_), CenterSubgroup::Z4) if r >= 3 && r % 2 == 1 => {
d_odd_class(dynkin) == 0
}
(RootSystem::D(_), sub) if r >= 2 && r.is_multiple_of(2) => {
let t = odd_index_sum(&dynkin[..r - 2]);
let p = (dynkin[r - 2] + t) % 2;
let q = (dynkin[r - 1] + t) % 2;
match sub {
CenterSubgroup::DHalfSpinPlus => p == 0,
CenterSubgroup::DHalfSpinMinus => q == 0,
CenterSubgroup::DFull => p == 0 && q == 0,
_ => false,
}
}
_ => false,
}
}
}
fn odd_index_sum(dynkin: &[i64]) -> i64 {
dynkin.iter().step_by(2).sum()
}
pub fn d_odd_central_class(dynkin: &[i64]) -> i64 {
if dynkin.len() < 2 {
return 0;
}
d_odd_class(dynkin)
}
fn d_odd_class(dynkin: &[i64]) -> i64 {
let r = dynkin.len();
(dynkin[r - 1] - dynkin[r - 2] + 2 * odd_index_sum(&dynkin[..r - 2])).rem_euclid(4)
}
fn sp_rank(m: usize) -> Result<usize, GroupError> {
if m < 2 || !m.is_multiple_of(2) {
return Err(GroupError::UnsupportedRank {
family: "sp",
value: m,
});
}
Ok(m / 2)
}
fn orthogonal(n: usize, family: &'static str) -> Result<RootSystem, GroupError> {
match n {
n if n < 3 => Err(GroupError::UnsupportedRank { family, value: n }),
n if n % 2 == 1 => Ok(RootSystem::B((n - 1) / 2)),
n => Ok(RootSystem::D(n / 2)),
}
}
fn half_spin_root_system(n: usize) -> Result<RootSystem, GroupError> {
if n < 8 || !n.is_multiple_of(4) {
return Err(GroupError::UnsupportedRank {
family: "half_spin",
value: n,
});
}
Ok(RootSystem::D(n / 2))
}