| 37 /// -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, \\\\ |
| 38 /// 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}, \\\\ |
| 39 /// -\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. \\\\ |
| 40 /// \end{cases} |
45 /// \end{cases} |
| 41 /// $$ |
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 // $$ |
| 42 #[derive(Copy,Clone,Debug,Serialize,Eq)] |
72 #[derive(Copy,Clone,Debug,Serialize,Eq)] |
| 43 pub struct HatConv<S : Constant, const N : usize> { |
73 pub struct HatConv<S : Constant, const N : usize> { |
| 44 /// The parameter $σ$ of the kernel. |
74 /// The parameter $σ$ of the kernel. |
| 45 pub radius : S, |
75 pub radius : S, |
| 46 } |
76 } |
| 59 pub fn radius(&self) -> S::Type { |
89 pub fn radius(&self) -> S::Type { |
| 60 self.radius.value() |
90 self.radius.value() |
| 61 } |
91 } |
| 62 } |
92 } |
| 63 |
93 |
| 64 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> |
| 65 where S : Constant { |
95 where S : Constant { |
| 66 type Output = S::Type; |
96 type Codomain = S::Type; |
| 67 #[inline] |
97 |
| 68 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 { |
| 69 let σ = self.radius(); |
100 let σ = self.radius(); |
| 70 y.product_map(|x| { |
101 y.cow().product_map(|x| { |
| 71 self.value_1d_σ1(x / σ) / σ |
102 self.value_1d_σ1(x / σ) / σ |
| 72 }) |
103 }) |
| 73 } |
|
| 74 } |
|
| 75 |
|
| 76 impl<'a, S, const N : usize> Apply<Loc<S::Type, N>> for HatConv<S, N> |
|
| 77 where S : Constant { |
|
| 78 type Output = S::Type; |
|
| 79 #[inline] |
|
| 80 fn apply(&self, y : Loc<S::Type, N>) -> Self::Output { |
|
| 81 self.apply(&y) |
|
| 82 } |
104 } |
| 83 } |
105 } |
| 84 |
106 |
| 85 #[replace_float_literals(S::Type::cast_from(literal))] |
107 #[replace_float_literals(S::Type::cast_from(literal))] |
| 86 impl<S, const N : usize> Lipschitz<L1> for HatConv<S, N> |
108 impl<S, const N : usize> Lipschitz<L1> for HatConv<S, N> |
| 93 // = [ψ_1(x_1)-ψ_1(y_1)] ∏_{i=2}^N ψ_i(x_i) |
115 // = [ψ_1(x_1)-ψ_1(y_1)] ∏_{i=2}^N ψ_i(x_i) |
| 94 // + ψ_1(y_1)[ ∏_{i=2}^N ψ_i(x_i) - ∏_{i=2}^N ψ_i(y_i)] |
116 // + ψ_1(y_1)[ ∏_{i=2}^N ψ_i(x_i) - ∏_{i=2}^N ψ_i(y_i)] |
| 95 // = ∑_{j=1}^N [ψ_j(x_j)-ψ_j(y_j)]∏_{i > j} ψ_i(x_i) ∏_{i < j} ψ_i(y_i) |
117 // = ∑_{j=1}^N [ψ_j(x_j)-ψ_j(y_j)]∏_{i > j} ψ_i(x_i) ∏_{i < j} ψ_i(y_i) |
| 96 // Thus |
118 // Thus |
| 97 // |∏_{i=1}^N ψ_i(x_i) - ∏_{i=1}^N ψ_i(y_i)| |
119 // |∏_{i=1}^N ψ_i(x_i) - ∏_{i=1}^N ψ_i(y_i)| |
| 98 // ≤ ∑_{j=1}^N |ψ_j(x_j)-ψ_j(y_j)| ∏_{j ≠ i} \max_i |ψ_i| |
120 // ≤ ∑_{j=1}^N |ψ_j(x_j)-ψ_j(y_j)| ∏_{j ≠ i} \max_j |ψ_j| |
| 99 let σ = self.radius(); |
121 let σ = self.radius(); |
| 100 let l1d = self.lipschitz_1d_σ1() / (σ*σ); |
122 let l1d = self.lipschitz_1d_σ1() / (σ*σ); |
| 101 let m1d = self.value_1d_σ1(0.0) / σ; |
123 let m1d = self.value_1d_σ1(0.0) / σ; |
| 102 Some(l1d * m1d.powi(N as i32 - 1)) |
124 Some(l1d * m1d.powi(N as i32 - 1)) |
| 103 } |
125 } |
| 111 self.lipschitz_factor(L1).map(|l1| l1 * <S::Type>::cast_from(N).sqrt()) |
133 self.lipschitz_factor(L1).map(|l1| l1 * <S::Type>::cast_from(N).sqrt()) |
| 112 } |
134 } |
| 113 } |
135 } |
| 114 |
136 |
| 115 |
137 |
| 116 impl<'a, S, const N : usize> Differentiable<&'a Loc<S::Type, N>> for HatConv<S, N> |
138 impl<'a, S, const N : usize> DifferentiableImpl<Loc<S::Type, N>> for HatConv<S, N> |
| 117 where S : Constant { |
139 where S : Constant { |
| 118 type Output = Loc<S::Type, N>; |
140 type Derivative = Loc<S::Type, N>; |
| 119 #[inline] |
141 |
| 120 fn differential(&self, y : &'a Loc<S::Type, N>) -> Self::Output { |
142 #[inline] |
| |
143 fn differential_impl<I : Instance<Loc<S::Type, N>>>(&self, y0 : I) -> Self::Derivative { |
| |
144 let y = y0.cow(); |
| 121 let σ = self.radius(); |
145 let σ = self.radius(); |
| 122 let σ2 = σ * σ; |
146 let σ2 = σ * σ; |
| 123 let vs = y.map(|x| { |
147 let vs = y.map(|x| { |
| 124 self.value_1d_σ1(x / σ) / σ |
148 self.value_1d_σ1(x / σ) / σ |
| 125 }); |
149 }); |
| 126 product_differential(y, &vs, |x| { |
150 product_differential(&*y, &vs, |x| { |
| 127 self.diff_1d_σ1(x / σ) / σ2 |
151 self.diff_1d_σ1(x / σ) / σ2 |
| 128 }) |
152 }) |
| 129 } |
153 } |
| 130 } |
154 } |
| 131 |
155 |
| 132 impl<'a, S, const N : usize> Differentiable<Loc<S::Type, N>> for HatConv<S, N> |
156 |
| 133 where S : Constant { |
157 #[replace_float_literals(S::Type::cast_from(literal))] |
| 134 type Output = Loc<S::Type, N>; |
158 impl<'a, F : Float, S, const N : usize> Lipschitz<L2> |
| 135 #[inline] |
159 for Differential<'a, Loc<F, N>, HatConv<S, N>> |
| 136 fn differential(&self, y : Loc<S::Type, N>) -> Self::Output { |
160 where S : Constant<Type=F> { |
| 137 self.differential(&y) |
161 type FloatType = F; |
| 138 } |
162 |
| 139 } |
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 )) |
| |
173 } |
| |
174 } |
| |
175 |
| 140 |
176 |
| 141 #[replace_float_literals(S::Type::cast_from(literal))] |
177 #[replace_float_literals(S::Type::cast_from(literal))] |
| 142 impl<'a, F : Float, S, const N : usize> HatConv<S, N> |
178 impl<'a, F : Float, S, const N : usize> HatConv<S, N> |
| 143 where S : Constant<Type=F> { |
179 where S : Constant<Type=F> { |
| 144 /// Computes the value of the kernel for $n=1$ with $σ=1$. |
180 /// Computes the value of the kernel for $n=1$ with $σ=1$. |
| 171 #[inline] |
207 #[inline] |
| 172 fn lipschitz_1d_σ1(&self) -> F { |
208 fn lipschitz_1d_σ1(&self) -> F { |
| 173 // Maximal absolute differential achieved at ±0.5 by diff_1d_σ1 analysis |
209 // Maximal absolute differential achieved at ±0.5 by diff_1d_σ1 analysis |
| 174 2.0 |
210 2.0 |
| 175 } |
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 } |
| 176 } |
240 } |
| 177 |
241 |
| 178 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> |
| 179 where S : Constant { |
243 where S : Constant { |
| 180 #[inline] |
244 #[inline] |
| 233 self.bounds().upper() |
297 self.bounds().upper() |
| 234 } |
298 } |
| 235 } |
299 } |
| 236 |
300 |
| 237 #[replace_float_literals(F::cast_from(literal))] |
301 #[replace_float_literals(F::cast_from(literal))] |
| 238 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>> |
| 239 for Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
303 for Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
| 240 where R : Constant<Type=F>, |
304 where R : Constant<Type=F>, |
| 241 C : Constant<Type=F> { |
305 C : Constant<Type=F> { |
| 242 |
306 |
| 243 type Output = F; |
307 type Codomain = F; |
| 244 |
308 |
| 245 #[inline] |
309 #[inline] |
| 246 fn apply(&self, y : &'a Loc<F, N>) -> F { |
310 fn apply<I : Instance<Loc<F, N>>>(&self, y : I) -> F { |
| 247 let Convolution(ref ind, ref hatconv) = self; |
311 let Convolution(ref ind, ref hatconv) = self; |
| 248 let β = ind.r.value(); |
312 let β = ind.r.value(); |
| 249 let σ = hatconv.radius(); |
313 let σ = hatconv.radius(); |
| 250 |
314 |
| 251 // This is just a product of one-dimensional versions |
315 // This is just a product of one-dimensional versions |
| 252 y.product_map(|x| { |
316 y.cow().product_map(|x| { |
| 253 // With $u_σ(x) = u_1(x/σ)/σ$ the normalised hat convolution |
317 // With $u_σ(x) = u_1(x/σ)/σ$ the normalised hat convolution |
| 254 // we have |
318 // we have |
| 255 // $$ |
319 // $$ |
| 256 // [χ_{-β,β} * u_σ](x) |
320 // [χ_{-β,β} * u_σ](x) |
| 257 // = ∫_{x-β}^{x+β} u_σ(z) d z |
321 // = ∫_{x-β}^{x+β} u_σ(z) d z |
| 262 self.value_1d_σ1(x / σ, β / σ) |
326 self.value_1d_σ1(x / σ, β / σ) |
| 263 }) |
327 }) |
| 264 } |
328 } |
| 265 } |
329 } |
| 266 |
330 |
| 267 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>> |
| 268 for Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
333 for Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
| 269 where R : Constant<Type=F>, |
334 where R : Constant<Type=F>, |
| 270 C : Constant<Type=F> { |
335 C : Constant<Type=F> { |
| 271 |
336 |
| 272 type Output = F; |
337 type Derivative = Loc<F, N>; |
| 273 |
338 |
| 274 #[inline] |
339 #[inline] |
| 275 fn apply(&self, y : Loc<F, N>) -> F { |
340 fn differential_impl<I : Instance<Loc<F, N>>>(&self, y0 : I) -> Loc<F, N> { |
| 276 self.apply(&y) |
341 let y = y0.cow(); |
| 277 } |
|
| 278 } |
|
| 279 |
|
| 280 #[replace_float_literals(F::cast_from(literal))] |
|
| 281 impl<'a, F : Float, R, C, const N : usize> Differentiable<&'a Loc<F, N>> |
|
| 282 for Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
|
| 283 where R : Constant<Type=F>, |
|
| 284 C : Constant<Type=F> { |
|
| 285 |
|
| 286 type Output = Loc<F, N>; |
|
| 287 |
|
| 288 #[inline] |
|
| 289 fn differential(&self, y : &'a Loc<F, N>) -> Loc<F, N> { |
|
| 290 let Convolution(ref ind, ref hatconv) = self; |
342 let Convolution(ref ind, ref hatconv) = self; |
| 291 let β = ind.r.value(); |
343 let β = ind.r.value(); |
| 292 let σ = hatconv.radius(); |
344 let σ = hatconv.radius(); |
| 293 let σ2 = σ * σ; |
345 let σ2 = σ * σ; |
| 294 |
346 |
| 295 let vs = y.map(|x| { |
347 let vs = y.map(|x| { |
| 296 self.value_1d_σ1(x / σ, β / σ) |
348 self.value_1d_σ1(x / σ, β / σ) |
| 297 }); |
349 }); |
| 298 product_differential(y, &vs, |x| { |
350 product_differential(&*y, &vs, |x| { |
| 299 self.diff_1d_σ1(x / σ, β / σ) / σ2 |
351 self.diff_1d_σ1(x / σ, β / σ) / σ2 |
| 300 }) |
352 }) |
| 301 } |
353 } |
| 302 } |
354 } |
| 303 |
355 |
| 304 impl<'a, F : Float, R, C, const N : usize> Differentiable<Loc<F, N>> |
|
| 305 for Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
|
| 306 where R : Constant<Type=F>, |
|
| 307 C : Constant<Type=F> { |
|
| 308 |
|
| 309 type Output = Loc<F, N>; |
|
| 310 |
|
| 311 #[inline] |
|
| 312 fn differential(&self, y : Loc<F, N>) -> Loc<F, N> { |
|
| 313 self.differential(&y) |
|
| 314 } |
|
| 315 } |
|
| 316 |
356 |
| 317 /// Integrate $f$, whose support is $[c, d]$, on $[a, b]$. |
357 /// Integrate $f$, whose support is $[c, d]$, on $[a, b]$. |
| 318 /// If $b > d$, add $g()$ to the result. |
358 /// If $b > d$, add $g()$ to the result. |
| 319 #[inline] |
359 #[inline] |
| 320 #[replace_float_literals(F::cast_from(literal))] |
360 #[replace_float_literals(F::cast_from(literal))] |
| 413 ) |
453 ) |
| 414 ) |
454 ) |
| 415 } |
455 } |
| 416 } |
456 } |
| 417 |
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 */ |
| |
473 |
| 418 impl<F : Float, R, C, const N : usize> |
474 impl<F : Float, R, C, const N : usize> |
| 419 Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
475 Convolution<CubeIndicator<R, N>, HatConv<C, N>> |
| 420 where R : Constant<Type=F>, |
476 where R : Constant<Type=F>, |
| 421 C : Constant<Type=F> { |
477 C : Constant<Type=F> { |
| 422 |
478 |