src/kernels/base.rs

branch
dev
changeset 35
b087e3eab191
parent 32
56c8adc32b09
child 38
0f59c0d02e13
equal deleted inserted replaced
34:efa60bc4f743 35:b087e3eab191
12 Bounds, 12 Bounds,
13 LocalAnalysis, 13 LocalAnalysis,
14 GlobalAnalysis, 14 GlobalAnalysis,
15 Bounded, 15 Bounded,
16 }; 16 };
17 use alg_tools::mapping::{Apply, Differentiable}; 17 use alg_tools::mapping::{
18 Mapping,
19 DifferentiableImpl,
20 DifferentiableMapping,
21 Differential,
22 };
23 use alg_tools::instance::{Instance, Space};
18 use alg_tools::maputil::{array_init, map2, map1_indexed}; 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;
22 use crate::types::Lipschitz; 28 use crate::types::*;
23 29
24 /// Representation of the product of two kernels. 30 /// Representation of the product of two kernels.
25 /// 31 ///
26 /// The kernels typically implement [`Support`] and [`Mapping`][alg_tools::mapping::Mapping]. 32 /// The kernels typically implement [`Support`] and [`Mapping`].
27 /// 33 ///
28 /// 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!
29 #[derive(Copy,Clone,Serialize,Debug)] 35 #[derive(Copy,Clone,Serialize,Debug)]
30 pub struct SupportProductFirst<A, B>( 36 pub struct SupportProductFirst<A, B>(
31 /// First kernel 37 /// First kernel
32 pub A, 38 pub A,
33 /// Second kernel 39 /// Second kernel
34 pub B 40 pub B
35 ); 41 );
36 42
37 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>>
38 for SupportProductFirst<A, B> 44 for SupportProductFirst<A, B>
39 where A : for<'a> Apply<&'a Loc<F, N>, Output=F>, 45 where
40 B : for<'a> Apply<&'a Loc<F, N>, Output=F> { 46 A : Mapping<Loc<F, N>, Codomain = F>,
41 type Output = F; 47 B : Mapping<Loc<F, N>, Codomain = F>,
42 #[inline] 48 {
43 fn apply(&self, x : Loc<F, N>) -> Self::Output { 49 type Codomain = F;
44 self.0.apply(&x) * self.1.apply(&x) 50
45 } 51 #[inline]
46 } 52 fn apply<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Codomain {
47 53 self.0.apply(x.ref_instance()) * self.1.apply(x)
48 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>>
49 for SupportProductFirst<A, B> 58 for SupportProductFirst<A, B>
50 where A : Apply<&'a Loc<F, N>, Output=F>, 59 where
51 B : Apply<&'a Loc<F, N>, Output=F> { 60 A : DifferentiableMapping<
52 type Output = F; 61 Loc<F, N>,
53 #[inline] 62 DerivativeDomain=Loc<F, N>,
54 fn apply(&self, x : &'a Loc<F, N>) -> Self::Output { 63 Codomain = F
55 self.0.apply(x) * self.1.apply(x) 64 >,
56 } 65 B : DifferentiableMapping<
57 } 66 Loc<F, N>,
58 67 DerivativeDomain=Loc<F, N>,
59 impl<A, B, F : Float, const N : usize> Differentiable<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>
60 for SupportProductFirst<A, B> 81 for SupportProductFirst<A, B>
61 where A : for<'a> Apply<&'a Loc<F, N>, Output=F> 82 where A : Lipschitz<M, FloatType = F> + Bounded<F>,
62 + for<'a> Differentiable<&'a Loc<F, N>, Output=Loc<F, N>>, 83 B : Lipschitz<M, FloatType = F> + Bounded<F> {
63 B : for<'a> Apply<&'a Loc<F, N>, Output=F> 84 type FloatType = F;
64 + for<'a> Differentiable<&'a Loc<F, N>, Output=Loc<F, N>> { 85 #[inline]
65 type Output = Loc<F, N>; 86 fn lipschitz_factor(&self, m : M) -> Option<F> {
66 #[inline] 87 // f(x)g(x) - f(y)g(y) = f(x)[g(x)-g(y)] - [f(y)-f(x)]g(y)
67 fn differential(&self, x : Loc<F, N>) -> Self::Output { 88 let &SupportProductFirst(ref f, ref g) = self;
68 self.0.differential(&x) * self.1.apply(&x) + self.1.differential(&x) * self.0.apply(&x) 89 f.lipschitz_factor(m).map(|l| l * g.bounds().uniform())
69 } 90 .zip(g.lipschitz_factor(m).map(|l| l * f.bounds().uniform()))
70 } 91 .map(|(a, b)| a + b)
71 92 }
72 impl<'a, A, B, F : Float, const N : usize> Differentiable<&'a Loc<F, N>> 93 }
73 for SupportProductFirst<A, B> 94
74 where A : Apply<&'a Loc<F, N>, Output=F> 95 impl<'a, A, B, M : Copy, Domain, F : Float> Lipschitz<M>
75 + Differentiable<&'a Loc<F, N>, Output=Loc<F, N>>, 96 for Differential<'a, Domain, SupportProductFirst<A, B>>
76 B : Apply<&'a Loc<F, N>, Output=F> 97 where
77 + Differentiable<&'a Loc<F, N>, Output=Loc<F, N>> { 98 Domain : Space,
78 type Output = Loc<F, N>; 99 A : Clone + DifferentiableMapping<Domain> + Lipschitz<M, FloatType = F> + Bounded<F>,
79 #[inline] 100 B : Clone + DifferentiableMapping<Domain> + Lipschitz<M, FloatType = F> + Bounded<F>,
80 fn differential(&self, x : &'a Loc<F, N>) -> Self::Output { 101 SupportProductFirst<A, B> : DifferentiableMapping<Domain>,
81 self.0.differential(&x) * self.1.apply(&x) + self.1.differential(&x) * self.0.apply(&x) 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()
82 } 121 }
83 } 122 }
84 123
85 124
86 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>
87 for SupportProductFirst<A, B> 126 for SupportProductFirst<A, B>
88 where A : Support<F, N>, 127 where
89 B : Support<F, N> { 128 A : Support<F, N>,
129 B : Support<F, N>
130 {
90 #[inline] 131 #[inline]
91 fn support_hint(&self) -> Cube<F, N> { 132 fn support_hint(&self) -> Cube<F, N> {
92 self.0.support_hint() 133 self.0.support_hint()
93 } 134 }
94 135
123 } 164 }
124 } 165 }
125 166
126 /// Representation of the sum of two kernels 167 /// Representation of the sum of two kernels
127 /// 168 ///
128 /// The kernels typically implement [`Support`] and [`Mapping`][alg_tools::mapping::Mapping]. 169 /// The kernels typically implement [`Support`] and [`Mapping`].
129 /// 170 ///
130 /// 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!
131 #[derive(Copy,Clone,Serialize,Debug)] 172 #[derive(Copy,Clone,Serialize,Debug)]
132 pub struct SupportSum<A, B>( 173 pub struct SupportSum<A, B>(
133 /// First kernel 174 /// First kernel
134 pub A, 175 pub A,
135 /// Second kernel 176 /// Second kernel
136 pub B 177 pub B
137 ); 178 );
138 179
139 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>>
140 for SupportSum<A, B> 181 for SupportSum<A, B>
141 where A : Apply<&'a Loc<F, N>, Output=F>, 182 where
142 B : Apply<&'a Loc<F, N>, Output=F> { 183 A : Mapping<Loc<F, N>, Codomain = F>,
143 type Output = F; 184 B : Mapping<Loc<F, N>, Codomain = F>,
144 #[inline] 185 {
145 fn apply(&self, x : &'a Loc<F, N>) -> Self::Output { 186 type Codomain = F;
146 self.0.apply(x) + self.1.apply(x) 187
147 } 188 #[inline]
148 } 189 fn apply<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Codomain {
149 190 self.0.apply(x.ref_instance()) + self.1.apply(x)
150 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>>
151 for SupportSum<A, B> 195 for SupportSum<A, B>
152 where A : for<'a> Apply<&'a Loc<F, N>, Output=F>, 196 where
153 B : for<'a> Apply<&'a Loc<F, N>, Output=F> { 197 A : DifferentiableMapping<
154 type Output = F; 198 Loc<F, N>,
155 #[inline] 199 DerivativeDomain = Loc<F, N>
156 fn apply(&self, x : Loc<F, N>) -> Self::Output { 200 >,
157 self.0.apply(&x) + self.1.apply(&x) 201 B : DifferentiableMapping<
158 } 202 Loc<F, N>,
159 } 203 DerivativeDomain = Loc<F, N>,
160 204 >
161 impl<'a, A, B, F : Float, const N : usize> Differentiable<&'a Loc<F, N>> 205 {
162 for SupportSum<A, B> 206
163 where A : Differentiable<&'a Loc<F, N>, Output=Loc<F, N>>, 207 type Derivative = Loc<F, N>;
164 B : Differentiable<&'a Loc<F, N>, Output=Loc<F, N>> { 208
165 type Output = Loc<F, N>; 209 #[inline]
166 #[inline] 210 fn differential_impl<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Derivative {
167 fn differential(&self, x : &'a Loc<F, N>) -> Self::Output { 211 self.0.differential(x.ref_instance()) + self.1.differential(x)
168 self.0.differential(x) + self.1.differential(x) 212 }
169 } 213 }
170 } 214
171
172 impl<A, B, F : Float, const N : usize> Differentiable<Loc<F, N>>
173 for SupportSum<A, B>
174 where A : for<'a> Differentiable<&'a Loc<F, N>, Output=Loc<F, N>>,
175 B : for<'a> Differentiable<&'a Loc<F, N>, Output=Loc<F, N>> {
176 type Output = Loc<F, N>;
177 #[inline]
178 fn differential(&self, x : Loc<F, N>) -> Self::Output {
179 self.0.differential(&x) + self.1.differential(&x)
180 }
181 }
182 215
183 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>
184 for SupportSum<A, B> 217 for SupportSum<A, B>
185 where A : Support<F, N>, 218 where A : Support<F, N>,
186 B : Support<F, N>, 219 B : Support<F, N>,
187 Cube<F, N> : SetOrd { 220 Cube<F, N> : SetOrd {
221
188 #[inline] 222 #[inline]
189 fn support_hint(&self) -> Cube<F, N> { 223 fn support_hint(&self) -> Cube<F, N> {
190 self.0.support_hint().common(&self.1.support_hint()) 224 self.0.support_hint().common(&self.1.support_hint())
191 } 225 }
192 226
235 _ => None 269 _ => None
236 } 270 }
237 } 271 }
238 } 272 }
239 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 }
240 293
241 /// Representation of the convolution of two kernels. 294 /// Representation of the convolution of two kernels.
242 /// 295 ///
243 /// The kernels typically implement [`Support`]s and [`Mapping`][alg_tools::mapping::Mapping]. 296 /// The kernels typically implement [`Support`]s and [`Mapping`].
244 // 297 //
245 /// Trait implementations have to be on a case-by-case basis. 298 /// Trait implementations have to be on a case-by-case basis.
246 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] 299 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)]
247 pub struct Convolution<A, B>( 300 pub struct Convolution<A, B>(
248 /// First kernel 301 /// First kernel
250 /// Second kernel 303 /// Second kernel
251 pub B 304 pub B
252 ); 305 );
253 306
254 impl<F : Float, M, A, B> Lipschitz<M> for Convolution<A, B> 307 impl<F : Float, M, A, B> Lipschitz<M> for Convolution<A, B>
255 where A : Bounded<F> , 308 where A : Norm<F, L1> ,
256 B : Lipschitz<M, FloatType = F> { 309 B : Lipschitz<M, FloatType = F> {
257 type FloatType = F; 310 type FloatType = F;
258 311
259 fn lipschitz_factor(&self, m : M) -> Option<F> { 312 fn lipschitz_factor(&self, m : M) -> Option<F> {
260 self.1.lipschitz_factor(m).map(|l| l * self.0.bounds().uniform()) 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))
261 } 341 }
262 } 342 }
263 343
264 /// Representation of the autoconvolution of a kernel. 344 /// Representation of the autoconvolution of a kernel.
265 /// 345 ///
266 /// The kernel typically implements [`Support`] and [`Mapping`][alg_tools::mapping::Mapping]. 346 /// The kernel typically implements [`Support`] and [`Mapping`].
267 /// 347 ///
268 /// Trait implementations have to be on a case-by-case basis. 348 /// Trait implementations have to be on a case-by-case basis.
269 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] 349 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)]
270 pub struct AutoConvolution<A>( 350 pub struct AutoConvolution<A>(
271 /// The kernel to be autoconvolved 351 /// The kernel to be autoconvolved
272 pub A 352 pub A
273 ); 353 );
274 354
275 impl<F : Float, M, C> Lipschitz<M> for AutoConvolution<C> 355 impl<F : Float, M, C> Lipschitz<M> for AutoConvolution<C>
276 where C : Lipschitz<M, FloatType = F> + Bounded<F> { 356 where C : Lipschitz<M, FloatType = F> + Norm<F, L1> {
277 type FloatType = F; 357 type FloatType = F;
278 358
279 fn lipschitz_factor(&self, m : M) -> Option<F> { 359 fn lipschitz_factor(&self, m : M) -> Option<F> {
280 self.0.lipschitz_factor(m).map(|l| l * self.0.bounds().uniform()) 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))
281 } 377 }
282 } 378 }
283 379
284 380
285 /// Representation a multi-dimensional product of a one-dimensional kernel. 381 /// Representation a multi-dimensional product of a one-dimensional kernel.
286 /// 382 ///
287 /// 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)$.
288 /// The kernel $G$ typically implements [`Support`] and [`Mapping`][alg_tools::mapping::Mapping] 384 /// The kernel $G$ typically implements [`Support`] and [`Mapping`]
289 /// 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>`].
290 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] 386 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)]
387 #[allow(dead_code)]
291 struct UniformProduct<G, const N : usize>( 388 struct UniformProduct<G, const N : usize>(
292 /// The one-dimensional kernel 389 /// The one-dimensional kernel
293 G 390 G
294 ); 391 );
295 392
296 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>>
297 for UniformProduct<G, N> 394 for UniformProduct<G, N>
298 where G : Apply<Loc<F, 1>, Output=F> { 395 where
299 type Output = F; 396 G : Mapping<Loc<F, 1>, Codomain = F>
300 #[inline] 397 {
301 fn apply(&self, x : &'a Loc<F, N>) -> F { 398 type Codomain = F;
302 x.iter().map(|&y| self.0.apply(Loc([y]))).product() 399
303 } 400 #[inline]
304 } 401 fn apply<I : Instance<Loc<F, N>>>(&self, x : I) -> F {
305 402 x.cow().iter().map(|&y| self.0.apply(Loc([y]))).product()
306 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>>
307 for UniformProduct<G, N> 409 for UniformProduct<G, N>
308 where G : Apply<Loc<F, 1>, Output=F> { 410 where
309 type Output = F; 411 G : DifferentiableMapping<
310 #[inline] 412 Loc<F, 1>,
311 fn apply(&self, x : Loc<F, N>) -> F { 413 DerivativeDomain = F,
312 x.into_iter().map(|y| self.0.apply(Loc([y]))).product() 414 Codomain = F,
313 } 415 >
314 } 416 {
315 417 type Derivative = Loc<F, N>;
316 impl<'a, G, F : Float, const N : usize> Differentiable<&'a Loc<F, N>> 418
317 for UniformProduct<G, N> 419 #[inline]
318 where G : Apply<Loc<F, 1>, Output=F> + Differentiable<Loc<F, 1>, Output=F> { 420 fn differential_impl<I : Instance<Loc<F, N>>>(&self, x0 : I) -> Loc<F, N> {
319 type Output = Loc<F, N>; 421 x0.eval(|x| {
320 #[inline] 422 let vs = x.map(|y| self.0.apply(Loc([y])));
321 fn differential(&self, x : &'a Loc<F, N>) -> Loc<F, N> { 423 product_differential(x, &vs, |y| self.0.differential(Loc([y])))
322 let vs = x.map(|y| self.0.apply(Loc([y]))); 424 })
323 product_differential(x, &vs, |y| self.0.differential(Loc([y])))
324 } 425 }
325 } 426 }
326 427
327 /// Helper function to calulate the differential of $f(x)=∏_{i=1}^N g(x_i)$. 428 /// Helper function to calulate the differential of $f(x)=∏_{i=1}^N g(x_i)$.
328 /// 429 ///
340 .filter_map(|(v, j)| (j != i).then_some(*v)) 441 .filter_map(|(v, j)| (j != i).then_some(*v))
341 .product() 442 .product()
342 }).into() 443 }).into()
343 } 444 }
344 445
345 impl<G, F : Float, const N : usize> Differentiable<Loc<F, N>> 446 /// Helper function to calulate the Lipschitz factor of $∇f$ for $f(x)=∏_{i=1}^N g(x_i)$.
346 for UniformProduct<G, N> 447 ///
347 where G : Apply<Loc<F, 1>, Output=F> + Differentiable<Loc<F, 1>, Output=F> { 448 /// The parameter `bound` is a bound on $|g|_∞$, `lip` is a Lipschitz factor for $g$,
348 type Output = Loc<F, N>; 449 /// `dbound` is a bound on $|∇g|_∞$, and `dlip` a Lipschitz factor for $∇g$.
349 #[inline] 450 #[inline]
350 fn differential(&self, x : Loc<F, N>) -> Loc<F, N> { 451 pub(crate) fn product_differential_lipschitz_factor<F : Float, const N : usize>(
351 self.differential(&x) 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")
352 } 479 }
353 } 480 }
354 481
355 impl<G, F : Float, const N : usize> Support<F, N> 482 impl<G, F : Float, const N : usize> Support<F, N>
356 for UniformProduct<G, N> 483 for UniformProduct<G, N>

mercurial