use num_rational::Ratio;
use num_traits::{One, Zero};
use crate::group::GlobalForm;
use super::{BcdError, Series};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Seed {
series: Series,
rank: usize,
dim: usize,
sp: Vec<Vec<(usize, usize, i64)>>,
sz: Vec<Vec<i64>>,
sz_scale: Ratio<i64>,
sp_scale2: Vec<Ratio<i64>>,
}
impl Seed {
pub fn series(&self) -> Series {
self.series
}
pub fn rank(&self) -> usize {
self.rank
}
pub fn dim(&self) -> usize {
self.dim
}
pub fn raising(&self) -> &[Vec<(usize, usize, i64)>] {
&self.sp
}
pub fn cartan(&self) -> &[Vec<i64>] {
&self.sz
}
pub fn cartan_scale(&self) -> Ratio<i64> {
self.sz_scale
}
pub fn raising_scale2(&self, i: usize) -> Ratio<i64> {
self.sp_scale2[i]
}
#[cfg(test)]
fn raising_mut(&mut self) -> &mut [Vec<(usize, usize, i64)>] {
&mut self.sp
}
#[cfg(test)]
fn cartan_mut(&mut self) -> &mut [Vec<i64>] {
&mut self.sz
}
}
pub fn defining_seed(series: Series, r: usize) -> Result<Seed, BcdError> {
if r < series.min_rank() {
return Err(BcdError::ExcludedRank {
series,
rank: r,
redirect: series.low_rank_redirect(GlobalForm::SimplyConnected),
});
}
let seed = match series {
Series::C => setup_spn(r),
Series::B => setup_son(r),
Series::D => setup_sen(r),
};
Ok(seed)
}
fn setup_spn(r: usize) -> Seed {
let d = 2 * r;
let mut sp = Vec::with_capacity(r);
let mut sz = Vec::with_capacity(r);
for i in 1..=r {
let mut z = vec![0i64; d];
let p: Vec<(usize, usize, i64)> = if i < r {
z[i] = -(i as i64); z[..i].fill(1); vec![(i - 1, i, 1), (2 * r - i - 1, 2 * r - i, 1)]
} else {
for z_j in z.iter_mut().take(r) {
*z_j = 1;
}
vec![(r - 1, r, 1)]
};
for j in 0..r {
z[r + j] = -z[r - 1 - j];
}
sp.push(p);
sz.push(z);
}
Seed {
series: Series::C,
rank: r,
dim: d,
sp,
sz,
sz_scale: Ratio::one(),
sp_scale2: vec![Ratio::one(); r],
}
}
fn setup_son(r: usize) -> Seed {
let d = 2 * r + 1;
let mut sp = Vec::with_capacity(r);
let mut sz = Vec::with_capacity(r);
for i in 1..=r {
let i2 = 2 * (i - 1);
let mut z = vec![0i64; d];
z[i2] = 1;
z[i2 + 1] = -1;
let p = if i < r {
vec![(i2 + 2, i2, 1), (i2 + 1, i2 + 3, 1)]
} else {
vec![(i2 + 2, 1, 1), (0, i2 + 2, 1)]
};
sp.push(p);
sz.push(z);
}
Seed {
series: Series::B,
rank: r,
dim: d,
sp,
sz,
sz_scale: Ratio::one(),
sp_scale2: vec![Ratio::one(); r],
}
}
fn setup_sen(r: usize) -> Seed {
let d = 2 * r;
let mut sp = Vec::with_capacity(r);
let mut sz = Vec::with_capacity(r);
for i in 1..=r {
let i2 = 2 * (i - 1);
let mut z = vec![0i64; d];
z[i2] = 1;
z[i2 + 1] = -1;
let p = if i < r {
vec![(i2 + 2, i2, 1), (i2 + 1, i2 + 3, 1)]
} else {
vec![(2, 1, 1), (0, 3, 1)]
};
sp.push(p);
sz.push(z);
}
Seed {
series: Series::D,
rank: r,
dim: d,
sp,
sz,
sz_scale: Ratio::one(),
sp_scale2: vec![Ratio::one(); r],
}
}
pub fn spinor_seeds(series: Series, r: usize) -> Result<Vec<(Vec<i64>, Seed)>, BcdError> {
if r < series.min_rank() {
return Err(BcdError::ExcludedRank {
series,
rank: r,
redirect: series.low_rank_redirect(GlobalForm::SimplyConnected),
});
}
let mut out = match series {
Series::C => Vec::new(),
Series::B => vec![fock_seed(Series::B, r, None)],
Series::D => vec![
fock_seed(Series::D, r, Some(0)),
fock_seed(Series::D, r, Some(1)),
],
};
out.sort_by(|a, b| a.0.cmp(&b.0));
Ok(out)
}
fn ann(m: usize, k: usize) -> Option<(usize, i64)> {
let bit = 1usize << (k - 1);
if m & bit == 0 {
return None;
}
let below = (m & (bit - 1)).count_ones();
Some((m ^ bit, if below.is_multiple_of(2) { 1 } else { -1 }))
}
fn cre(m: usize, k: usize) -> Option<(usize, i64)> {
let bit = 1usize << (k - 1);
if m & bit != 0 {
return None;
}
let below = (m & (bit - 1)).count_ones();
Some((m | bit, if below.is_multiple_of(2) { 1 } else { -1 }))
}
fn fock_seed(series: Series, r: usize, sector: Option<u32>) -> (Vec<i64>, Seed) {
let mut states: Vec<usize> = (0..1usize << r)
.filter(|m| sector.is_none_or(|p| m.count_ones() % 2 == p))
.collect();
let rev_key = |m: &usize| -> std::cmp::Reverse<Vec<u8>> {
std::cmp::Reverse((1..=r).map(|k| ((m >> (k - 1)) & 1) as u8).rev().collect())
};
states.sort_by_key(rev_key);
let d = states.len();
let mut pos = vec![usize::MAX; 1usize << r];
for (p, &m) in states.iter().enumerate() {
pos[m] = p;
}
let sz: Vec<Vec<i64>> = (0..r)
.map(|j0| {
let bit = 1usize << (r - 1 - j0); states
.iter()
.map(|&m| if m & bit != 0 { 1 } else { -1 })
.collect()
})
.collect();
let mut sp: Vec<Vec<(usize, usize, i64)>> = Vec::with_capacity(r);
let mut sp_scale2 = vec![Ratio::<i64>::one(); r];
for i in 1..=r {
let mut recs = Vec::new();
for (col, &m) in states.iter().enumerate() {
let acted = if i < r {
ann(m, r - i + 1).and_then(|(m1, s1)| cre(m1, r - i).map(|(m2, s2)| (m2, s1 * s2)))
} else if series == Series::B {
cre(m, r)
} else {
cre(m, r).and_then(|(m1, s1)| cre(m1, r - 1).map(|(m2, s2)| (m2, s1 * s2)))
};
if let Some((m2, sign)) = acted {
recs.push((pos[m2], col, sign));
}
}
recs.sort_unstable();
sp.push(recs);
}
if series == Series::B {
sp_scale2[r - 1] = Ratio::new(1, 2);
}
let seed = Seed {
series,
rank: r,
dim: d,
sp,
sz,
sz_scale: Ratio::new(1, 2),
sp_scale2,
};
(highest_weight_dynkin(&seed, &states, r), seed)
}
fn highest_weight_dynkin(seed: &Seed, states: &[usize], r: usize) -> Vec<i64> {
let mut hw: Option<usize> = None;
for col in 0..states.len() {
if seed
.sp
.iter()
.all(|recs| !recs.iter().any(|&(_, c, _)| c == col))
{
assert!(hw.is_none(), "spinor seed carrier is not irreducible");
hw = Some(col);
}
}
let hw = hw.expect("an irreducible carrier has a highest-weight state");
let m = states[hw];
let two_lambda: Vec<i64> = (1..=r)
.map(|k| if m & (1usize << (k - 1)) != 0 { 1 } else { -1 })
.collect();
super::two_partition_to_dynkin(seed.series, &two_lambda)
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CommReport {
pub cartan_coeffs: Vec<Vec<Ratio<i64>>>,
pub root_weights: Vec<Vec<Ratio<i64>>>,
}
pub fn check_commutators(seed: &Seed) -> Result<CommReport, BcdError> {
let d = seed.dim;
let r = seed.rank;
let series = seed.series;
let viol = |relation: &'static str, i: usize, j: usize| BcdError::CommutatorViolation {
series,
relation,
i,
j,
};
let sp_dense: Vec<Vec<i64>> = seed.sp.iter().map(|p| dense(p, d)).collect();
for i in 0..r {
if seed.sz[i].iter().all(|&x| x == 0) {
return Err(viol("cartan is zero", i, i));
}
for j in i + 1..r {
if fro_diag(&seed.sz[i], &seed.sz[j]) != 0 {
return Err(viol("cartan not mutually orthogonal", i, j));
}
}
}
let mut cartan_coeffs = vec![vec![Ratio::<i64>::zero(); r]; r];
for i in 0..r {
let spt = transpose(&sp_dense[i], d);
let c = commutator(&sp_dense[i], &spt, d);
if c.iter().all(|&x| x == 0) {
return Err(viol("[Sp,Sp^dagger] has norm 0", i, i));
}
let c_diag: Vec<i64> = (0..d).map(|p| c[p * d + p]).collect();
let scale = seed.sp_scale2[i] / seed.sz_scale;
cartan_coeffs[i] = seed
.sz
.iter()
.map(|szk| Ratio::new(fro_diag(&c_diag, szk), fro_diag(szk, szk)))
.collect();
for row in 0..d {
for col in 0..d {
let mut res = Ratio::from_integer(c[row * d + col]);
if row == col {
for (fk, szk) in cartan_coeffs[i].iter().zip(&seed.sz) {
res -= *fk * szk[row];
}
}
if !res.is_zero() {
return Err(viol("[Sp,Sp^dagger] not in span(Sz)", i, i));
}
}
}
for f in cartan_coeffs[i].iter_mut() {
*f *= scale;
}
}
let mut root_weights = vec![vec![Ratio::<i64>::zero(); r]; r];
for i in 0..r {
#[allow(clippy::needless_range_loop)]
for j in 0..r {
let szj = diag(&seed.sz[j], d);
let bc = commutator(&szj, &sp_dense[i], d);
let (r0, c0, v0) = seed.sp[i][0];
let dz = Ratio::new(bc[r0 * d + c0], v0);
root_weights[i][j] = dz * seed.sz_scale;
for row in 0..d {
for col in 0..d {
let res =
Ratio::from_integer(bc[row * d + col]) - dz * sp_dense[i][row * d + col];
if !res.is_zero() {
return Err(viol("[Sz,Sp] not proportional to Sp", i, j));
}
}
}
}
}
Ok(CommReport {
cartan_coeffs,
root_weights,
})
}
fn dense(recs: &[(usize, usize, i64)], d: usize) -> Vec<i64> {
let mut m = vec![0i64; d * d];
for &(row, col, v) in recs {
m[row * d + col] = v;
}
m
}
fn diag(diagonal: &[i64], d: usize) -> Vec<i64> {
let mut m = vec![0i64; d * d];
for (p, &v) in diagonal.iter().enumerate() {
m[p * d + p] = v;
}
m
}
fn transpose(a: &[i64], d: usize) -> Vec<i64> {
let mut t = vec![0i64; d * d];
for row in 0..d {
for col in 0..d {
t[col * d + row] = a[row * d + col];
}
}
t
}
fn matmul(a: &[i64], b: &[i64], d: usize) -> Vec<i64> {
let mut m = vec![0i64; d * d];
for row in 0..d {
for k in 0..d {
let aik = a[row * d + k];
if aik == 0 {
continue;
}
for col in 0..d {
m[row * d + col] += aik * b[k * d + col];
}
}
}
m
}
fn commutator(a: &[i64], b: &[i64], d: usize) -> Vec<i64> {
let ab = matmul(a, b, d);
let ba = matmul(b, a, d);
(0..d * d).map(|p| ab[p] - ba[p]).collect()
}
fn fro_diag(u: &[i64], v: &[i64]) -> i64 {
u.iter().zip(v).map(|(&a, &b)| a * b).sum()
}
#[cfg(test)]
mod tests;