use num_rational::Ratio;
use num_traits::Zero;
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>>,
}
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
}
#[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(),
});
}
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,
}
}
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,
}
}
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,
}
}
#[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();
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));
}
}
}
}
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;
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;