alice-math 0.1.0

A topology and algebra library from first principles
Documentation
use alloc::boxed::Box;
use crate::core::scalar::Scalar;
use crate::topology::manifold::{Atlas, Chart, Manifold, Point, TangentVector};

pub struct SmoothMap<S: Scalar> {
    domain_dim: usize,
    codomain_dim: usize,
    f: Box<dyn Fn(&Point<S>) -> Point<S> + Send + Sync>,
    differential: Box<dyn Fn(&Point<S>, &TangentVector<S>) -> TangentVector<S> + Send + Sync>,
}

impl<S: Scalar> SmoothMap<S> {
    pub fn new<F, D>(domain_dim: usize, codomain_dim: usize, f: F, differential: D) -> Self
    where
        F: Fn(&Point<S>) -> Point<S> + Send + Sync + 'static,
        D: Fn(&Point<S>, &TangentVector<S>) -> TangentVector<S> + Send + Sync + 'static,
    {
        Self {
            domain_dim,
            codomain_dim,
            f: Box::new(f),
            differential: Box::new(differential),
        }
    }

    pub fn apply(&self, p: &Point<S>) -> Point<S> {
        (self.f)(p)
    }

    pub fn differential(&self, p: &Point<S>, v: &TangentVector<S>) -> TangentVector<S> {
        (self.differential)(p, v)
    }

    pub fn domain_dim(&self) -> usize {
        self.domain_dim
    }

    pub fn codomain_dim(&self) -> usize {
        self.codomain_dim
    }
}

pub trait SmoothManifold: Manifold {
    fn transition_map(
        &self,
        from_chart: &Chart<Self::Scalar>,
        to_chart: &Chart<Self::Scalar>,
    ) -> SmoothMap<Self::Scalar>;

    fn pushforward(
        &self,
        map: &SmoothMap<Self::Scalar>,
        p: &Point<Self::Scalar>,
        v: &TangentVector<Self::Scalar>,
    ) -> TangentVector<Self::Scalar> {
        map.differential(p, v)
    }
}

pub struct EuclideanManifold<S: Scalar> {
    dim: usize,
    atlas: Atlas<S>,
}

impl<S: Scalar + 'static> EuclideanManifold<S> {
    pub fn new(dim: usize) -> Self {
        let mut atlas = Atlas::new(dim);
        let d = dim;
        atlas.add_chart(Chart::new(
            d,
            |p| p.clone(),
            |p| p.clone(),
            move |p| p.dim() == d,
        ));
        Self { dim, atlas }
    }
}

impl<S: Scalar> Manifold for EuclideanManifold<S> {
    type Scalar = S;

    fn dim(&self) -> usize {
        self.dim
    }

    fn atlas(&self) -> &Atlas<S> {
        &self.atlas
    }
}

impl<S: Scalar + 'static> SmoothManifold for EuclideanManifold<S> {
    fn transition_map(
        &self,
        _from_chart: &Chart<S>,
        _to_chart: &Chart<S>,
    ) -> SmoothMap<S> {
        let d = self.dim;
        SmoothMap::new(
            d,
            d,
            |p| p.clone(),
            |_p, v| v.clone(),
        )
    }
}

pub fn verify_transition_smoothness<M: SmoothManifold>(
    manifold: &M,
    test_points: &[Point<M::Scalar>],
) -> bool
where
    M::Scalar: PartialEq,
{
    let charts = manifold.atlas().charts();
    for i in 0..charts.len() {
        for j in 0..charts.len() {
            let t = manifold.transition_map(&charts[i], &charts[j]);
            for p in test_points {
                if charts[i].contains(p) && charts[j].contains(p) {
                    let q = t.apply(p);
                    if q.dim() != manifold.dim() {
                        return false;
                    }
                }
            }
        }
    }
    true
}

pub fn compose_smooth_maps<S: Scalar + 'static>(
    f: SmoothMap<S>,
    g: SmoothMap<S>,
) -> SmoothMap<S> {
    assert_eq!(
        g.codomain_dim(),
        f.domain_dim(),
        "map dimensions must be compatible for composition"
    );

    let domain_dim = g.domain_dim();
    let codomain_dim = f.codomain_dim();

    let f = alloc::sync::Arc::new(f);
    let g = alloc::sync::Arc::new(g);

    let f1 = f.clone();
    let g1 = g.clone();
    let f2 = f.clone();
    let g2 = g.clone();

    SmoothMap::new(
        domain_dim,
        codomain_dim,
        move |p| f1.apply(&g1.apply(p)),
        move |p, v| {
            let gp = g2.apply(p);
            let dg_v = g2.differential(p, v);
            f2.differential(&gp, &dg_v)
        },
    )
}