| |
| |
| |
| |
| |
| |
|
|
| use hyperkitty_core::float_eq; |
|
|
| |
| #[derive(Debug, Clone, PartialEq)] |
| pub struct SpinFactor { |
| pub alpha: f64, |
| pub v: Vec<f64>, |
| } |
|
|
| impl SpinFactor { |
| |
| pub fn new(alpha: f64, v: Vec<f64>) -> Self { |
| Self { alpha, v } |
| } |
|
|
| |
| pub fn dim(&self) -> usize { |
| self.v.len() |
| } |
|
|
| |
| pub fn identity(n: usize) -> Self { |
| Self { |
| alpha: 1.0, |
| v: vec![0.0; n], |
| } |
| } |
|
|
| |
| pub fn zero(n: usize) -> Self { |
| Self { |
| alpha: 0.0, |
| v: vec![0.0; n], |
| } |
| } |
|
|
| |
| pub fn v_norm(&self) -> f64 { |
| self.v.iter().map(|x| x * x).sum::<f64>().sqrt() |
| } |
|
|
| |
| pub fn v_hat(&self) -> Vec<f64> { |
| let norm = self.v_norm(); |
| if norm < 1e-15 { |
| vec![0.0; self.v.len()] |
| } else { |
| self.v.iter().map(|x| x / norm).collect() |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| pub fn product(&self, other: &Self) -> Self { |
| assert_eq!(self.v.len(), other.v.len(), "Dimension mismatch in Jordan product"); |
|
|
| |
| let dot: f64 = self.v.iter().zip(&other.v).map(|(a, b)| a * b).sum(); |
| let alpha = self.alpha * other.alpha + dot; |
|
|
| |
| let v: Vec<f64> = self |
| .v |
| .iter() |
| .zip(&other.v) |
| .map(|(&vi, &wi)| self.alpha * wi + other.alpha * vi) |
| .collect(); |
|
|
| Self { alpha, v } |
| } |
|
|
| |
| pub fn scale(&self, c: f64) -> Self { |
| Self { |
| alpha: c * self.alpha, |
| v: self.v.iter().map(|x| c * x).collect(), |
| } |
| } |
|
|
| |
| pub fn add(&self, other: &Self) -> Self { |
| assert_eq!(self.v.len(), other.v.len(), "Dimension mismatch in addition"); |
| Self { |
| alpha: self.alpha + other.alpha, |
| v: self.v.iter().zip(&other.v).map(|(a, b)| a + b).collect(), |
| } |
| } |
|
|
| |
| pub fn sub(&self, other: &Self) -> Self { |
| assert_eq!(self.v.len(), other.v.len(), "Dimension mismatch in subtraction"); |
| Self { |
| alpha: self.alpha - other.alpha, |
| v: self.v.iter().zip(&other.v).map(|(a, b)| a - b).collect(), |
| } |
| } |
|
|
| |
| pub fn is_commutative_with(&self, other: &Self) -> bool { |
| let xy = self.product(other); |
| let yx = other.product(self); |
| float_eq(xy.alpha, yx.alpha, 1e-10) |
| && xy |
| .v |
| .iter() |
| .zip(&yx.v) |
| .all(|(a, b)| float_eq(*a, *b, 1e-10)) |
| } |
|
|
| |
| pub fn is_associative_triple(&self, y: &Self, z: &Self) -> bool { |
| let xy = self.product(y); |
| let left = xy.product(z); |
|
|
| let yz = y.product(z); |
| let right = self.product(&yz); |
|
|
| float_eq(left.alpha, right.alpha, 1e-10) |
| && left |
| .v |
| .iter() |
| .zip(&right.v) |
| .all(|(a, b)| float_eq(*a, *b, 1e-10)) |
| } |
|
|
| |
| pub fn is_idempotent(&self) -> bool { |
| let ee = self.product(self); |
| float_eq(ee.alpha, self.alpha, 1e-10) |
| && ee |
| .v |
| .iter() |
| .zip(&self.v) |
| .all(|(a, b)| float_eq(*a, *b, 1e-10)) |
| } |
|
|
| |
| pub fn approx_eq(&self, other: &Self, epsilon: f64) -> bool { |
| float_eq(self.alpha, other.alpha, epsilon) |
| && self.v.len() == other.v.len() |
| && self |
| .v |
| .iter() |
| .zip(&other.v) |
| .all(|(a, b)| float_eq(*a, *b, epsilon)) |
| } |
| } |
|
|
| |
| |
| |
| |
| pub fn demonstrate_non_associativity() -> (SpinFactor, SpinFactor) { |
| |
| |
| let x = SpinFactor::new(1.0, vec![1.0, 0.0]); |
| let y = SpinFactor::new(0.0, vec![0.0, 1.0]); |
| let z = SpinFactor::new(1.0, vec![1.0, 1.0]); |
|
|
| |
| |
| |
| let xy = x.product(&y); |
| let left = xy.product(&z); |
|
|
| |
| |
| |
| let yz = y.product(&z); |
| let right = x.product(&yz); |
|
|
| (left, right) |
| } |
|
|
| #[cfg(test)] |
| mod tests { |
| use super::*; |
|
|
| #[test] |
| fn test_jordan_product_basic() { |
| let x = SpinFactor::new(2.0, vec![1.0, 0.0, 0.0]); |
| let y = SpinFactor::new(3.0, vec![0.0, 1.0, 0.0]); |
|
|
| let xy = x.product(&y); |
| |
| assert!(float_eq(xy.alpha, 6.0, 1e-10)); |
| |
| assert!(float_eq(xy.v[0], 3.0, 1e-10)); |
| assert!(float_eq(xy.v[1], 2.0, 1e-10)); |
| assert!(float_eq(xy.v[2], 0.0, 1e-10)); |
| } |
|
|
| #[test] |
| fn test_commutativity() { |
| let x = SpinFactor::new(1.5, vec![2.0, -1.0, 3.0]); |
| let y = SpinFactor::new(-0.5, vec![1.0, 4.0, -2.0]); |
| assert!(x.is_commutative_with(&y)); |
| } |
|
|
| #[test] |
| fn test_commutativity_many() { |
| |
| let elements = vec![ |
| SpinFactor::new(1.0, vec![1.0, 0.0]), |
| SpinFactor::new(0.0, vec![0.0, 1.0]), |
| SpinFactor::new(-2.5, vec![3.14, -1.7]), |
| SpinFactor::new(0.618, vec![-0.5, 0.5]), |
| ]; |
|
|
| for i in 0..elements.len() { |
| for j in (i + 1)..elements.len() { |
| assert!( |
| elements[i].is_commutative_with(&elements[j]), |
| "Commutativity failed for elements {} and {}", |
| i, |
| j |
| ); |
| } |
| } |
| } |
|
|
| #[test] |
| fn test_non_associativity() { |
| let (left, right) = demonstrate_non_associativity(); |
| |
| assert!( |
| !left.approx_eq(&right, 1e-10), |
| "Expected non-associativity but got equal results" |
| ); |
| } |
|
|
| #[test] |
| fn test_non_associativity_detailed() { |
| let x = SpinFactor::new(1.0, vec![1.0, 0.0]); |
| let y = SpinFactor::new(0.0, vec![0.0, 1.0]); |
| let z = SpinFactor::new(1.0, vec![1.0, 1.0]); |
| assert!(!x.is_associative_triple(&y, &z)); |
| } |
|
|
| #[test] |
| fn test_identity_is_identity() { |
| let e = SpinFactor::identity(3); |
| let x = SpinFactor::new(2.5, vec![1.0, -1.0, 0.5]); |
| let ex = e.product(&x); |
| assert!(ex.approx_eq(&x, 1e-10)); |
| } |
|
|
| #[test] |
| fn test_identity_is_idempotent() { |
| let e = SpinFactor::identity(4); |
| assert!(e.is_idempotent()); |
| } |
|
|
| #[test] |
| fn test_zero_product() { |
| let z = SpinFactor::zero(3); |
| let x = SpinFactor::new(5.0, vec![1.0, 2.0, 3.0]); |
| let zx = z.product(&x); |
| assert!(zx.approx_eq(&SpinFactor::zero(3), 1e-10)); |
| } |
|
|
| #[test] |
| fn test_scale() { |
| let x = SpinFactor::new(2.0, vec![1.0, -1.0]); |
| let scaled = x.scale(3.0); |
| assert!(float_eq(scaled.alpha, 6.0, 1e-10)); |
| assert!(float_eq(scaled.v[0], 3.0, 1e-10)); |
| assert!(float_eq(scaled.v[1], -3.0, 1e-10)); |
| } |
|
|
| #[test] |
| fn test_add_sub() { |
| let x = SpinFactor::new(1.0, vec![2.0, 3.0]); |
| let y = SpinFactor::new(4.0, vec![5.0, 6.0]); |
| let sum = x.add(&y); |
| assert!(float_eq(sum.alpha, 5.0, 1e-10)); |
| assert!(float_eq(sum.v[0], 7.0, 1e-10)); |
| assert!(float_eq(sum.v[1], 9.0, 1e-10)); |
|
|
| let diff = x.sub(&y); |
| assert!(float_eq(diff.alpha, -3.0, 1e-10)); |
| assert!(float_eq(diff.v[0], -3.0, 1e-10)); |
| assert!(float_eq(diff.v[1], -3.0, 1e-10)); |
| } |
|
|
| #[test] |
| fn test_v_norm() { |
| let x = SpinFactor::new(0.0, vec![3.0, 4.0]); |
| assert!(float_eq(x.v_norm(), 5.0, 1e-10)); |
| } |
|
|
| #[test] |
| fn test_v_hat() { |
| let x = SpinFactor::new(0.0, vec![3.0, 4.0]); |
| let hat = x.v_hat(); |
| assert!(float_eq(hat[0], 0.6, 1e-10)); |
| assert!(float_eq(hat[1], 0.8, 1e-10)); |
| } |
| } |
|
|