Sat, 09 Nov 2024 20:54:32 -0500
Some Differential GATs
/*! Traits for mathematical functions. */ use std::marker::PhantomData; use crate::types::Float; use serde::Serialize; use crate::loc::Loc; /// Trait for application of `Self` as a mathematical function or operator on `X`. pub trait Apply<X> { type Output; /// Compute the value of `self` at `x`. fn apply(&self, x : X) -> Self::Output; } /// This blanket implementation is a workaround helper to Rust trait system limitations. /// /// It is introduced because the trait system does not allow blanket implementations of both /// [`Apply<X>`] and [`Apply<&'a X>`]. With this, the latter is implemented automatically for /// the reference, which can be sufficient to apply the operation in another blanket implementation. impl<'a, T, X> Apply<X> for &'a T where T : Apply<X> { type Output = <T as Apply<X>>::Output; #[inline] fn apply(&self, x : X) -> Self::Output { (*self).apply(x) } } /// A mapping from `Domain` to `Codomain`. /// /// This is automatically implemented when the relevant [`Apply`] are implemented. pub trait Mapping<Domain> : Apply<Domain, Output=Self::Codomain> + for<'a> Apply<&'a Domain, Output=Self::Codomain> { type Codomain; } impl<Domain, Codomain, T> Mapping<Domain> for T where T : Apply<Domain, Output=Codomain> + for<'a> Apply<&'a Domain, Output=Codomain> { type Codomain = Codomain; } /// Automatically implemented shorthand for referring to [`Mapping`]s from [`Loc<F, N>`] to `F`. pub trait RealMapping<F : Float, const N : usize> : Mapping<Loc<F, N>, Codomain = F> {} impl<F : Float, T, const N : usize> RealMapping<F, N> for T where T : Mapping<Loc<F, N>, Codomain = F> {} /// Automatically implemented shorthand for referring to differentiable [`Mapping`]s from /// [`Loc<F, N>`] to `F`. pub trait DifferentiableRealMapping<F : Float, const N : usize> : DifferentiableMapping<Loc<F, N>, Codomain = F, DerivativeDomain=Loc<F, N>> {} impl<F : Float, T, const N : usize> DifferentiableRealMapping<F, N> for T where T : DifferentiableMapping<Loc<F, N>, Codomain = F, DerivativeDomain=Loc<F, N>> {} /// A helper trait alias for referring to [`Mapping`]s from [`Loc<F, N>`] to [`Loc<F, M>`]. pub trait RealVectorField<F : Float, const N : usize, const M : usize> : Mapping<Loc<F, N>, Codomain = Loc<F, M>> {} impl<F : Float, T, const N : usize, const M : usize> RealVectorField<F, N, M> for T where T : Mapping<Loc<F, N>, Codomain = Loc<F, M>> {} /// Trait for calculation the differential of `Self` as a mathematical function on `X`. pub trait Differentiable<X> : Sized { type Derivative; /// Compute the differential of `self` at `x`. fn differential(&self, x : X) -> Self::Derivative; } /// A differentiable mapping from `Domain` to [`Mapping::Codomain`], with differentials /// `Differential`. /// /// This is automatically implemented when the relevant [`Differentiate`] are implemented. pub trait DifferentiableMapping<Domain> : Mapping<Domain> + Differentiable<Domain, Derivative=Self::DerivativeDomain> + for<'a> Differentiable<&'a Domain, Derivative=Self::DerivativeDomain> { type DerivativeDomain; type Differential : Mapping<Domain, Codomain=Self::DerivativeDomain>; type DifferentialRef<'b> : Mapping<Domain, Codomain=Self::DerivativeDomain> where Self : 'b; /// Form the differential mapping of `self`. fn diff(self) -> Self::Differential; /// Form the differential mapping of `self`. fn diff_ref(&self) -> Self::DifferentialRef<'_>; } impl<Domain, Derivative, T> DifferentiableMapping<Domain> for T where T : Mapping<Domain> + Differentiable<Domain, Derivative=Derivative> + for<'a> Differentiable<&'a Domain,Derivative=Derivative> { type DerivativeDomain = Derivative; type Differential = Differential<Domain, Self>; type DifferentialRef<'b> = Differential<Domain, Ref<'b, Self>> where Self : 'b; /// Form the differential mapping of `self`. fn diff(self) -> Self::Differential { Differential{ g : self, _space : PhantomData } } /// Form the differential mapping of `self`. fn diff_ref(&self) -> Self::DifferentialRef<'_> { Differential{ g : Ref(self), _space : PhantomData } } } /// A sum of [`Mapping`]s. #[derive(Serialize, Debug, Clone)] pub struct Sum<Domain, M : Mapping<Domain>> { components : Vec<M>, _domain : PhantomData<Domain>, } impl<Domain, M : Mapping<Domain>> Sum<Domain, M> { /// Construct from an iterator. pub fn new<I : Iterator<Item = M>>(iter : I) -> Self { Sum { components : iter.collect(), _domain : PhantomData } } /// Iterate over the component functions of the sum pub fn iter(&self) -> std::slice::Iter<'_, M> { self.components.iter() } } impl<Domain, M> Apply<Domain> for Sum<Domain, M> where M : Mapping<Domain>, M::Codomain : std::iter::Sum { type Output = M::Codomain; fn apply(&self, x : Domain) -> Self::Output { self.components.iter().map(|c| c.apply(&x)).sum() } } impl<'a, Domain, M> Apply<&'a Domain> for Sum<Domain, M> where M : Mapping<Domain>, M::Codomain : std::iter::Sum { type Output = M::Codomain; fn apply(&self, x : &'a Domain) -> Self::Output { self.components.iter().map(|c| c.apply(x)).sum() } } impl<Domain, M> Differentiable<Domain> for Sum<Domain, M> where M : DifferentiableMapping<Domain>, M :: Codomain : std::iter::Sum, M :: DerivativeDomain : std::iter::Sum, Domain : Copy { type Derivative = M::DerivativeDomain; fn differential(&self, x : Domain) -> Self::Derivative { self.components.iter().map(|c| c.differential(x)).sum() } } /// Container for the differential [`Mapping`] of a [`Differentiable`] mapping. pub struct Differential<X, G : DifferentiableMapping<X>> { g : G, _space : PhantomData<X> } impl<X, G : DifferentiableMapping<X>> Apply<X> for Differential<X, G> { type Output = G::DerivativeDomain; #[inline] fn apply(&self, x : X) -> Self::Output { self.g.differential(x) } } impl<'a, X, G : DifferentiableMapping<X>> Apply<&'a X> for Differential<X, G> { type Output = G::DerivativeDomain; #[inline] fn apply(&self, x : &'a X) -> Self::Output { self.g.differential(x) } } /// Container for flattening [`Loc`]`<F, 1>` codomain of a [`Mapping`] to `F`. pub struct FlattenedCodomain<X, F, G : Mapping<X, Codomain=Loc<F, 1>>> { g : G, _phantoms : PhantomData<(X, F)> } impl<X, F, G : Mapping<X, Codomain=Loc<F, 1>>> Apply<X> for FlattenedCodomain<X, F, G> { type Output = F; #[inline] fn apply(&self, x : X) -> Self::Output { self.g.apply(x).flatten1d() } } /// An auto-trait for constructing a [`FlattenCodomain`] structure for /// flattening the codomain of a [`Mapping`] from [`Loc`]`<F, 1>` to `F`. pub trait FlattenCodomain<X, F> : Mapping<X, Codomain=Loc<F, 1>> + Sized { /// Flatten the codomain from [`Loc`]`<F, 1>` to `F`. fn flatten_codomain(self) -> FlattenedCodomain<X, F, Self> { FlattenedCodomain{ g : self, _phantoms : PhantomData } } } impl<X, F, G : Sized + Mapping<X, Codomain=Loc<F, 1>>> FlattenCodomain<X, F> for G {} /// Container for dimensional slicing [`Loc`]`<F, N>` codomain of a [`Mapping`] to `F`. pub struct SlicedCodomain<X, F, G : Mapping<X, Codomain=Loc<F, N>>, const N : usize> { g : G, slice : usize, _phantoms : PhantomData<(X, F)> } impl<X, F : Copy, G : Mapping<X, Codomain=Loc<F, N>>, const N : usize> Apply<X> for SlicedCodomain<X, F, G, N> { type Output = F; #[inline] fn apply(&self, x : X) -> Self::Output { let tmp : [F; N] = self.g.apply(x).into(); // Safety: `slice_codomain` below checks the range. unsafe { *tmp.get_unchecked(self.slice) } } } impl<'a, X, F : Copy, G : Mapping<X, Codomain=Loc<F, N>>, const N : usize> Apply<&'a X> for SlicedCodomain<X, F, G, N> { type Output = F; #[inline] fn apply(&self, x : &'a X) -> Self::Output { let tmp : [F; N] = self.g.apply(x).into(); // Safety: `slice_codomain` below checks the range. unsafe { *tmp.get_unchecked(self.slice) } } } /// An auto-trait for constructing a [`FlattenCodomain`] structure for /// flattening the codomain of a [`Mapping`] from [`Loc`]`<F, 1>` to `F`. pub trait SliceCodomain<X, F : Copy, const N : usize> : Mapping<X, Codomain=Loc<F, N>> + Sized { /// Flatten the codomain from [`Loc`]`<F, 1>` to `F`. fn slice_codomain(self, slice : usize) -> SlicedCodomain<X, F, Self, N> { assert!(slice < N); SlicedCodomain{ g : self, slice, _phantoms : PhantomData } } /// Flatten the codomain from [`Loc`]`<F, 1>` to `F`. fn slice_codomain_ref(&self, slice : usize) -> SlicedCodomain<X, F, Ref<'_, Self>, N> { assert!(slice < N); SlicedCodomain{ g : Ref(self), slice, _phantoms : PhantomData } } } impl<X, F : Copy, G : Sized + Mapping<X, Codomain=Loc<F, N>>, const N : usize> SliceCodomain<X, F, N> for G {} /// Helper struct for doing operations on references of mappings while avoiding /// conflicting implementations that `&'g G` would cause. pub struct Ref<'g, G>(&'g G); impl<'g, X, G : Apply<X>> Apply<X> for Ref<'g, G> { type Output = G::Output; #[inline] fn apply(&self, x : X) -> Self::Output { self.0.apply(x) } } impl<'g, X, G : Differentiable<X>> Differentiable<X> for Ref<'g, G> { type Derivative = G::Derivative; #[inline] fn differential(&self, x : X) -> Self::Derivative { self.0.differential(x) } }