50 lines
1.8 KiB
Rust
50 lines
1.8 KiB
Rust
//! Versor operations: the three primitives.
|
|
//!
|
|
//! versor_apply V*F*reverse(V) — the only allowed field transition
|
|
//! normalize_to_versor F/sqrt(|F*rev(F)|) — called once at injection gate
|
|
//! versor_condition ||F*rev(F)-1||_F — used in tests and gate only
|
|
|
|
use crate::cl41::{geometric_product_raw, reverse_raw, Cl41Error};
|
|
use thiserror::Error;
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum VersorError {
|
|
#[error("Cl41 error: {0}")]
|
|
Cl41(#[from] Cl41Error),
|
|
#[error("Cannot normalize: norm^2 too small ({0})")]
|
|
NullVersor(f32),
|
|
}
|
|
|
|
/// Sandwich product V * F * reverse(V).
|
|
/// Allocation-free. This is the hot path — called every generation step.
|
|
pub fn versor_apply_raw(v: &[f32; 32], f: &[f32; 32]) -> Result<[f32; 32], VersorError> {
|
|
let rev_v = reverse_raw(v);
|
|
let vf = geometric_product_raw(v, f)?;
|
|
let vfrv = geometric_product_raw(&vf, &rev_v)?;
|
|
Ok(vfrv)
|
|
}
|
|
|
|
/// Project F onto versor manifold: F / sqrt(|scalar_part(F*rev(F))|).
|
|
/// Called ONCE at ingest/gate. Never mid-propagation.
|
|
pub fn normalize_to_versor_raw(f: &[f32; 32]) -> Result<[f32; 32], VersorError> {
|
|
let rev_f = reverse_raw(f);
|
|
let frv = geometric_product_raw(f, &rev_f)?;
|
|
let n2 = frv[0]; // grade-0 = scalar part
|
|
if n2.abs() < 1e-12 {
|
|
return Err(VersorError::NullVersor(n2));
|
|
}
|
|
let inv_norm = 1.0 / n2.abs().sqrt();
|
|
let mut result = *f;
|
|
for x in result.iter_mut() { *x *= inv_norm; }
|
|
Ok(result)
|
|
}
|
|
|
|
/// ||F * reverse(F) - 1||_F.
|
|
/// Returns scalar f32. Used in tests and injection gate only.
|
|
pub fn versor_condition_raw(f: &[f32; 32]) -> Result<f32, VersorError> {
|
|
let rev_f = reverse_raw(f);
|
|
let mut frv = geometric_product_raw(f, &rev_f)?;
|
|
frv[0] -= 1.0; // subtract identity
|
|
let norm_sq: f32 = frv.iter().map(|x| x * x).sum();
|
|
Ok(norm_sq.sqrt())
|
|
}
|