alga2 0.1.0

A modern abstract-algebra hierarchy for Rust — the successor to alga, powered by batch-impl
Documentation
//! The analytic layer (bare-core): lattices, numeric subset embeddings,
//! operator-closure markers, the reals, and the normed/euclidean structures
//! over the numerics and `Complex<T>` — all generated by batch-impl.
//!
//! Normed/inner-product/finite-dimensional spaces require a *field* scalar,
//! so only the floats and `Complex<f64>` inhabit them (the integers' norm
//! would be real-valued while their vectors stay integer — not a vector
//! space). Tuples join in `tuples.rs` via per-arity specs (the scalar
//! reduction is not expressible with the variadic repeat).

use batch_impl::{batch_impl_only, batch_trait};

use crate::complex::Complex;
use crate::op::{Additive, Multiplicative};
use crate::tower::{
    AffineSpace, BilinearForm, BooleanAlgebra, ClosedAdd, ClosedDiv, ClosedMul, ClosedNeg,
    ClosedRem, ClosedSub, ComplementedLattice, DistributiveLattice, EuclideanSpace,
    FiniteDimInnerSpace, FiniteDimVectorSpace, Group, InnerSpace, JoinSemilattice, Lattice, Magma,
    MeetSemilattice, Module, Monoid, NormedSpace, OrderedField, PositiveDefinite, Real, SubsetOf,
    SymmetricBilinearForm, VectorSpace,
};

// ---- numeric subset embedding: lossless integer chains, then floats ----
// `SubsetOf` keeps `batch_impl_only`: the 13-entry matrix shares three
// method bodies whose `T` is the trait parameter (`*self as T`) — the one
// place the directive system earns its keep (zero duplication).

#[batch_impl_only(
    [
        SubsetOf<u16> u8,
        SubsetOf<u32> u16,
        SubsetOf<u64> u32,
        SubsetOf<u128> u64,
        SubsetOf<i16> i8,
        SubsetOf<i32> i16,
        SubsetOf<i64> i32,
        SubsetOf<i128> i64,
        SubsetOf<i16> u8,
        SubsetOf<i32> u16,
        SubsetOf<i64> u32,
        SubsetOf<i128> u64,
        SubsetOf<f64> [@u*, @i*]
    ] #to_superset{*self as T}
        #is_in_subset{(*element as Self) as T == *element}
        #from_superset_unchecked{*element as Self},
    SubsetOf<Complex<f64>> f64 #to_superset{Complex::new(*self, 0.)}
        #is_in_subset{*element.im() == 0.}
        #from_superset_unchecked{*element.re()},
    SubsetOf<Complex<f64>> f32 #to_superset{Complex::new(*self as f64, 0.)}
        #is_in_subset{*element.im() == 0.}
        #from_superset_unchecked{*element.re() as f32},
)]
trait SubsetOf<T>: Sized {
    fn to_superset(&self) -> T;
    fn is_in_subset(element: &T) -> bool;
    fn from_superset(element: &T) -> Option<Self>;
    fn from_superset_unchecked(element: &T) -> Self;
}

// ---- operator-closure markers: the numerics ----

// ---- the reals: floats only ----

