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