src/kernels/base.rs

branch
dev
changeset 61
4f468d35fa29
parent 35
b087e3eab191
equal deleted inserted replaced
60:9738b51d90d7 61:4f468d35fa29
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 }
428 /// Helper function to calulate the differential of $f(x)=∏_{i=1}^N g(x_i)$. 408 /// Helper function to calulate the differential of $f(x)=∏_{i=1}^N g(x_i)$.
429 /// 409 ///
430 /// The vector `x` is the location, `vs` consists of the values `g(x_i)`, and 410 /// The vector `x` is the location, `vs` consists of the values `g(x_i)`, and
431 /// `gd` calculates the derivative `g'`. 411 /// `gd` calculates the derivative `g'`.
432 #[inline] 412 #[inline]
433 pub(crate) fn product_differential<F : Float, G : Fn(F) -> F, const N : usize>( 413 pub(crate) fn product_differential<F: Float, G: Fn(F) -> F, const N: usize>(
434 x : &Loc<F, N>, 414 x: &Loc<N, F>,
435 vs : &Loc<F, N>, 415 vs: &Loc<N, F>,
436 gd : G 416 gd: G,
437 ) -> Loc<F, N> { 417 ) -> Loc<N, F> {
438 map1_indexed(x, |i, &y| { 418 map1_indexed(x, |i, &y| {
439 gd(y) * vs.iter() 419 gd(y)
440 .zip(0..) 420 * vs.iter()
441 .filter_map(|(v, j)| (j != i).then_some(*v)) 421 .zip(0..)
442 .product() 422 .filter_map(|(v, j)| (j != i).then_some(*v))
443 }).into() 423 .product()
424 })
425 .into()
444 } 426 }
445 427
446 /// Helper function to calulate the Lipschitz factor of $∇f$ for $f(x)=∏_{i=1}^N g(x_i)$. 428 /// Helper function to calulate the Lipschitz factor of $∇f$ for $f(x)=∏_{i=1}^N g(x_i)$.
447 /// 429 ///
448 /// The parameter `bound` is a bound on $|g|_∞$, `lip` is a Lipschitz factor for $g$, 430 /// The parameter `bound` is a bound on $|g|_∞$, `lip` is a Lipschitz factor for $g$,
449 /// `dbound` is a bound on $|∇g|_∞$, and `dlip` a Lipschitz factor for $∇g$. 431 /// `dbound` is a bound on $|∇g|_∞$, and `dlip` a Lipschitz factor for $∇g$.
450 #[inline] 432 #[inline]
451 pub(crate) fn product_differential_lipschitz_factor<F : Float, const N : usize>( 433 pub(crate) fn product_differential_lipschitz_factor<F: Float, const N: usize>(
452 bound : F, 434 bound: F,
453 lip : F, 435 lip: F,
454 dbound : F, 436 dbound: F,
455 dlip : F 437 dlip: F,
456 ) -> F { 438 ) -> DynResult<F> {
457 // For arbitrary ψ(x) = ∏_{i=1}^n ψ_i(x_i), we have 439 // For arbitrary ψ(x) = ∏_{i=1}^n ψ_i(x_i), we have
458 // ψ(x) - ψ(y) = ∑_i [ψ_i(x_i)-ψ_i(y_i)] ∏_{j ≠ i} ψ_j(x_j) 440 // ψ(x) - ψ(y) = ∑_i [ψ_i(x_i)-ψ_i(y_i)] ∏_{j ≠ i} ψ_j(x_j)
459 // by a simple recursive argument. In particular, if ψ_i=g for all i, j, we have 441 // by a simple recursive argument. In particular, if ψ_i=g for all i, j, we have
460 // |ψ(x) - ψ(y)| ≤ ∑_i L_g M_g^{n-1}|x-y|, where L_g is the Lipschitz factor of g, and 442 // |ψ(x) - ψ(y)| ≤ ∑_i L_g M_g^{n-1}|x-y|, where L_g is the Lipschitz factor of g, and
461 // M_g a bound on it. 443 // M_g a bound on it.
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 {
620 /// gives the longer length for the shorter of the two subintervals. If none of these points 608 /// gives the longer length for the shorter of the two subintervals. If none of these points
621 /// lies within $[a, b]$, or the resulting interval would be shorter than $0.3r$, `None` is 609 /// lies within $[a, b]$, or the resulting interval would be shorter than $0.3r$, `None` is
622 /// returned. 610 /// returned.
623 #[replace_float_literals(F::cast_from(literal))] 611 #[replace_float_literals(F::cast_from(literal))]
624 #[inline] 612 #[inline]
625 pub(super) fn symmetric_peak_hint<F : Float>(r : F, a : F, b : F) -> Option<F> { 613 pub(super) fn symmetric_peak_hint<F: Float>(r: F, a: F, b: F) -> Option<F> {
626 let stage1 = if a < -r { 614 let stage1 = if a < -r {
627 if b <= -r { 615 if b <= -r {
628 None 616 None
629 } else if a + r < -b { 617 } else if a + r < -b {
630 Some(-r) 618 Some(-r)
646 }; 634 };
647 635
648 // Ignore stage1 hint if either side of subdivision would be just a small fraction of the 636 // Ignore stage1 hint if either side of subdivision would be just a small fraction of the
649 // interval 637 // interval
650 match stage1 { 638 match stage1 {
651 Some(h) if (h - a).min(b-h) >= 0.3 * r => Some(h), 639 Some(h) if (h - a).min(b - h) >= 0.3 * r => Some(h),
652 _ => None 640 _ => None,
653 } 641 }
654 } 642 }

mercurial