| 1 /*! |
1 /*! |
| 2 Traits for mathematical functions. |
2 Traits for mathematical functions. |
| 3 */ |
3 */ |
| 4 |
4 |
| 5 use std::marker::PhantomData; |
5 use std::marker::PhantomData; |
| 6 use crate::types::{Float}; |
6 use std::borrow::Cow; |
| 7 use serde::Serialize; |
7 use crate::types::{Num, Float, ClosedMul}; |
| 8 use crate::loc::Loc; |
8 use crate::loc::Loc; |
| 9 |
9 pub use crate::instance::{Instance, Decomposition, BasicDecomposition, Space}; |
| 10 /// Trait for application of `Self` as a mathematical function or operator on `X`. |
10 use crate::norms::{Norm, NormExponent}; |
| 11 pub trait Apply<X> { |
11 use crate::operator_arithmetic::{Weighted, Constant}; |
| 12 type Output; |
12 |
| |
13 /// A mapping from `Domain` to `Self::Codomain`. |
| |
14 pub trait Mapping<Domain : Space> { |
| |
15 type Codomain : Space; |
| 13 |
16 |
| 14 /// Compute the value of `self` at `x`. |
17 /// Compute the value of `self` at `x`. |
| 15 fn apply(&self, x : X) -> Self::Output; |
18 fn apply<I : Instance<Domain>>(&self, x : I) -> Self::Codomain; |
| 16 } |
19 |
| 17 |
20 #[inline] |
| 18 /// This blanket implementation is a workaround helper to Rust trait system limitations. |
21 /// Form the composition `self ∘ other` |
| 19 /// |
22 fn compose<X : Space, T : Mapping<X, Codomain=Domain>>(self, other : T) |
| 20 /// It is introduced because the trait system does not allow blanket implementations of both |
23 -> Composition<Self, T> |
| 21 /// [`Apply<X>`] and [`Apply<&'a X>`]. With this, the latter is implemented automatically for |
24 where |
| 22 /// the reference, which can be sufficient to apply the operation in another blanket implementation. |
25 Self : Sized |
| 23 impl<'a, T, X> Apply<X> for &'a T where T : Apply<X> { |
26 { |
| 24 type Output = <T as Apply<X>>::Output; |
27 Composition{ outer : self, inner : other, intermediate_norm_exponent : () } |
| 25 |
28 } |
| 26 #[inline] |
29 |
| 27 fn apply(&self, x : X) -> Self::Output { |
30 |
| 28 (*self).apply(x) |
31 #[inline] |
| 29 } |
32 /// Form the composition `self ∘ other`, assigning a norm to the inermediate space |
| 30 } |
33 fn compose_with_norm<F, X, T, E>( |
| 31 |
34 self, other : T, norm : E |
| 32 /// A mapping from `Domain` to `Codomain`. |
35 ) -> Composition<Self, T, E> |
| 33 /// |
36 where |
| 34 /// This is automatically implemented when the relevant [`Apply`] are implemented. |
37 Self : Sized, |
| 35 pub trait Mapping<Domain> : Apply<Domain, Output=Self::Codomain> |
38 X : Space, |
| 36 + for<'a> Apply<&'a Domain, Output=Self::Codomain> { |
39 T : Mapping<X, Codomain=Domain>, |
| 37 type Codomain; |
40 E : NormExponent, |
| 38 } |
41 Domain : Norm<F, E>, |
| 39 |
42 F : Num |
| 40 impl<Domain, Codomain, T> Mapping<Domain> for T |
43 { |
| 41 where T : Apply<Domain, Output=Codomain> + for<'a> Apply<&'a Domain, Output=Codomain> { |
44 Composition{ outer : self, inner : other, intermediate_norm_exponent : norm } |
| 42 type Codomain = Codomain; |
45 } |
| 43 } |
46 |
| 44 |
47 /// Multiply `self` by the scalar `a`. |
| 45 |
48 #[inline] |
| 46 /// A helper trait alias for referring to [`Mapping`]s from [`Loc<F, N>`] to `F` a [`Float`]. |
49 fn weigh<C>(self, a : C) -> Weighted<Self, C> |
| 47 pub trait RealMapping<F : Float, const N : usize> : Mapping<Loc<F, N>, Codomain = F> {} |
50 where |
| |
51 Self : Sized, |
| |
52 C : Constant, |
| |
53 Self::Codomain : ClosedMul<C::Type>, |
| |
54 { |
| |
55 Weighted { weight : a, base_fn : self } |
| |
56 } |
| |
57 } |
| |
58 |
| |
59 /// Automatically implemented shorthand for referring to [`Mapping`]s from [`Loc<F, N>`] to `F`. |
| |
60 pub trait RealMapping<F : Float, const N : usize> |
| |
61 : Mapping<Loc<F, N>, Codomain = F> {} |
| 48 |
62 |
| 49 impl<F : Float, T, const N : usize> RealMapping<F, N> for T |
63 impl<F : Float, T, const N : usize> RealMapping<F, N> for T |
| 50 where T : Mapping<Loc<F, N>, Codomain = F> {} |
64 where T : Mapping<Loc<F, N>, Codomain = F> {} |
| 51 |
65 |
| 52 |
66 /// A helper trait alias for referring to [`Mapping`]s from [`Loc<F, N>`] to [`Loc<F, M>`]. |
| 53 /// Trait for calculation the differential of `Self` as a mathematical function on `X`. |
67 pub trait RealVectorField<F : Float, const N : usize, const M : usize> |
| 54 pub trait Differentiate<X> { |
68 : Mapping<Loc<F, N>, Codomain = Loc<F, M>> {} |
| 55 type Output; |
69 |
| 56 |
70 impl<F : Float, T, const N : usize, const M : usize> RealVectorField<F, N, M> for T |
| 57 /// Compute the differential of `self` at `x`. |
71 where T : Mapping<Loc<F, N>, Codomain = Loc<F, M>> {} |
| 58 fn differential(&self, x : X) -> Self::Output; |
|
| 59 } |
|
| 60 |
|
| 61 |
72 |
| 62 /// A differentiable mapping from `Domain` to [`Mapping::Codomain`], with differentials |
73 /// A differentiable mapping from `Domain` to [`Mapping::Codomain`], with differentials |
| 63 /// `Differential`. |
74 /// `Differential`. |
| 64 /// |
75 /// |
| 65 /// This is automatically implemented when the relevant [`Differentiate`] are implemented. |
76 /// This is automatically implemented when [`DifferentiableImpl`] is. |
| 66 pub trait DifferentiableMapping<Domain> |
77 pub trait DifferentiableMapping<Domain : Space> : Mapping<Domain> { |
| 67 : Mapping<Domain> |
78 type DerivativeDomain : Space; |
| 68 + Differentiate<Domain, Output=Self::Differential> |
79 type Differential<'b> : Mapping<Domain, Codomain=Self::DerivativeDomain> where Self : 'b; |
| 69 + for<'a> Differentiate<&'a Domain, Output=Self::Differential>{ |
80 |
| 70 type Differential; |
81 /// Calculate differential at `x` |
| 71 } |
82 fn differential<I : Instance<Domain>>(&self, x : I) -> Self::DerivativeDomain; |
| 72 |
83 |
| 73 |
84 /// Form the differential mapping of `self`. |
| 74 impl<Domain, Differential, T> DifferentiableMapping<Domain> for T |
85 fn diff(self) -> Self::Differential<'static>; |
| 75 where T : Mapping<Domain> |
86 |
| 76 + Differentiate<Domain, Output=Differential> |
87 /// Form the differential mapping of `self`. |
| 77 + for<'a> Differentiate<&'a Domain, Output=Differential> { |
88 fn diff_ref(&self) -> Self::Differential<'_>; |
| 78 type Differential = Differential; |
89 } |
| 79 } |
90 |
| 80 |
91 /// Automatically implemented shorthand for referring to differentiable [`Mapping`]s from |
| 81 /// A sum of [`Mapping`]s. |
92 /// [`Loc<F, N>`] to `F`. |
| 82 #[derive(Serialize, Debug, Clone)] |
93 pub trait DifferentiableRealMapping<F : Float, const N : usize> |
| 83 pub struct Sum<Domain, M : Mapping<Domain>> { |
94 : DifferentiableMapping<Loc<F, N>, Codomain = F, DerivativeDomain = Loc<F, N>> {} |
| 84 components : Vec<M>, |
95 |
| 85 _domain : PhantomData<Domain>, |
96 impl<F : Float, T, const N : usize> DifferentiableRealMapping<F, N> for T |
| 86 } |
97 where T : DifferentiableMapping<Loc<F, N>, Codomain = F, DerivativeDomain = Loc<F, N>> {} |
| 87 |
98 |
| 88 impl<Domain, M : Mapping<Domain>> Sum<Domain, M> { |
99 /// Helper trait for implementing [`DifferentiableMapping`] |
| 89 /// Construct from an iterator. |
100 pub trait DifferentiableImpl<X : Space> : Sized { |
| 90 pub fn new<I : Iterator<Item = M>>(iter : I) -> Self { |
101 type Derivative : Space; |
| 91 Sum { components : iter.collect(), _domain : PhantomData } |
102 |
| 92 } |
103 /// Compute the differential of `self` at `x`, consuming the input. |
| 93 } |
104 fn differential_impl<I : Instance<X>>(&self, x : I) -> Self::Derivative; |
| 94 |
105 } |
| 95 |
106 |
| 96 impl<Domain : Copy, M> Apply<Domain> for Sum<Domain, M> |
107 impl<T, Domain> DifferentiableMapping<Domain> for T |
| 97 where M : Mapping<Domain>, |
108 where |
| 98 M::Codomain : std::iter::Sum { |
109 Domain : Space, |
| 99 type Output = M::Codomain; |
110 T : Clone + Mapping<Domain> + DifferentiableImpl<Domain> |
| 100 |
111 { |
| 101 fn apply(&self, x : Domain) -> Self::Output { |
112 type DerivativeDomain = T::Derivative; |
| 102 self.components.iter().map(|c| c.apply(x)).sum() |
113 type Differential<'b> = Differential<'b, Domain, Self> where Self : 'b; |
| 103 } |
114 |
| 104 } |
115 #[inline] |
| 105 |
116 fn differential<I : Instance<Domain>>(&self, x : I) -> Self::DerivativeDomain { |
| 106 impl<Domain, M> Differentiate<Domain> for Sum<Domain, M> |
117 self.differential_impl(x) |
| 107 where M : DifferentiableMapping<Domain>, |
118 } |
| 108 M :: Codomain : std::iter::Sum, |
119 |
| 109 M :: Differential : std::iter::Sum, |
120 fn diff(self) -> Differential<'static, Domain, Self> { |
| 110 Domain : Copy { |
121 Differential{ g : Cow::Owned(self), _space : PhantomData } |
| 111 |
122 } |
| 112 type Output = M::Differential; |
123 |
| 113 |
124 fn diff_ref(&self) -> Differential<'_, Domain, Self> { |
| 114 fn differential(&self, x : Domain) -> Self::Output { |
125 Differential{ g : Cow::Borrowed(self), _space : PhantomData } |
| 115 self.components.iter().map(|c| c.differential(x)).sum() |
126 } |
| 116 } |
127 } |
| 117 } |
128 |
| |
129 |
| |
130 /// Container for the differential [`Mapping`] of a [`DifferentiableMapping`]. |
| |
131 pub struct Differential<'a, X, G : Clone> { |
| |
132 g : Cow<'a, G>, |
| |
133 _space : PhantomData<X> |
| |
134 } |
| |
135 |
| |
136 impl<'a, X, G : Clone> Differential<'a, X, G> { |
| |
137 pub fn base_fn(&self) -> &G { |
| |
138 &self.g |
| |
139 } |
| |
140 } |
| |
141 |
| |
142 impl<'a, X, G> Mapping<X> for Differential<'a, X, G> |
| |
143 where |
| |
144 X : Space, |
| |
145 G : Clone + DifferentiableMapping<X> |
| |
146 { |
| |
147 type Codomain = G::DerivativeDomain; |
| |
148 |
| |
149 #[inline] |
| |
150 fn apply<I : Instance<X>>(&self, x : I) -> Self::Codomain { |
| |
151 (*self.g).differential(x) |
| |
152 } |
| |
153 } |
| |
154 |
| |
155 /// Container for flattening [`Loc`]`<F, 1>` codomain of a [`Mapping`] to `F`. |
| |
156 pub struct FlattenedCodomain<X, F, G> { |
| |
157 g : G, |
| |
158 _phantoms : PhantomData<(X, F)> |
| |
159 } |
| |
160 |
| |
161 impl<F : Space, X, G> Mapping<X> for FlattenedCodomain<X, F, G> |
| |
162 where |
| |
163 X : Space, |
| |
164 G: Mapping<X, Codomain=Loc<F, 1>> |
| |
165 { |
| |
166 type Codomain = F; |
| |
167 |
| |
168 #[inline] |
| |
169 fn apply<I : Instance<X>>(&self, x : I) -> Self::Codomain { |
| |
170 self.g.apply(x).flatten1d() |
| |
171 } |
| |
172 } |
| |
173 |
| |
174 /// An auto-trait for constructing a [`FlattenCodomain`] structure for |
| |
175 /// flattening the codomain of a [`Mapping`] from [`Loc`]`<F, 1>` to `F`. |
| |
176 pub trait FlattenCodomain<X : Space, F> : Mapping<X, Codomain=Loc<F, 1>> + Sized { |
| |
177 /// Flatten the codomain from [`Loc`]`<F, 1>` to `F`. |
| |
178 fn flatten_codomain(self) -> FlattenedCodomain<X, F, Self> { |
| |
179 FlattenedCodomain{ g : self, _phantoms : PhantomData } |
| |
180 } |
| |
181 } |
| |
182 |
| |
183 impl<X : Space, F, G : Sized + Mapping<X, Codomain=Loc<F, 1>>> FlattenCodomain<X, F> for G {} |
| |
184 |
| |
185 /// Container for dimensional slicing [`Loc`]`<F, N>` codomain of a [`Mapping`] to `F`. |
| |
186 pub struct SlicedCodomain<'a, X, F, G : Clone, const N : usize> { |
| |
187 g : Cow<'a, G>, |
| |
188 slice : usize, |
| |
189 _phantoms : PhantomData<(X, F)> |
| |
190 } |
| |
191 |
| |
192 impl<'a, X, F, G, const N : usize> Mapping<X> for SlicedCodomain<'a, X, F, G, N> |
| |
193 where |
| |
194 X : Space, |
| |
195 F : Copy + Space, |
| |
196 G : Mapping<X, Codomain=Loc<F, N>> + Clone, |
| |
197 { |
| |
198 type Codomain = F; |
| |
199 |
| |
200 #[inline] |
| |
201 fn apply<I : Instance<X>>(&self, x : I) -> Self::Codomain { |
| |
202 let tmp : [F; N] = (*self.g).apply(x).into(); |
| |
203 // Safety: `slice_codomain` below checks the range. |
| |
204 unsafe { *tmp.get_unchecked(self.slice) } |
| |
205 } |
| |
206 } |
| |
207 |
| |
208 /// An auto-trait for constructing a [`FlattenCodomain`] structure for |
| |
209 /// flattening the codomain of a [`Mapping`] from [`Loc`]`<F, 1>` to `F`. |
| |
210 pub trait SliceCodomain<X : Space, F : Copy, const N : usize> |
| |
211 : Mapping<X, Codomain=Loc<F, N>> + Clone + Sized |
| |
212 { |
| |
213 /// Flatten the codomain from [`Loc`]`<F, 1>` to `F`. |
| |
214 fn slice_codomain(self, slice : usize) -> SlicedCodomain<'static, X, F, Self, N> { |
| |
215 assert!(slice < N); |
| |
216 SlicedCodomain{ g : Cow::Owned(self), slice, _phantoms : PhantomData } |
| |
217 } |
| |
218 |
| |
219 /// Flatten the codomain from [`Loc`]`<F, 1>` to `F`. |
| |
220 fn slice_codomain_ref(&self, slice : usize) -> SlicedCodomain<'_, X, F, Self, N> { |
| |
221 assert!(slice < N); |
| |
222 SlicedCodomain{ g : Cow::Borrowed(self), slice, _phantoms : PhantomData } |
| |
223 } |
| |
224 } |
| |
225 |
| |
226 impl<X : Space, F : Copy, G : Sized + Mapping<X, Codomain=Loc<F, N>> + Clone, const N : usize> |
| |
227 SliceCodomain<X, F, N> |
| |
228 for G {} |
| |
229 |
| |
230 |
| |
231 /// The composition S ∘ T. `E` is for storing a `NormExponent` for the intermediate space. |
| |
232 pub struct Composition<S, T, E = ()> { |
| |
233 pub outer : S, |
| |
234 pub inner : T, |
| |
235 pub intermediate_norm_exponent : E |
| |
236 } |
| |
237 |
| |
238 impl<S, T, X, E> Mapping<X> for Composition<S, T, E> |
| |
239 where |
| |
240 X : Space, |
| |
241 T : Mapping<X>, |
| |
242 S : Mapping<T::Codomain> |
| |
243 { |
| |
244 type Codomain = S::Codomain; |
| |
245 |
| |
246 #[inline] |
| |
247 fn apply<I : Instance<X>>(&self, x : I) -> Self::Codomain { |
| |
248 self.outer.apply(self.inner.apply(x)) |
| |
249 } |
| |
250 } |