Sun, 26 Jan 2025 11:29:57 -0500
Remove initial transport adaptation—it is not needed, after all
35 | 1 | /*! |
2 | Sensor grid forward model | |
3 | */ | |
4 | ||
5 | use numeric_literals::replace_float_literals; | |
6 | use nalgebra::base::{ | |
7 | DMatrix, | |
8 | DVector | |
9 | }; | |
10 | use std::iter::Zip; | |
11 | use std::ops::RangeFrom; | |
12 | ||
13 | pub use alg_tools::linops::*; | |
14 | use alg_tools::norms::{ | |
15 | L1, Linfinity, L2, Norm | |
16 | }; | |
17 | use alg_tools::bisection_tree::*; | |
18 | use alg_tools::mapping::{ | |
19 | RealMapping, | |
20 | DifferentiableMapping | |
21 | }; | |
22 | use alg_tools::lingrid::*; | |
23 | use alg_tools::iter::{MapX, Mappable}; | |
24 | use alg_tools::nalgebra_support::ToNalgebraRealField; | |
25 | use alg_tools::tabledump::write_csv; | |
26 | use alg_tools::error::DynError; | |
27 | use alg_tools::maputil::map2; | |
28 | use alg_tools::instance::Instance; | |
29 | ||
30 | use crate::types::*; | |
31 | use crate::measures::{DiscreteMeasure, Radon}; | |
32 | use crate::seminorms::{ | |
33 | ConvolutionOp, | |
34 | SimpleConvolutionKernel, | |
35 | }; | |
36 | use crate::kernels::{ | |
37 | Convolution, | |
38 | AutoConvolution, | |
39 | BoundedBy, | |
40 | }; | |
41 | use crate::types::L2Squared; | |
42 | use crate::transport::TransportLipschitz; | |
43 | use crate::preadjoint_helper::PreadjointHelper; | |
44 | use super::{ | |
45 | ForwardModel, | |
46 | LipschitzValues, | |
47 | AdjointProductBoundedBy | |
48 | }; | |
49 | use crate::frank_wolfe::FindimQuadraticModel; | |
50 | ||
51 | type RNDM<F, const N : usize> = DiscreteMeasure<Loc<F,N>, F>; | |
52 | ||
53 | pub type ShiftedSensor<F, S, P, const N : usize> = Shift<Convolution<S, P>, F, N>; | |
54 | ||
55 | /// Trait for physical convolution models. Has blanket implementation for all cases. | |
56 | pub trait Spread<F : Float, const N : usize> | |
57 | : 'static + Clone + Support<F, N> + RealMapping<F, N> + Bounded<F> {} | |
58 | ||
59 | impl<F, T, const N : usize> Spread<F, N> for T | |
60 | where F : Float, | |
61 | T : 'static + Clone + Support<F, N> + Bounded<F> + RealMapping<F, N> {} | |
62 | ||
63 | /// Trait for compactly supported sensors. Has blanket implementation for all cases. | |
64 | pub trait Sensor<F : Float, const N : usize> : Spread<F, N> + Norm<F, L1> + Norm<F, Linfinity> {} | |
65 | ||
66 | impl<F, T, const N : usize> Sensor<F, N> for T | |
67 | where F : Float, | |
68 | T : Spread<F, N> + Norm<F, L1> + Norm<F, Linfinity> {} | |
69 | ||
70 | ||
71 | pub trait SensorGridBT<F, S, P, const N : usize> : | |
72 | Clone + BTImpl<F, N, Data=usize, Agg=Bounds<F>> | |
73 | where F : Float, | |
74 | S : Sensor<F, N>, | |
75 | P : Spread<F, N> {} | |
76 | ||
77 | impl<F, S, P, T, const N : usize> | |
78 | SensorGridBT<F, S, P, N> | |
79 | for T | |
80 | where T : Clone + BTImpl<F, N, Data=usize, Agg=Bounds<F>>, | |
81 | F : Float, | |
82 | S : Sensor<F, N>, | |
83 | P : Spread<F, N> {} | |
84 | ||
85 | // We need type alias bounds to access associated types | |
86 | #[allow(type_alias_bounds)] | |
87 | pub type SensorGridBTFN<F, S, P, BT : SensorGridBT<F, S, P, N>, const N : usize> | |
88 | = BTFN<F, SensorGridSupportGenerator<F, S, P, N>, BT, N>; | |
89 | ||
90 | /// Sensor grid forward model | |
91 | #[derive(Clone)] | |
92 | pub struct SensorGrid<F, S, P, BT, const N : usize> | |
93 | where F : Float, | |
94 | S : Sensor<F, N>, | |
95 | P : Spread<F, N>, | |
96 | Convolution<S, P> : Spread<F, N>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
97 | BT : SensorGridBT<F, S, P, N>, |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
98 | { |
35 | 99 | domain : Cube<F, N>, |
100 | sensor_count : [usize; N], | |
101 | sensor : S, | |
102 | spread : P, | |
103 | base_sensor : Convolution<S, P>, | |
104 | bt : BT, | |
105 | } | |
106 | ||
107 | impl<F, S, P, BT, const N : usize> SensorGrid<F, S, P, BT, N> | |
108 | where F : Float, | |
109 | BT : SensorGridBT<F, S, P, N>, | |
110 | S : Sensor<F, N>, | |
111 | P : Spread<F, N>, | |
112 | Convolution<S, P> : Spread<F, N> + LocalAnalysis<F, BT::Agg, N>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
113 | { |
35 | 114 | |
115 | /// Create a new sensor grid. | |
116 | /// | |
117 | /// The parameter `depth` indicates the search depth of the created [`BT`]s | |
118 | /// for the adjoint values. | |
119 | pub fn new( | |
120 | domain : Cube<F, N>, | |
121 | sensor_count : [usize; N], | |
122 | sensor : S, | |
123 | spread : P, | |
124 | depth : BT::Depth | |
125 | ) -> Self { | |
126 | let base_sensor = Convolution(sensor.clone(), spread.clone()); | |
127 | let bt = BT::new(domain, depth); | |
128 | let mut sensorgrid = SensorGrid { | |
129 | domain, | |
130 | sensor_count, | |
131 | sensor, | |
132 | spread, | |
133 | base_sensor, | |
134 | bt, | |
135 | }; | |
136 | ||
137 | for (x, id) in sensorgrid.grid().into_iter().zip(0usize..) { | |
138 | let s = sensorgrid.shifted_sensor(x); | |
139 | sensorgrid.bt.insert(id, &s); | |
140 | } | |
141 | ||
142 | sensorgrid | |
143 | } | |
144 | } | |
145 | ||
146 | ||
147 | impl<F, S, P, BT, const N : usize> SensorGrid<F, S, P, BT, N> | |
148 | where F : Float, | |
149 | BT : SensorGridBT<F, S, P, N>, | |
150 | S : Sensor<F, N>, | |
151 | P : Spread<F, N>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
152 | Convolution<S, P> : Spread<F, N> |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
153 | { |
35 | 154 | |
155 | /// Return the grid of sensor locations. | |
156 | pub fn grid(&self) -> LinGrid<F, N> { | |
157 | lingrid_centered(&self.domain, &self.sensor_count) | |
158 | } | |
159 | ||
160 | /// Returns the number of sensors (number of grid points) | |
161 | pub fn n_sensors(&self) -> usize { | |
162 | self.sensor_count.iter().product() | |
163 | } | |
164 | ||
165 | /// Constructs a sensor shifted by `x`. | |
166 | #[inline] | |
167 | fn shifted_sensor(&self, x : Loc<F, N>) -> ShiftedSensor<F, S, P, N> { | |
168 | self.base_sensor.clone().shift(x) | |
169 | } | |
170 | ||
171 | #[inline] | |
172 | fn _zero_observable(&self) -> DVector<F> { | |
173 | DVector::zeros(self.n_sensors()) | |
174 | } | |
175 | ||
176 | /// Returns the maximum number of overlapping sensors $N_\psi$. | |
177 | pub fn max_overlapping(&self) -> F { | |
178 | let w = self.base_sensor.support_hint().width(); | |
179 | let d = map2(self.domain.width(), &self.sensor_count, |wi, &i| wi/F::cast_from(i)); | |
180 | w.iter() | |
181 | .zip(d.iter()) | |
182 | .map(|(&wi, &di)| (wi/di).ceil()) | |
183 | .reduce(F::mul) | |
184 | .unwrap() | |
185 | } | |
186 | } | |
187 | ||
188 | impl<F, S, P, BT, const N : usize> Mapping<RNDM<F, N>> for SensorGrid<F, S, P, BT, N> | |
189 | where | |
190 | F : Float, | |
191 | BT : SensorGridBT<F, S, P, N>, | |
192 | S : Sensor<F, N>, | |
193 | P : Spread<F, N>, | |
194 | Convolution<S, P> : Spread<F, N>, | |
195 | { | |
196 | ||
197 | type Codomain = DVector<F>; | |
198 | ||
199 | #[inline] | |
200 | fn apply<I : Instance<RNDM<F, N>>>(&self, μ : I) -> DVector<F> { | |
201 | let mut y = self._zero_observable(); | |
202 | self.apply_add(&mut y, μ); | |
203 | y | |
204 | } | |
205 | } | |
206 | ||
207 | ||
208 | impl<F, S, P, BT, const N : usize> Linear<RNDM<F, N>> for SensorGrid<F, S, P, BT, N> | |
209 | where | |
210 | F : Float, | |
211 | BT : SensorGridBT<F, S, P, N>, | |
212 | S : Sensor<F, N>, | |
213 | P : Spread<F, N>, | |
214 | Convolution<S, P> : Spread<F, N>, | |
215 | { } | |
216 | ||
217 | ||
218 | #[replace_float_literals(F::cast_from(literal))] | |
219 | impl<F, S, P, BT, const N : usize> GEMV<F, RNDM<F, N>, DVector<F>> for SensorGrid<F, S, P, BT, N> | |
220 | where F : Float, | |
221 | BT : SensorGridBT<F, S, P, N>, | |
222 | S : Sensor<F, N>, | |
223 | P : Spread<F, N>, | |
224 | Convolution<S, P> : Spread<F, N>, | |
225 | { | |
226 | ||
227 | fn gemv<I : Instance<RNDM<F, N>>>( | |
228 | &self, y : &mut DVector<F>, α : F, μ : I, β : F | |
229 | ) { | |
230 | let grid = self.grid(); | |
231 | if β == 0.0 { | |
232 | y.fill(0.0) | |
233 | } else if β != 1.0 { | |
234 | *y *= β; // Need to multiply first, as we have to be able to add to y. | |
235 | } | |
236 | if α == 1.0 { | |
237 | self.apply_add(y, μ) | |
238 | } else { | |
239 | for δ in μ.ref_instance() { | |
240 | for &d in self.bt.iter_at(&δ.x) { | |
241 | let sensor = self.shifted_sensor(grid.entry_linear_unchecked(d)); | |
242 | y[d] += sensor.apply(&δ.x) * (α * δ.α); | |
243 | } | |
244 | } | |
245 | } | |
246 | } | |
247 | ||
248 | fn apply_add<I : Instance<RNDM<F, N>>>( | |
249 | &self, y : &mut DVector<F>, μ : I | |
250 | ) { | |
251 | let grid = self.grid(); | |
252 | for δ in μ.ref_instance() { | |
253 | for &d in self.bt.iter_at(&δ.x) { | |
254 | let sensor = self.shifted_sensor(grid.entry_linear_unchecked(d)); | |
255 | y[d] += sensor.apply(&δ.x) * δ.α; | |
256 | } | |
257 | } | |
258 | } | |
259 | ||
260 | } | |
261 | ||
262 | ||
263 | impl<F, S, P, BT, const N : usize> | |
264 | BoundedLinear<RNDM<F, N>, Radon, L2, F> | |
265 | for SensorGrid<F, S, P, BT, N> | |
266 | where F : Float, | |
267 | BT : SensorGridBT<F, S, P, N, Agg=Bounds<F>>, | |
268 | S : Sensor<F, N>, | |
269 | P : Spread<F, N>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
270 | Convolution<S, P> : Spread<F, N> + LocalAnalysis<F, BT::Agg, N> |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
271 | { |
35 | 272 | |
273 | /// An estimate on the operator norm in $𝕃(ℳ(Ω); ℝ^n)$ with $ℳ(Ω)$ equipped | |
274 | /// with the Radon norm, and $ℝ^n$ with the Euclidean norm. | |
275 | fn opnorm_bound(&self, _ : Radon, _ : L2) -> F { | |
276 | // With {x_i}_{i=1}^n the grid centres and φ the kernel, we have | |
277 | // |Aμ|_2 = sup_{|z|_2 ≤ 1} ⟨z,Αμ⟩ = sup_{|z|_2 ≤ 1} ⟨A^*z|μ⟩ | |
278 | // ≤ sup_{|z|_2 ≤ 1} |A^*z|_∞ |μ|_ℳ | |
279 | // = sup_{|z|_2 ≤ 1} |∑ φ(· - x_i)z_i|_∞ |μ|_ℳ | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
280 | // ≤ sup_{|z|_2 ≤ 1} |φ(y)| ∑_{i:th sensor active at y}|z_i| |μ|_ℳ |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
281 | // where the supremum of |∑ φ(· - x_i)z_i|_∞ is reached at y |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
282 | // ≤ sup_{|z|_2 ≤ 1} |φ|_∞ √N_ψ |z|_2 |μ|_ℳ |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
283 | // where N_ψ is the maximum number of sensors that overlap, and |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
284 | // |z|_2 is restricted to the active sensors. |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
285 | // = |φ|_∞ √N_ψ |μ|_ℳ. |
35 | 286 | // Hence |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
287 | let n = self.max_overlapping(); |
35 | 288 | self.base_sensor.bounds().uniform() * n.sqrt() |
289 | } | |
290 | } | |
291 | ||
292 | type SensorGridPreadjoint<'a, A, F, const N : usize> = PreadjointHelper<'a, A, RNDM<F,N>>; | |
293 | ||
294 | ||
295 | impl<F, S, P, BT, const N : usize> | |
296 | Preadjointable<RNDM<F, N>, DVector<F>> | |
297 | for SensorGrid<F, S, P, BT, N> | |
298 | where F : Float, | |
299 | BT : SensorGridBT<F, S, P, N>, | |
300 | S : Sensor<F, N>, | |
301 | P : Spread<F, N>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
302 | Convolution<S, P> : Spread<F, N> + LocalAnalysis<F, BT::Agg, N> |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
303 | { |
35 | 304 | type PreadjointCodomain = BTFN<F, SensorGridSupportGenerator<F, S, P, N>, BT, N>; |
305 | type Preadjoint<'a> = SensorGridPreadjoint<'a, Self, F, N> where Self : 'a; | |
306 | ||
307 | fn preadjoint(&self) -> Self::Preadjoint<'_> { | |
308 | PreadjointHelper::new(self) | |
309 | } | |
310 | } | |
311 | ||
312 | #[replace_float_literals(F::cast_from(literal))] | |
313 | impl<'a, F, S, P, BT, const N : usize> LipschitzValues | |
314 | for SensorGridPreadjoint<'a, SensorGrid<F, S, P, BT, N>, F, N> | |
315 | where F : Float, | |
316 | BT : SensorGridBT<F, S, P, N>, | |
317 | S : Sensor<F, N>, | |
318 | P : Spread<F, N>, | |
319 | Convolution<S, P> : Spread<F, N> + Lipschitz<L2, FloatType=F> + DifferentiableMapping<Loc<F,N>> + LocalAnalysis<F, BT::Agg, N>, | |
320 | for<'b> <Convolution<S, P> as DifferentiableMapping<Loc<F,N>>>::Differential<'b> : Lipschitz<L2, FloatType=F>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
321 | { |
35 | 322 | |
323 | type FloatType = F; | |
324 | ||
325 | fn value_unit_lipschitz_factor(&self) -> Option<F> { | |
326 | // The Lipschitz factor of the sensors has to be scaled by the square root of twice | |
327 | // the number of overlapping sensors at a single ponit, as Lipschitz estimates involve | |
328 | // two points. | |
329 | let fw = self.forward_op; | |
330 | let n = fw.max_overlapping(); | |
331 | fw.base_sensor.lipschitz_factor(L2).map(|l| (2.0 * n).sqrt() * l) | |
332 | } | |
333 | ||
334 | fn value_diff_unit_lipschitz_factor(&self) -> Option<F> { | |
335 | // The Lipschitz factor of the sensors has to be scaled by the square root of twice | |
336 | // the number of overlapping sensors at a single ponit, as Lipschitz estimates involve | |
337 | // two points. | |
338 | let fw = self.forward_op; | |
339 | let n = fw.max_overlapping(); | |
340 | fw.base_sensor.diff_ref().lipschitz_factor(L2).map(|l| (2.0 * n).sqrt() * l) | |
341 | } | |
342 | } | |
343 | ||
344 | #[derive(Clone,Debug)] | |
345 | pub struct SensorGridSupportGenerator<F, S, P, const N : usize> | |
346 | where F : Float, | |
347 | S : Sensor<F, N>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
348 | P : Spread<F, N> |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
349 | { |
35 | 350 | base_sensor : Convolution<S, P>, |
351 | grid : LinGrid<F, N>, | |
352 | weights : DVector<F> | |
353 | } | |
354 | ||
355 | impl<F, S, P, const N : usize> SensorGridSupportGenerator<F, S, P, N> | |
356 | where F : Float, | |
357 | S : Sensor<F, N>, | |
358 | P : Spread<F, N>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
359 | Convolution<S, P> : Spread<F, N> |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
360 | { |
35 | 361 | |
362 | #[inline] | |
363 | fn construct_sensor(&self, id : usize, w : F) -> Weighted<ShiftedSensor<F, S, P, N>, F> { | |
364 | let x = self.grid.entry_linear_unchecked(id); | |
365 | self.base_sensor.clone().shift(x).weigh(w) | |
366 | } | |
367 | ||
368 | #[inline] | |
369 | fn construct_sensor_and_id<'a>(&'a self, (id, w) : (usize, &'a F)) | |
370 | -> (usize, Weighted<ShiftedSensor<F, S, P, N>, F>) { | |
371 | (id.into(), self.construct_sensor(id, *w)) | |
372 | } | |
373 | } | |
374 | ||
375 | impl<F, S, P, const N : usize> SupportGenerator<F, N> | |
376 | for SensorGridSupportGenerator<F, S, P, N> | |
377 | where F : Float, | |
378 | S : Sensor<F, N>, | |
379 | P : Spread<F, N>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
380 | Convolution<S, P> : Spread<F, N> |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
381 | { |
35 | 382 | type Id = usize; |
383 | type SupportType = Weighted<ShiftedSensor<F, S, P, N>, F>; | |
384 | type AllDataIter<'a> = MapX<'a, Zip<RangeFrom<usize>, | |
385 | std::slice::Iter<'a, F>>, | |
386 | Self, | |
387 | (Self::Id, Self::SupportType)> | |
388 | where Self : 'a; | |
389 | ||
390 | #[inline] | |
391 | fn support_for(&self, d : Self::Id) -> Self::SupportType { | |
392 | self.construct_sensor(d, self.weights[d]) | |
393 | } | |
394 | ||
395 | #[inline] | |
396 | fn support_count(&self) -> usize { | |
397 | self.weights.len() | |
398 | } | |
399 | ||
400 | #[inline] | |
401 | fn all_data(&self) -> Self::AllDataIter<'_> { | |
402 | (0..).zip(self.weights.as_slice().iter()).mapX(self, Self::construct_sensor_and_id) | |
403 | } | |
404 | } | |
405 | ||
406 | impl<F, S, P, BT, const N : usize> ForwardModel<DiscreteMeasure<Loc<F, N>, F>, F> | |
407 | for SensorGrid<F, S, P, BT, N> | |
408 | where F : Float + ToNalgebraRealField<MixedType=F> + nalgebra::RealField, | |
409 | BT : SensorGridBT<F, S, P, N>, | |
410 | S : Sensor<F, N>, | |
411 | P : Spread<F, N>, | |
412 | Convolution<S, P> : Spread<F, N> + LocalAnalysis<F, BT::Agg, N>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
413 | { |
35 | 414 | type Observable = DVector<F>; |
415 | ||
416 | fn write_observable(&self, b : &Self::Observable, prefix : String) -> DynError { | |
417 | let it = self.grid().into_iter().zip(b.iter()).map(|(x, &v)| (x, v)); | |
418 | write_csv(it, prefix + ".txt") | |
419 | } | |
420 | ||
421 | #[inline] | |
422 | fn zero_observable(&self) -> Self::Observable { | |
423 | self._zero_observable() | |
424 | } | |
425 | } | |
426 | ||
427 | impl<F, S, P, BT, const N : usize> FindimQuadraticModel<Loc<F, N>, F> | |
428 | for SensorGrid<F, S, P, BT, N> | |
429 | where F : Float + ToNalgebraRealField<MixedType=F> + nalgebra::RealField, | |
430 | BT : SensorGridBT<F, S, P, N>, | |
431 | S : Sensor<F, N>, | |
432 | P : Spread<F, N>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
433 | Convolution<S, P> : Spread<F, N> + LocalAnalysis<F, BT::Agg, N> |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
434 | { |
35 | 435 | |
436 | fn findim_quadratic_model( | |
437 | &self, | |
438 | μ : &DiscreteMeasure<Loc<F, N>, F>, | |
439 | b : &Self::Observable | |
440 | ) -> (DMatrix<F::MixedType>, DVector<F::MixedType>) { | |
441 | assert_eq!(b.len(), self.n_sensors()); | |
442 | let mut mA = DMatrix::zeros(self.n_sensors(), μ.len()); | |
443 | let grid = self.grid(); | |
444 | for (mut mAcol, δ) in mA.column_iter_mut().zip(μ.iter_spikes()) { | |
445 | for &d in self.bt.iter_at(&δ.x) { | |
446 | let sensor = self.shifted_sensor(grid.entry_linear_unchecked(d)); | |
447 | mAcol[d] += sensor.apply(&δ.x); | |
448 | } | |
449 | } | |
450 | let mAt = mA.transpose(); | |
451 | (&mAt * mA, &mAt * b) | |
452 | } | |
453 | } | |
454 | ||
455 | /// Implements the calculation a factor $L$ such that $A_*A ≤ L 𝒟$ for $A$ the forward model | |
456 | /// and $𝒟$ a seminorm of suitable form. | |
457 | /// | |
458 | /// **This assumes (but does not check) that the sensors are not overlapping.** | |
459 | #[replace_float_literals(F::cast_from(literal))] | |
460 | impl<F, BT, S, P, K, const N : usize> | |
461 | AdjointProductBoundedBy<RNDM<F, N>, ConvolutionOp<F, K, BT, N>> | |
462 | for SensorGrid<F, S, P, BT, N> | |
463 | where F : Float + nalgebra::RealField + ToNalgebraRealField, | |
464 | BT : SensorGridBT<F, S, P, N>, | |
465 | S : Sensor<F, N>, | |
466 | P : Spread<F, N>, | |
467 | Convolution<S, P> : Spread<F, N>, | |
468 | K : SimpleConvolutionKernel<F, N>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
469 | AutoConvolution<P> : BoundedBy<F, K> |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
470 | { |
35 | 471 | |
472 | type FloatType = F; | |
473 | ||
474 | fn adjoint_product_bound(&self, seminorm : &ConvolutionOp<F, K, BT, N>) -> Option<F> { | |
475 | // Sensors should not take on negative values to allow | |
476 | // A_*A to be upper bounded by a simple convolution of `spread`. | |
477 | if self.sensor.bounds().lower() < 0.0 { | |
478 | return None | |
479 | } | |
480 | ||
481 | // Calculate the factor $L_1$ for betwee $ℱ[ψ * ψ] ≤ L_1 ℱ[ρ]$ for $ψ$ the base spread | |
482 | // and $ρ$ the kernel of the seminorm. | |
483 | let l1 = AutoConvolution(self.spread.clone()).bounding_factor(seminorm.kernel())?; | |
484 | ||
485 | // Calculate the factor for transitioning from $A_*A$ to `AutoConvolution<P>`, where A | |
486 | // consists of several `Convolution<S, P>` for the physical model `P` and the sensor `S`. | |
487 | let l0 = self.sensor.norm(Linfinity) * self.sensor.norm(L1); | |
488 | ||
489 | // The final transition factor is: | |
490 | Some(l0 * l1) | |
491 | } | |
492 | } | |
493 | ||
494 | #[replace_float_literals(F::cast_from(literal))] | |
495 | impl<F, BT, S, P, const N : usize> TransportLipschitz<L2Squared> | |
496 | for SensorGrid<F, S, P, BT, N> | |
497 | where F : Float + ToNalgebraRealField, | |
498 | BT : SensorGridBT<F, S, P, N>, | |
499 | S : Sensor<F, N>, | |
500 | P : Spread<F, N>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
501 | Convolution<S, P> : Spread<F, N> + Lipschitz<L2, FloatType = F> |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
502 | { |
35 | 503 | type FloatType = F; |
504 | ||
505 | fn transport_lipschitz_factor(&self, L2Squared : L2Squared) -> Self::FloatType { | |
506 | // We estimate the factor by N_ψL^2, where L is the 2-norm Lipschitz factor of | |
507 | // the base sensor (sensor * base_spread), and N_ψ the maximum overlap. | |
508 | // The factors two comes from Lipschitz estimates having two possible | |
509 | // points of overlap. | |
510 | let l = self.base_sensor.lipschitz_factor(L2).unwrap(); | |
511 | 2.0 * self.max_overlapping() * l.powi(2) | |
512 | } | |
513 | } | |
514 | ||
515 | ||
516 | macro_rules! make_sensorgridsupportgenerator_scalarop_rhs { | |
517 | ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => { | |
518 | impl<F, S, P, const N : usize> | |
519 | std::ops::$trait_assign<F> | |
520 | for SensorGridSupportGenerator<F, S, P, N> | |
521 | where F : Float, | |
522 | S : Sensor<F, N>, | |
523 | P : Spread<F, N>, | |
524 | Convolution<S, P> : Spread<F, N> { | |
525 | fn $fn_assign(&mut self, t : F) { | |
526 | self.weights.$fn_assign(t); | |
527 | } | |
528 | } | |
529 | ||
530 | impl<F, S, P, const N : usize> | |
531 | std::ops::$trait<F> | |
532 | for SensorGridSupportGenerator<F, S, P, N> | |
533 | where F : Float, | |
534 | S : Sensor<F, N>, | |
535 | P : Spread<F, N>, | |
536 | Convolution<S, P> : Spread<F, N> { | |
537 | type Output = SensorGridSupportGenerator<F, S, P, N>; | |
538 | fn $fn(mut self, t : F) -> Self::Output { | |
539 | std::ops::$trait_assign::$fn_assign(&mut self.weights, t); | |
540 | self | |
541 | } | |
542 | } | |
543 | ||
544 | impl<'a, F, S, P, const N : usize> | |
545 | std::ops::$trait<F> | |
546 | for &'a SensorGridSupportGenerator<F, S, P, N> | |
547 | where F : Float, | |
548 | S : Sensor<F, N>, | |
549 | P : Spread<F, N>, | |
550 | Convolution<S, P> : Spread<F, N> { | |
551 | type Output = SensorGridSupportGenerator<F, S, P, N>; | |
552 | fn $fn(self, t : F) -> Self::Output { | |
553 | SensorGridSupportGenerator{ | |
554 | base_sensor : self.base_sensor.clone(), | |
555 | grid : self.grid, | |
556 | weights : (&self.weights).$fn(t) | |
557 | } | |
558 | } | |
559 | } | |
560 | } | |
561 | } | |
562 | ||
563 | make_sensorgridsupportgenerator_scalarop_rhs!(Mul, mul, MulAssign, mul_assign); | |
564 | make_sensorgridsupportgenerator_scalarop_rhs!(Div, div, DivAssign, div_assign); | |
565 | ||
566 | macro_rules! make_sensorgridsupportgenerator_unaryop { | |
567 | ($trait:ident, $fn:ident) => { | |
568 | impl<F, S, P, const N : usize> | |
569 | std::ops::$trait | |
570 | for SensorGridSupportGenerator<F, S, P, N> | |
571 | where F : Float, | |
572 | S : Sensor<F, N>, | |
573 | P : Spread<F, N>, | |
574 | Convolution<S, P> : Spread<F, N> { | |
575 | type Output = SensorGridSupportGenerator<F, S, P, N>; | |
576 | fn $fn(mut self) -> Self::Output { | |
577 | self.weights = self.weights.$fn(); | |
578 | self | |
579 | } | |
580 | } | |
581 | ||
582 | impl<'a, F, S, P, const N : usize> | |
583 | std::ops::$trait | |
584 | for &'a SensorGridSupportGenerator<F, S, P, N> | |
585 | where F : Float, | |
586 | S : Sensor<F, N>, | |
587 | P : Spread<F, N>, | |
588 | Convolution<S, P> : Spread<F, N> { | |
589 | type Output = SensorGridSupportGenerator<F, S, P, N>; | |
590 | fn $fn(self) -> Self::Output { | |
591 | SensorGridSupportGenerator{ | |
592 | base_sensor : self.base_sensor.clone(), | |
593 | grid : self.grid, | |
594 | weights : (&self.weights).$fn() | |
595 | } | |
596 | } | |
597 | } | |
598 | } | |
599 | } | |
600 | ||
601 | make_sensorgridsupportgenerator_unaryop!(Neg, neg); | |
602 | ||
603 | impl<'a, F, S, P, BT, const N : usize> Mapping<DVector<F>> | |
604 | for PreadjointHelper<'a, SensorGrid<F, S, P, BT, N>, RNDM<F,N>> | |
605 | where F : Float, | |
606 | BT : SensorGridBT<F, S, P, N>, | |
607 | S : Sensor<F, N>, | |
608 | P : Spread<F, N>, | |
609 | Convolution<S, P> : Spread<F, N> + LocalAnalysis<F, Bounds<F>, N>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
610 | { |
35 | 611 | |
612 | type Codomain = SensorGridBTFN<F, S, P, BT, N>; | |
613 | ||
614 | fn apply<I : Instance<DVector<F>>>(&self, x : I) -> Self::Codomain { | |
615 | let fwd = &self.forward_op; | |
616 | let generator = SensorGridSupportGenerator{ | |
617 | base_sensor : fwd.base_sensor.clone(), | |
618 | grid : fwd.grid(), | |
619 | weights : x.own() | |
620 | }; | |
621 | BTFN::new_refresh(&fwd.bt, generator) | |
622 | } | |
623 | } | |
624 | ||
625 | impl<'a, F, S, P, BT, const N : usize> Linear<DVector<F>> | |
626 | for PreadjointHelper<'a, SensorGrid<F, S, P, BT, N>, RNDM<F,N>> | |
627 | where F : Float, | |
628 | BT : SensorGridBT<F, S, P, N>, | |
629 | S : Sensor<F, N>, | |
630 | P : Spread<F, N>, | |
631 | Convolution<S, P> : Spread<F, N> + LocalAnalysis<F, Bounds<F>, N>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
632 | { } |