use super::basis::MAX_BASIS_DIM;
use crate::scalar::Scalar;
use std::collections::BTreeMap;
pub type MetricParts<S> = (
Vec<S>,
BTreeMap<(usize, usize), S>,
BTreeMap<(usize, usize), S>,
);
#[derive(Clone, Debug, PartialEq)]
pub struct Metric<S: Scalar> {
pub(crate) q: Vec<S>,
pub(crate) b: BTreeMap<(usize, usize), S>,
pub(crate) a: BTreeMap<(usize, usize), S>,
}
impl<S: Scalar> Metric<S> {
pub fn diagonal(q: Vec<S>) -> Self {
Metric {
q,
b: BTreeMap::new(),
a: BTreeMap::new(),
}
}
pub fn grassmann(n: usize) -> Self {
Metric {
q: vec![S::zero(); n],
b: BTreeMap::new(),
a: BTreeMap::new(),
}
}
pub fn new(q: Vec<S>, b: impl IntoIterator<Item = ((usize, usize), S)>) -> Self {
let metric = Metric {
q,
b: b.into_iter().collect(),
a: BTreeMap::new(),
};
metric.validate_for_dim(metric.q.len());
metric
}
pub fn general(
q: Vec<S>,
b: impl IntoIterator<Item = ((usize, usize), S)>,
a: impl IntoIterator<Item = ((usize, usize), S)>,
) -> Self {
let metric = Metric {
q,
b: b.into_iter().collect(),
a: a.into_iter().collect(),
};
metric.validate_for_dim(metric.q.len());
metric
}
pub fn dim(&self) -> usize {
self.q.len()
}
pub fn q(&self) -> &[S] {
&self.q
}
pub fn b(&self) -> &BTreeMap<(usize, usize), S> {
&self.b
}
pub fn a(&self) -> &BTreeMap<(usize, usize), S> {
&self.a
}
pub fn into_parts(self) -> MetricParts<S> {
(self.q, self.b, self.a)
}
pub(crate) fn validate_for_dim(&self, dim: usize) {
assert!(
dim <= MAX_BASIS_DIM,
"CliffordAlgebra supports at most {MAX_BASIS_DIM} generators"
);
assert_eq!(
self.q.len(),
dim,
"metric q length must equal algebra dimension"
);
Self::validate_keys("b", &self.b, dim);
Self::validate_keys("a", &self.a, dim);
}
fn validate_keys(name: &str, map: &BTreeMap<(usize, usize), S>, dim: usize) {
for &(i, j) in map.keys() {
assert!(i < j, "{name}-keys must satisfy i < j");
assert!(
j < dim,
"{name}-key ({i},{j}) is out of range for dimension {dim}"
);
}
}
pub fn has_upper(&self) -> bool {
self.a.values().any(|v| !v.is_zero())
}
pub(crate) fn is_orthogonal(&self) -> bool {
self.b.values().all(|v| v.is_zero()) && self.a.values().all(|v| v.is_zero())
}
pub fn direct_sum(&self, other: &Metric<S>) -> Metric<S> {
let n = self.q.len();
let mut q = self.q.clone();
q.extend(other.q.iter().cloned());
let mut b = self.b.clone();
for (&(i, j), v) in &other.b {
b.insert((i + n, j + n), v.clone());
}
let mut a = self.a.clone();
for (&(i, j), v) in &other.a {
a.insert((i + n, j + n), v.clone());
}
Metric::general(q, b, a)
}
pub(crate) fn q_val(&self, i: usize) -> S {
self.q.get(i).cloned().unwrap_or_else(S::zero)
}
pub fn map<T: Scalar>(&self, f: impl Fn(&S) -> T) -> Metric<T> {
Metric {
q: self.q.iter().map(&f).collect(),
b: self.b.iter().map(|(&k, v)| (k, f(v))).collect(),
a: self.a.iter().map(|(&k, v)| (k, f(v))).collect(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::clifford::CliffordAlgebra;
use crate::scalar::Rational;
fn r(n: i128) -> Rational {
Rational::from_int(n)
}
#[test]
fn direct_sum_shifts_right_bs_and_as_by_left_dim() {
let mut bl = BTreeMap::new();
bl.insert((0usize, 1usize), r(5));
let mut al = BTreeMap::new();
al.insert((0usize, 1usize), r(7));
let left = Metric::general(vec![r(1), r(2)], bl, al);
let mut br = BTreeMap::new();
br.insert((0usize, 2usize), r(11));
let mut ar = BTreeMap::new();
ar.insert((1usize, 2usize), r(13));
let right = Metric::general(vec![r(3), r(4), r(5)], br, ar);
let sum = left.direct_sum(&right);
assert_eq!(sum.dim(), 5);
assert_eq!(sum.q(), &[r(1), r(2), r(3), r(4), r(5)]);
assert_eq!(sum.b().get(&(0, 1)), Some(&r(5)));
assert_eq!(sum.a().get(&(0, 1)), Some(&r(7)));
assert_eq!(sum.b().get(&(2, 4)), Some(&r(11)));
assert_eq!(sum.a().get(&(3, 4)), Some(&r(13)));
assert_eq!(sum.b().len(), 2);
assert_eq!(sum.a().len(), 2);
}
#[test]
fn direct_sum_shift_takes_effect_in_the_joined_algebras_products() {
let mut bl = BTreeMap::new();
bl.insert((0usize, 1usize), r(1));
let left = Metric::new(vec![r(2), r(3)], bl);
let mut br = BTreeMap::new();
br.insert((0usize, 1usize), r(1));
let right = Metric::new(vec![r(5), r(7)], br);
let left_alg = CliffordAlgebra::new(2, left);
let right_alg = CliffordAlgebra::new(2, right);
let joined = left_alg.graded_tensor(&right_alg);
let e2 = joined.e(2);
let e3 = joined.e(3);
let anti23 = joined.add(&joined.mul(&e2, &e3), &joined.mul(&e3, &e2));
assert_eq!(anti23, joined.scalar(r(1)));
let e0 = joined.e(0);
let anti02 = joined.add(&joined.mul(&e0, &e2), &joined.mul(&e2, &e0));
assert!(anti02.is_zero());
}
}