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 } |