amari_core/
verified.rs

1//! Formally verified geometric algebra with phantom types and Creusot annotations
2//!
3//! This module provides type-safe, mathematically verified implementations of
4//! geometric algebra operations using phantom types for compile-time invariants
5//! and Creusot annotations for formal verification.
6
7use num_traits::{Float, One, Zero};
8use std::marker::PhantomData;
9
10#[cfg(feature = "formal-verification")]
11use creusot_contracts::{ensures, requires};
12
13/// Phantom type encoding the metric signature of a Clifford algebra Cl(p,q,r)
14/// - P: number of positive basis vectors (e_i² = +1)
15/// - Q: number of negative basis vectors (e_i² = -1)
16/// - R: number of null basis vectors (e_i² = 0)
17pub struct Signature<const P: usize, const Q: usize, const R: usize>;
18
19/// Phantom type for compile-time grade tracking
20pub struct Grade<const G: usize>;
21
22/// Type-level dimension marker for vectors
23pub struct Dim<const D: usize>;
24
25/// A verified multivector with compile-time signature guarantees
26///
27/// This structure ensures at the type level that:
28/// 1. The signature is fixed and consistent
29/// 2. Operations preserve algebraic laws
30/// 3. Dimension bounds are respected
31#[derive(Debug, Clone)]
32pub struct VerifiedMultivector<T, const P: usize, const Q: usize, const R: usize>
33where
34    T: Float + Zero + One,
35{
36    /// Coefficients in lexicographic basis blade order
37    pub(crate) coefficients: Vec<T>,
38    /// Phantom marker for signature
39    _signature: PhantomData<Signature<P, Q, R>>,
40}
41
42impl<T, const P: usize, const Q: usize, const R: usize> VerifiedMultivector<T, P, Q, R>
43where
44    T: Float + Zero + One,
45{
46    /// The total dimension of the algebra
47    pub const DIM: usize = P + Q + R;
48
49    /// The number of basis blades (2^n)
50    pub const BASIS_SIZE: usize = 1 << (P + Q + R);
51
52    /// Create a new verified multivector from coefficients
53    ///
54    /// # Type Invariants
55    /// - Coefficients array must have exactly 2^(P+Q+R) elements
56    /// - Signature is encoded at type level and cannot be violated
57    #[cfg_attr(feature = "formal-verification",
58        requires(coefficients.len() == Self::BASIS_SIZE))]
59    pub fn new(coefficients: Vec<T>) -> Result<Self, &'static str> {
60        if coefficients.len() != Self::BASIS_SIZE {
61            return Err("Coefficient array size must equal 2^(P+Q+R)");
62        }
63
64        Ok(Self {
65            coefficients,
66            _signature: PhantomData,
67        })
68    }
69
70    /// Create a scalar multivector
71    #[cfg_attr(feature = "formal-verification",
72        ensures(result.is_scalar()),
73        ensures(result.coefficients[0] == value),
74        ensures(result.coefficients.len() == Self::BASIS_SIZE))]
75    pub fn scalar(value: T) -> Self {
76        let mut coefficients = vec![T::zero(); Self::BASIS_SIZE];
77        coefficients[0] = value;
78
79        Self {
80            coefficients,
81            _signature: PhantomData,
82        }
83    }
84
85    /// Create a basis vector e_i
86    ///
87    /// # Type Safety
88    /// The index is bounds-checked against the signature dimensions
89    #[cfg_attr(feature = "formal-verification",
90        requires(index < Self::DIM),
91        ensures(result.grade() == 1))]
92    pub fn basis_vector(index: usize) -> Result<Self, &'static str> {
93        if index >= Self::DIM {
94            return Err("Basis vector index exceeds dimension");
95        }
96
97        let mut coefficients = vec![T::zero(); Self::BASIS_SIZE];
98        coefficients[1 << index] = T::one();
99
100        Ok(Self {
101            coefficients,
102            _signature: PhantomData,
103        })
104    }
105
106    /// Check if this is a pure scalar
107    #[cfg_attr(feature = "formal-verification",
108        ensures(result == (self.coefficients[1..].iter().all(|c| c.is_zero()))))]
109    pub fn is_scalar(&self) -> bool {
110        self.coefficients[1..].iter().all(|c| c.is_zero())
111    }
112
113    /// Compute the grade (highest non-zero grade component)
114    #[cfg_attr(feature = "formal-verification",
115        ensures(result <= Self::DIM))]
116    pub fn grade(&self) -> usize {
117        // Count bits to determine grade of each basis blade
118        for (i, coeff) in self.coefficients.iter().enumerate().rev() {
119            if !coeff.is_zero() {
120                return i.count_ones() as usize;
121            }
122        }
123        0
124    }
125
126    /// Addition of multivectors (same signature enforced by types)
127    #[cfg_attr(feature = "formal-verification",
128        ensures(result.coefficients.len() == self.coefficients.len()))]
129    pub fn add(&self, other: &Self) -> Self {
130        let coefficients: Vec<T> = self
131            .coefficients
132            .iter()
133            .zip(&other.coefficients)
134            .map(|(a, b)| *a + *b)
135            .collect();
136
137        Self {
138            coefficients,
139            _signature: PhantomData,
140        }
141    }
142
143    /// Geometric product with compile-time signature matching
144    ///
145    /// # Mathematical Properties (verified by Creusot when enabled)
146    /// - Associativity: (AB)C = A(BC)
147    /// - Distributivity: A(B+C) = AB + AC
148    /// - Identity: 1*A = A*1 = A
149    #[cfg_attr(feature = "formal-verification",
150        requires(self.coefficients.len() == Self::BASIS_SIZE),
151        requires(other.coefficients.len() == Self::BASIS_SIZE),
152        ensures(result.coefficients.len() == Self::BASIS_SIZE))]
153    pub fn geometric_product(&self, other: &Self) -> Self {
154        // Simplified geometric product implementation
155        // In practice, this would use the Cayley table for the signature
156        let mut result = vec![T::zero(); Self::BASIS_SIZE];
157
158        for i in 0..Self::BASIS_SIZE {
159            for j in 0..Self::BASIS_SIZE {
160                let sign = self.compute_product_sign(i, j);
161                let target_index = i ^ j; // XOR gives the resulting basis blade
162                result[target_index] = result[target_index]
163                    + self.coefficients[i] * other.coefficients[j] * T::from(sign).unwrap();
164            }
165        }
166
167        Self {
168            coefficients: result,
169            _signature: PhantomData,
170        }
171    }
172
173    /// Compute the sign of the product of two basis blades
174    fn compute_product_sign(&self, blade1: usize, blade2: usize) -> i32 {
175        // Simplified sign computation
176        // Full implementation would consider the signature (P,Q,R)
177        let swaps = self.count_swaps(blade1, blade2);
178        if swaps.is_multiple_of(2) {
179            1
180        } else {
181            -1
182        }
183    }
184
185    /// Count the number of swaps needed to reorder basis vectors
186    fn count_swaps(&self, blade1: usize, blade2: usize) -> usize {
187        // Count inversions when combining blade indices
188        let mut count = 0;
189        for i in 0..Self::DIM {
190            if blade1 & (1 << i) != 0 {
191                for j in 0..i {
192                    if blade2 & (1 << j) != 0 {
193                        count += 1;
194                    }
195                }
196            }
197        }
198        count
199    }
200}
201
202/// Type-safe k-vector (homogeneous grade element)
203///
204/// This type guarantees at compile-time that the multivector
205/// contains only elements of grade K
206pub struct KVector<T, const K: usize, const P: usize, const Q: usize, const R: usize>
207where
208    T: Float + Zero + One,
209{
210    multivector: VerifiedMultivector<T, P, Q, R>,
211    _grade: PhantomData<Grade<K>>,
212}
213
214impl<T, const K: usize, const P: usize, const Q: usize, const R: usize> KVector<T, K, P, Q, R>
215where
216    T: Float + Zero + One,
217{
218    /// Create a k-vector from a general multivector by grade projection
219    #[cfg_attr(feature = "formal-verification",
220        requires(K <= P + Q + R),
221        ensures(result.grade() == K))]
222    pub fn from_multivector(mv: VerifiedMultivector<T, P, Q, R>) -> Self {
223        let mut coefficients = vec![T::zero(); VerifiedMultivector::<T, P, Q, R>::BASIS_SIZE];
224
225        // Extract only grade-K components
226        for (i, coeff) in mv.coefficients.iter().enumerate() {
227            if i.count_ones() as usize == K {
228                coefficients[i] = *coeff;
229            }
230        }
231
232        Self {
233            multivector: VerifiedMultivector {
234                coefficients,
235                _signature: PhantomData,
236            },
237            _grade: PhantomData,
238        }
239    }
240
241    /// Get the grade (always returns K due to type constraint)
242    pub const fn grade(&self) -> usize {
243        K
244    }
245
246    /// Inner product with another k-vector (produces a scalar)
247    pub fn inner_product(&self, other: &Self) -> T {
248        // Inner product of k-vectors of same grade
249        self.multivector
250            .coefficients
251            .iter()
252            .zip(&other.multivector.coefficients)
253            .map(|(a, b)| *a * *b)
254            .fold(T::zero(), |acc, x| acc + x)
255    }
256}
257
258// Trait for outer product with specific grade combinations
259// This works around const generic arithmetic limitations
260pub trait OuterProduct<T, const J: usize, const P: usize, const Q: usize, const R: usize>
261where
262    T: Float + Zero + One,
263{
264    type Output;
265    fn outer_product(&self, other: &KVector<T, J, P, Q, R>) -> Self::Output;
266}
267
268// Implement outer product for specific grade combinations
269// Grade 1 ∧ Grade 1 = Grade 2 (vector ∧ vector = bivector)
270impl<T, const P: usize, const Q: usize, const R: usize> OuterProduct<T, 1, P, Q, R>
271    for KVector<T, 1, P, Q, R>
272where
273    T: Float + Zero + One,
274{
275    type Output = KVector<T, 2, P, Q, R>;
276
277    fn outer_product(&self, other: &KVector<T, 1, P, Q, R>) -> Self::Output {
278        let mut coefficients = vec![T::zero(); VerifiedMultivector::<T, P, Q, R>::BASIS_SIZE];
279
280        // Compute outer product for grade-1 vectors
281        for i in 0..VerifiedMultivector::<T, P, Q, R>::DIM {
282            for j in i + 1..VerifiedMultivector::<T, P, Q, R>::DIM {
283                let blade_i = 1 << i;
284                let blade_j = 1 << j;
285                let blade_ij = blade_i | blade_j;
286
287                coefficients[blade_ij] = self.multivector.coefficients[blade_i]
288                    * other.multivector.coefficients[blade_j]
289                    - self.multivector.coefficients[blade_j]
290                        * other.multivector.coefficients[blade_i];
291            }
292        }
293
294        KVector {
295            multivector: VerifiedMultivector {
296                coefficients,
297                _signature: PhantomData,
298            },
299            _grade: PhantomData,
300        }
301    }
302}
303
304// Grade 1 ∧ Grade 2 = Grade 3 (vector ∧ bivector = trivector)
305impl<T, const P: usize, const Q: usize, const R: usize> OuterProduct<T, 2, P, Q, R>
306    for KVector<T, 1, P, Q, R>
307where
308    T: Float + Zero + One,
309{
310    type Output = KVector<T, 3, P, Q, R>;
311
312    fn outer_product(&self, _other: &KVector<T, 2, P, Q, R>) -> Self::Output {
313        // Implementation for vector ∧ bivector
314        todo!("Implement grade 1 ∧ grade 2")
315    }
316}
317
318// Grade 2 ∧ Grade 1 = Grade 3 (bivector ∧ vector = trivector)
319impl<T, const P: usize, const Q: usize, const R: usize> OuterProduct<T, 1, P, Q, R>
320    for KVector<T, 2, P, Q, R>
321where
322    T: Float + Zero + One,
323{
324    type Output = KVector<T, 3, P, Q, R>;
325
326    fn outer_product(&self, _other: &KVector<T, 1, P, Q, R>) -> Self::Output {
327        // Implementation for bivector ∧ vector
328        todo!("Implement grade 2 ∧ grade 1")
329    }
330}
331
332/// A verified rotor with compile-time guarantees
333///
334/// Rotors are even-grade multivectors with unit norm that
335/// represent rotations in the geometric algebra
336pub struct VerifiedRotor<T, const P: usize, const Q: usize, const R: usize>
337where
338    T: Float + Zero + One,
339{
340    multivector: VerifiedMultivector<T, P, Q, R>,
341    /// Phantom data ensuring this is a valid rotor
342    _rotor_invariant: PhantomData<()>,
343}
344
345impl<T, const P: usize, const Q: usize, const R: usize> VerifiedRotor<T, P, Q, R>
346where
347    T: Float + Zero + One,
348{
349    /// Create a rotor from a multivector
350    ///
351    /// # Invariants (verified at runtime, would be proven with Creusot)
352    /// - Must be even grade
353    /// - Must have unit norm
354    #[cfg_attr(feature = "formal-verification",
355        requires(mv.is_even_grade()),
356        requires((mv.norm() - T::one()).abs() < T::from(0.0001).unwrap()),
357        ensures(result.is_ok()))]
358    pub fn new(mv: VerifiedMultivector<T, P, Q, R>) -> Result<Self, &'static str> {
359        if !Self::is_even_grade(&mv) {
360            return Err("Rotor must have even grade");
361        }
362
363        let norm = Self::compute_norm(&mv);
364        if (norm - T::one()).abs() > T::from(0.0001).unwrap() {
365            return Err("Rotor must have unit norm");
366        }
367
368        Ok(Self {
369            multivector: mv,
370            _rotor_invariant: PhantomData,
371        })
372    }
373
374    /// Check if multivector has only even grade components
375    fn is_even_grade(mv: &VerifiedMultivector<T, P, Q, R>) -> bool {
376        for (i, coeff) in mv.coefficients.iter().enumerate() {
377            let grade = i.count_ones() as usize;
378            if !grade.is_multiple_of(2) && !coeff.is_zero() {
379                return false;
380            }
381        }
382        true
383    }
384
385    /// Compute the norm of a multivector
386    fn compute_norm(mv: &VerifiedMultivector<T, P, Q, R>) -> T {
387        mv.coefficients
388            .iter()
389            .map(|c| *c * *c)
390            .fold(T::zero(), |acc, x| acc + x)
391            .sqrt()
392    }
393
394    /// Compose two rotors (rotation composition)
395    ///
396    /// # Type Safety
397    /// - Result is guaranteed to be a valid rotor
398    /// - Unit norm is preserved
399    #[cfg_attr(feature = "formal-verification",
400        ensures(Self::compute_norm(&result.multivector) == T::one()))]
401    pub fn compose(&self, other: &Self) -> Self {
402        let composed = self.multivector.geometric_product(&other.multivector);
403
404        // Normalize to ensure unit norm (should already be close due to properties)
405        let norm = Self::compute_norm(&composed);
406        let normalized = VerifiedMultivector {
407            coefficients: composed.coefficients.iter().map(|c| *c / norm).collect(),
408            _signature: PhantomData,
409        };
410
411        Self {
412            multivector: normalized,
413            _rotor_invariant: PhantomData,
414        }
415    }
416
417    /// Apply rotor to a vector (rotation/reflection)
418    pub fn apply<const D: usize>(&self, _v: &Vector<T, D>) -> Vector<T, D>
419    where
420        [(); D]: Sized,
421    {
422        // R v R† transformation
423        todo!("Rotor application")
424    }
425}
426
427/// Type-safe vector with compile-time dimension
428pub struct Vector<T, const D: usize>
429where
430    T: Float + Zero + One,
431{
432    pub(crate) data: Vec<T>,
433    _dim: PhantomData<Dim<D>>,
434}
435
436impl<T, const D: usize> Vector<T, D>
437where
438    T: Float + Zero + One,
439{
440    /// Create a new vector (dimension checked at compile time)
441    #[cfg_attr(feature = "formal-verification",
442        requires(data.len() == D),
443        ensures(result.dimension() == D))]
444    pub fn new(data: Vec<T>) -> Result<Self, &'static str> {
445        if data.len() != D {
446            return Err("Vector data length must match dimension");
447        }
448
449        Ok(Self {
450            data,
451            _dim: PhantomData,
452        })
453    }
454
455    /// Get the dimension (always D due to type parameter)
456    pub const fn dimension(&self) -> usize {
457        D
458    }
459
460    /// Dot product (only defined for same dimension - enforced by types)
461    #[cfg_attr(feature = "formal-verification",
462        ensures(result >= T::zero()))] // For positive-definite metrics
463    pub fn dot(&self, other: &Self) -> T {
464        self.data
465            .iter()
466            .zip(&other.data)
467            .map(|(a, b)| *a * *b)
468            .fold(T::zero(), |acc, x| acc + x)
469    }
470
471    /// Add two vectors (same dimension enforced at compile time)
472    pub fn add(&self, other: &Self) -> Self {
473        Self {
474            data: self
475                .data
476                .iter()
477                .zip(&other.data)
478                .map(|(a, b)| *a + *b)
479                .collect(),
480            _dim: PhantomData,
481        }
482    }
483}
484
485#[cfg(test)]
486mod tests {
487    use super::*;
488
489    #[test]
490    fn test_phantom_type_dimension_safety() {
491        // This would fail to compile if dimensions don't match
492        let v1 = Vector::<f64, 3>::new(vec![1.0, 2.0, 3.0]).unwrap();
493        let v2 = Vector::<f64, 3>::new(vec![4.0, 5.0, 6.0]).unwrap();
494        let _v3 = v1.add(&v2); // OK - same dimensions
495
496        // The following would not compile:
497        // let v4 = Vector::<f64, 2>::new(vec![1.0, 2.0]).unwrap();
498        // let v5 = v1.add(&v4); // ERROR: type mismatch
499    }
500
501    #[test]
502    fn test_signature_type_safety() {
503        // Clifford algebra Cl(3,0,0) - 3D Euclidean space
504        let mv1 = VerifiedMultivector::<f64, 3, 0, 0>::scalar(2.0);
505        let mv2 = VerifiedMultivector::<f64, 3, 0, 0>::scalar(3.0);
506        let _mv3 = mv1.add(&mv2); // OK - same signature
507
508        // The following would not compile:
509        // let mv4 = VerifiedMultivector::<f64, 2, 1, 0>::scalar(1.0); // Different signature
510        // let mv5 = mv1.add(&mv4); // ERROR: type mismatch
511    }
512
513    #[test]
514    fn test_grade_preservation() {
515        // Create a bivector (grade 2)
516        let bivector =
517            KVector::<f64, 2, 3, 0, 0>::from_multivector(VerifiedMultivector::scalar(1.0));
518
519        assert_eq!(bivector.grade(), 2);
520        // Grade is guaranteed at compile time
521    }
522
523    #[test]
524    fn test_outer_product_type_safety() {
525        use super::OuterProduct;
526
527        // Create two vectors (grade 1) in 3D Euclidean space
528        let v1 = KVector::<f64, 1, 3, 0, 0>::from_multivector(
529            VerifiedMultivector::basis_vector(0).unwrap(),
530        );
531        let v2 = KVector::<f64, 1, 3, 0, 0>::from_multivector(
532            VerifiedMultivector::basis_vector(1).unwrap(),
533        );
534
535        // Outer product of two vectors gives a bivector (grade 2)
536        let bivector: KVector<f64, 2, 3, 0, 0> = v1.outer_product(&v2);
537
538        // The type system guarantees this is grade 2
539        assert_eq!(bivector.grade(), 2);
540
541        // The following would not compile due to type mismatch:
542        // let wrong: KVector<f64, 3, 3, 0, 0> = v1.outer_product(&v2);
543        // Error: expected KVector<_, 3, _, _, _>, found KVector<_, 2, _, _, _>
544    }
545}