use crate::algebra::mv::Mv;
use crate::algebra::ops;
use crate::algebra::signature::Signature;
use crate::scalar::Scalar;
#[derive(Clone, Debug)]
pub enum InverseError {
ZeroMv,
NullElement,
NotInvertible,
DidNotConverge { iterations: usize },
}
impl std::fmt::Display for InverseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
InverseError::ZeroMv => write!(f, "cannot invert zero multivector"),
InverseError::NullElement => write!(f, "null element (norm² = 0) is not invertible"),
InverseError::NotInvertible => {
write!(f, "multivector is not invertible in this algebra")
}
InverseError::DidNotConverge { iterations } => write!(
f,
"iterative inversion did not converge after {} iterations",
iterations
),
}
}
}
pub fn is_versor(m: &Mv, sig: &Signature) -> bool {
if m.is_zero() {
return false;
}
let m_rev = ops::reverse(m);
let product = ops::geometric(m, &m_rev, sig);
if product.len() != 1 {
return false;
}
let scalar_coeff = product.coefficient(0);
!scalar_coeff.is_zero()
}
pub fn inverse(m: &Mv, sig: &Signature) -> Result<Mv, InverseError> {
if m.is_zero() {
return Err(InverseError::ZeroMv);
}
if m.len() == 1 {
return blade_inverse(m, sig);
}
if let Some(inv) = try_versor_inverse(m, sig) {
return Ok(inv);
}
general_inverse(m, sig)
}
fn blade_inverse(m: &Mv, sig: &Signature) -> Result<Mv, InverseError> {
debug_assert_eq!(m.len(), 1);
let (mask, coeff) = m.blades().next().unwrap();
let (_, sign) = crate::algebra::product_new::blade_product(mask, mask, sig);
if sign == 0 {
return Err(InverseError::NullElement);
}
let b_squared = coeff.clone() * coeff.clone() * Scalar::from(sign as i64);
if b_squared.is_zero() {
return Err(InverseError::NullElement);
}
let inv_b_sq = Scalar::from(1i64) / b_squared;
Ok(m.scale(&inv_b_sq))
}
fn try_versor_inverse(m: &Mv, sig: &Signature) -> Option<Mv> {
let m_rev = ops::reverse(m);
let product = ops::geometric(m, &m_rev, sig);
if product.len() != 1 {
return None;
}
let ns = product.coefficient(0);
if ns.is_zero() {
return None;
}
let inv_ns = Scalar::from(1i64) / ns;
Some(m_rev.scale(&inv_ns))
}
fn general_inverse(m: &Mv, sig: &Signature) -> Result<Mv, InverseError> {
let conj = ops::conjugate(m);
let m_conj = ops::geometric(m, &conj, sig);
if m_conj.len() == 1 {
let ns = m_conj.coefficient(0);
if !ns.is_zero() {
let inv_ns = Scalar::from(1i64) / ns;
return Ok(conj.scale(&inv_ns));
}
}
let n = sig.dimension() as usize;
let max_iter = n.min(64); let mut w = m.clone();
let mut prev_adj = Mv::scalar(Scalar::from(1i64));
for k in 1..=max_iter {
let trace_k = w.coefficient(0); let n_k = trace_k / Scalar::from(k as i64);
let adj_k = w.clone() - Mv::scalar(n_k.clone());
if k == max_iter {
if n_k.is_zero() {
return Err(InverseError::NotInvertible);
}
let inv_det = Scalar::from(1i64) / n_k;
return Ok(prev_adj.scale(&inv_det));
}
prev_adj = adj_k.clone();
w = ops::geometric(m, &adj_k, sig);
}
Err(InverseError::DidNotConverge {
iterations: max_iter,
})
}
pub fn sandwich_normalized(m: &Mv, v: &Mv, sig: &Signature) -> Result<Mv, InverseError> {
let m_inv = inverse(m, sig)?;
let mv = ops::geometric(m, v, sig);
Ok(ops::geometric(&mv, &m_inv, sig))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::algebra::blade_new::BladeMask;
use crate::scalar::Rat;
fn sig_vga3() -> Signature {
Signature::new(0, 0, 3).unwrap()
}
fn sig_pga3() -> Signature {
Signature::new(0, 1, 3).unwrap()
}
fn sig_cga2() -> Signature {
Signature::new(1, 0, 3).unwrap()
}
fn e(k: u8) -> Mv {
Mv::generator(k)
}
#[test]
fn blade_inverse_vector() {
let sig = sig_vga3();
let v = e(0); let inv = inverse(&v, &sig).unwrap();
let product = ops::geometric(&v, &inv, &sig);
assert_eq!(product.coefficient(0), Scalar::from(1));
assert_eq!(product.len(), 1);
}
#[test]
fn blade_inverse_scaled_vector() {
let sig = sig_vga3();
let v = Mv::term(0b001, Scalar::from(3)); let inv = inverse(&v, &sig).unwrap();
let product = ops::geometric(&v, &inv, &sig);
assert_eq!(product.coefficient(0), Scalar::from(1));
}
#[test]
fn blade_inverse_bivector() {
let sig = sig_vga3();
let bv = Mv::term(0b011, Scalar::from(1)); let inv = inverse(&bv, &sig).unwrap();
let product = ops::geometric(&bv, &inv, &sig);
assert_eq!(product.coefficient(0), Scalar::from(1));
assert_eq!(product.len(), 1);
}
#[test]
fn blade_inverse_negative_square() {
let sig = Signature::new(1, 0, 0).unwrap();
let v = e(0);
let inv = inverse(&v, &sig).unwrap();
assert_eq!(inv.coefficient(0b1), Scalar::from(-1));
}
#[test]
fn blade_inverse_degenerate_fails() {
let sig = sig_pga3();
let v = e(0); let result = inverse(&v, &sig);
assert!(result.is_err());
}
#[test]
fn versor_inverse_rotor_vga3() {
let sig = sig_vga3();
let r = ops::geometric(&e(0), &e(1), &sig); assert!(is_versor(&r, &sig));
let inv = inverse(&r, &sig).unwrap();
let product = ops::geometric(&r, &inv, &sig);
assert_eq!(product.coefficient(0), Scalar::from(1));
assert_eq!(product.len(), 1);
}
#[test]
fn versor_check_vector() {
let sig = sig_vga3();
assert!(is_versor(&e(0), &sig));
}
#[test]
fn versor_check_zero() {
let sig = sig_vga3();
assert!(!is_versor(&Mv::new(), &sig));
}
#[test]
fn versor_check_null_vector() {
let sig = sig_cga2();
let null = Mv::from_rat_terms(&[(0b01, Rat::from(1)), (0b10, Rat::from(1))]);
assert!(!is_versor(&null, &sig)); }
#[test]
fn inverse_scalar() {
let sig = sig_vga3();
let s = Mv::scalar(Scalar::from(5));
let inv = inverse(&s, &sig).unwrap();
let product = ops::geometric(&s, &inv, &sig);
assert_eq!(product.coefficient(0), Scalar::from(1));
}
#[test]
fn inverse_sum_of_vectors() {
let sig = sig_vga3();
let v = Mv::from_rat_terms(&[(0b001, Rat::from(1)), (0b010, Rat::from(1))]);
let inv = inverse(&v, &sig).unwrap();
let product = ops::geometric(&v, &inv, &sig);
assert_eq!(product.coefficient(0), Scalar::from(1));
assert_eq!(product.len(), 1);
}
#[test]
fn inverse_zero_fails() {
let sig = sig_vga3();
assert!(matches!(
inverse(&Mv::new(), &sig),
Err(InverseError::ZeroMv)
));
}
#[test]
fn inverse_null_vector_fails() {
let sig = sig_cga2();
let null = Mv::from_rat_terms(&[(0b01, Rat::from(1)), (0b10, Rat::from(1))]);
assert!(inverse(&null, &sig).is_err());
}
#[test]
fn sandwich_normalized_unit_versor() {
let sig = sig_vga3();
let r = e(0);
let v = e(1);
let s1 = ops::sandwich(&r, &v, &sig);
let s2 = sandwich_normalized(&r, &v, &sig).unwrap();
assert_eq!(s1, s2);
}
#[test]
fn sandwich_normalized_scaled_versor() {
let sig = sig_vga3();
let r = Mv::term(0b001, Scalar::from(2)); let v = e(1);
let result = sandwich_normalized(&r, &v, &sig).unwrap();
assert_eq!(result.coefficient(0b010), Scalar::from(-1));
assert_eq!(result.len(), 1);
}
#[test]
fn inverse_roundtrip_all_single_blades_vga3() {
let sig = sig_vga3();
for k in 0..sig.n() {
let blade = e(k);
let inv = inverse(&blade, &sig).unwrap();
let product = ops::geometric(&blade, &inv, &sig);
assert_eq!(
product.coefficient(0),
Scalar::from(1),
"roundtrip failed for generator {}",
k
);
assert_eq!(product.len(), 1);
}
}
#[test]
fn inverse_roundtrip_bivectors_vga3() {
let sig = sig_vga3();
let blades: Vec<BladeMask> = vec![0b011, 0b101, 0b110];
for mask in blades {
let bv = Mv::term(mask, Scalar::from(1));
let inv = inverse(&bv, &sig).unwrap();
let product = ops::geometric(&bv, &inv, &sig);
assert_eq!(
product.coefficient(0),
Scalar::from(1),
"roundtrip failed for blade {:#b}",
mask
);
}
}
}