| 1 |
|
| 2 //! Things for constructing new kernels from component kernels and traits for analysing them |
1 //! Things for constructing new kernels from component kernels and traits for analysing them |
| |
2 use numeric_literals::replace_float_literals; |
| 3 use serde::Serialize; |
3 use serde::Serialize; |
| 4 use numeric_literals::replace_float_literals; |
4 |
| 5 |
5 use alg_tools::bisection_tree::Support; |
| |
6 use alg_tools::bounds::{Bounded, Bounds, GlobalAnalysis, LocalAnalysis}; |
| |
7 use alg_tools::instance::{Instance, Space}; |
| |
8 use alg_tools::loc::Loc; |
| |
9 use alg_tools::mapping::{ |
| |
10 DifferentiableImpl, DifferentiableMapping, LipschitzDifferentiableImpl, Mapping, |
| |
11 }; |
| |
12 use alg_tools::maputil::{array_init, map1_indexed, map2}; |
| |
13 use alg_tools::norms::*; |
| |
14 use alg_tools::sets::Cube; |
| |
15 use alg_tools::sets::SetOrd; |
| 6 use alg_tools::types::*; |
16 use alg_tools::types::*; |
| 7 use alg_tools::norms::*; |
17 use anyhow::anyhow; |
| 8 use alg_tools::loc::Loc; |
|
| 9 use alg_tools::sets::Cube; |
|
| 10 use alg_tools::bisection_tree::{ |
|
| 11 Support, |
|
| 12 Bounds, |
|
| 13 LocalAnalysis, |
|
| 14 GlobalAnalysis, |
|
| 15 Bounded, |
|
| 16 }; |
|
| 17 use alg_tools::mapping::{ |
|
| 18 Mapping, |
|
| 19 DifferentiableImpl, |
|
| 20 DifferentiableMapping, |
|
| 21 Differential, |
|
| 22 }; |
|
| 23 use alg_tools::instance::{Instance, Space}; |
|
| 24 use alg_tools::maputil::{array_init, map2, map1_indexed}; |
|
| 25 use alg_tools::sets::SetOrd; |
|
| 26 |
18 |
| 27 use crate::fourier::Fourier; |
19 use crate::fourier::Fourier; |
| 28 use crate::types::*; |
20 use crate::types::*; |
| 29 |
21 |
| 30 /// Representation of the product of two kernels. |
22 /// Representation of the product of two kernels. |
| 31 /// |
23 /// |
| 32 /// The kernels typically implement [`Support`] and [`Mapping`]. |
24 /// The kernels typically implement [`Support`] and [`Mapping`]. |
| 33 /// |
25 /// |
| 34 /// The implementation [`Support`] only uses the [`Support::support_hint`] of the first parameter! |
26 /// The implementation [`Support`] only uses the [`Support::support_hint`] of the first parameter! |
| 35 #[derive(Copy,Clone,Serialize,Debug)] |
27 #[derive(Copy, Clone, Serialize, Debug)] |
| 36 pub struct SupportProductFirst<A, B>( |
28 pub struct SupportProductFirst<A, B>( |
| 37 /// First kernel |
29 /// First kernel |
| 38 pub A, |
30 pub A, |
| 39 /// Second kernel |
31 /// Second kernel |
| 40 pub B |
32 pub B, |
| 41 ); |
33 ); |
| 42 |
34 |
| 43 impl<A, B, F : Float, const N : usize> Mapping<Loc<F, N>> |
35 impl<A, B, F: Float, const N: usize> Mapping<Loc<N, F>> for SupportProductFirst<A, B> |
| 44 for SupportProductFirst<A, B> |
36 where |
| 45 where |
37 A: Mapping<Loc<N, F>, Codomain = F>, |
| 46 A : Mapping<Loc<F, N>, Codomain = F>, |
38 B: Mapping<Loc<N, F>, Codomain = F>, |
| 47 B : Mapping<Loc<F, N>, Codomain = F>, |
|
| 48 { |
39 { |
| 49 type Codomain = F; |
40 type Codomain = F; |
| 50 |
41 |
| 51 #[inline] |
42 #[inline] |
| 52 fn apply<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Codomain { |
43 fn apply<I: Instance<Loc<N, F>>>(&self, x: I) -> Self::Codomain { |
| 53 self.0.apply(x.ref_instance()) * self.1.apply(x) |
44 x.eval_ref(|r| self.0.apply(r)) * self.1.apply(x) |
| 54 } |
45 } |
| 55 } |
46 } |
| 56 |
47 |
| 57 impl<A, B, F : Float, const N : usize> DifferentiableImpl<Loc<F, N>> |
48 impl<A, B, F: Float, const N: usize> DifferentiableImpl<Loc<N, F>> for SupportProductFirst<A, B> |
| 58 for SupportProductFirst<A, B> |
49 where |
| 59 where |
50 A: DifferentiableMapping<Loc<N, F>, DerivativeDomain = Loc<N, F>, Codomain = F>, |
| 60 A : DifferentiableMapping< |
51 B: DifferentiableMapping<Loc<N, F>, DerivativeDomain = Loc<N, F>, Codomain = F>, |
| 61 Loc<F, N>, |
52 { |
| 62 DerivativeDomain=Loc<F, N>, |
53 type Derivative = Loc<N, F>; |
| 63 Codomain = F |
54 |
| 64 >, |
55 #[inline] |
| 65 B : DifferentiableMapping< |
56 fn differential_impl<I: Instance<Loc<N, F>>>(&self, x: I) -> Self::Derivative { |
| 66 Loc<F, N>, |
57 x.eval_ref(|xr| { |
| 67 DerivativeDomain=Loc<F, N>, |
58 self.0.differential(xr) * self.1.apply(xr) + self.1.differential(xr) * self.0.apply(xr) |
| 68 Codomain = F, |
59 }) |
| 69 > |
60 } |
| 70 { |
61 } |
| 71 type Derivative = Loc<F, N>; |
62 |
| 72 |
63 impl<A, B, M: Copy, F: Float> Lipschitz<M> for SupportProductFirst<A, B> |
| 73 #[inline] |
64 where |
| 74 fn differential_impl<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Derivative { |
65 A: Lipschitz<M, FloatType = F> + Bounded<F>, |
| 75 let xr = x.ref_instance(); |
66 B: Lipschitz<M, FloatType = F> + Bounded<F>, |
| 76 self.0.differential(xr) * self.1.apply(xr) + self.1.differential(xr) * self.0.apply(x) |
67 { |
| 77 } |
68 type FloatType = F; |
| 78 } |
69 #[inline] |
| 79 |
70 fn lipschitz_factor(&self, m: M) -> DynResult<F> { |
| 80 impl<A, B, M : Copy, F : Float> Lipschitz<M> |
|
| 81 for SupportProductFirst<A, B> |
|
| 82 where A : Lipschitz<M, FloatType = F> + Bounded<F>, |
|
| 83 B : Lipschitz<M, FloatType = F> + Bounded<F> { |
|
| 84 type FloatType = F; |
|
| 85 #[inline] |
|
| 86 fn lipschitz_factor(&self, m : M) -> Option<F> { |
|
| 87 // f(x)g(x) - f(y)g(y) = f(x)[g(x)-g(y)] - [f(y)-f(x)]g(y) |
71 // f(x)g(x) - f(y)g(y) = f(x)[g(x)-g(y)] - [f(y)-f(x)]g(y) |
| 88 let &SupportProductFirst(ref f, ref g) = self; |
72 let &SupportProductFirst(ref f, ref g) = self; |
| 89 f.lipschitz_factor(m).map(|l| l * g.bounds().uniform()) |
73 Ok(f.lipschitz_factor(m)? * g.bounds().uniform() |
| 90 .zip(g.lipschitz_factor(m).map(|l| l * f.bounds().uniform())) |
74 + g.lipschitz_factor(m)? * f.bounds().uniform()) |
| 91 .map(|(a, b)| a + b) |
75 } |
| 92 } |
76 } |
| 93 } |
77 |
| 94 |
78 impl<'a, A, B, M: Copy, Domain, F: Float> LipschitzDifferentiableImpl<Domain, M> |
| 95 impl<'a, A, B, M : Copy, Domain, F : Float> Lipschitz<M> |
79 for SupportProductFirst<A, B> |
| 96 for Differential<'a, Domain, SupportProductFirst<A, B>> |
80 where |
| 97 where |
81 Domain: Space, |
| 98 Domain : Space, |
82 Self: DifferentiableImpl<Domain>, |
| 99 A : Clone + DifferentiableMapping<Domain> + Lipschitz<M, FloatType = F> + Bounded<F>, |
83 A: DifferentiableMapping<Domain> + Lipschitz<M, FloatType = F> + Bounded<F>, |
| 100 B : Clone + DifferentiableMapping<Domain> + Lipschitz<M, FloatType = F> + Bounded<F>, |
84 B: DifferentiableMapping<Domain> + Lipschitz<M, FloatType = F> + Bounded<F>, |
| 101 SupportProductFirst<A, B> : DifferentiableMapping<Domain>, |
85 SupportProductFirst<A, B>: DifferentiableMapping<Domain>, |
| 102 for<'b> A::Differential<'b> : Lipschitz<M, FloatType = F> + NormBounded<L2, FloatType=F>, |
86 for<'b> A::Differential<'b>: Lipschitz<M, FloatType = F> + NormBounded<L2, FloatType = F>, |
| 103 for<'b> B::Differential<'b> : Lipschitz<M, FloatType = F> + NormBounded<L2, FloatType=F> |
87 for<'b> B::Differential<'b>: Lipschitz<M, FloatType = F> + NormBounded<L2, FloatType = F>, |
| 104 { |
88 { |
| 105 type FloatType = F; |
89 type FloatType = F; |
| 106 #[inline] |
90 #[inline] |
| 107 fn lipschitz_factor(&self, m : M) -> Option<F> { |
91 fn diff_lipschitz_factor(&self, m: M) -> DynResult<F> { |
| 108 // ∇[gf] = f∇g + g∇f |
92 // ∇[gf] = f∇g + g∇f |
| 109 // ⟹ ∇[gf](x) - ∇[gf](y) = f(x)∇g(x) + g(x)∇f(x) - f(y)∇g(y) + g(y)∇f(y) |
93 // ⟹ ∇[gf](x) - ∇[gf](y) = f(x)∇g(x) + g(x)∇f(x) - f(y)∇g(y) + g(y)∇f(y) |
| 110 // = f(x)[∇g(x)-∇g(y)] + g(x)∇f(x) - [f(y)-f(x)]∇g(y) + g(y)∇f(y) |
94 // = f(x)[∇g(x)-∇g(y)] + g(x)∇f(x) - [f(y)-f(x)]∇g(y) + g(y)∇f(y) |
| 111 // = f(x)[∇g(x)-∇g(y)] + g(x)[∇f(x)-∇f(y)] |
95 // = f(x)[∇g(x)-∇g(y)] + g(x)[∇f(x)-∇f(y)] |
| 112 // - [f(y)-f(x)]∇g(y) + [g(y)-g(x)]∇f(y) |
96 // - [f(y)-f(x)]∇g(y) + [g(y)-g(x)]∇f(y) |
| 113 let &SupportProductFirst(ref f, ref g) = self.base_fn(); |
97 let &SupportProductFirst(ref f, ref g) = self; |
| 114 let (df, dg) = (f.diff_ref(), g.diff_ref()); |
98 let (df, dg) = (f.diff_ref(), g.diff_ref()); |
| 115 [ |
99 Ok([ |
| 116 df.lipschitz_factor(m).map(|l| l * g.bounds().uniform()), |
100 df.lipschitz_factor(m)? * g.bounds().uniform(), |
| 117 dg.lipschitz_factor(m).map(|l| l * f.bounds().uniform()), |
101 dg.lipschitz_factor(m)? * f.bounds().uniform(), |
| 118 f.lipschitz_factor(m).map(|l| l * dg.norm_bound(L2)), |
102 f.lipschitz_factor(m)? * dg.norm_bound(L2), |
| 119 g.lipschitz_factor(m).map(|l| l * df.norm_bound(L2)) |
103 g.lipschitz_factor(m)? * df.norm_bound(L2), |
| 120 ].into_iter().sum() |
104 ] |
| 121 } |
105 .into_iter() |
| 122 } |
106 .sum()) |
| 123 |
107 } |
| 124 |
108 } |
| 125 impl<'a, A, B, F : Float, const N : usize> Support<F, N> |
109 |
| 126 for SupportProductFirst<A, B> |
110 impl<'a, A, B, F: Float, const N: usize> Support<N, F> for SupportProductFirst<A, B> |
| 127 where |
111 where |
| 128 A : Support<F, N>, |
112 A: Support<N, F>, |
| 129 B : Support<F, N> |
113 B: Support<N, F>, |
| 130 { |
114 { |
| 131 #[inline] |
115 #[inline] |
| 132 fn support_hint(&self) -> Cube<F, N> { |
116 fn support_hint(&self) -> Cube<N, F> { |
| 133 self.0.support_hint() |
117 self.0.support_hint() |
| 134 } |
118 } |
| 135 |
119 |
| 136 #[inline] |
120 #[inline] |
| 137 fn in_support(&self, x : &Loc<F, N>) -> bool { |
121 fn in_support(&self, x: &Loc<N, F>) -> bool { |
| 138 self.0.in_support(x) |
122 self.0.in_support(x) |
| 139 } |
123 } |
| 140 |
124 |
| 141 #[inline] |
125 #[inline] |
| 142 fn bisection_hint(&self, cube : &Cube<F, N>) -> [Option<F>; N] { |
126 fn bisection_hint(&self, cube: &Cube<N, F>) -> [Option<F>; N] { |
| 143 self.0.bisection_hint(cube) |
127 self.0.bisection_hint(cube) |
| 144 } |
128 } |
| 145 } |
129 } |
| 146 |
130 |
| 147 impl<'a, A, B, F : Float> GlobalAnalysis<F, Bounds<F>> |
131 impl<'a, A, B, F: Float> GlobalAnalysis<F, Bounds<F>> for SupportProductFirst<A, B> |
| 148 for SupportProductFirst<A, B> |
132 where |
| 149 where A : GlobalAnalysis<F, Bounds<F>>, |
133 A: GlobalAnalysis<F, Bounds<F>>, |
| 150 B : GlobalAnalysis<F, Bounds<F>> { |
134 B: GlobalAnalysis<F, Bounds<F>>, |
| |
135 { |
| 151 #[inline] |
136 #[inline] |
| 152 fn global_analysis(&self) -> Bounds<F> { |
137 fn global_analysis(&self) -> Bounds<F> { |
| 153 self.0.global_analysis() * self.1.global_analysis() |
138 self.0.global_analysis() * self.1.global_analysis() |
| 154 } |
139 } |
| 155 } |
140 } |
| 156 |
141 |
| 157 impl<'a, A, B, F : Float, const N : usize> LocalAnalysis<F, Bounds<F>, N> |
142 impl<'a, A, B, F: Float, const N: usize> LocalAnalysis<F, Bounds<F>, N> |
| 158 for SupportProductFirst<A, B> |
143 for SupportProductFirst<A, B> |
| 159 where A : LocalAnalysis<F, Bounds<F>, N>, |
144 where |
| 160 B : LocalAnalysis<F, Bounds<F>, N> { |
145 A: LocalAnalysis<F, Bounds<F>, N>, |
| 161 #[inline] |
146 B: LocalAnalysis<F, Bounds<F>, N>, |
| 162 fn local_analysis(&self, cube : &Cube<F, N>) -> Bounds<F> { |
147 { |
| |
148 #[inline] |
| |
149 fn local_analysis(&self, cube: &Cube<N, F>) -> Bounds<F> { |
| 163 self.0.local_analysis(cube) * self.1.local_analysis(cube) |
150 self.0.local_analysis(cube) * self.1.local_analysis(cube) |
| 164 } |
151 } |
| 165 } |
152 } |
| 166 |
153 |
| 167 /// Representation of the sum of two kernels |
154 /// Representation of the sum of two kernels |
| 168 /// |
155 /// |
| 169 /// The kernels typically implement [`Support`] and [`Mapping`]. |
156 /// The kernels typically implement [`Support`] and [`Mapping`]. |
| 170 /// |
157 /// |
| 171 /// The implementation [`Support`] only uses the [`Support::support_hint`] of the first parameter! |
158 /// The implementation [`Support`] only uses the [`Support::support_hint`] of the first parameter! |
| 172 #[derive(Copy,Clone,Serialize,Debug)] |
159 #[derive(Copy, Clone, Serialize, Debug)] |
| 173 pub struct SupportSum<A, B>( |
160 pub struct SupportSum<A, B>( |
| 174 /// First kernel |
161 /// First kernel |
| 175 pub A, |
162 pub A, |
| 176 /// Second kernel |
163 /// Second kernel |
| 177 pub B |
164 pub B, |
| 178 ); |
165 ); |
| 179 |
166 |
| 180 impl<'a, A, B, F : Float, const N : usize> Mapping<Loc<F, N>> |
167 impl<'a, A, B, F: Float, const N: usize> Mapping<Loc<N, F>> for SupportSum<A, B> |
| 181 for SupportSum<A, B> |
168 where |
| 182 where |
169 A: Mapping<Loc<N, F>, Codomain = F>, |
| 183 A : Mapping<Loc<F, N>, Codomain = F>, |
170 B: Mapping<Loc<N, F>, Codomain = F>, |
| 184 B : Mapping<Loc<F, N>, Codomain = F>, |
|
| 185 { |
171 { |
| 186 type Codomain = F; |
172 type Codomain = F; |
| 187 |
173 |
| 188 #[inline] |
174 #[inline] |
| 189 fn apply<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Codomain { |
175 fn apply<I: Instance<Loc<N, F>>>(&self, x: I) -> Self::Codomain { |
| 190 self.0.apply(x.ref_instance()) + self.1.apply(x) |
176 x.eval_ref(|r| self.0.apply(r)) + self.1.apply(x) |
| 191 } |
177 } |
| 192 } |
178 } |
| 193 |
179 |
| 194 impl<'a, A, B, F : Float, const N : usize> DifferentiableImpl<Loc<F, N>> |
180 impl<'a, A, B, F: Float, const N: usize> DifferentiableImpl<Loc<N, F>> for SupportSum<A, B> |
| 195 for SupportSum<A, B> |
181 where |
| 196 where |
182 A: DifferentiableMapping<Loc<N, F>, DerivativeDomain = Loc<N, F>>, |
| 197 A : DifferentiableMapping< |
183 B: DifferentiableMapping<Loc<N, F>, DerivativeDomain = Loc<N, F>>, |
| 198 Loc<F, N>, |
184 { |
| 199 DerivativeDomain = Loc<F, N> |
185 type Derivative = Loc<N, F>; |
| 200 >, |
186 |
| 201 B : DifferentiableMapping< |
187 #[inline] |
| 202 Loc<F, N>, |
188 fn differential_impl<I: Instance<Loc<N, F>>>(&self, x: I) -> Self::Derivative { |
| 203 DerivativeDomain = Loc<F, N>, |
189 x.eval_ref(|r| self.0.differential(r)) + self.1.differential(x) |
| 204 > |
190 } |
| 205 { |
191 } |
| 206 |
192 |
| 207 type Derivative = Loc<F, N>; |
193 impl<'a, A, B, F: Float, const N: usize> Support<N, F> for SupportSum<A, B> |
| 208 |
194 where |
| 209 #[inline] |
195 A: Support<N, F>, |
| 210 fn differential_impl<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Derivative { |
196 B: Support<N, F>, |
| 211 self.0.differential(x.ref_instance()) + self.1.differential(x) |
197 Cube<N, F>: SetOrd, |
| 212 } |
198 { |
| 213 } |
199 #[inline] |
| 214 |
200 fn support_hint(&self) -> Cube<N, F> { |
| 215 |
|
| 216 impl<'a, A, B, F : Float, const N : usize> Support<F, N> |
|
| 217 for SupportSum<A, B> |
|
| 218 where A : Support<F, N>, |
|
| 219 B : Support<F, N>, |
|
| 220 Cube<F, N> : SetOrd { |
|
| 221 |
|
| 222 #[inline] |
|
| 223 fn support_hint(&self) -> Cube<F, N> { |
|
| 224 self.0.support_hint().common(&self.1.support_hint()) |
201 self.0.support_hint().common(&self.1.support_hint()) |
| 225 } |
202 } |
| 226 |
203 |
| 227 #[inline] |
204 #[inline] |
| 228 fn in_support(&self, x : &Loc<F, N>) -> bool { |
205 fn in_support(&self, x: &Loc<N, F>) -> bool { |
| 229 self.0.in_support(x) || self.1.in_support(x) |
206 self.0.in_support(x) || self.1.in_support(x) |
| 230 } |
207 } |
| 231 |
208 |
| 232 #[inline] |
209 #[inline] |
| 233 fn bisection_hint(&self, cube : &Cube<F, N>) -> [Option<F>; N] { |
210 fn bisection_hint(&self, cube: &Cube<N, F>) -> [Option<F>; N] { |
| 234 map2(self.0.bisection_hint(cube), |
211 map2( |
| 235 self.1.bisection_hint(cube), |
212 self.0.bisection_hint(cube), |
| 236 |a, b| a.or(b)) |
213 self.1.bisection_hint(cube), |
| 237 } |
214 |a, b| a.or(b), |
| 238 } |
215 ) |
| 239 |
216 } |
| 240 impl<'a, A, B, F : Float> GlobalAnalysis<F, Bounds<F>> |
217 } |
| 241 for SupportSum<A, B> |
218 |
| 242 where A : GlobalAnalysis<F, Bounds<F>>, |
219 impl<'a, A, B, F: Float> GlobalAnalysis<F, Bounds<F>> for SupportSum<A, B> |
| 243 B : GlobalAnalysis<F, Bounds<F>> { |
220 where |
| |
221 A: GlobalAnalysis<F, Bounds<F>>, |
| |
222 B: GlobalAnalysis<F, Bounds<F>>, |
| |
223 { |
| 244 #[inline] |
224 #[inline] |
| 245 fn global_analysis(&self) -> Bounds<F> { |
225 fn global_analysis(&self) -> Bounds<F> { |
| 246 self.0.global_analysis() + self.1.global_analysis() |
226 self.0.global_analysis() + self.1.global_analysis() |
| 247 } |
227 } |
| 248 } |
228 } |
| 249 |
229 |
| 250 impl<'a, A, B, F : Float, const N : usize> LocalAnalysis<F, Bounds<F>, N> |
230 impl<'a, A, B, F: Float, const N: usize> LocalAnalysis<F, Bounds<F>, N> for SupportSum<A, B> |
| 251 for SupportSum<A, B> |
231 where |
| 252 where A : LocalAnalysis<F, Bounds<F>, N>, |
232 A: LocalAnalysis<F, Bounds<F>, N>, |
| 253 B : LocalAnalysis<F, Bounds<F>, N>, |
233 B: LocalAnalysis<F, Bounds<F>, N>, |
| 254 Cube<F, N> : SetOrd { |
234 Cube<N, F>: SetOrd, |
| 255 #[inline] |
235 { |
| 256 fn local_analysis(&self, cube : &Cube<F, N>) -> Bounds<F> { |
236 #[inline] |
| |
237 fn local_analysis(&self, cube: &Cube<N, F>) -> Bounds<F> { |
| 257 self.0.local_analysis(cube) + self.1.local_analysis(cube) |
238 self.0.local_analysis(cube) + self.1.local_analysis(cube) |
| 258 } |
239 } |
| 259 } |
240 } |
| 260 |
241 |
| 261 impl<F : Float, M : Copy, A, B> Lipschitz<M> for SupportSum<A, B> |
242 impl<F: Float, M: Copy, A, B> Lipschitz<M> for SupportSum<A, B> |
| 262 where A : Lipschitz<M, FloatType = F>, |
243 where |
| 263 B : Lipschitz<M, FloatType = F> { |
244 A: Lipschitz<M, FloatType = F>, |
| 264 type FloatType = F; |
245 B: Lipschitz<M, FloatType = F>, |
| 265 |
246 { |
| 266 fn lipschitz_factor(&self, m : M) -> Option<F> { |
247 type FloatType = F; |
| 267 match (self.0.lipschitz_factor(m), self.1.lipschitz_factor(m)) { |
248 |
| 268 (Some(l0), Some(l1)) => Some(l0 + l1), |
249 fn lipschitz_factor(&self, m: M) -> DynResult<F> { |
| 269 _ => None |
250 Ok(self.0.lipschitz_factor(m)? * self.1.lipschitz_factor(m)?) |
| 270 } |
251 } |
| 271 } |
252 } |
| 272 } |
253 |
| 273 |
254 impl<'b, F: Float, M: Copy, A, B, Domain> LipschitzDifferentiableImpl<Domain, M> |
| 274 impl<'b, F : Float, M : Copy, A, B, Domain> Lipschitz<M> |
255 for SupportSum<A, B> |
| 275 for Differential<'b, Domain, SupportSum<A, B>> |
256 where |
| 276 where |
257 Domain: Space, |
| 277 Domain : Space, |
258 Self: DifferentiableImpl<Domain>, |
| 278 A : Clone + DifferentiableMapping<Domain, Codomain=F>, |
259 A: DifferentiableMapping<Domain, Codomain = F>, |
| 279 B : Clone + DifferentiableMapping<Domain, Codomain=F>, |
260 B: DifferentiableMapping<Domain, Codomain = F>, |
| 280 SupportSum<A, B> : DifferentiableMapping<Domain, Codomain=F>, |
261 SupportSum<A, B>: DifferentiableMapping<Domain, Codomain = F>, |
| 281 for<'a> A :: Differential<'a> : Lipschitz<M, FloatType = F>, |
262 for<'a> A::Differential<'a>: Lipschitz<M, FloatType = F>, |
| 282 for<'a> B :: Differential<'a> : Lipschitz<M, FloatType = F> |
263 for<'a> B::Differential<'a>: Lipschitz<M, FloatType = F>, |
| 283 { |
264 { |
| 284 type FloatType = F; |
265 type FloatType = F; |
| 285 |
266 |
| 286 fn lipschitz_factor(&self, m : M) -> Option<F> { |
267 fn diff_lipschitz_factor(&self, m: M) -> DynResult<F> { |
| 287 let base = self.base_fn(); |
268 Ok(self.0.diff_ref().lipschitz_factor(m)? + self.1.diff_ref().lipschitz_factor(m)?) |
| 288 base.0.diff_ref().lipschitz_factor(m) |
|
| 289 .zip(base.1.diff_ref().lipschitz_factor(m)) |
|
| 290 .map(|(a, b)| a + b) |
|
| 291 } |
269 } |
| 292 } |
270 } |
| 293 |
271 |
| 294 /// Representation of the convolution of two kernels. |
272 /// Representation of the convolution of two kernels. |
| 295 /// |
273 /// |
| 296 /// The kernels typically implement [`Support`]s and [`Mapping`]. |
274 /// The kernels typically implement [`Support`]s and [`Mapping`]. |
| 297 // |
275 // |
| 298 /// Trait implementations have to be on a case-by-case basis. |
276 /// Trait implementations have to be on a case-by-case basis. |
| 299 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] |
277 #[derive(Copy, Clone, Serialize, Debug, Eq, PartialEq)] |
| 300 pub struct Convolution<A, B>( |
278 pub struct Convolution<A, B>( |
| 301 /// First kernel |
279 /// First kernel |
| 302 pub A, |
280 pub A, |
| 303 /// Second kernel |
281 /// Second kernel |
| 304 pub B |
282 pub B, |
| 305 ); |
283 ); |
| 306 |
284 |
| 307 impl<F : Float, M, A, B> Lipschitz<M> for Convolution<A, B> |
285 impl<F: Float, M, A, B> Lipschitz<M> for Convolution<A, B> |
| 308 where A : Norm<F, L1> , |
286 where |
| 309 B : Lipschitz<M, FloatType = F> { |
287 A: Norm<L1, F>, |
| 310 type FloatType = F; |
288 B: Lipschitz<M, FloatType = F>, |
| 311 |
289 { |
| 312 fn lipschitz_factor(&self, m : M) -> Option<F> { |
290 type FloatType = F; |
| |
291 |
| |
292 fn lipschitz_factor(&self, m: M) -> DynResult<F> { |
| 313 // For [f * g](x) = ∫ f(x-y)g(y) dy we have |
293 // For [f * g](x) = ∫ f(x-y)g(y) dy we have |
| 314 // [f * g](x) - [f * g](z) = ∫ [f(x-y)-f(z-y)]g(y) dy. |
294 // [f * g](x) - [f * g](z) = ∫ [f(x-y)-f(z-y)]g(y) dy. |
| 315 // Hence |[f * g](x) - [f * g](z)| ≤ ∫ |f(x-y)-f(z-y)|g(y)| dy. |
295 // Hence |[f * g](x) - [f * g](z)| ≤ ∫ |f(x-y)-f(z-y)|g(y)| dy. |
| 316 // ≤ L|x-z| ∫ |g(y)| dy, |
296 // ≤ L|x-z| ∫ |g(y)| dy, |
| 317 // where L is the Lipschitz factor of f. |
297 // where L is the Lipschitz factor of f. |
| 318 self.1.lipschitz_factor(m).map(|l| l * self.0.norm(L1)) |
298 Ok(self.1.lipschitz_factor(m)? * self.0.norm(L1)) |
| 319 } |
299 } |
| 320 } |
300 } |
| 321 |
301 |
| 322 impl<'b, F : Float, M, A, B, Domain> Lipschitz<M> |
302 impl<'b, F: Float, M, A, B, Domain> LipschitzDifferentiableImpl<Domain, M> for Convolution<A, B> |
| 323 for Differential<'b, Domain, Convolution<A, B>> |
303 where |
| 324 where |
304 Domain: Space, |
| 325 Domain : Space, |
305 Self: DifferentiableImpl<Domain>, |
| 326 A : Clone + Norm<F, L1> , |
306 A: Norm<L1, F>, |
| 327 Convolution<A, B> : DifferentiableMapping<Domain, Codomain=F>, |
307 Convolution<A, B>: DifferentiableMapping<Domain, Codomain = F>, |
| 328 B : Clone + DifferentiableMapping<Domain, Codomain=F>, |
308 B: DifferentiableMapping<Domain, Codomain = F>, |
| 329 for<'a> B :: Differential<'a> : Lipschitz<M, FloatType = F> |
309 for<'a> B::Differential<'a>: Lipschitz<M, FloatType = F>, |
| 330 { |
310 { |
| 331 type FloatType = F; |
311 type FloatType = F; |
| 332 |
312 |
| 333 fn lipschitz_factor(&self, m : M) -> Option<F> { |
313 fn diff_lipschitz_factor(&self, m: M) -> DynResult<F> { |
| 334 // For [f * g](x) = ∫ f(x-y)g(y) dy we have |
314 // For [f * g](x) = ∫ f(x-y)g(y) dy we have |
| 335 // ∇[f * g](x) - ∇[f * g](z) = ∫ [∇f(x-y)-∇f(z-y)]g(y) dy. |
315 // ∇[f * g](x) - ∇[f * g](z) = ∫ [∇f(x-y)-∇f(z-y)]g(y) dy. |
| 336 // Hence |∇[f * g](x) - ∇[f * g](z)| ≤ ∫ |∇f(x-y)-∇f(z-y)|g(y)| dy. |
316 // Hence |∇[f * g](x) - ∇[f * g](z)| ≤ ∫ |∇f(x-y)-∇f(z-y)|g(y)| dy. |
| 337 // ≤ L|x-z| ∫ |g(y)| dy, |
317 // ≤ L|x-z| ∫ |g(y)| dy, |
| 338 // where L is the Lipschitz factor of ∇f. |
318 // where L is the Lipschitz factor of ∇f. |
| 339 let base = self.base_fn(); |
319 self.1 |
| 340 base.1.diff_ref().lipschitz_factor(m).map(|l| l * base.0.norm(L1)) |
320 .diff_ref() |
| |
321 .lipschitz_factor(m) |
| |
322 .map(|l| l * self.0.norm(L1)) |
| 341 } |
323 } |
| 342 } |
324 } |
| 343 |
325 |
| 344 /// Representation of the autoconvolution of a kernel. |
326 /// Representation of the autoconvolution of a kernel. |
| 345 /// |
327 /// |
| 346 /// The kernel typically implements [`Support`] and [`Mapping`]. |
328 /// The kernel typically implements [`Support`] and [`Mapping`]. |
| 347 /// |
329 /// |
| 348 /// Trait implementations have to be on a case-by-case basis. |
330 /// Trait implementations have to be on a case-by-case basis. |
| 349 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] |
331 #[derive(Copy, Clone, Serialize, Debug, Eq, PartialEq)] |
| 350 pub struct AutoConvolution<A>( |
332 pub struct AutoConvolution<A>( |
| 351 /// The kernel to be autoconvolved |
333 /// The kernel to be autoconvolved |
| 352 pub A |
334 pub A, |
| 353 ); |
335 ); |
| 354 |
336 |
| 355 impl<F : Float, M, C> Lipschitz<M> for AutoConvolution<C> |
337 impl<F: Float, M, C> Lipschitz<M> for AutoConvolution<C> |
| 356 where C : Lipschitz<M, FloatType = F> + Norm<F, L1> { |
338 where |
| 357 type FloatType = F; |
339 C: Lipschitz<M, FloatType = F> + Norm<L1, F>, |
| 358 |
340 { |
| 359 fn lipschitz_factor(&self, m : M) -> Option<F> { |
341 type FloatType = F; |
| |
342 |
| |
343 fn lipschitz_factor(&self, m: M) -> DynResult<F> { |
| 360 self.0.lipschitz_factor(m).map(|l| l * self.0.norm(L1)) |
344 self.0.lipschitz_factor(m).map(|l| l * self.0.norm(L1)) |
| 361 } |
345 } |
| 362 } |
346 } |
| 363 |
347 |
| 364 impl<'b, F : Float, M, C, Domain> Lipschitz<M> |
348 impl<'b, F: Float, M, C, Domain> LipschitzDifferentiableImpl<Domain, M> for AutoConvolution<C> |
| 365 for Differential<'b, Domain, AutoConvolution<C>> |
349 where |
| 366 where |
350 Domain: Space, |
| 367 Domain : Space, |
351 Self: DifferentiableImpl<Domain>, |
| 368 C : Clone + Norm<F, L1> + DifferentiableMapping<Domain, Codomain=F>, |
352 C: Norm<L1, F> + DifferentiableMapping<Domain, Codomain = F>, |
| 369 AutoConvolution<C> : DifferentiableMapping<Domain, Codomain=F>, |
353 AutoConvolution<C>: DifferentiableMapping<Domain, Codomain = F>, |
| 370 for<'a> C :: Differential<'a> : Lipschitz<M, FloatType = F> |
354 for<'a> C::Differential<'a>: Lipschitz<M, FloatType = F>, |
| 371 { |
355 { |
| 372 type FloatType = F; |
356 type FloatType = F; |
| 373 |
357 |
| 374 fn lipschitz_factor(&self, m : M) -> Option<F> { |
358 fn diff_lipschitz_factor(&self, m: M) -> DynResult<F> { |
| 375 let base = self.base_fn(); |
359 self.0 |
| 376 base.0.diff_ref().lipschitz_factor(m).map(|l| l * base.0.norm(L1)) |
360 .diff_ref() |
| 377 } |
361 .lipschitz_factor(m) |
| 378 } |
362 .map(|l| l * self.0.norm(L1)) |
| 379 |
363 } |
| |
364 } |
| 380 |
365 |
| 381 /// Representation a multi-dimensional product of a one-dimensional kernel. |
366 /// Representation a multi-dimensional product of a one-dimensional kernel. |
| 382 /// |
367 /// |
| 383 /// For $G: ℝ → ℝ$, this is the function $F(x\_1, …, x\_n) := \prod_{i=1}^n G(x\_i)$. |
368 /// For $G: ℝ → ℝ$, this is the function $F(x\_1, …, x\_n) := \prod_{i=1}^n G(x\_i)$. |
| 384 /// The kernel $G$ typically implements [`Support`] and [`Mapping`] |
369 /// The kernel $G$ typically implements [`Support`] and [`Mapping`] |
| 385 /// on [`Loc<F, 1>`]. Then the product implements them on [`Loc<F, N>`]. |
370 /// on [`Loc<1, F>`]. Then the product implements them on [`Loc<N, F>`]. |
| 386 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] |
371 #[derive(Copy, Clone, Serialize, Debug, Eq, PartialEq)] |
| 387 #[allow(dead_code)] |
372 #[allow(dead_code)] |
| 388 struct UniformProduct<G, const N : usize>( |
373 struct UniformProduct<G, const N: usize>( |
| 389 /// The one-dimensional kernel |
374 /// The one-dimensional kernel |
| 390 G |
375 G, |
| 391 ); |
376 ); |
| 392 |
377 |
| 393 impl<'a, G, F : Float, const N : usize> Mapping<Loc<F, N>> |
378 impl<'a, G, F: Float, const N: usize> Mapping<Loc<N, F>> for UniformProduct<G, N> |
| 394 for UniformProduct<G, N> |
379 where |
| 395 where |
380 G: Mapping<Loc<1, F>, Codomain = F>, |
| 396 G : Mapping<Loc<F, 1>, Codomain = F> |
|
| 397 { |
381 { |
| 398 type Codomain = F; |
382 type Codomain = F; |
| 399 |
383 |
| 400 #[inline] |
384 #[inline] |
| 401 fn apply<I : Instance<Loc<F, N>>>(&self, x : I) -> F { |
385 fn apply<I: Instance<Loc<N, F>>>(&self, x: I) -> F { |
| 402 x.cow().iter().map(|&y| self.0.apply(Loc([y]))).product() |
386 x.decompose() |
| 403 } |
387 .iter() |
| 404 } |
388 .map(|&y| self.0.apply(Loc([y]))) |
| 405 |
389 .product() |
| 406 |
390 } |
| 407 |
391 } |
| 408 impl<'a, G, F : Float, const N : usize> DifferentiableImpl<Loc<F, N>> |
392 |
| 409 for UniformProduct<G, N> |
393 impl<'a, G, F: Float, const N: usize> DifferentiableImpl<Loc<N, F>> for UniformProduct<G, N> |
| 410 where |
394 where |
| 411 G : DifferentiableMapping< |
395 G: DifferentiableMapping<Loc<1, F>, DerivativeDomain = F, Codomain = F>, |
| 412 Loc<F, 1>, |
396 { |
| 413 DerivativeDomain = F, |
397 type Derivative = Loc<N, F>; |
| 414 Codomain = F, |
398 |
| 415 > |
399 #[inline] |
| 416 { |
400 fn differential_impl<I: Instance<Loc<N, F>>>(&self, x0: I) -> Loc<N, F> { |
| 417 type Derivative = Loc<F, N>; |
|
| 418 |
|
| 419 #[inline] |
|
| 420 fn differential_impl<I : Instance<Loc<F, N>>>(&self, x0 : I) -> Loc<F, N> { |
|
| 421 x0.eval(|x| { |
401 x0.eval(|x| { |
| 422 let vs = x.map(|y| self.0.apply(Loc([y]))); |
402 let vs = x.map(|y| self.0.apply(Loc([y]))); |
| 423 product_differential(x, &vs, |y| self.0.differential(Loc([y]))) |
403 product_differential(x, &vs, |y| self.0.differential(Loc([y]))) |
| 424 }) |
404 }) |
| 425 } |
405 } |
| 468 // With $ψ_i=g for all i, j, it follows that |
450 // With $ψ_i=g for all i, j, it follows that |
| 469 // |∇ψ(x) - ∇ψ(y)| ≤ ∑_i L_{∇g} M_g^{n-1} + ∑_{k ≠ i} L_g M_g^{n-2} M_{∇g} |
451 // |∇ψ(x) - ∇ψ(y)| ≤ ∑_i L_{∇g} M_g^{n-1} + ∑_{k ≠ i} L_g M_g^{n-2} M_{∇g} |
| 470 // = n [L_{∇g} M_g^{n-1} + (n-1) L_g M_g^{n-2} M_{∇g}]. |
452 // = n [L_{∇g} M_g^{n-1} + (n-1) L_g M_g^{n-2} M_{∇g}]. |
| 471 // = n M_g^{n-2}[L_{∇g} M_g + (n-1) L_g M_{∇g}]. |
453 // = n M_g^{n-2}[L_{∇g} M_g + (n-1) L_g M_{∇g}]. |
| 472 if N >= 2 { |
454 if N >= 2 { |
| 473 F::cast_from(N) * bound.powi((N-2) as i32) |
455 Ok(F::cast_from(N) |
| 474 * (dlip * bound + F::cast_from(N-1) * lip * dbound) |
456 * bound.powi((N - 2) as i32) |
| 475 } else if N==1 { |
457 * (dlip * bound + F::cast_from(N - 1) * lip * dbound)) |
| 476 dlip |
458 } else if N == 1 { |
| |
459 Ok(dlip) |
| 477 } else { |
460 } else { |
| 478 panic!("Invalid dimension") |
461 Err(anyhow!("Invalid dimension")) |
| 479 } |
462 } |
| 480 } |
463 } |
| 481 |
464 |
| 482 impl<G, F : Float, const N : usize> Support<F, N> |
465 impl<G, F: Float, const N: usize> Support<N, F> for UniformProduct<G, N> |
| 483 for UniformProduct<G, N> |
466 where |
| 484 where G : Support<F, 1> { |
467 G: Support<1, F>, |
| 485 #[inline] |
468 { |
| 486 fn support_hint(&self) -> Cube<F, N> { |
469 #[inline] |
| 487 let [a] : [[F; 2]; 1] = self.0.support_hint().into(); |
470 fn support_hint(&self) -> Cube<N, F> { |
| |
471 let [a]: [[F; 2]; 1] = self.0.support_hint().into(); |
| 488 array_init(|| a.clone()).into() |
472 array_init(|| a.clone()).into() |
| 489 } |
473 } |
| 490 |
474 |
| 491 #[inline] |
475 #[inline] |
| 492 fn in_support(&self, x : &Loc<F, N>) -> bool { |
476 fn in_support(&self, x: &Loc<N, F>) -> bool { |
| 493 x.iter().all(|&y| self.0.in_support(&Loc([y]))) |
477 x.iter().all(|&y| self.0.in_support(&Loc([y]))) |
| 494 } |
478 } |
| 495 |
479 |
| 496 #[inline] |
480 #[inline] |
| 497 fn bisection_hint(&self, cube : &Cube<F, N>) -> [Option<F>; N] { |
481 fn bisection_hint(&self, cube: &Cube<N, F>) -> [Option<F>; N] { |
| 498 cube.map(|a, b| { |
482 cube.map(|a, b| { |
| 499 let [h] = self.0.bisection_hint(&[[a, b]].into()); |
483 let [h] = self.0.bisection_hint(&[[a, b]].into()); |
| 500 h |
484 h |
| 501 }) |
485 }) |
| 502 } |
486 } |
| 503 } |
487 } |
| 504 |
488 |
| 505 impl<G, F : Float, const N : usize> GlobalAnalysis<F, Bounds<F>> |
489 impl<G, F: Float, const N: usize> GlobalAnalysis<F, Bounds<F>> for UniformProduct<G, N> |
| 506 for UniformProduct<G, N> |
490 where |
| 507 where G : GlobalAnalysis<F, Bounds<F>> { |
491 G: GlobalAnalysis<F, Bounds<F>>, |
| |
492 { |
| 508 #[inline] |
493 #[inline] |
| 509 fn global_analysis(&self) -> Bounds<F> { |
494 fn global_analysis(&self) -> Bounds<F> { |
| 510 let g = self.0.global_analysis(); |
495 let g = self.0.global_analysis(); |
| 511 (0..N).map(|_| g).product() |
496 (0..N).map(|_| g).product() |
| 512 } |
497 } |
| 513 } |
498 } |
| 514 |
499 |
| 515 impl<G, F : Float, const N : usize> LocalAnalysis<F, Bounds<F>, N> |
500 impl<G, F: Float, const N: usize> LocalAnalysis<F, Bounds<F>, N> for UniformProduct<G, N> |
| 516 for UniformProduct<G, N> |
501 where |
| 517 where G : LocalAnalysis<F, Bounds<F>, 1> { |
502 G: LocalAnalysis<F, Bounds<F>, 1>, |
| 518 #[inline] |
503 { |
| 519 fn local_analysis(&self, cube : &Cube<F, N>) -> Bounds<F> { |
504 #[inline] |
| 520 cube.iter_coords().map( |
505 fn local_analysis(&self, cube: &Cube<N, F>) -> Bounds<F> { |
| 521 |&[a, b]| self.0.local_analysis(&([[a, b]].into())) |
506 cube.iter_coords() |
| 522 ).product() |
507 .map(|&[a, b]| self.0.local_analysis(&([[a, b]].into()))) |
| |
508 .product() |
| 523 } |
509 } |
| 524 } |
510 } |
| 525 |
511 |
| 526 macro_rules! product_lpnorm { |
512 macro_rules! product_lpnorm { |
| 527 ($lp:ident) => { |
513 ($lp:ident) => { |
| 528 impl<G, F : Float, const N : usize> Norm<F, $lp> |
514 impl<G, F: Float, const N: usize> Norm<$lp, F> for UniformProduct<G, N> |
| 529 for UniformProduct<G, N> |
515 where |
| 530 where G : Norm<F, $lp> { |
516 G: Norm<$lp, F>, |
| |
517 { |
| 531 #[inline] |
518 #[inline] |
| 532 fn norm(&self, lp : $lp) -> F { |
519 fn norm(&self, lp: $lp) -> F { |
| 533 self.0.norm(lp).powi(N as i32) |
520 self.0.norm(lp).powi(N as i32) |
| 534 } |
521 } |
| 535 } |
522 } |
| 536 } |
523 }; |
| 537 } |
524 } |
| 538 |
525 |
| 539 product_lpnorm!(L1); |
526 product_lpnorm!(L1); |
| 540 product_lpnorm!(L2); |
527 product_lpnorm!(L2); |
| 541 product_lpnorm!(Linfinity); |
528 product_lpnorm!(Linfinity); |
| 542 |
529 |
| 543 |
|
| 544 /// Trait for bounding one kernel with respect to another. |
530 /// Trait for bounding one kernel with respect to another. |
| 545 /// |
531 /// |
| 546 /// The type `F` is the scalar field, and `T` another kernel to which `Self` is compared. |
532 /// The type `F` is the scalar field, and `T` another kernel to which `Self` is compared. |
| 547 pub trait BoundedBy<F : Num, T> { |
533 pub trait BoundedBy<F: Num, T> { |
| 548 /// Calclate a bounding factor $c$ such that the Fourier transforms $ℱ\[v\] ≤ c ℱ\[u\]$ for |
534 /// Calclate a bounding factor $c$ such that the Fourier transforms $ℱ\[v\] ≤ c ℱ\[u\]$ for |
| 549 /// $v$ `self` and $u$ `other`. |
535 /// $v$ `self` and $u$ `other`. |
| 550 /// |
536 /// |
| 551 /// If no such factors exits, `None` is returned. |
537 /// If no such factors exits, `None` is returned. |
| 552 fn bounding_factor(&self, other : &T) -> Option<F>; |
538 fn bounding_factor(&self, other: &T) -> DynResult<F>; |
| 553 } |
539 } |
| 554 |
540 |
| 555 /// This [`BoundedBy`] implementation bounds $(uv) * (uv)$ by $(ψ * ψ) u$. |
541 /// This [`BoundedBy`] implementation bounds $(uv) * (uv)$ by $(ψ * ψ) u$. |
| 556 #[replace_float_literals(F::cast_from(literal))] |
542 #[replace_float_literals(F::cast_from(literal))] |
| 557 impl<F, C, BaseP> |
543 impl<F, C, BaseP> BoundedBy<F, SupportProductFirst<AutoConvolution<C>, BaseP>> |
| 558 BoundedBy<F, SupportProductFirst<AutoConvolution<C>, BaseP>> |
544 for AutoConvolution<SupportProductFirst<C, BaseP>> |
| 559 for AutoConvolution<SupportProductFirst<C, BaseP>> |
545 where |
| 560 where F : Float, |
546 F: Float, |
| 561 C : Clone + PartialEq, |
547 C: PartialEq, |
| 562 BaseP : Fourier<F> + PartialOrd, // TODO: replace by BoundedBy, |
548 BaseP: Fourier<F> + PartialOrd, // TODO: replace by BoundedBy, |
| 563 <BaseP as Fourier<F>>::Transformed : Bounded<F> + Norm<F, L1> { |
549 <BaseP as Fourier<F>>::Transformed: Bounded<F> + Norm<L1, F>, |
| 564 |
550 { |
| 565 fn bounding_factor(&self, kernel : &SupportProductFirst<AutoConvolution<C>, BaseP>) -> Option<F> { |
551 fn bounding_factor( |
| |
552 &self, |
| |
553 kernel: &SupportProductFirst<AutoConvolution<C>, BaseP>, |
| |
554 ) -> DynResult<F> { |
| 566 let SupportProductFirst(AutoConvolution(ref cutoff2), base_spread2) = kernel; |
555 let SupportProductFirst(AutoConvolution(ref cutoff2), base_spread2) = kernel; |
| 567 let AutoConvolution(SupportProductFirst(ref cutoff, ref base_spread)) = self; |
556 let AutoConvolution(SupportProductFirst(ref cutoff, ref base_spread)) = self; |
| 568 let v̂ = base_spread.fourier(); |
557 let v̂ = base_spread.fourier(); |
| 569 |
558 |
| 570 // Verify that the cut-off and ideal physical model (base spread) are the same. |
559 // Verify that the cut-off and ideal physical model (base spread) are the same. |
| 571 if cutoff == cutoff2 |
560 if cutoff == cutoff2 && base_spread <= base_spread2 && v̂.bounds().lower() >= 0.0 { |
| 572 && base_spread <= base_spread2 |
|
| 573 && v̂.bounds().lower() >= 0.0 { |
|
| 574 // Calculate the factor between the convolution approximation |
561 // Calculate the factor between the convolution approximation |
| 575 // `AutoConvolution<SupportProductFirst<C, BaseP>>` of $A_*A$ and the |
562 // `AutoConvolution<SupportProductFirst<C, BaseP>>` of $A_*A$ and the |
| 576 // kernel of the seminorm. This depends on the physical model P being |
563 // kernel of the seminorm. This depends on the physical model P being |
| 577 // `SupportProductFirst<C, BaseP>` with the kernel `K` being |
564 // `SupportProductFirst<C, BaseP>` with the kernel `K` being |
| 578 // a `SupportSum` involving `SupportProductFirst<AutoConvolution<C>, BaseP>`. |
565 // a `SupportSum` involving `SupportProductFirst<AutoConvolution<C>, BaseP>`. |
| 579 Some(v̂.norm(L1)) |
566 Ok(v̂.norm(L1)) |
| 580 } else { |
567 } else { |
| 581 // We cannot compare |
568 // We cannot compare |
| 582 None |
569 Err(anyhow!("Incomprable kernels")) |
| 583 } |
570 } |
| 584 } |
571 } |
| 585 } |
572 } |
| 586 |
573 |
| 587 impl<F : Float, A, B, C> BoundedBy<F, SupportSum<B, C>> for A |
574 impl<F: Float, A, B, C> BoundedBy<F, SupportSum<B, C>> for A |
| 588 where A : BoundedBy<F, B>, |
575 where |
| 589 C : Bounded<F> { |
576 A: BoundedBy<F, B>, |
| 590 |
577 C: Bounded<F>, |
| |
578 { |
| 591 #[replace_float_literals(F::cast_from(literal))] |
579 #[replace_float_literals(F::cast_from(literal))] |
| 592 fn bounding_factor(&self, SupportSum(ref kernel1, kernel2) : &SupportSum<B, C>) -> Option<F> { |
580 fn bounding_factor(&self, SupportSum(ref kernel1, kernel2): &SupportSum<B, C>) -> DynResult<F> { |
| 593 if kernel2.bounds().lower() >= 0.0 { |
581 if kernel2.bounds().lower() >= 0.0 { |
| 594 self.bounding_factor(kernel1) |
582 self.bounding_factor(kernel1) |
| 595 } else { |
583 } else { |
| 596 None |
584 Err(anyhow!("Component kernel not lower-bounded by zero")) |
| 597 } |
585 } |
| 598 } |
586 } |
| 599 } |
587 } |
| 600 |
588 |
| 601 /// Generates on $[a, b]$ [`Support::support_hint`] for a symmetric interval $[-r, r]$. |
589 /// Generates on $[a, b]$ [`Support::support_hint`] for a symmetric interval $[-r, r]$. |
| 602 /// |
590 /// |
| 603 /// It will attempt to place the subdivision point at $-r$ or $r$. |
591 /// It will attempt to place the subdivision point at $-r$ or $r$. |
| 604 /// If neither of these points lies within $[a, b]$, `None` is returned. |
592 /// If neither of these points lies within $[a, b]$, `None` is returned. |
| 605 #[inline] |
593 #[inline] |
| 606 pub(super) fn symmetric_interval_hint<F : Float>(r : F, a : F, b : F) -> Option<F> { |
594 pub(super) fn symmetric_interval_hint<F: Float>(r: F, a: F, b: F) -> Option<F> { |
| 607 if a < -r && -r < b { |
595 if a < -r && -r < b { |
| 608 Some(-r) |
596 Some(-r) |
| 609 } else if a < r && r < b { |
597 } else if a < r && r < b { |
| 610 Some(r) |
598 Some(r) |
| 611 } else { |
599 } else { |