src/kernels/gaussian.rs

branch
dev
changeset 35
b087e3eab191
parent 34
efa60bc4f743
child 38
0f59c0d02e13
equal deleted inserted replaced
34:efa60bc4f743 35:b087e3eab191
15 LocalAnalysis, 15 LocalAnalysis,
16 GlobalAnalysis, 16 GlobalAnalysis,
17 Weighted, 17 Weighted,
18 Bounded, 18 Bounded,
19 }; 19 };
20 use alg_tools::mapping::{Apply, Differentiable}; 20 use alg_tools::mapping::{
21 Mapping,
22 Instance,
23 Differential,
24 DifferentiableImpl,
25 };
21 use alg_tools::maputil::array_init; 26 use alg_tools::maputil::array_init;
22 27
23 use crate::types::Lipschitz; 28 use crate::types::*;
24 use crate::fourier::Fourier; 29 use crate::fourier::Fourier;
25 use super::base::*; 30 use super::base::*;
26 use super::ball_indicator::CubeIndicator; 31 use super::ball_indicator::CubeIndicator;
27 32
28 /// Storage presentation of the the anisotropic gaussian kernel of `variance` $σ^2$. 33 /// Storage presentation of the the anisotropic gaussian kernel of `variance` $σ^2$.
57 } 62 }
58 } 63 }
59 64
60 65
61 #[replace_float_literals(S::Type::cast_from(literal))] 66 #[replace_float_literals(S::Type::cast_from(literal))]
62 impl<'a, S, const N : usize> Apply<&'a Loc<S::Type, N>> for Gaussian<S, N> 67 impl<'a, S, const N : usize> Mapping<Loc<S::Type, N>> for Gaussian<S, N>
63 where S : Constant { 68 where
64 type Output = S::Type; 69 S : Constant
70 {
71 type Codomain = S::Type;
72
65 // This is not normalised to neither to have value 1 at zero or integral 1 73 // This is not normalised to neither to have value 1 at zero or integral 1
66 // (unless the cut-off ε=0). 74 // (unless the cut-off ε=0).
67 #[inline] 75 #[inline]
68 fn apply(&self, x : &'a Loc<S::Type, N>) -> Self::Output { 76 fn apply<I : Instance<Loc<S::Type, N>>>(&self, x : I) -> Self::Codomain {
69 let d_squared = x.norm2_squared(); 77 let d_squared = x.eval(|x| x.norm2_squared());
70 let σ2 = self.variance.value(); 78 let σ2 = self.variance.value();
71 let scale = self.scale(); 79 let scale = self.scale();
72 (-d_squared / (2.0 * σ2)).exp() / scale 80 (-d_squared / (2.0 * σ2)).exp() / scale
73 } 81 }
74 } 82 }
75 83
76 impl<S, const N : usize> Apply<Loc<S::Type, N>> for Gaussian<S, N> 84 #[replace_float_literals(S::Type::cast_from(literal))]
77 where S : Constant { 85 impl<'a, S, const N : usize> DifferentiableImpl<Loc<S::Type, N>> for Gaussian<S, N>
78 type Output = S::Type; 86 where S : Constant {
79 #[inline] 87 type Derivative = Loc<S::Type, N>;
80 fn apply(&self, x : Loc<S::Type, N>) -> Self::Output { 88
81 self.apply(&x) 89 #[inline]
82 } 90 fn differential_impl<I : Instance<Loc<S::Type, N>>>(&self, x0 : I) -> Self::Derivative {
83 } 91 let x = x0.cow();
84 92 let f = -self.apply(&*x) / self.variance.value();
85 #[replace_float_literals(S::Type::cast_from(literal))] 93 *x * f
86 impl<'a, S, const N : usize> Differentiable<&'a Loc<S::Type, N>> for Gaussian<S, N> 94 }
87 where S : Constant { 95 }
88 type Output = Loc<S::Type, N>; 96
89 #[inline] 97
90 fn differential(&self, x : &'a Loc<S::Type, N>) -> Self::Output { 98 // To calculate the the Lipschitz factors, we consider
91 x * (self.apply(x) / self.variance.value()) 99 // f(t) = e^{-t²/2}
92 } 100 // f'(t) = -t f(t) which has max at t=1 by f''(t)=0
93 } 101 // f''(t) = (t²-1)f(t) which has max at t=√3 by f'''(t)=0
94 102 // f'''(t) = -(t³-3t)
95 impl<S, const N : usize> Differentiable<Loc<S::Type, N>> for Gaussian<S, N> 103 // So f has the Lipschitz factor L=f'(1), and f' has the Lipschitz factor L'=f''(√3).
96 where S : Constant { 104 //
97 type Output = Loc<S::Type, N>; 105 // Now g(x) = Cf(‖x‖/σ) for a scaling factor C is the Gaussian.
98 // This is not normalised to neither to have value 1 at zero or integral 1 106 // Thus ‖g(x)-g(y)‖ = C‖f(‖x‖/σ)-f(‖y‖/σ)‖ ≤ (C/σ)L‖x-y‖,
99 // (unless the cut-off ε=0). 107 // so g has the Lipschitz factor (C/σ)f'(1) = (C/σ)exp(-0.5).
100 #[inline] 108 //
101 fn differential(&self, x : Loc<S::Type, N>) -> Self::Output { 109 // Also ∇g(x)= Cx/(σ‖x‖)f'(‖x‖/σ) (*)
102 x * (self.apply(&x) / self.variance.value()) 110 // = -(C/σ²)xf(‖x‖/σ)
103 } 111 // = -C/σ (x/σ) f(‖x/σ‖)
104 } 112 // ∇²g(x) = -(C/σ)[Id/σ f(‖x‖/σ) + x ⊗ x/(σ²‖x‖) f'(‖x‖/σ)]
113 // = (C/σ²)[-Id + x ⊗ x/σ²]f(‖x‖/σ).
114 // Thus ‖∇²g(x)‖ = (C/σ²)‖-Id + x ⊗ x/σ²‖f(‖x‖/σ), where
115 // ‖-Id + x ⊗ x/σ²‖ = ‖[-Id + x ⊗ x/σ²](x/‖x‖)‖ = |-1 + ‖x²/σ^2‖|.
116 // This means that ‖∇²g(x)‖ = (C/σ²)|f''(‖x‖/σ)|, which is maximised with ‖x‖/σ=√3.
117 // Hence the Lipschitz factor of ∇g is (C/σ²)f''(√3) = (C/σ²)2e^{-3/2}.
105 118
106 #[replace_float_literals(S::Type::cast_from(literal))] 119 #[replace_float_literals(S::Type::cast_from(literal))]
107 impl<S, const N : usize> Lipschitz<L2> for Gaussian<S, N> 120 impl<S, const N : usize> Lipschitz<L2> for Gaussian<S, N>
108 where S : Constant { 121 where S : Constant {
109 type FloatType = S::Type; 122 type FloatType = S::Type;
110 fn lipschitz_factor(&self, L2 : L2) -> Option<Self::FloatType> { 123 fn lipschitz_factor(&self, L2 : L2) -> Option<Self::FloatType> {
111 // f(x)=f_1(‖x‖_2/σ) * √(2π) / √(2πσ)^N, where f_1 is one-dimensional Gaussian with
112 // variance 1. The Lipschitz factor of f_1 is e^{-1/2}/√(2π), see, e.g.,
113 // https://math.stackexchange.com/questions/3630967/is-the-gaussian-density-lipschitz-continuous
114 // Thus the Lipschitz factor we want is e^{-1/2} / (√(2πσ)^N * σ).
115 Some((-0.5).exp() / (self.scale() * self.variance.value().sqrt())) 124 Some((-0.5).exp() / (self.scale() * self.variance.value().sqrt()))
116 } 125 }
117 } 126 }
127
128
129 #[replace_float_literals(S::Type::cast_from(literal))]
130 impl<'a, S : Constant, const N : usize> Lipschitz<L2>
131 for Differential<'a, Loc<S::Type, N>, Gaussian<S, N>> {
132 type FloatType = S::Type;
133
134 fn lipschitz_factor(&self, _l2 : L2) -> Option<S::Type> {
135 let g = self.base_fn();
136 let σ2 = g.variance.value();
137 let scale = g.scale();
138 Some(2.0*(-3.0/2.0).exp()/(σ2*scale))
139 }
140 }
141
142 // From above, norm bounds on the differnential can be calculated as achieved
143 // for f' at t=1, i.e., the bound is |f'(1)|.
144 // For g then |C/σ f'(1)|.
145 // It follows that the norm bounds on the differential are just the Lipschitz
146 // factors of the undifferentiated function, given how the latter is calculed above.
147
148 #[replace_float_literals(S::Type::cast_from(literal))]
149 impl<'b, S : Constant, const N : usize> NormBounded<L2>
150 for Differential<'b, Loc<S::Type, N>, Gaussian<S, N>> {
151 type FloatType = S::Type;
152
153 fn norm_bound(&self, _l2 : L2) -> S::Type {
154 self.base_fn().lipschitz_factor(L2).unwrap()
155 }
156 }
157
158 #[replace_float_literals(S::Type::cast_from(literal))]
159 impl<'b, 'a, S : Constant, const N : usize> NormBounded<L2>
160 for Differential<'b, Loc<S::Type, N>, &'a Gaussian<S, N>> {
161 type FloatType = S::Type;
162
163 fn norm_bound(&self, _l2 : L2) -> S::Type {
164 self.base_fn().lipschitz_factor(L2).unwrap()
165 }
166 }
167
118 168
119 #[replace_float_literals(S::Type::cast_from(literal))] 169 #[replace_float_literals(S::Type::cast_from(literal))]
120 impl<'a, S, const N : usize> Gaussian<S, N> 170 impl<'a, S, const N : usize> Gaussian<S, N>
121 where S : Constant { 171 where S : Constant {
122 172
202 252
203 253
204 /// This implements $g := χ\_{[-b, b]^n} \* (f χ\_{[-a, a]^n})$ where $a,b>0$ and $f$ is 254 /// This implements $g := χ\_{[-b, b]^n} \* (f χ\_{[-a, a]^n})$ where $a,b>0$ and $f$ is
205 /// a gaussian kernel on $ℝ^n$. For an expression for $g$, see Lemma 3.9 in the manuscript. 255 /// a gaussian kernel on $ℝ^n$. For an expression for $g$, see Lemma 3.9 in the manuscript.
206 #[replace_float_literals(F::cast_from(literal))] 256 #[replace_float_literals(F::cast_from(literal))]
207 impl<'a, F : Float, R, C, S, const N : usize> Apply<&'a Loc<F, N>> 257 impl<'a, F : Float, R, C, S, const N : usize> Mapping<Loc<F, N>>
208 for Convolution<CubeIndicator<R, N>, BasicCutGaussian<C, S, N>> 258 for Convolution<CubeIndicator<R, N>, BasicCutGaussian<C, S, N>>
209 where R : Constant<Type=F>, 259 where R : Constant<Type=F>,
210 C : Constant<Type=F>, 260 C : Constant<Type=F>,
211 S : Constant<Type=F> { 261 S : Constant<Type=F> {
212 262
213 type Output = F; 263 type Codomain = F;
214 264
215 #[inline] 265 #[inline]
216 fn apply(&self, y : &'a Loc<F, N>) -> F { 266 fn apply<I : Instance<Loc<F, N>>>(&self, y : I) -> F {
217 let Convolution(ref ind, 267 let Convolution(ref ind,
218 SupportProductFirst(ref cut, 268 SupportProductFirst(ref cut,
219 ref gaussian)) = self; 269 ref gaussian)) = self;
220 let a = cut.r.value(); 270 let a = cut.r.value();
221 let b = ind.r.value(); 271 let b = ind.r.value();
222 let σ = gaussian.variance.value().sqrt(); 272 let σ = gaussian.variance.value().sqrt();
223 let t = F::SQRT_2 * σ; 273 let t = F::SQRT_2 * σ;
224 let c = 0.5; // 1/(σ√(2π) * σ√(π/2) = 1/2 274 let c = 0.5; // 1/(σ√(2π) * σ√(π/2) = 1/2
225 275
226 // This is just a product of one-dimensional versions 276 // This is just a product of one-dimensional versions
227 y.product_map(|x| { 277 y.cow().product_map(|x| {
228 let c1 = -(a.min(b + x)); //(-a).max(-x-b); 278 let c1 = -(a.min(b + x)); //(-a).max(-x-b);
229 let c2 = a.min(b - x); 279 let c2 = a.min(b - x);
230 if c1 >= c2 { 280 if c1 >= c2 {
231 0.0 281 0.0
232 } else { 282 } else {
237 } 287 }
238 }) 288 })
239 } 289 }
240 } 290 }
241 291
242 impl<F : Float, R, C, S, const N : usize> Apply<Loc<F, N>>
243 for Convolution<CubeIndicator<R, N>, BasicCutGaussian<C, S, N>>
244 where R : Constant<Type=F>,
245 C : Constant<Type=F>,
246 S : Constant<Type=F> {
247
248 type Output = F;
249
250 #[inline]
251 fn apply(&self, y : Loc<F, N>) -> F {
252 self.apply(&y)
253 }
254 }
255
256 /// This implements the differential of $g := χ\_{[-b, b]^n} \* (f χ\_{[-a, a]^n})$ where $a,b>0$ 292 /// This implements the differential of $g := χ\_{[-b, b]^n} \* (f χ\_{[-a, a]^n})$ where $a,b>0$
257 /// and $f$ is a gaussian kernel on $ℝ^n$. For an expression for the value of $g$, from which the 293 /// and $f$ is a gaussian kernel on $ℝ^n$. For an expression for the value of $g$, from which the
258 /// derivative readily arises (at points of differentiability), see Lemma 3.9 in the manuscript. 294 /// derivative readily arises (at points of differentiability), see Lemma 3.9 in the manuscript.
259 #[replace_float_literals(F::cast_from(literal))] 295 #[replace_float_literals(F::cast_from(literal))]
260 impl<'a, F : Float, R, C, S, const N : usize> Differentiable<&'a Loc<F, N>> 296 impl<'a, F : Float, R, C, S, const N : usize> DifferentiableImpl<Loc<F, N>>
261 for Convolution<CubeIndicator<R, N>, BasicCutGaussian<C, S, N>> 297 for Convolution<CubeIndicator<R, N>, BasicCutGaussian<C, S, N>>
262 where R : Constant<Type=F>, 298 where R : Constant<Type=F>,
263 C : Constant<Type=F>, 299 C : Constant<Type=F>,
264 S : Constant<Type=F> { 300 S : Constant<Type=F> {
265 301
266 type Output = Loc<F, N>; 302 type Derivative = Loc<F, N>;
267 303
268 #[inline] 304 /// Although implemented, this function is not differentiable.
269 fn differential(&self, y : &'a Loc<F, N>) -> Loc<F, N> { 305 #[inline]
306 fn differential_impl<I : Instance<Loc<F, N>>>(&self, y0 : I) -> Loc<F, N> {
270 let Convolution(ref ind, 307 let Convolution(ref ind,
271 SupportProductFirst(ref cut, 308 SupportProductFirst(ref cut,
272 ref gaussian)) = self; 309 ref gaussian)) = self;
310 let y = y0.cow();
273 let a = cut.r.value(); 311 let a = cut.r.value();
274 let b = ind.r.value(); 312 let b = ind.r.value();
275 let σ = gaussian.variance.value().sqrt(); 313 let σ = gaussian.variance.value().sqrt();
276 let t = F::SQRT_2 * σ; 314 let t = F::SQRT_2 * σ;
277 let c = 0.5; // 1/(σ√(2π) * σ√(π/2) = 1/2 315 let c = 0.5; // 1/(σ√(2π) * σ√(π/2) = 1/2
278 let c_div_t = c / t; 316 let c_mul_erf_scale_div_t = c * F::FRAC_2_SQRT_PI / t;
279 317
280 // Calculate the values for all component functions of the 318 // Calculate the values for all component functions of the
281 // product. This is just the loop from apply above. 319 // product. This is just the loop from apply above.
282 let unscaled_vs = y.map(|x| { 320 let unscaled_vs = y.map(|x| {
283 let c1 = -(a.min(b + x)); //(-a).max(-x-b); 321 let c1 = -(a.min(b + x)); //(-a).max(-x-b);
290 debug_assert!(e2 >= e1); 328 debug_assert!(e2 >= e1);
291 c * (e2 - e1) 329 c * (e2 - e1)
292 } 330 }
293 }); 331 });
294 // This computes the gradient for each coordinate 332 // This computes the gradient for each coordinate
295 product_differential(y, &unscaled_vs, |x| { 333 product_differential(&*y, &unscaled_vs, |x| {
296 let c1 = -(a.min(b + x)); //(-a).max(-x-b); 334 let c1 = -(a.min(b + x)); //(-a).max(-x-b);
297 let c2 = a.min(b - x); 335 let c2 = a.min(b - x);
298 if c1 >= c2 { 336 if c1 >= c2 {
299 0.0 337 0.0
300 } else { 338 } else {
301 // erf'(z) = (2/√π)*exp(-z^2), and we get extra factor -1/(√2*σ) = -1/t 339 // erf'(z) = (2/√π)*exp(-z^2), and we get extra factor 1/(√2*σ) = -1/t
302 // from the chain rule (the minus comes from inside c_1 or c_2). 340 // from the chain rule (the minus comes from inside c_1 or c_2, and changes the
303 let de1 = (-(c1/t).powi(2)).exp(); 341 // order of de2 and de1 in the final calculation).
304 let de2 = (-(c2/t).powi(2)).exp(); 342 let de1 = if b + x < a {
305 c_div_t * (de1 - de2) 343 (-((b+x)/t).powi(2)).exp()
344 } else {
345 0.0
346 };
347 let de2 = if b - x < a {
348 (-((b-x)/t).powi(2)).exp()
349 } else {
350 0.0
351 };
352 c_mul_erf_scale_div_t * (de1 - de2)
306 } 353 }
307 }) 354 })
308 } 355 }
309 } 356 }
310 357
311 impl<F : Float, R, C, S, const N : usize> Differentiable<Loc<F, N>>
312 for Convolution<CubeIndicator<R, N>, BasicCutGaussian<C, S, N>>
313 where R : Constant<Type=F>,
314 C : Constant<Type=F>,
315 S : Constant<Type=F> {
316
317 type Output = Loc<F, N>;
318
319 #[inline]
320 fn differential(&self, y : Loc<F, N>) -> Loc<F, N> {
321 self.differential(&y)
322 }
323 }
324 358
325 #[replace_float_literals(F::cast_from(literal))] 359 #[replace_float_literals(F::cast_from(literal))]
326 impl<'a, F : Float, R, C, S, const N : usize> Lipschitz<L1> 360 impl<'a, F : Float, R, C, S, const N : usize> Lipschitz<L1>
327 for Convolution<CubeIndicator<R, N>, BasicCutGaussian<C, S, N>> 361 for Convolution<CubeIndicator<R, N>, BasicCutGaussian<C, S, N>>
328 where R : Constant<Type=F>, 362 where R : Constant<Type=F>,
376 let e0 = F::cast_from(erf((a.min(b) / t).as_())); 410 let e0 = F::cast_from(erf((a.min(b) / t).as_()));
377 Some(l1d * e0.powi(N as i32-1)) 411 Some(l1d * e0.powi(N as i32-1))
378 } 412 }
379 } 413 }
380 414
415 /*
381 impl<'a, F : Float, R, C, S, const N : usize> Lipschitz<L2> 416 impl<'a, F : Float, R, C, S, const N : usize> Lipschitz<L2>
382 for Convolution<CubeIndicator<R, N>, BasicCutGaussian<C, S, N>> 417 for Convolution<CubeIndicator<R, N>, BasicCutGaussian<C, S, N>>
383 where R : Constant<Type=F>, 418 where R : Constant<Type=F>,
384 C : Constant<Type=F>, 419 C : Constant<Type=F>,
385 S : Constant<Type=F> { 420 S : Constant<Type=F> {
387 #[inline] 422 #[inline]
388 fn lipschitz_factor(&self, L2 : L2) -> Option<Self::FloatType> { 423 fn lipschitz_factor(&self, L2 : L2) -> Option<Self::FloatType> {
389 self.lipschitz_factor(L1).map(|l1| l1 * <S::Type>::cast_from(N).sqrt()) 424 self.lipschitz_factor(L1).map(|l1| l1 * <S::Type>::cast_from(N).sqrt())
390 } 425 }
391 } 426 }
427 */
392 428
393 impl<F : Float, R, C, S, const N : usize> 429 impl<F : Float, R, C, S, const N : usize>
394 Convolution<CubeIndicator<R, N>, BasicCutGaussian<C, S, N>> 430 Convolution<CubeIndicator<R, N>, BasicCutGaussian<C, S, N>>
395 where R : Constant<Type=F>, 431 where R : Constant<Type=F>,
396 C : Constant<Type=F>, 432 C : Constant<Type=F>,

mercurial