36 /// -2 y^3-2 y^2+\frac{1}{3} & -\frac{1}{2}<y\leq 0, \\\\ |
42 /// -2 y^3-2 y^2+\frac{1}{3} & -\frac{1}{2}<y\leq 0, \\\\ |
37 /// 2 y^3-2 y^2+\frac{1}{3} & 0<y<\frac{1}{2}, \\\\ |
43 /// 2 y^3-2 y^2+\frac{1}{3} & 0<y<\frac{1}{2}, \\\\ |
38 /// -\frac{2}{3} (y-1)^3 & \frac{1}{2}\leq y<1. \\\\ |
44 /// -\frac{2}{3} (y-1)^3 & \frac{1}{2}\leq y<1. \\\\ |
39 /// \end{cases} |
45 /// \end{cases} |
40 /// $$ |
46 /// $$ |
|
47 // Hence |
|
48 // $$ |
|
49 // (h\*h)'(y) = |
|
50 // \begin{cases} |
|
51 // 2 (y+1)^2 & -1<y\leq -\frac{1}{2}, \\\\ |
|
52 // -6 y^2-4 y & -\frac{1}{2}<y\leq 0, \\\\ |
|
53 // 6 y^2-4 y & 0<y<\frac{1}{2}, \\\\ |
|
54 // -2 (y-1)^2 & \frac{1}{2}\leq y<1. \\\\ |
|
55 // \end{cases} |
|
56 // $$ |
|
57 // as well as |
|
58 // $$ |
|
59 // (h\*h)''(y) = |
|
60 // \begin{cases} |
|
61 // 4 (y+1) & -1<y\leq -\frac{1}{2}, \\\\ |
|
62 // -12 y-4 & -\frac{1}{2}<y\leq 0, \\\\ |
|
63 // 12 y-4 & 0<y<\frac{1}{2}, \\\\ |
|
64 // -4 (y-1) & \frac{1}{2}\leq y<1. \\\\ |
|
65 // \end{cases} |
|
66 // $$ |
|
67 // This is maximised at y=±1/2 with value 2, and minimised at y=0 with value -4. |
|
68 // Now observe that |
|
69 // $$ |
|
70 // [∇f(x\_1, …, x\_n)]_j = \frac{4}{σ} (h\*h)'(x\_j/σ) \prod\_{j ≠ i} \frac{4}{σ} (h\*h)(x\_i/σ) |
|
71 // $$ |
41 #[derive(Copy,Clone,Debug,Serialize,Eq)] |
72 #[derive(Copy,Clone,Debug,Serialize,Eq)] |
42 pub struct HatConv<S : Constant, const N : usize> { |
73 pub struct HatConv<S : Constant, const N : usize> { |
43 /// The parameter $σ$ of the kernel. |
74 /// The parameter $σ$ of the kernel. |
44 pub radius : S, |
75 pub radius : S, |
45 } |
76 } |
58 pub fn radius(&self) -> S::Type { |
89 pub fn radius(&self) -> S::Type { |
59 self.radius.value() |
90 self.radius.value() |
60 } |
91 } |
61 } |
92 } |
62 |
93 |
63 impl<'a, S, const N : usize> Apply<&'a Loc<S::Type, N>> for HatConv<S, N> |
94 impl<'a, S, const N : usize> Mapping<Loc<S::Type, N>> for HatConv<S, N> |
64 where S : Constant { |
95 where S : Constant { |
65 type Output = S::Type; |
96 type Codomain = S::Type; |
66 #[inline] |
97 |
67 fn apply(&self, y : &'a Loc<S::Type, N>) -> Self::Output { |
98 #[inline] |
|
99 fn apply<I : Instance<Loc<S::Type, N>>>(&self, y : I) -> Self::Codomain { |
68 let σ = self.radius(); |
100 let σ = self.radius(); |
69 y.product_map(|x| { |
101 y.cow().product_map(|x| { |
70 self.value_1d_σ1(x / σ) / σ |
102 self.value_1d_σ1(x / σ) / σ |
71 }) |
103 }) |
72 } |
104 } |
73 } |
105 } |
74 |
106 |
75 impl<'a, S, const N : usize> Apply<Loc<S::Type, N>> for HatConv<S, N> |
107 #[replace_float_literals(S::Type::cast_from(literal))] |
|
108 impl<S, const N : usize> Lipschitz<L1> for HatConv<S, N> |
76 where S : Constant { |
109 where S : Constant { |
77 type Output = S::Type; |
110 type FloatType = S::Type; |
78 #[inline] |
111 #[inline] |
79 fn apply(&self, y : Loc<S::Type, N>) -> Self::Output { |
112 fn lipschitz_factor(&self, L1 : L1) -> Option<Self::FloatType> { |
80 self.apply(&y) |
113 // For any ψ_i, we have |
|
114 // ∏_{i=1}^N ψ_i(x_i) - ∏_{i=1}^N ψ_i(y_i) |
|
115 // = [ψ_1(x_1)-ψ_1(y_1)] ∏_{i=2}^N ψ_i(x_i) |
|
116 // + ψ_1(y_1)[ ∏_{i=2}^N ψ_i(x_i) - ∏_{i=2}^N ψ_i(y_i)] |
|
117 // = ∑_{j=1}^N [ψ_j(x_j)-ψ_j(y_j)]∏_{i > j} ψ_i(x_i) ∏_{i < j} ψ_i(y_i) |
|
118 // Thus |
|
119 // |∏_{i=1}^N ψ_i(x_i) - ∏_{i=1}^N ψ_i(y_i)| |
|
120 // ≤ ∑_{j=1}^N |ψ_j(x_j)-ψ_j(y_j)| ∏_{j ≠ i} \max_j |ψ_j| |
|
121 let σ = self.radius(); |
|
122 let l1d = self.lipschitz_1d_σ1() / (σ*σ); |
|
123 let m1d = self.value_1d_σ1(0.0) / σ; |
|
124 Some(l1d * m1d.powi(N as i32 - 1)) |
|
125 } |
|
126 } |
|
127 |
|
128 impl<S, const N : usize> Lipschitz<L2> for HatConv<S, N> |
|
129 where S : Constant { |
|
130 type FloatType = S::Type; |
|
131 #[inline] |
|
132 fn lipschitz_factor(&self, L2 : L2) -> Option<Self::FloatType> { |
|
133 self.lipschitz_factor(L1).map(|l1| l1 * <S::Type>::cast_from(N).sqrt()) |
|
134 } |
|
135 } |
|
136 |
|
137 |
|
138 impl<'a, S, const N : usize> DifferentiableImpl<Loc<S::Type, N>> for HatConv<S, N> |
|
139 where S : Constant { |
|
140 type Derivative = Loc<S::Type, N>; |
|
141 |
|
142 #[inline] |
|
143 fn differential_impl<I : Instance<Loc<S::Type, N>>>(&self, y0 : I) -> Self::Derivative { |
|
144 let y = y0.cow(); |
|
145 let σ = self.radius(); |
|
146 let σ2 = σ * σ; |
|
147 let vs = y.map(|x| { |
|
148 self.value_1d_σ1(x / σ) / σ |
|
149 }); |
|
150 product_differential(&*y, &vs, |x| { |
|
151 self.diff_1d_σ1(x / σ) / σ2 |
|
152 }) |
|
153 } |
|
154 } |
|
155 |
|
156 |
|
157 #[replace_float_literals(S::Type::cast_from(literal))] |
|
158 impl<'a, F : Float, S, const N : usize> Lipschitz<L2> |
|
159 for Differential<'a, Loc<F, N>, HatConv<S, N>> |
|
160 where S : Constant<Type=F> { |
|
161 type FloatType = F; |
|
162 |
|
163 #[inline] |
|
164 fn lipschitz_factor(&self, _l2 : L2) -> Option<F> { |
|
165 let h = self.base_fn(); |
|
166 let σ = h.radius(); |
|
167 Some(product_differential_lipschitz_factor::<F, N>( |
|
168 h.value_1d_σ1(0.0) / σ, |
|
169 h.lipschitz_1d_σ1() / (σ*σ), |
|
170 h.maxabsdiff_1d_σ1() / (σ*σ), |
|
171 h.lipschitz_diff_1d_σ1() / (σ*σ), |
|
172 )) |
81 } |
173 } |
82 } |
174 } |
83 |
175 |
84 |
176 |
85 #[replace_float_literals(S::Type::cast_from(literal))] |
177 #[replace_float_literals(S::Type::cast_from(literal))] |
95 - (8.0/3.0) * (y - 1.0).powi(3) |
187 - (8.0/3.0) * (y - 1.0).powi(3) |
96 } else /* 0 ≤ y ≤ 0.5 */ { |
188 } else /* 0 ≤ y ≤ 0.5 */ { |
97 (4.0/3.0) + 8.0 * y * y * (y - 1.0) |
189 (4.0/3.0) + 8.0 * y * y * (y - 1.0) |
98 } |
190 } |
99 } |
191 } |
|
192 |
|
193 /// Computes the differential of the kernel for $n=1$ with $σ=1$. |
|
194 #[inline] |
|
195 fn diff_1d_σ1(&self, x : F) -> F { |
|
196 let y = x.abs(); |
|
197 if y >= 1.0 { |
|
198 0.0 |
|
199 } else if y > 0.5 { |
|
200 - 8.0 * (y - 1.0).powi(2) |
|
201 } else /* 0 ≤ y ≤ 0.5 */ { |
|
202 (24.0 * y - 16.0) * y |
|
203 } |
|
204 } |
|
205 |
|
206 /// Computes the Lipschitz factor of the kernel for $n=1$ with $σ=1$. |
|
207 #[inline] |
|
208 fn lipschitz_1d_σ1(&self) -> F { |
|
209 // Maximal absolute differential achieved at ±0.5 by diff_1d_σ1 analysis |
|
210 2.0 |
|
211 } |
|
212 |
|
213 /// Computes the maximum absolute differential of the kernel for $n=1$ with $σ=1$. |
|
214 #[inline] |
|
215 fn maxabsdiff_1d_σ1(&self) -> F { |
|
216 // Maximal absolute differential achieved at ±0.5 by diff_1d_σ1 analysis |
|
217 2.0 |
|
218 } |
|
219 |
|
220 /// Computes the second differential of the kernel for $n=1$ with $σ=1$. |
|
221 #[inline] |
|
222 #[allow(dead_code)] |
|
223 fn diff2_1d_σ1(&self, x : F) -> F { |
|
224 let y = x.abs(); |
|
225 if y >= 1.0 { |
|
226 0.0 |
|
227 } else if y > 0.5 { |
|
228 - 16.0 * (y - 1.0) |
|
229 } else /* 0 ≤ y ≤ 0.5 */ { |
|
230 48.0 * y - 16.0 |
|
231 } |
|
232 } |
|
233 |
|
234 /// Computes the differential of the kernel for $n=1$ with $σ=1$. |
|
235 #[inline] |
|
236 fn lipschitz_diff_1d_σ1(&self) -> F { |
|
237 // Maximal absolute second differential achieved at 0 by diff2_1d_σ1 analysis |
|
238 16.0 |
|
239 } |
100 } |
240 } |
101 |
241 |
102 impl<'a, S, const N : usize> Support<S::Type, N> for HatConv<S, N> |
242 impl<'a, S, const N : usize> Support<S::Type, N> for HatConv<S, N> |
103 where S : Constant { |
243 where S : Constant { |
104 #[inline] |
244 #[inline] |
157 self.bounds().upper() |
297 self.bounds().upper() |
158 } |
298 } |
159 } |
299 } |
160 |
300 |
161 #[replace_float_literals(F::cast_from(literal))] |
301 #[replace_float_literals(F::cast_from(literal))] |
162 impl<'a, F : Float, R, C, const N : usize> Apply<&'a Loc<F, N>> |
302 impl<'a, F : Float, R, C, const N : usize> Mapping<Loc<F, N>> |
163 for Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
303 for Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
164 where R : Constant<Type=F>, |
304 where R : Constant<Type=F>, |
165 C : Constant<Type=F> { |
305 C : Constant<Type=F> { |
166 |
306 |
167 type Output = F; |
307 type Codomain = F; |
168 |
308 |
169 #[inline] |
309 #[inline] |
170 fn apply(&self, y : &'a Loc<F, N>) -> F { |
310 fn apply<I : Instance<Loc<F, N>>>(&self, y : I) -> F { |
171 let Convolution(ref ind, ref hatconv) = self; |
311 let Convolution(ref ind, ref hatconv) = self; |
172 let β = ind.r.value(); |
312 let β = ind.r.value(); |
173 let σ = hatconv.radius(); |
313 let σ = hatconv.radius(); |
174 |
314 |
175 // This is just a product of one-dimensional versions |
315 // This is just a product of one-dimensional versions |
176 y.product_map(|x| { |
316 y.cow().product_map(|x| { |
177 // With $u_σ(x) = u_1(x/σ)/σ$ the normalised hat convolution |
317 // With $u_σ(x) = u_1(x/σ)/σ$ the normalised hat convolution |
178 // we have |
318 // we have |
179 // $$ |
319 // $$ |
180 // [χ_{-β,β} * u_σ](x) |
320 // [χ_{-β,β} * u_σ](x) |
181 // = ∫_{x-β}^{x+β} u_σ(z) d z |
321 // = ∫_{x-β}^{x+β} u_σ(z) d z |
186 self.value_1d_σ1(x / σ, β / σ) |
326 self.value_1d_σ1(x / σ, β / σ) |
187 }) |
327 }) |
188 } |
328 } |
189 } |
329 } |
190 |
330 |
191 impl<'a, F : Float, R, C, const N : usize> Apply<Loc<F, N>> |
331 #[replace_float_literals(F::cast_from(literal))] |
|
332 impl<'a, F : Float, R, C, const N : usize> DifferentiableImpl<Loc<F, N>> |
192 for Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
333 for Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
193 where R : Constant<Type=F>, |
334 where R : Constant<Type=F>, |
194 C : Constant<Type=F> { |
335 C : Constant<Type=F> { |
195 |
336 |
196 type Output = F; |
337 type Derivative = Loc<F, N>; |
197 |
338 |
198 #[inline] |
339 #[inline] |
199 fn apply(&self, y : Loc<F, N>) -> F { |
340 fn differential_impl<I : Instance<Loc<F, N>>>(&self, y0 : I) -> Loc<F, N> { |
200 self.apply(&y) |
341 let y = y0.cow(); |
201 } |
342 let Convolution(ref ind, ref hatconv) = self; |
202 } |
343 let β = ind.r.value(); |
203 |
344 let σ = hatconv.radius(); |
|
345 let σ2 = σ * σ; |
|
346 |
|
347 let vs = y.map(|x| { |
|
348 self.value_1d_σ1(x / σ, β / σ) |
|
349 }); |
|
350 product_differential(&*y, &vs, |x| { |
|
351 self.diff_1d_σ1(x / σ, β / σ) / σ2 |
|
352 }) |
|
353 } |
|
354 } |
|
355 |
|
356 |
|
357 /// Integrate $f$, whose support is $[c, d]$, on $[a, b]$. |
|
358 /// If $b > d$, add $g()$ to the result. |
|
359 #[inline] |
|
360 #[replace_float_literals(F::cast_from(literal))] |
|
361 fn i<F: Float>(a : F, b : F, c : F, d : F, f : impl Fn(F) -> F, |
|
362 g : impl Fn() -> F) -> F { |
|
363 if b < c { |
|
364 0.0 |
|
365 } else if b <= d { |
|
366 if a <= c { |
|
367 f(b) - f(c) |
|
368 } else { |
|
369 f(b) - f(a) |
|
370 } |
|
371 } else /* b > d */ { |
|
372 g() + if a <= c { |
|
373 f(d) - f(c) |
|
374 } else if a < d { |
|
375 f(d) - f(a) |
|
376 } else { |
|
377 0.0 |
|
378 } |
|
379 } |
|
380 } |
204 |
381 |
205 #[replace_float_literals(F::cast_from(literal))] |
382 #[replace_float_literals(F::cast_from(literal))] |
206 impl<F : Float, C, R, const N : usize> Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
383 impl<F : Float, C, R, const N : usize> Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
207 where R : Constant<Type=F>, |
384 where R : Constant<Type=F>, |
208 C : Constant<Type=F> { |
385 C : Constant<Type=F> { |
|
386 |
|
387 /// Calculates the value of the 1D hat convolution further convolved by a interval indicator. |
|
388 /// As both functions are piecewise polynomials, this is implemented by explicit integral over |
|
389 /// all subintervals of polynomiality of the cube indicator, using easily formed |
|
390 /// antiderivatives. |
209 #[inline] |
391 #[inline] |
210 pub fn value_1d_σ1(&self, x : F, β : F) -> F { |
392 pub fn value_1d_σ1(&self, x : F, β : F) -> F { |
211 // The integration interval |
393 // The integration interval |
212 let a = x - β; |
394 let a = x - β; |
213 let b = x + β; |
395 let b = x + β; |
216 fn pow4<F : Float>(x : F) -> F { |
398 fn pow4<F : Float>(x : F) -> F { |
217 let y = x * x; |
399 let y = x * x; |
218 y * y |
400 y * y |
219 } |
401 } |
220 |
402 |
221 /// Integrate $f$, whose support is $[c, d]$, on $[a, b]$. |
|
222 /// If $b > d$, add $g()$ to the result. |
|
223 #[inline] |
|
224 fn i<F: Float>(a : F, b : F, c : F, d : F, f : impl Fn(F) -> F, |
|
225 g : impl Fn() -> F) -> F { |
|
226 if b < c { |
|
227 0.0 |
|
228 } else if b <= d { |
|
229 if a <= c { |
|
230 f(b) - f(c) |
|
231 } else { |
|
232 f(b) - f(a) |
|
233 } |
|
234 } else /* b > d */ { |
|
235 g() + if a <= c { |
|
236 f(d) - f(c) |
|
237 } else if a < d { |
|
238 f(d) - f(a) |
|
239 } else { |
|
240 0.0 |
|
241 } |
|
242 } |
|
243 } |
|
244 |
|
245 // Observe the factor 1/6 at the front from the antiderivatives below. |
403 // Observe the factor 1/6 at the front from the antiderivatives below. |
246 // The factor 4 is from normalisation of the original function. |
404 // The factor 4 is from normalisation of the original function. |
247 (4.0/6.0) * i(a, b, -1.0, -0.5, |
405 (4.0/6.0) * i(a, b, -1.0, -0.5, |
248 // (2/3) (y+1)^3 on -1 < y ≤ - 1/2 |
406 // (2/3) (y+1)^3 on -1 < y ≤ -1/2 |
249 // The antiderivative is (2/12)(y+1)^4 = (1/6)(y+1)^4 |
407 // The antiderivative is (2/12)(y+1)^4 = (1/6)(y+1)^4 |
250 |y| pow4(y+1.0), |
408 |y| pow4(y+1.0), |
251 || i(a, b, -0.5, 0.0, |
409 || i(a, b, -0.5, 0.0, |
252 // -2 y^3 - 2 y^2 + 1/3 on -1/2 < y ≤ 0 |
410 // -2 y^3 - 2 y^2 + 1/3 on -1/2 < y ≤ 0 |
253 // The antiderivative is -1/2 y^4 - 2/3 y^3 + 1/3 y |
411 // The antiderivative is -1/2 y^4 - 2/3 y^3 + 1/3 y |
264 ) |
422 ) |
265 ) |
423 ) |
266 ) |
424 ) |
267 ) |
425 ) |
268 } |
426 } |
269 } |
427 |
|
428 /// Calculates the derivative of the 1D hat convolution further convolved by a interval |
|
429 /// indicator. The implementation is similar to [`Self::value_1d_σ1`], using the fact that |
|
430 /// $(θ * ψ)' = θ * ψ'$. |
|
431 #[inline] |
|
432 pub fn diff_1d_σ1(&self, x : F, β : F) -> F { |
|
433 // The integration interval |
|
434 let a = x - β; |
|
435 let b = x + β; |
|
436 |
|
437 // The factor 4 is from normalisation of the original function. |
|
438 4.0 * i(a, b, -1.0, -0.5, |
|
439 // (2/3) (y+1)^3 on -1 < y ≤ -1/2 |
|
440 |y| (2.0/3.0) * (y + 1.0).powi(3), |
|
441 || i(a, b, -0.5, 0.0, |
|
442 // -2 y^3 - 2 y^2 + 1/3 on -1/2 < y ≤ 0 |
|
443 |y| -2.0*(y + 1.0) * y * y + (1.0/3.0), |
|
444 || i(a, b, 0.0, 0.5, |
|
445 // 2 y^3 - 2 y^2 + 1/3 on 0 < y < 1/2 |
|
446 |y| 2.0*(y - 1.0) * y * y + (1.0/3.0), |
|
447 || i(a, b, 0.5, 1.0, |
|
448 // -(2/3) (y-1)^3 on 1/2 < y ≤ 1 |
|
449 |y| -(2.0/3.0) * (y - 1.0).powi(3), |
|
450 || 0.0 |
|
451 ) |
|
452 ) |
|
453 ) |
|
454 ) |
|
455 } |
|
456 } |
|
457 |
|
458 /* |
|
459 impl<'a, F : Float, R, C, const N : usize> Lipschitz<L2> |
|
460 for Differential<Loc<F, N>, Convolution<CubeIndicator<R, N>, HatConv<C, N>>> |
|
461 where R : Constant<Type=F>, |
|
462 C : Constant<Type=F> { |
|
463 |
|
464 type FloatType = F; |
|
465 |
|
466 #[inline] |
|
467 fn lipschitz_factor(&self, _l2 : L2) -> Option<F> { |
|
468 dbg!("unimplemented"); |
|
469 None |
|
470 } |
|
471 } |
|
472 */ |
270 |
473 |
271 impl<F : Float, R, C, const N : usize> |
474 impl<F : Float, R, C, const N : usize> |
272 Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
475 Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
273 where R : Constant<Type=F>, |
476 where R : Constant<Type=F>, |
274 C : Constant<Type=F> { |
477 C : Constant<Type=F> { |