12 Bounds, |
12 Bounds, |
13 LocalAnalysis, |
13 LocalAnalysis, |
14 GlobalAnalysis, |
14 GlobalAnalysis, |
15 Bounded, |
15 Bounded, |
16 }; |
16 }; |
17 use alg_tools::mapping::Apply; |
17 use alg_tools::mapping::{ |
18 use alg_tools::maputil::{array_init, map2}; |
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}; |
19 use alg_tools::sets::SetOrd; |
25 use alg_tools::sets::SetOrd; |
20 |
26 |
21 use crate::fourier::Fourier; |
27 use crate::fourier::Fourier; |
|
28 use crate::types::*; |
22 |
29 |
23 /// Representation of the product of two kernels. |
30 /// Representation of the product of two kernels. |
24 /// |
31 /// |
25 /// The kernels typically implement [`Support`] and [`Mapping`][alg_tools::mapping::Mapping]. |
32 /// The kernels typically implement [`Support`] and [`Mapping`]. |
26 /// |
33 /// |
27 /// The implementation [`Support`] only uses the [`Support::support_hint`] of the first parameter! |
34 /// The implementation [`Support`] only uses the [`Support::support_hint`] of the first parameter! |
28 #[derive(Copy,Clone,Serialize,Debug)] |
35 #[derive(Copy,Clone,Serialize,Debug)] |
29 pub struct SupportProductFirst<A, B>( |
36 pub struct SupportProductFirst<A, B>( |
30 /// First kernel |
37 /// First kernel |
31 pub A, |
38 pub A, |
32 /// Second kernel |
39 /// Second kernel |
33 pub B |
40 pub B |
34 ); |
41 ); |
35 |
42 |
36 impl<A, B, F : Float, const N : usize> Apply<Loc<F, N>> |
43 impl<A, B, F : Float, const N : usize> Mapping<Loc<F, N>> |
37 for SupportProductFirst<A, B> |
44 for SupportProductFirst<A, B> |
38 where A : for<'a> Apply<&'a Loc<F, N>, Output=F>, |
45 where |
39 B : for<'a> Apply<&'a Loc<F, N>, Output=F> { |
46 A : Mapping<Loc<F, N>, Codomain = F>, |
40 type Output = F; |
47 B : Mapping<Loc<F, N>, Codomain = F>, |
41 #[inline] |
48 { |
42 fn apply(&self, x : Loc<F, N>) -> Self::Output { |
49 type Codomain = F; |
43 self.0.apply(&x) * self.1.apply(&x) |
50 |
44 } |
51 #[inline] |
45 } |
52 fn apply<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Codomain { |
46 |
53 self.0.apply(x.ref_instance()) * self.1.apply(x) |
47 impl<'a, A, B, F : Float, const N : usize> Apply<&'a Loc<F, N>> |
54 } |
|
55 } |
|
56 |
|
57 impl<A, B, F : Float, const N : usize> DifferentiableImpl<Loc<F, N>> |
48 for SupportProductFirst<A, B> |
58 for SupportProductFirst<A, B> |
49 where A : Apply<&'a Loc<F, N>, Output=F>, |
59 where |
50 B : Apply<&'a Loc<F, N>, Output=F> { |
60 A : DifferentiableMapping< |
51 type Output = F; |
61 Loc<F, N>, |
52 #[inline] |
62 DerivativeDomain=Loc<F, N>, |
53 fn apply(&self, x : &'a Loc<F, N>) -> Self::Output { |
63 Codomain = F |
54 self.0.apply(x) * self.1.apply(x) |
64 >, |
55 } |
65 B : DifferentiableMapping< |
56 } |
66 Loc<F, N>, |
|
67 DerivativeDomain=Loc<F, N>, |
|
68 Codomain = F, |
|
69 > |
|
70 { |
|
71 type Derivative = Loc<F, N>; |
|
72 |
|
73 #[inline] |
|
74 fn differential_impl<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Derivative { |
|
75 let xr = x.ref_instance(); |
|
76 self.0.differential(xr) * self.1.apply(xr) + self.1.differential(xr) * self.0.apply(x) |
|
77 } |
|
78 } |
|
79 |
|
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) |
|
88 let &SupportProductFirst(ref f, ref g) = self; |
|
89 f.lipschitz_factor(m).map(|l| l * g.bounds().uniform()) |
|
90 .zip(g.lipschitz_factor(m).map(|l| l * f.bounds().uniform())) |
|
91 .map(|(a, b)| a + b) |
|
92 } |
|
93 } |
|
94 |
|
95 impl<'a, A, B, M : Copy, Domain, F : Float> Lipschitz<M> |
|
96 for Differential<'a, Domain, SupportProductFirst<A, B>> |
|
97 where |
|
98 Domain : Space, |
|
99 A : Clone + DifferentiableMapping<Domain> + Lipschitz<M, FloatType = F> + Bounded<F>, |
|
100 B : Clone + DifferentiableMapping<Domain> + Lipschitz<M, FloatType = F> + Bounded<F>, |
|
101 SupportProductFirst<A, B> : DifferentiableMapping<Domain>, |
|
102 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> |
|
104 { |
|
105 type FloatType = F; |
|
106 #[inline] |
|
107 fn lipschitz_factor(&self, m : M) -> Option<F> { |
|
108 // ∇[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) |
|
110 // = 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)] |
|
112 // - [f(y)-f(x)]∇g(y) + [g(y)-g(x)]∇f(y) |
|
113 let &SupportProductFirst(ref f, ref g) = self.base_fn(); |
|
114 let (df, dg) = (f.diff_ref(), g.diff_ref()); |
|
115 [ |
|
116 df.lipschitz_factor(m).map(|l| l * g.bounds().uniform()), |
|
117 dg.lipschitz_factor(m).map(|l| l * f.bounds().uniform()), |
|
118 f.lipschitz_factor(m).map(|l| l * dg.norm_bound(L2)), |
|
119 g.lipschitz_factor(m).map(|l| l * df.norm_bound(L2)) |
|
120 ].into_iter().sum() |
|
121 } |
|
122 } |
|
123 |
57 |
124 |
58 impl<'a, A, B, F : Float, const N : usize> Support<F, N> |
125 impl<'a, A, B, F : Float, const N : usize> Support<F, N> |
59 for SupportProductFirst<A, B> |
126 for SupportProductFirst<A, B> |
60 where A : Support<F, N>, |
127 where |
61 B : Support<F, N> { |
128 A : Support<F, N>, |
|
129 B : Support<F, N> |
|
130 { |
62 #[inline] |
131 #[inline] |
63 fn support_hint(&self) -> Cube<F, N> { |
132 fn support_hint(&self) -> Cube<F, N> { |
64 self.0.support_hint() |
133 self.0.support_hint() |
65 } |
134 } |
66 |
135 |
95 } |
164 } |
96 } |
165 } |
97 |
166 |
98 /// Representation of the sum of two kernels |
167 /// Representation of the sum of two kernels |
99 /// |
168 /// |
100 /// The kernels typically implement [`Support`] and [`Mapping`][alg_tools::mapping::Mapping]. |
169 /// The kernels typically implement [`Support`] and [`Mapping`]. |
101 /// |
170 /// |
102 /// The implementation [`Support`] only uses the [`Support::support_hint`] of the first parameter! |
171 /// The implementation [`Support`] only uses the [`Support::support_hint`] of the first parameter! |
103 #[derive(Copy,Clone,Serialize,Debug)] |
172 #[derive(Copy,Clone,Serialize,Debug)] |
104 pub struct SupportSum<A, B>( |
173 pub struct SupportSum<A, B>( |
105 /// First kernel |
174 /// First kernel |
106 pub A, |
175 pub A, |
107 /// Second kernel |
176 /// Second kernel |
108 pub B |
177 pub B |
109 ); |
178 ); |
110 |
179 |
111 impl<'a, A, B, F : Float, const N : usize> Apply<&'a Loc<F, N>> |
180 impl<'a, A, B, F : Float, const N : usize> Mapping<Loc<F, N>> |
112 for SupportSum<A, B> |
181 for SupportSum<A, B> |
113 where A : Apply<&'a Loc<F, N>, Output=F>, |
182 where |
114 B : Apply<&'a Loc<F, N>, Output=F> { |
183 A : Mapping<Loc<F, N>, Codomain = F>, |
115 type Output = F; |
184 B : Mapping<Loc<F, N>, Codomain = F>, |
116 #[inline] |
185 { |
117 fn apply(&self, x : &'a Loc<F, N>) -> Self::Output { |
186 type Codomain = F; |
118 self.0.apply(x) + self.1.apply(x) |
187 |
119 } |
188 #[inline] |
120 } |
189 fn apply<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Codomain { |
121 |
190 self.0.apply(x.ref_instance()) + self.1.apply(x) |
122 impl<A, B, F : Float, const N : usize> Apply<Loc<F, N>> |
191 } |
|
192 } |
|
193 |
|
194 impl<'a, A, B, F : Float, const N : usize> DifferentiableImpl<Loc<F, N>> |
123 for SupportSum<A, B> |
195 for SupportSum<A, B> |
124 where A : for<'a> Apply<&'a Loc<F, N>, Output=F>, |
196 where |
125 B : for<'a> Apply<&'a Loc<F, N>, Output=F> { |
197 A : DifferentiableMapping< |
126 type Output = F; |
198 Loc<F, N>, |
127 #[inline] |
199 DerivativeDomain = Loc<F, N> |
128 fn apply(&self, x : Loc<F, N>) -> Self::Output { |
200 >, |
129 self.0.apply(&x) + self.1.apply(&x) |
201 B : DifferentiableMapping< |
130 } |
202 Loc<F, N>, |
131 } |
203 DerivativeDomain = Loc<F, N>, |
|
204 > |
|
205 { |
|
206 |
|
207 type Derivative = Loc<F, N>; |
|
208 |
|
209 #[inline] |
|
210 fn differential_impl<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Derivative { |
|
211 self.0.differential(x.ref_instance()) + self.1.differential(x) |
|
212 } |
|
213 } |
|
214 |
132 |
215 |
133 impl<'a, A, B, F : Float, const N : usize> Support<F, N> |
216 impl<'a, A, B, F : Float, const N : usize> Support<F, N> |
134 for SupportSum<A, B> |
217 for SupportSum<A, B> |
135 where A : Support<F, N>, |
218 where A : Support<F, N>, |
136 B : Support<F, N>, |
219 B : Support<F, N>, |
137 Cube<F, N> : SetOrd { |
220 Cube<F, N> : SetOrd { |
|
221 |
138 #[inline] |
222 #[inline] |
139 fn support_hint(&self) -> Cube<F, N> { |
223 fn support_hint(&self) -> Cube<F, N> { |
140 self.0.support_hint().common(&self.1.support_hint()) |
224 self.0.support_hint().common(&self.1.support_hint()) |
141 } |
225 } |
142 |
226 |
172 fn local_analysis(&self, cube : &Cube<F, N>) -> Bounds<F> { |
256 fn local_analysis(&self, cube : &Cube<F, N>) -> Bounds<F> { |
173 self.0.local_analysis(cube) + self.1.local_analysis(cube) |
257 self.0.local_analysis(cube) + self.1.local_analysis(cube) |
174 } |
258 } |
175 } |
259 } |
176 |
260 |
|
261 impl<F : Float, M : Copy, A, B> Lipschitz<M> for SupportSum<A, B> |
|
262 where A : Lipschitz<M, FloatType = F>, |
|
263 B : Lipschitz<M, FloatType = F> { |
|
264 type FloatType = F; |
|
265 |
|
266 fn lipschitz_factor(&self, m : M) -> Option<F> { |
|
267 match (self.0.lipschitz_factor(m), self.1.lipschitz_factor(m)) { |
|
268 (Some(l0), Some(l1)) => Some(l0 + l1), |
|
269 _ => None |
|
270 } |
|
271 } |
|
272 } |
|
273 |
|
274 impl<'b, F : Float, M : Copy, A, B, Domain> Lipschitz<M> |
|
275 for Differential<'b, Domain, SupportSum<A, B>> |
|
276 where |
|
277 Domain : Space, |
|
278 A : Clone + DifferentiableMapping<Domain, Codomain=F>, |
|
279 B : Clone + DifferentiableMapping<Domain, Codomain=F>, |
|
280 SupportSum<A, B> : DifferentiableMapping<Domain, Codomain=F>, |
|
281 for<'a> A :: Differential<'a> : Lipschitz<M, FloatType = F>, |
|
282 for<'a> B :: Differential<'a> : Lipschitz<M, FloatType = F> |
|
283 { |
|
284 type FloatType = F; |
|
285 |
|
286 fn lipschitz_factor(&self, m : M) -> Option<F> { |
|
287 let base = self.base_fn(); |
|
288 base.0.diff_ref().lipschitz_factor(m) |
|
289 .zip(base.1.diff_ref().lipschitz_factor(m)) |
|
290 .map(|(a, b)| a + b) |
|
291 } |
|
292 } |
|
293 |
177 /// Representation of the convolution of two kernels. |
294 /// Representation of the convolution of two kernels. |
178 /// |
295 /// |
179 /// The kernels typically implement [`Support`]s and [`Mapping`][alg_tools::mapping::Mapping]. |
296 /// The kernels typically implement [`Support`]s and [`Mapping`]. |
180 // |
297 // |
181 /// Trait implementations have to be on a case-by-case basis. |
298 /// Trait implementations have to be on a case-by-case basis. |
182 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] |
299 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] |
183 pub struct Convolution<A, B>( |
300 pub struct Convolution<A, B>( |
184 /// First kernel |
301 /// First kernel |
185 pub A, |
302 pub A, |
186 /// Second kernel |
303 /// Second kernel |
187 pub B |
304 pub B |
188 ); |
305 ); |
189 |
306 |
|
307 impl<F : Float, M, A, B> Lipschitz<M> for Convolution<A, B> |
|
308 where A : Norm<F, L1> , |
|
309 B : Lipschitz<M, FloatType = F> { |
|
310 type FloatType = F; |
|
311 |
|
312 fn lipschitz_factor(&self, m : M) -> Option<F> { |
|
313 // 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. |
|
315 // Hence |[f * g](x) - [f * g](z)| ≤ ∫ |f(x-y)-f(z-y)|g(y)| dy. |
|
316 // ≤ L|x-z| ∫ |g(y)| dy, |
|
317 // where L is the Lipschitz factor of f. |
|
318 self.1.lipschitz_factor(m).map(|l| l * self.0.norm(L1)) |
|
319 } |
|
320 } |
|
321 |
|
322 impl<'b, F : Float, M, A, B, Domain> Lipschitz<M> |
|
323 for Differential<'b, Domain, Convolution<A, B>> |
|
324 where |
|
325 Domain : Space, |
|
326 A : Clone + Norm<F, L1> , |
|
327 Convolution<A, B> : DifferentiableMapping<Domain, Codomain=F>, |
|
328 B : Clone + DifferentiableMapping<Domain, Codomain=F>, |
|
329 for<'a> B :: Differential<'a> : Lipschitz<M, FloatType = F> |
|
330 { |
|
331 type FloatType = F; |
|
332 |
|
333 fn lipschitz_factor(&self, m : M) -> Option<F> { |
|
334 // 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. |
|
336 // Hence |∇[f * g](x) - ∇[f * g](z)| ≤ ∫ |∇f(x-y)-∇f(z-y)|g(y)| dy. |
|
337 // ≤ L|x-z| ∫ |g(y)| dy, |
|
338 // where L is the Lipschitz factor of ∇f. |
|
339 let base = self.base_fn(); |
|
340 base.1.diff_ref().lipschitz_factor(m).map(|l| l * base.0.norm(L1)) |
|
341 } |
|
342 } |
|
343 |
190 /// Representation of the autoconvolution of a kernel. |
344 /// Representation of the autoconvolution of a kernel. |
191 /// |
345 /// |
192 /// The kernel typically implements [`Support`] and [`Mapping`][alg_tools::mapping::Mapping]. |
346 /// The kernel typically implements [`Support`] and [`Mapping`]. |
193 /// |
347 /// |
194 /// Trait implementations have to be on a case-by-case basis. |
348 /// Trait implementations have to be on a case-by-case basis. |
195 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] |
349 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] |
196 pub struct AutoConvolution<A>( |
350 pub struct AutoConvolution<A>( |
197 /// The kernel to be autoconvolved |
351 /// The kernel to be autoconvolved |
198 pub A |
352 pub A |
199 ); |
353 ); |
200 |
354 |
|
355 impl<F : Float, M, C> Lipschitz<M> for AutoConvolution<C> |
|
356 where C : Lipschitz<M, FloatType = F> + Norm<F, L1> { |
|
357 type FloatType = F; |
|
358 |
|
359 fn lipschitz_factor(&self, m : M) -> Option<F> { |
|
360 self.0.lipschitz_factor(m).map(|l| l * self.0.norm(L1)) |
|
361 } |
|
362 } |
|
363 |
|
364 impl<'b, F : Float, M, C, Domain> Lipschitz<M> |
|
365 for Differential<'b, Domain, AutoConvolution<C>> |
|
366 where |
|
367 Domain : Space, |
|
368 C : Clone + Norm<F, L1> + DifferentiableMapping<Domain, Codomain=F>, |
|
369 AutoConvolution<C> : DifferentiableMapping<Domain, Codomain=F>, |
|
370 for<'a> C :: Differential<'a> : Lipschitz<M, FloatType = F> |
|
371 { |
|
372 type FloatType = F; |
|
373 |
|
374 fn lipschitz_factor(&self, m : M) -> Option<F> { |
|
375 let base = self.base_fn(); |
|
376 base.0.diff_ref().lipschitz_factor(m).map(|l| l * base.0.norm(L1)) |
|
377 } |
|
378 } |
|
379 |
|
380 |
201 /// Representation a multi-dimensional product of a one-dimensional kernel. |
381 /// Representation a multi-dimensional product of a one-dimensional kernel. |
202 /// |
382 /// |
203 /// For $G: ℝ → ℝ$, this is the function $F(x\_1, …, x\_n) := \prod_{i=1}^n G(x\_i)$. |
383 /// For $G: ℝ → ℝ$, this is the function $F(x\_1, …, x\_n) := \prod_{i=1}^n G(x\_i)$. |
204 /// The kernel $G$ typically implements [`Support`] and [`Mapping`][alg_tools::mapping::Mapping] |
384 /// The kernel $G$ typically implements [`Support`] and [`Mapping`] |
205 /// on [`Loc<F, 1>`]. Then the product implements them on [`Loc<F, N>`]. |
385 /// on [`Loc<F, 1>`]. Then the product implements them on [`Loc<F, N>`]. |
206 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] |
386 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] |
|
387 #[allow(dead_code)] |
207 struct UniformProduct<G, const N : usize>( |
388 struct UniformProduct<G, const N : usize>( |
208 /// The one-dimensional kernel |
389 /// The one-dimensional kernel |
209 G |
390 G |
210 ); |
391 ); |
211 |
392 |
212 impl<'a, G, F : Float, const N : usize> Apply<&'a Loc<F, N>> |
393 impl<'a, G, F : Float, const N : usize> Mapping<Loc<F, N>> |
213 for UniformProduct<G, N> |
394 for UniformProduct<G, N> |
214 where G : Apply<Loc<F, 1>, Output=F> { |
395 where |
215 type Output = F; |
396 G : Mapping<Loc<F, 1>, Codomain = F> |
216 #[inline] |
397 { |
217 fn apply(&self, x : &'a Loc<F, N>) -> F { |
398 type Codomain = F; |
218 x.iter().map(|&y| self.0.apply(Loc([y]))).product() |
399 |
219 } |
400 #[inline] |
220 } |
401 fn apply<I : Instance<Loc<F, N>>>(&self, x : I) -> F { |
221 |
402 x.cow().iter().map(|&y| self.0.apply(Loc([y]))).product() |
222 impl<G, F : Float, const N : usize> Apply<Loc<F, N>> |
403 } |
|
404 } |
|
405 |
|
406 |
|
407 |
|
408 impl<'a, G, F : Float, const N : usize> DifferentiableImpl<Loc<F, N>> |
223 for UniformProduct<G, N> |
409 for UniformProduct<G, N> |
224 where G : Apply<Loc<F, 1>, Output=F> { |
410 where |
225 type Output = F; |
411 G : DifferentiableMapping< |
226 #[inline] |
412 Loc<F, 1>, |
227 fn apply(&self, x : Loc<F, N>) -> F { |
413 DerivativeDomain = F, |
228 x.into_iter().map(|y| self.0.apply(Loc([y]))).product() |
414 Codomain = F, |
|
415 > |
|
416 { |
|
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| { |
|
422 let vs = x.map(|y| self.0.apply(Loc([y]))); |
|
423 product_differential(x, &vs, |y| self.0.differential(Loc([y]))) |
|
424 }) |
|
425 } |
|
426 } |
|
427 |
|
428 /// Helper function to calulate the differential of $f(x)=∏_{i=1}^N g(x_i)$. |
|
429 /// |
|
430 /// The vector `x` is the location, `vs` consists of the values `g(x_i)`, and |
|
431 /// `gd` calculates the derivative `g'`. |
|
432 #[inline] |
|
433 pub(crate) fn product_differential<F : Float, G : Fn(F) -> F, const N : usize>( |
|
434 x : &Loc<F, N>, |
|
435 vs : &Loc<F, N>, |
|
436 gd : G |
|
437 ) -> Loc<F, N> { |
|
438 map1_indexed(x, |i, &y| { |
|
439 gd(y) * vs.iter() |
|
440 .zip(0..) |
|
441 .filter_map(|(v, j)| (j != i).then_some(*v)) |
|
442 .product() |
|
443 }).into() |
|
444 } |
|
445 |
|
446 /// Helper function to calulate the Lipschitz factor of $∇f$ for $f(x)=∏_{i=1}^N g(x_i)$. |
|
447 /// |
|
448 /// 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$. |
|
450 #[inline] |
|
451 pub(crate) fn product_differential_lipschitz_factor<F : Float, const N : usize>( |
|
452 bound : F, |
|
453 lip : F, |
|
454 dbound : F, |
|
455 dlip : F |
|
456 ) -> F { |
|
457 // 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) |
|
459 // 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 |
|
461 // M_g a bound on it. |
|
462 // |
|
463 // We also have in the general case ∇ψ(x) = ∑_i ∇ψ_i(x_i) ∏_{j ≠ i} ψ_j(x_j), whence |
|
464 // using the previous formula for each i with f_i=∇ψ_i and f_j=ψ_j for j ≠ i, we get |
|
465 // ∇ψ(x) - ∇ψ(y) = ∑_i[ ∇ψ_i(x_i)∏_{j ≠ i} ψ_j(x_j) - ∇ψ_i(y_i)∏_{j ≠ i} ψ_j(y_j)] |
|
466 // = ∑_i[ [∇ψ_i(x_i) - ∇ψ_j(x_j)] ∏_{j ≠ i}ψ_j(x_j) |
|
467 // + [∑_{k ≠ i} [ψ_k(x_k) - ∇ψ_k(x_k)] ∏_{j ≠ i, k}ψ_j(x_j)]∇ψ_i(x_i)]. |
|
468 // 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} |
|
470 // = 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}]. |
|
472 if N >= 2 { |
|
473 F::cast_from(N) * bound.powi((N-2) as i32) |
|
474 * (dlip * bound + F::cast_from(N-1) * lip * dbound) |
|
475 } else if N==1 { |
|
476 dlip |
|
477 } else { |
|
478 panic!("Invalid dimension") |
229 } |
479 } |
230 } |
480 } |
231 |
481 |
232 impl<G, F : Float, const N : usize> Support<F, N> |
482 impl<G, F : Float, const N : usize> Support<F, N> |
233 for UniformProduct<G, N> |
483 for UniformProduct<G, N> |