batch_trait! {
    @with=Additive, Multiplicative;
    @trait_with=@trait<@with>;
    MeetSemilattice:[@num,bool]{
        fn meet(&self, other: &Self) -> Self {
            (*self).min(*other)
        }
    };
    JoinSemilattice: [@num,bool]{
        fn join(&self, other: &Self) -> Self {
            (*self).max(*other)
        }
    };
    Lattice: @num, bool;
    ClosedAdd:@num;
    ClosedSub:@num;
    ClosedMul:@num;
    ClosedDiv:@num;
    ClosedRem:@num;
    FiniteDimInnerSpace:@trait_with [@f*,Complex<f64>,Complex<f32>];
    ClosedNeg:[@i*, @f*];
    DistributiveLattice: @num, bool;
    ComplementedLattice: bool {
        fn complement(&self) -> Self { !*self }
    };
    BooleanAlgebra: bool;
    OrderedField: @trait_with @f*;
    Real: @f* {
        fn sqrt(self) -> Self { self.sqrt() }
        fn abs(self) -> Self { self.abs() }
        fn acos(self) -> Self { self.acos() }
    };
    NormedSpace: @trait_with @f*{
        type RealField = Self;
        fn norm_squared(&self) -> Self { *self * *self }
        fn scale_real(&self, r: Self) -> Self { *self * r }
    },  @trait_with <T: Real + ClosedAdd + ClosedMul + Copy> Complex<T>{
        type RealField = T;
        fn norm_squared(&self) -> T { *self.re() * *self.re() + *self.im() * *self.im() }
        fn scale_real(&self, r: T) -> Self {
            <Complex<T> as Module<Additive, Multiplicative>>::scale(&r, *self)
        }
    };
    InnerSpace: @trait_with [@f*{
        fn inner_product(&self, other: &Self) -> Self { *self * *other }
    },
        <T: Real + ClosedAdd + ClosedMul + Copy> Complex<T>{
        fn inner_product(&self, other: &Self) -> T {
            *self.re() * *other.re() + *self.im() * *other.im()
        }
    }];
    FiniteDimVectorSpace: @trait_with [@f*{
        fn dimension() -> usize { 1 }
        fn canonical_basis_element(_i: usize) -> Self { 1. }
        fn dot(&self, other: &Self) -> Self { *self * *other }
    },  <T: Real + ClosedAdd + ClosedMul + Copy> Complex<T>{
        fn dimension() -> usize { 2 }
        fn canonical_basis_element(_i: usize) -> Self {
            if _i == 0 {
                Complex::new(
                    <T as Monoid<Multiplicative>>::identity(),
                    <T as Monoid>::identity(),
                )
            } else {
                Complex::new(
                    <T as Monoid>::identity(),
                    <T as Monoid<Multiplicative>>::identity(),
                )
            }
        }
        fn dot(&self, other: &Self) -> T {
            *self.re() * *other.re() + *self.im() * *other.im()
        }
    }];
    BilinearForm: <T: Real + Copy> T where
        T: VectorSpace<@with, Scalar = T>,
    {
        type Space = T;
        type Scalar = T;
        fn apply(&self, u: &T, v: &T) -> T { *u * *v }
    };
    SymmetricBilinearForm: <T: Real + Copy> T where
        T: VectorSpace<@with, Scalar = T>,
    ;
    PositiveDefinite: <T: Real + Copy> T where
        T: VectorSpace<@with, Scalar = T>,
        T: OrderedField<@with>,
    ;
    // euclidean / affine spaces: the numerics as their own spaces.
    // Tuple caps at 12: `EuclideanSpace` requires `Clone + PartialEq`, which
    // std only implements for tuples up to 12.
    EuclideanSpace: @f* {
        type Coordinates = Self;
        fn origin() -> Self { 0. }
        fn from_coordinates(coords: Self) -> Self { coords }
        fn coordinates(&self) -> Self { *self }
    },  (f64,).1..=12 impl{(A@..,)}{
        type Coordinates = (@(f64,)..);
        fn origin() -> Self { (@(0.,)..) }
        fn from_coordinates(coords: Self::Coordinates) -> Self { coords }
        fn coordinates(&self) -> Self::Coordinates { *self }
    };
    AffineSpace: @f*{
        type Translation = Self; fn origin() -> Self { 0. }
        fn from_point_translation(origin: &Self, translation: &Self) -> Self {
            <Self as Magma>::combine(origin, translation)
        }
        fn translate_by(&self, t: &Self) -> Self {
            <Self as Magma>::combine(self, t)
        }
        fn translation(&self, other: &Self) -> Self {
            <Self as Magma>::combine(other, &<Self as Group>::inverse(self))
        }
    };
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tower::{
        AffineSpace, EuclideanSpace, FiniteDimInnerSpace, FiniteDimVectorSpace, InnerSpace,
        JoinSemilattice, MeetSemilattice, NormedSpace, SubsetOf,
    };

    #[test]
    fn numeric_lattice() {
        assert_eq!(3u8.meet(&5), 3);
        assert_eq!(3u8.join(&5), 5);
        assert_eq!(3.5f64.meet(&-1.0), -1.0);
        // tuple lattice is componentwise
        assert_eq!((3u8, 7u8).meet(&(5, 2)), (3, 2));
        assert_eq!((3u8, 7u8).join(&(5, 2)), (5, 7));
    }

    #[test]
    fn subset_embedding() {
        let up: u16 = 3u8.to_superset();
        assert_eq!(up, 3u16);
        assert_eq!(<u8 as SubsetOf<u16>>::from_superset(&300u16), None);
        assert_eq!(<u8 as SubsetOf<u16>>::from_superset(&3u16), Some(3u8));
        // lossy int → f64: only exactly-representable values round-trip
        assert!(<u64 as SubsetOf<f64>>::is_in_subset(&42.0));
        assert!(!<u64 as SubsetOf<f64>>::is_in_subset(&1e30));
        // real → complex
        let z: Complex<f64> = 2.0f64.to_superset();
        assert_eq!(z, Complex::new(2.0, 0.0));
    }

    #[test]
    fn reals() {
        assert_eq!(9.0f64.sqrt(), 3.0);
        assert_eq!((-5.0f64).abs(), 5.0);
        assert_eq!(1.0f64.acos(), 0.0);
    }

    #[test]
    fn euclidean_norms() {
        // f64: ‖x‖ = |x|
        assert_eq!(3.0f64.norm_squared(), 9.0);
        assert_eq!((-4.0f64).norm(), 4.0);
        // tuple: euclidean (3, 4) → 5
        let v = (3.0f64, 4.0f64);
        assert_eq!(v.norm_squared(), 25.0);
        assert_eq!(v.norm(), 5.0);
        let n = v.normalize();
        assert!((n.norm() - 1.0).abs() < 1e-12);
        // inner product
        assert_eq!((1.0f64, 2.0f64).inner_product(&(3.0, 4.0)), 11.0);
        // complex norm
        let z = Complex::new(3.0f64, 4.0);
        assert_eq!(z.norm(), 5.0);
    }

    #[test]
    fn finite_dimension_and_basis() {
        assert_eq!(<f64 as FiniteDimVectorSpace<Additive, Multiplicative>>::dimension(), 1);
        assert_eq!(<(f64, f64) as FiniteDimVectorSpace<Additive, Multiplicative>>::dimension(), 2);
        let e0 =
            <(f64, f64) as FiniteDimVectorSpace<Additive, Multiplicative>>::canonical_basis_element(
                0,
            );
        assert_eq!(e0, (1.0, 0.0));
        assert_eq!((1.0f64, 2.0).dot(&(3.0, 4.0)), 11.0);
    }

    #[test]
    fn gram_schmidt_orthonormalizes() {
        let mut vs = [(1.0f64, 0.0), (1.0, 1.0)];
        let n =
            <(f64, f64) as FiniteDimInnerSpace<Additive, Multiplicative>>::orthonormalize(&mut vs);
        assert_eq!(n, 2);
        assert!((vs[0].norm() - 1.0).abs() < 1e-12);
        assert!((vs[1].norm() - 1.0).abs() < 1e-12);
        // orthogonality: ⟨u0, u1⟩ ≈ 0
        assert!(vs[0].inner_product(&vs[1]).abs() < 1e-12);
    }

    #[test]
    fn euclidean_distance_and_affine() {
        assert_eq!(1.0f64.distance(&4.0), 3.0);
        assert_eq!((0.0f64, 0.0).distance(&(3.0, 4.0)), 5.0);
        // affine: translate by 2
        assert_eq!(1.0f64.translate_by(&2.0), 3.0);
    }

    #[test]
    fn euclidean_space_tuples_to_12() {
        // Clone + PartialEq supertraits cap std at arity 12.
        let v: (f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64) =
            (1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0);
        assert_eq!(
            <(f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64) as EuclideanSpace>::origin(),
            (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
        );
        assert_eq!(
            <(f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64) as EuclideanSpace>::coordinates(&v),
            v
        );
    }

    #[test]
    fn complex_finite_dim_is_two() {
        // C is a 2-dimensional vector space over R with basis {1, i}.
        assert_eq!(
            <Complex<f64> as FiniteDimVectorSpace<Additive, Multiplicative>>::dimension(),
            2
        );
        let e0 =
            <Complex<f64> as FiniteDimVectorSpace<Additive, Multiplicative>>::canonical_basis_element(
                0,
            );
        let e1 =
            <Complex<f64> as FiniteDimVectorSpace<Additive, Multiplicative>>::canonical_basis_element(
                1,
            );
        assert_eq!(e0, Complex::new(1.0, 0.0));
        assert_eq!(e1, Complex::new(0.0, 1.0));
        // Complex<f32> mirrors f64.
        assert_eq!(
            <Complex<f32> as FiniteDimVectorSpace<Additive, Multiplicative>>::dimension(),
            2
        );
        assert_eq!(
            <Complex<f32> as NormedSpace<Additive, Multiplicative>>::norm_squared(&Complex::new(
                3.0f32, 4.0
            )),
            25.0
        );
    }

    #[test]
    fn complex_subset_checks_imag_part() {
        // The real line embeds into C; anything off it is rejected.
        assert!(<f64 as SubsetOf<Complex<f64>>>::is_in_subset(&Complex::new(1.0, 0.0)));
        assert!(!<f64 as SubsetOf<Complex<f64>>>::is_in_subset(&Complex::new(1.0, 2.0)));
        assert_eq!(<f64 as SubsetOf<Complex<f64>>>::from_superset(&Complex::new(1.0, 2.0)), None);
        assert_eq!(
            <f64 as SubsetOf<Complex<f64>>>::from_superset(&Complex::new(1.0, 0.0)),
            Some(1.0)
        );
        // f32 embeds into Complex<f64> as well.
        assert!(!<f32 as SubsetOf<Complex<f64>>>::is_in_subset(&Complex::new(1.0, 2.0)));
    }

    #[test]
    fn multiplication_is_a_bilinear_form() {
        use crate::tower::{BilinearForm, PositiveDefinite, SymmetricBilinearForm};
        // B(u, v) = u·v on f64.
        let form = 1.0f64;
        assert_eq!(form.apply(&2.0, &3.0), 6.0);
        // Symmetric and positive-definite.
        fn assert_sym<F: SymmetricBilinearForm<Space = f64, Scalar = f64>>() {}
        fn assert_pd<F: PositiveDefinite<Space = f64, Scalar = f64>>() {}
        assert_sym::<f64>();
        assert_pd::<f64>();
    }
}