|
1 /*! |
|
2 Simple disrete gradient operators |
|
3 */ |
|
4 use numeric_literals::replace_float_literals; |
|
5 use nalgebra::{ |
|
6 DVector, Matrix, U1, Storage, StorageMut, Dyn |
|
7 }; |
|
8 use crate::types::Float; |
|
9 use crate::instance::Instance; |
|
10 use crate::linops::{Mapping, Linear, BoundedLinear, Adjointable, GEMV}; |
|
11 use crate::norms::{Norm, L2}; |
|
12 |
|
13 #[derive(Copy, Clone, Debug)] |
|
14 /// Forward differences with Neumann boundary conditions |
|
15 pub struct ForwardNeumann; |
|
16 |
|
17 #[derive(Copy, Clone, Debug)] |
|
18 /// Forward differences with Dirichlet boundary conditions |
|
19 pub struct ForwardDirichlet; |
|
20 |
|
21 #[derive(Copy, Clone, Debug)] |
|
22 /// Backward differences with Dirichlet boundary conditions |
|
23 pub struct BackwardDirichlet; |
|
24 |
|
25 #[derive(Copy, Clone, Debug)] |
|
26 /// Backward differences with Neumann boundary conditions |
|
27 pub struct BackwardNeumann; |
|
28 |
|
29 /// Finite differences gradient |
|
30 pub struct Grad< |
|
31 F : Float + nalgebra::RealField, |
|
32 B : Discretisation<F>, |
|
33 const N : usize |
|
34 > { |
|
35 dims : [usize; N], |
|
36 h : F, // may be negative to implement adjoints! |
|
37 discretisation : B, |
|
38 } |
|
39 |
|
40 |
|
41 /// Finite differences divergence |
|
42 pub struct Div< |
|
43 F : Float + nalgebra::RealField, |
|
44 B : Discretisation<F>, |
|
45 const N : usize |
|
46 > { |
|
47 dims : [usize; N], |
|
48 h : F, // may be negative to implement adjoints! |
|
49 discretisation : B, |
|
50 } |
|
51 |
|
52 /// Internal: classification of a point in a 1D discretisation |
|
53 pub enum DiscretisationOrInterior { |
|
54 /// center, forward |
|
55 LeftBoundary(usize, usize), |
|
56 /// center, backward |
|
57 RightBoundary(usize, usize), |
|
58 /// center, (backward, forward) |
|
59 Interior(usize, (usize, usize)), |
|
60 } |
|
61 |
|
62 use DiscretisationOrInterior::*; |
|
63 |
|
64 /// Trait for different discretisations |
|
65 pub trait Discretisation<F : Float + nalgebra::RealField> : Copy { |
|
66 /// Opposite discretisation, appropriate for adjoints with negated cell width. |
|
67 type Opposite : Discretisation<F>; |
|
68 |
|
69 /// Add to appropiate index of `v` (as determined by `b`) the appropriate difference |
|
70 /// of `x` with cell width `h`. |
|
71 fn add_diff_mut<SMut, S>( |
|
72 &self, |
|
73 v : &mut Matrix<F, Dyn, U1, SMut>, |
|
74 x : &Matrix<F, Dyn, U1, S>, |
|
75 α : F, |
|
76 b : DiscretisationOrInterior, |
|
77 ) where |
|
78 SMut : StorageMut<F, Dyn, U1>, |
|
79 S : Storage<F, Dyn, U1>; |
|
80 |
|
81 /// Give the opposite discretisation, appropriate for adjoints with negated `h`. |
|
82 fn opposite(&self) -> Self::Opposite; |
|
83 |
|
84 /// Bound for the corresponding operator norm. |
|
85 #[replace_float_literals(F::cast_from(literal))] |
|
86 fn opnorm_bound(&self, h : F) -> F { |
|
87 // See: Chambolle, “An Algorithm for Total Variation Minimization and Applications”. |
|
88 // Ok for forward and backward differences. |
|
89 // |
|
90 // Fuck nalgebra for polluting everything with its own shit. |
|
91 num_traits::Float::sqrt(8.0) / h |
|
92 } |
|
93 } |
|
94 |
|
95 impl<F : Float + nalgebra::RealField> Discretisation<F> for ForwardNeumann { |
|
96 type Opposite = BackwardDirichlet; |
|
97 |
|
98 #[inline] |
|
99 fn add_diff_mut<SMut, S>( |
|
100 &self, |
|
101 v : &mut Matrix<F, Dyn, U1, SMut>, |
|
102 x : &Matrix<F, Dyn, U1, S>, |
|
103 α : F, |
|
104 b : DiscretisationOrInterior, |
|
105 ) where |
|
106 SMut : StorageMut<F, Dyn, U1>, |
|
107 S : Storage<F, Dyn, U1> |
|
108 { |
|
109 match b { |
|
110 Interior(c, (_, f)) | LeftBoundary(c, f) => { v[c] += (x[f] - x[c]) * α }, |
|
111 RightBoundary(_c, _b) => { }, |
|
112 } |
|
113 } |
|
114 |
|
115 #[inline] |
|
116 fn opposite(&self) -> Self::Opposite { |
|
117 BackwardDirichlet |
|
118 } |
|
119 } |
|
120 |
|
121 impl<F : Float + nalgebra::RealField> Discretisation<F> for BackwardNeumann { |
|
122 type Opposite = ForwardDirichlet; |
|
123 |
|
124 #[inline] |
|
125 fn add_diff_mut<SMut, S>( |
|
126 &self, |
|
127 v : &mut Matrix<F, Dyn, U1, SMut>, |
|
128 x : &Matrix<F, Dyn, U1, S>, |
|
129 α : F, |
|
130 b : DiscretisationOrInterior, |
|
131 ) where |
|
132 SMut : StorageMut<F, Dyn, U1>, |
|
133 S : Storage<F, Dyn, U1> |
|
134 { |
|
135 match b { |
|
136 Interior(c, (b, _)) | RightBoundary(c, b) => { v[c] += (x[c] - x[b]) * α }, |
|
137 LeftBoundary(_c, _f) => { }, |
|
138 } |
|
139 } |
|
140 |
|
141 #[inline] |
|
142 fn opposite(&self) -> Self::Opposite { |
|
143 ForwardDirichlet |
|
144 } |
|
145 } |
|
146 |
|
147 impl<F : Float + nalgebra::RealField> Discretisation<F> for BackwardDirichlet { |
|
148 type Opposite = ForwardNeumann; |
|
149 |
|
150 #[inline] |
|
151 fn add_diff_mut<SMut, S>( |
|
152 &self, |
|
153 v : &mut Matrix<F, Dyn, U1, SMut>, |
|
154 x : &Matrix<F, Dyn, U1, S>, |
|
155 α : F, |
|
156 b : DiscretisationOrInterior, |
|
157 ) where |
|
158 SMut : StorageMut<F, Dyn, U1>, |
|
159 S : Storage<F, Dyn, U1> |
|
160 { |
|
161 match b { |
|
162 Interior(c, (b, _f)) => { v[c] += (x[c] - x[b]) * α }, |
|
163 LeftBoundary(c, _f) => { v[c] += x[c] * α }, |
|
164 RightBoundary(c, b) => { v[c] -= x[b] * α }, |
|
165 } |
|
166 } |
|
167 |
|
168 #[inline] |
|
169 fn opposite(&self) -> Self::Opposite { |
|
170 ForwardNeumann |
|
171 } |
|
172 } |
|
173 |
|
174 impl<F : Float + nalgebra::RealField> Discretisation<F> for ForwardDirichlet { |
|
175 type Opposite = BackwardNeumann; |
|
176 |
|
177 #[inline] |
|
178 fn add_diff_mut<SMut, S>( |
|
179 &self, |
|
180 v : &mut Matrix<F, Dyn, U1, SMut>, |
|
181 x : &Matrix<F, Dyn, U1, S>, |
|
182 α : F, |
|
183 b : DiscretisationOrInterior, |
|
184 ) where |
|
185 SMut : StorageMut<F, Dyn, U1>, |
|
186 S : Storage<F, Dyn, U1> |
|
187 { |
|
188 match b { |
|
189 Interior(c, (_b, f)) => { v[c] += (x[f] - x[c]) * α }, |
|
190 LeftBoundary(c, f) => { v[c] += x[f] * α }, |
|
191 RightBoundary(c, _b) => { v[c] -= x[c] * α }, |
|
192 } |
|
193 } |
|
194 |
|
195 #[inline] |
|
196 fn opposite(&self) -> Self::Opposite { |
|
197 BackwardNeumann |
|
198 } |
|
199 } |
|
200 |
|
201 struct Iter<'a, const N : usize> { |
|
202 /// Dimensions |
|
203 dims : &'a [usize; N], |
|
204 /// Dimension along which to calculate differences |
|
205 d : usize, |
|
206 /// Stride along coordinate d |
|
207 d_stride : usize, |
|
208 /// Cartesian indices |
|
209 i : [usize; N], |
|
210 /// Linear index |
|
211 k : usize, |
|
212 /// Maximal linear index |
|
213 len : usize |
|
214 } |
|
215 |
|
216 impl<'a, const N : usize> Iter<'a, N> { |
|
217 fn new(dims : &'a [usize; N], d : usize) -> Self { |
|
218 let d_stride = dims[0..d].iter().product::<usize>(); |
|
219 let len = dims.iter().product::<usize>(); |
|
220 Iter{ dims, d, d_stride, i : [0; N], k : 0, len } |
|
221 } |
|
222 } |
|
223 |
|
224 impl<'a, const N : usize> Iterator for Iter<'a, N> { |
|
225 type Item = DiscretisationOrInterior; |
|
226 fn next(&mut self) -> Option<Self::Item> { |
|
227 let res = if self.k >= self.len { |
|
228 None |
|
229 } else { |
|
230 let cartesian_idx = self.i[self.d]; |
|
231 let cartesian_max = self.dims[self.d]; |
|
232 let k = self.k; |
|
233 |
|
234 if cartesian_idx == 0 { |
|
235 Some(LeftBoundary(k, k + self.d_stride)) |
|
236 } else if cartesian_idx + 1 >= cartesian_max { |
|
237 Some(RightBoundary(k, k - self.d_stride)) |
|
238 } else { |
|
239 Some(Interior(k, (k - self.d_stride, k + self.d_stride))) |
|
240 } |
|
241 }; |
|
242 self.k += 1; |
|
243 for j in 0..N { |
|
244 if self.i[j] + 1 < self.dims[j] { |
|
245 self.i[j] += 1; |
|
246 break |
|
247 } |
|
248 self.i[j] = 0 |
|
249 } |
|
250 res |
|
251 } |
|
252 } |
|
253 |
|
254 impl<F, B, const N : usize> Mapping<DVector<F>> |
|
255 for Grad<F, B, N> |
|
256 where |
|
257 B : Discretisation<F>, |
|
258 F : Float + nalgebra::RealField, |
|
259 { |
|
260 type Codomain = DVector<F>; |
|
261 fn apply<I : Instance<DVector<F>>>(&self, i : I) -> DVector<F> { |
|
262 let mut y = DVector::zeros(N * self.len()); |
|
263 self.apply_add(&mut y, i); |
|
264 y |
|
265 } |
|
266 } |
|
267 |
|
268 #[replace_float_literals(F::cast_from(literal))] |
|
269 impl<F, B, const N : usize> GEMV<F, DVector<F>> |
|
270 for Grad<F, B, N> |
|
271 where |
|
272 B : Discretisation<F>, |
|
273 F : Float + nalgebra::RealField, |
|
274 { |
|
275 fn gemv<I : Instance<DVector<F>>>( |
|
276 &self, y : &mut DVector<F>, α : F, i : I, β : F |
|
277 ) { |
|
278 if β == 0.0 { |
|
279 y.as_mut_slice().iter_mut().for_each(|x| *x = 0.0); |
|
280 } else if β != 1.0 { |
|
281 //*y *= β; |
|
282 y.as_mut_slice().iter_mut().for_each(|x| *x *= β); |
|
283 } |
|
284 let h = self.h; |
|
285 let m = self.len(); |
|
286 i.eval(|x| { |
|
287 assert_eq!(x.len(), m); |
|
288 for d in 0..N { |
|
289 let mut v = y.generic_view_mut((d*m, 0), (Dyn(m), U1)); |
|
290 for b in Iter::new(&self.dims, d) { |
|
291 self.discretisation.add_diff_mut(&mut v, x, α/h, b) |
|
292 } |
|
293 } |
|
294 }) |
|
295 } |
|
296 } |
|
297 |
|
298 impl<F, B, const N : usize> Mapping<DVector<F>> |
|
299 for Div<F, B, N> |
|
300 where |
|
301 B : Discretisation<F>, |
|
302 F : Float + nalgebra::RealField, |
|
303 { |
|
304 type Codomain = DVector<F>; |
|
305 fn apply<I : Instance<DVector<F>>>(&self, i : I) -> DVector<F> { |
|
306 let mut y = DVector::zeros(self.len()); |
|
307 self.apply_add(&mut y, i); |
|
308 y |
|
309 } |
|
310 } |
|
311 |
|
312 #[replace_float_literals(F::cast_from(literal))] |
|
313 impl<F, B, const N : usize> GEMV<F, DVector<F>> |
|
314 for Div<F, B, N> |
|
315 where |
|
316 B : Discretisation<F>, |
|
317 F : Float + nalgebra::RealField, |
|
318 { |
|
319 fn gemv<I : Instance<DVector<F>>>( |
|
320 &self, y : &mut DVector<F>, α : F, i : I, β : F |
|
321 ) { |
|
322 if β == 0.0 { |
|
323 y.as_mut_slice().iter_mut().for_each(|x| *x = 0.0); |
|
324 } else if β != 1.0 { |
|
325 //*y *= β; |
|
326 y.as_mut_slice().iter_mut().for_each(|x| *x *= β); |
|
327 } |
|
328 let h = self.h; |
|
329 let m = self.len(); |
|
330 i.eval(|x| { |
|
331 assert_eq!(x.len(), N * m); |
|
332 for d in 0..N { |
|
333 let v = x.generic_view((d*m, 0), (Dyn(m), U1)); |
|
334 for b in Iter::new(&self.dims, d) { |
|
335 self.discretisation.add_diff_mut(y, &v, α/h, b) |
|
336 } |
|
337 } |
|
338 }) |
|
339 } |
|
340 } |
|
341 |
|
342 impl<F, B, const N : usize> Grad<F, B, N> |
|
343 where |
|
344 B : Discretisation<F>, |
|
345 F : Float + nalgebra::RealField |
|
346 { |
|
347 /// Creates a new discrete gradient operator for the vector `u`, verifying dimensions. |
|
348 pub fn new_for(u : &DVector<F>, h : F, dims : [usize; N], discretisation : B) |
|
349 -> Option<Self> |
|
350 { |
|
351 if u.len() == dims.iter().product::<usize>() { |
|
352 Some(Grad { dims, h, discretisation } ) |
|
353 } else { |
|
354 None |
|
355 } |
|
356 } |
|
357 |
|
358 fn len(&self) -> usize { |
|
359 self.dims.iter().product::<usize>() |
|
360 } |
|
361 } |
|
362 |
|
363 |
|
364 impl<F, B, const N : usize> Div<F, B, N> |
|
365 where |
|
366 B : Discretisation<F>, |
|
367 F : Float + nalgebra::RealField |
|
368 { |
|
369 /// Creates a new discrete gradient operator for the vector `u`, verifying dimensions. |
|
370 pub fn new_for(u : &DVector<F>, h : F, dims : [usize; N], discretisation : B) |
|
371 -> Option<Self> |
|
372 { |
|
373 if u.len() == dims.iter().product::<usize>() * N { |
|
374 Some(Div { dims, h, discretisation } ) |
|
375 } else { |
|
376 None |
|
377 } |
|
378 } |
|
379 |
|
380 fn len(&self) -> usize { |
|
381 self.dims.iter().product::<usize>() |
|
382 } |
|
383 } |
|
384 |
|
385 impl<F, B, const N : usize> Linear<DVector<F>> |
|
386 for Grad<F, B, N> |
|
387 where |
|
388 B : Discretisation<F>, |
|
389 F : Float + nalgebra::RealField, |
|
390 { |
|
391 } |
|
392 |
|
393 impl<F, B, const N : usize> Linear<DVector<F>> |
|
394 for Div<F, B, N> |
|
395 where |
|
396 B : Discretisation<F>, |
|
397 F : Float + nalgebra::RealField, |
|
398 { |
|
399 } |
|
400 |
|
401 impl<F, B, const N : usize> BoundedLinear<DVector<F>, L2, L2, F> |
|
402 for Grad<F, B, N> |
|
403 where |
|
404 B : Discretisation<F>, |
|
405 F : Float + nalgebra::RealField, |
|
406 DVector<F> : Norm<F, L2>, |
|
407 { |
|
408 fn opnorm_bound(&self, _ : L2, _ : L2) -> F { |
|
409 // Fuck nalgebra. |
|
410 self.discretisation.opnorm_bound(num_traits::Float::abs(self.h)) |
|
411 } |
|
412 } |
|
413 |
|
414 |
|
415 impl<F, B, const N : usize> BoundedLinear<DVector<F>, L2, L2, F> |
|
416 for Div<F, B, N> |
|
417 where |
|
418 B : Discretisation<F>, |
|
419 F : Float + nalgebra::RealField, |
|
420 DVector<F> : Norm<F, L2>, |
|
421 { |
|
422 fn opnorm_bound(&self, _ : L2, _ : L2) -> F { |
|
423 // Fuck nalgebra. |
|
424 self.discretisation.opnorm_bound(num_traits::Float::abs(self.h)) |
|
425 } |
|
426 } |
|
427 |
|
428 impl<F, B, const N : usize> |
|
429 Adjointable<DVector<F>, DVector<F>> |
|
430 for Grad<F, B, N> |
|
431 where |
|
432 B : Discretisation<F>, |
|
433 F : Float + nalgebra::RealField, |
|
434 { |
|
435 type AdjointCodomain = DVector<F>; |
|
436 type Adjoint<'a> = Div<F, B::Opposite, N> where Self : 'a; |
|
437 |
|
438 /// Form the adjoint operator of `self`. |
|
439 fn adjoint(&self) -> Self::Adjoint<'_> { |
|
440 Div { |
|
441 dims : self.dims, |
|
442 h : -self.h, |
|
443 discretisation : self.discretisation.opposite(), |
|
444 } |
|
445 } |
|
446 } |
|
447 |
|
448 |
|
449 impl<F, B, const N : usize> |
|
450 Adjointable<DVector<F>, DVector<F>> |
|
451 for Div<F, B, N> |
|
452 where |
|
453 B : Discretisation<F>, |
|
454 F : Float + nalgebra::RealField, |
|
455 { |
|
456 type AdjointCodomain = DVector<F>; |
|
457 type Adjoint<'a> = Grad<F, B::Opposite, N> where Self : 'a; |
|
458 |
|
459 /// Form the adjoint operator of `self`. |
|
460 fn adjoint(&self) -> Self::Adjoint<'_> { |
|
461 Grad { |
|
462 dims : self.dims, |
|
463 h : -self.h, |
|
464 discretisation : self.discretisation.opposite(), |
|
465 } |
|
466 } |
|
467 } |
|
468 |
|
469 #[cfg(test)] |
|
470 mod tests { |
|
471 use super::*; |
|
472 |
|
473 #[test] |
|
474 fn grad_adjoint() { |
|
475 let im = DVector::from( (0..9).map(|t| t as f64).collect::<Vec<_>>()); |
|
476 let v = DVector::from( (0..18).map(|t| t as f64).collect::<Vec<_>>()); |
|
477 |
|
478 let grad = Grad::new_for(&im, 1.0, [3, 3], ForwardNeumann).unwrap(); |
|
479 let a = grad.apply(&im).dot(&v); |
|
480 let b = grad.adjoint().apply(&v).dot(&im); |
|
481 assert_eq!(a, b); |
|
482 |
|
483 let grad = Grad::new_for(&im, 1.0, [3, 3], ForwardDirichlet).unwrap(); |
|
484 let a = grad.apply(&im).dot(&v); |
|
485 let b = grad.adjoint().apply(&v).dot(&im); |
|
486 assert_eq!(a, b); |
|
487 |
|
488 } |
|
489 } |