| 11 |
11 |
| 12 * Bredies K., Pikkarainen H. - _Inverse problems in spaces of measures_, |
12 * Bredies K., Pikkarainen H. - _Inverse problems in spaces of measures_, |
| 13 DOI: [10.1051/cocv/2011205](https://doi.org/0.1051/cocv/2011205). |
13 DOI: [10.1051/cocv/2011205](https://doi.org/0.1051/cocv/2011205). |
| 14 */ |
14 */ |
| 15 |
15 |
| |
16 use nalgebra::{DMatrix, DVector}; |
| 16 use numeric_literals::replace_float_literals; |
17 use numeric_literals::replace_float_literals; |
| 17 use nalgebra::{DMatrix, DVector}; |
18 use serde::{Deserialize, Serialize}; |
| 18 use serde::{Serialize, Deserialize}; |
|
| 19 //use colored::Colorize; |
19 //use colored::Colorize; |
| 20 |
20 use crate::dataterm::QuadraticDataTerm; |
| 21 use alg_tools::iterate::{ |
|
| 22 AlgIteratorFactory, |
|
| 23 AlgIteratorOptions, |
|
| 24 ValueIteratorFactory, |
|
| 25 }; |
|
| 26 use alg_tools::euclidean::Euclidean; |
|
| 27 use alg_tools::norms::Norm; |
|
| 28 use alg_tools::linops::Mapping; |
|
| 29 use alg_tools::sets::Cube; |
|
| 30 use alg_tools::loc::Loc; |
|
| 31 use alg_tools::bisection_tree::{ |
|
| 32 BTFN, |
|
| 33 Bounds, |
|
| 34 BTNodeLookup, |
|
| 35 BTNode, |
|
| 36 BTSearch, |
|
| 37 P2Minimise, |
|
| 38 SupportGenerator, |
|
| 39 LocalAnalysis, |
|
| 40 }; |
|
| 41 use alg_tools::mapping::RealMapping; |
|
| 42 use alg_tools::nalgebra_support::ToNalgebraRealField; |
|
| 43 use alg_tools::norms::L2; |
|
| 44 |
|
| 45 use crate::types::*; |
|
| 46 use crate::measures::{ |
|
| 47 RNDM, |
|
| 48 DiscreteMeasure, |
|
| 49 DeltaMeasure, |
|
| 50 Radon, |
|
| 51 }; |
|
| 52 use crate::measures::merging::{ |
|
| 53 SpikeMergingMethod, |
|
| 54 SpikeMerging, |
|
| 55 }; |
|
| 56 use crate::forward_model::ForwardModel; |
21 use crate::forward_model::ForwardModel; |
| |
22 use crate::measures::merging::{SpikeMerging, SpikeMergingMethod}; |
| |
23 use crate::measures::{DeltaMeasure, DiscreteMeasure, Radon, RNDM}; |
| |
24 use crate::plot::Plotter; |
| |
25 use crate::regularisation::{NonnegRadonRegTerm, RadonRegTerm, RegTerm}; |
| 57 #[allow(unused_imports)] // Used in documentation |
26 #[allow(unused_imports)] // Used in documentation |
| 58 use crate::subproblem::{ |
27 use crate::subproblem::{ |
| 59 unconstrained::quadratic_unconstrained, |
28 nonneg::quadratic_nonneg, unconstrained::quadratic_unconstrained, InnerMethod, InnerSettings, |
| 60 nonneg::quadratic_nonneg, |
|
| 61 InnerSettings, |
|
| 62 InnerMethod, |
|
| 63 }; |
29 }; |
| 64 use crate::tolerance::Tolerance; |
30 use crate::tolerance::Tolerance; |
| 65 use crate::plot::{ |
31 use crate::types::*; |
| 66 SeqPlotter, |
32 use alg_tools::bisection_tree::P2Minimise; |
| 67 Plotting, |
33 use alg_tools::bounds::MinMaxMapping; |
| 68 PlotLookup |
34 use alg_tools::error::DynResult; |
| 69 }; |
35 use alg_tools::euclidean::Euclidean; |
| 70 use crate::regularisation::{ |
36 use alg_tools::instance::Instance; |
| 71 NonnegRadonRegTerm, |
37 use alg_tools::iterate::{AlgIteratorFactory, AlgIteratorOptions, ValueIteratorFactory}; |
| 72 RadonRegTerm, |
38 use alg_tools::linops::Mapping; |
| 73 RegTerm |
39 use alg_tools::loc::Loc; |
| 74 }; |
40 use alg_tools::nalgebra_support::ToNalgebraRealField; |
| |
41 use alg_tools::norms::Norm; |
| |
42 use alg_tools::norms::L2; |
| |
43 use alg_tools::sets::Cube; |
| 75 |
44 |
| 76 /// Settings for [`pointsource_fw_reg`]. |
45 /// Settings for [`pointsource_fw_reg`]. |
| 77 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] |
46 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] |
| 78 #[serde(default)] |
47 #[serde(default)] |
| 79 pub struct FWConfig<F : Float> { |
48 pub struct FWConfig<F: Float> { |
| 80 /// Tolerance for branch-and-bound new spike location discovery |
49 /// Tolerance for branch-and-bound new spike location discovery |
| 81 pub tolerance : Tolerance<F>, |
50 pub tolerance: Tolerance<F>, |
| 82 /// Inner problem solution configuration. Has to have `method` set to [`InnerMethod::FB`] |
51 /// Inner problem solution configuration. Has to have `method` set to [`InnerMethod::FB`] |
| 83 /// as the conditional gradient subproblems' optimality conditions do not in general have an |
52 /// as the conditional gradient subproblems' optimality conditions do not in general have an |
| 84 /// invertible Newton derivative for SSN. |
53 /// invertible Newton derivative for SSN. |
| 85 pub inner : InnerSettings<F>, |
54 pub inner: InnerSettings<F>, |
| 86 /// Variant of the conditional gradient method |
55 /// Variant of the conditional gradient method |
| 87 pub variant : FWVariant, |
56 pub variant: FWVariant, |
| 88 /// Settings for branch and bound refinement when looking for predual maxima |
57 /// Settings for branch and bound refinement when looking for predual maxima |
| 89 pub refinement : RefinementSettings<F>, |
58 pub refinement: RefinementSettings<F>, |
| 90 /// Spike merging heuristic |
59 /// Spike merging heuristic |
| 91 pub merging : SpikeMergingMethod<F>, |
60 pub merging: SpikeMergingMethod<F>, |
| 92 } |
61 } |
| 93 |
62 |
| 94 /// Conditional gradient method variant; see also [`FWConfig`]. |
63 /// Conditional gradient method variant; see also [`FWConfig`]. |
| 95 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] |
64 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] |
| 96 #[allow(dead_code)] |
65 #[allow(dead_code)] |
| 99 FullyCorrective, |
68 FullyCorrective, |
| 100 /// Bredies–Pikkarainen. Forces `FWConfig.inner.max_iter = 1`. |
69 /// Bredies–Pikkarainen. Forces `FWConfig.inner.max_iter = 1`. |
| 101 Relaxed, |
70 Relaxed, |
| 102 } |
71 } |
| 103 |
72 |
| 104 impl<F : Float> Default for FWConfig<F> { |
73 impl<F: Float> Default for FWConfig<F> { |
| 105 fn default() -> Self { |
74 fn default() -> Self { |
| 106 FWConfig { |
75 FWConfig { |
| 107 tolerance : Default::default(), |
76 tolerance: Default::default(), |
| 108 refinement : Default::default(), |
77 refinement: Default::default(), |
| 109 inner : Default::default(), |
78 inner: Default::default(), |
| 110 variant : FWVariant::FullyCorrective, |
79 variant: FWVariant::FullyCorrective, |
| 111 merging : SpikeMergingMethod { enabled : true, ..Default::default() }, |
80 merging: SpikeMergingMethod { enabled: true, ..Default::default() }, |
| 112 } |
81 } |
| 113 } |
82 } |
| 114 } |
83 } |
| 115 |
84 |
| 116 pub trait FindimQuadraticModel<Domain, F> : ForwardModel<DiscreteMeasure<Domain, F>, F> |
85 pub trait FindimQuadraticModel<Domain, F>: ForwardModel<DiscreteMeasure<Domain, F>, F> |
| 117 where |
86 where |
| 118 F : Float + ToNalgebraRealField, |
87 F: Float + ToNalgebraRealField, |
| 119 Domain : Clone + PartialEq, |
88 Domain: Clone + PartialEq, |
| 120 { |
89 { |
| 121 /// Return A_*A and A_* b |
90 /// Return A_*A and A_* b |
| 122 fn findim_quadratic_model( |
91 fn findim_quadratic_model( |
| 123 &self, |
92 &self, |
| 124 μ : &DiscreteMeasure<Domain, F>, |
93 μ: &DiscreteMeasure<Domain, F>, |
| 125 b : &Self::Observable |
94 b: &Self::Observable, |
| 126 ) -> (DMatrix<F::MixedType>, DVector<F::MixedType>); |
95 ) -> (DMatrix<F::MixedType>, DVector<F::MixedType>); |
| 127 } |
96 } |
| 128 |
97 |
| 129 /// Helper struct for pre-initialising the finite-dimensional subproblem solver. |
98 /// Helper struct for pre-initialising the finite-dimensional subproblem solver. |
| 130 pub struct FindimData<F : Float> { |
99 pub struct FindimData<F: Float> { |
| 131 /// ‖A‖^2 |
100 /// ‖A‖^2 |
| 132 opAnorm_squared : F, |
101 opAnorm_squared: F, |
| 133 /// Bound $M_0$ from the Bredies–Pikkarainen article. |
102 /// Bound $M_0$ from the Bredies–Pikkarainen article. |
| 134 m0 : F |
103 m0: F, |
| 135 } |
104 } |
| 136 |
105 |
| 137 /// Trait for finite dimensional weight optimisation. |
106 /// Trait for finite dimensional weight optimisation. |
| 138 pub trait WeightOptim< |
107 pub trait WeightOptim< |
| 139 F : Float + ToNalgebraRealField, |
108 F: Float + ToNalgebraRealField, |
| 140 A : ForwardModel<RNDM<F, N>, F>, |
109 A: ForwardModel<RNDM<N, F>, F>, |
| 141 I : AlgIteratorFactory<F>, |
110 I: AlgIteratorFactory<F>, |
| 142 const N : usize |
111 const N: usize, |
| 143 > { |
112 > |
| 144 |
113 { |
| 145 /// Return a pre-initialisation struct for [`Self::optimise_weights`]. |
114 /// Return a pre-initialisation struct for [`Self::optimise_weights`]. |
| 146 /// |
115 /// |
| 147 /// The parameter `opA` is the forward operator $A$. |
116 /// The parameter `opA` is the forward operator $A$. |
| 148 fn prepare_optimise_weights(&self, opA : &A, b : &A::Observable) -> FindimData<F>; |
117 fn prepare_optimise_weights(&self, opA: &A, b: &A::Observable) -> DynResult<FindimData<F>>; |
| 149 |
118 |
| 150 /// Solve the finite-dimensional weight optimisation problem for the 2-norm-squared data fidelity |
119 /// Solve the finite-dimensional weight optimisation problem for the 2-norm-squared data fidelity |
| 151 /// point source localisation problem. |
120 /// point source localisation problem. |
| 152 /// |
121 /// |
| 153 /// That is, we minimise |
122 /// That is, we minimise |
| 164 /// prepared using [`Self::prepare_optimise_weights`]: |
133 /// prepared using [`Self::prepare_optimise_weights`]: |
| 165 /// |
134 /// |
| 166 /// Returns the number of iterations taken by the method configured in `inner`. |
135 /// Returns the number of iterations taken by the method configured in `inner`. |
| 167 fn optimise_weights<'a>( |
136 fn optimise_weights<'a>( |
| 168 &self, |
137 &self, |
| 169 μ : &mut RNDM<F, N>, |
138 μ: &mut RNDM<N, F>, |
| 170 opA : &'a A, |
139 opA: &'a A, |
| 171 b : &A::Observable, |
140 b: &A::Observable, |
| 172 findim_data : &FindimData<F>, |
141 findim_data: &FindimData<F>, |
| 173 inner : &InnerSettings<F>, |
142 inner: &InnerSettings<F>, |
| 174 iterator : I |
143 iterator: I, |
| 175 ) -> usize; |
144 ) -> usize; |
| 176 } |
145 } |
| 177 |
146 |
| 178 /// Trait for regularisation terms supported by [`pointsource_fw_reg`]. |
147 /// Trait for regularisation terms supported by [`pointsource_fw_reg`]. |
| 179 pub trait RegTermFW< |
148 pub trait RegTermFW< |
| 180 F : Float + ToNalgebraRealField, |
149 F: Float + ToNalgebraRealField, |
| 181 A : ForwardModel<RNDM<F, N>, F>, |
150 A: ForwardModel<RNDM<N, F>, F>, |
| 182 I : AlgIteratorFactory<F>, |
151 I: AlgIteratorFactory<F>, |
| 183 const N : usize |
152 const N: usize, |
| 184 > : RegTerm<F, N> |
153 >: RegTerm<Loc<N, F>, F> + WeightOptim<F, A, I, N> + Mapping<RNDM<N, F>, Codomain = F> |
| 185 + WeightOptim<F, A, I, N> |
154 { |
| 186 + Mapping<RNDM<F, N>, Codomain = F> { |
|
| 187 |
|
| 188 /// With $g = A\_\*(Aμ-b)$, returns $(x, g(x))$ for $x$ a new point to be inserted |
155 /// With $g = A\_\*(Aμ-b)$, returns $(x, g(x))$ for $x$ a new point to be inserted |
| 189 /// into $μ$, as determined by the regulariser. |
156 /// into $μ$, as determined by the regulariser. |
| 190 /// |
157 /// |
| 191 /// The parameters `refinement_tolerance` and `max_steps` are passed to relevant |
158 /// The parameters `refinement_tolerance` and `max_steps` are passed to relevant |
| 192 /// [`BTFN`] minimisation and maximisation routines. |
159 /// [`MinMaxMapping`] minimisation and maximisation routines. |
| 193 fn find_insertion( |
160 fn find_insertion( |
| 194 &self, |
161 &self, |
| 195 g : &mut A::PreadjointCodomain, |
162 g: &mut A::PreadjointCodomain, |
| 196 refinement_tolerance : F, |
163 refinement_tolerance: F, |
| 197 max_steps : usize |
164 max_steps: usize, |
| 198 ) -> (Loc<F, N>, F); |
165 ) -> (Loc<N, F>, F); |
| 199 |
166 |
| 200 /// Insert point `ξ` into `μ` for the relaxed algorithm from Bredies–Pikkarainen. |
167 /// Insert point `ξ` into `μ` for the relaxed algorithm from Bredies–Pikkarainen. |
| 201 fn relaxed_insert<'a>( |
168 fn relaxed_insert<'a>( |
| 202 &self, |
169 &self, |
| 203 μ : &mut RNDM<F, N>, |
170 μ: &mut RNDM<N, F>, |
| 204 g : &A::PreadjointCodomain, |
171 g: &A::PreadjointCodomain, |
| 205 opA : &'a A, |
172 opA: &'a A, |
| 206 ξ : Loc<F, N>, |
173 ξ: Loc<N, F>, |
| 207 v_ξ : F, |
174 v_ξ: F, |
| 208 findim_data : &FindimData<F> |
175 findim_data: &FindimData<F>, |
| 209 ); |
176 ); |
| 210 } |
177 } |
| 211 |
178 |
| 212 #[replace_float_literals(F::cast_from(literal))] |
179 #[replace_float_literals(F::cast_from(literal))] |
| 213 impl<F : Float + ToNalgebraRealField, A, I, const N : usize> WeightOptim<F, A, I, N> |
180 impl<F: Float + ToNalgebraRealField, A, I, const N: usize> WeightOptim<F, A, I, N> |
| 214 for RadonRegTerm<F> |
181 for RadonRegTerm<F> |
| 215 where I : AlgIteratorFactory<F>, |
182 where |
| 216 A : FindimQuadraticModel<Loc<F, N>, F> { |
183 I: AlgIteratorFactory<F>, |
| 217 |
184 A: FindimQuadraticModel<Loc<N, F>, F>, |
| 218 fn prepare_optimise_weights(&self, opA : &A, b : &A::Observable) -> FindimData<F> { |
185 { |
| 219 FindimData{ |
186 fn prepare_optimise_weights(&self, opA: &A, b: &A::Observable) -> DynResult<FindimData<F>> { |
| 220 opAnorm_squared : opA.opnorm_bound(Radon, L2).powi(2), |
187 Ok(FindimData { |
| 221 m0 : b.norm2_squared() / (2.0 * self.α()), |
188 opAnorm_squared: opA.opnorm_bound(Radon, L2)?.powi(2), |
| 222 } |
189 m0: b.norm2_squared() / (2.0 * self.α()), |
| |
190 }) |
| 223 } |
191 } |
| 224 |
192 |
| 225 fn optimise_weights<'a>( |
193 fn optimise_weights<'a>( |
| 226 &self, |
194 &self, |
| 227 μ : &mut RNDM<F, N>, |
195 μ: &mut RNDM<N, F>, |
| 228 opA : &'a A, |
196 opA: &'a A, |
| 229 b : &A::Observable, |
197 b: &A::Observable, |
| 230 findim_data : &FindimData<F>, |
198 findim_data: &FindimData<F>, |
| 231 inner : &InnerSettings<F>, |
199 inner: &InnerSettings<F>, |
| 232 iterator : I |
200 iterator: I, |
| 233 ) -> usize { |
201 ) -> usize { |
| 234 |
|
| 235 // Form and solve finite-dimensional subproblem. |
202 // Form and solve finite-dimensional subproblem. |
| 236 let (Ã, g̃) = opA.findim_quadratic_model(&μ, b); |
203 let (Ã, g̃) = opA.findim_quadratic_model(&μ, b); |
| 237 let mut x = μ.masses_dvector(); |
204 let mut x = μ.masses_dvector(); |
| 238 |
205 |
| 239 // `inner_τ1` is based on an estimate of the operator norm of $A$ from ℳ(Ω) to |
206 // `inner_τ1` is based on an estimate of the operator norm of $A$ from ℳ(Ω) to |
| 243 // ‖A‖_{2,2} := sup_{‖x‖_2 ≤ 1} ‖Ax‖_2 ≤ sup_{‖x‖_1 ≤ C} ‖Ax‖_2 |
210 // ‖A‖_{2,2} := sup_{‖x‖_2 ≤ 1} ‖Ax‖_2 ≤ sup_{‖x‖_1 ≤ C} ‖Ax‖_2 |
| 244 // = C sup_{‖x‖_1 ≤ 1} ‖Ax‖_2 = C ‖A‖_{1,2}, |
211 // = C sup_{‖x‖_1 ≤ 1} ‖Ax‖_2 = C ‖A‖_{1,2}, |
| 245 // where C = √m satisfies ‖x‖_1 ≤ C ‖x‖_2. Since we are intested in ‖A_*A‖, no |
212 // where C = √m satisfies ‖x‖_1 ≤ C ‖x‖_2. Since we are intested in ‖A_*A‖, no |
| 246 // square root is needed when we scale: |
213 // square root is needed when we scale: |
| 247 let normest = findim_data.opAnorm_squared * F::cast_from(μ.len()); |
214 let normest = findim_data.opAnorm_squared * F::cast_from(μ.len()); |
| 248 let iters = quadratic_unconstrained(&Ã, &g̃, self.α(), &mut x, |
215 let iters = quadratic_unconstrained(&Ã, &g̃, self.α(), &mut x, normest, inner, iterator); |
| 249 normest, inner, iterator); |
|
| 250 // Update masses of μ based on solution of finite-dimensional subproblem. |
216 // Update masses of μ based on solution of finite-dimensional subproblem. |
| 251 μ.set_masses_dvector(&x); |
217 μ.set_masses_dvector(&x); |
| 252 |
218 |
| 253 iters |
219 iters |
| 254 } |
220 } |
| 255 } |
221 } |
| 256 |
222 |
| 257 #[replace_float_literals(F::cast_from(literal))] |
223 #[replace_float_literals(F::cast_from(literal))] |
| 258 impl<F : Float + ToNalgebraRealField, A, I, S, GA, BTA, const N : usize> RegTermFW<F, A, I, N> |
224 impl<F: Float + ToNalgebraRealField, A, I, const N: usize> RegTermFW<F, A, I, N> for RadonRegTerm<F> |
| 259 for RadonRegTerm<F> |
|
| 260 where |
225 where |
| 261 Cube<F, N> : P2Minimise<Loc<F, N>, F>, |
226 Cube<N, F>: P2Minimise<Loc<N, F>, F>, |
| 262 I : AlgIteratorFactory<F>, |
227 I: AlgIteratorFactory<F>, |
| 263 S: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N>, |
228 A: FindimQuadraticModel<Loc<N, F>, F>, |
| 264 GA : SupportGenerator<F, N, SupportType = S, Id = usize> + Clone, |
229 A::PreadjointCodomain: MinMaxMapping<Loc<N, F>, F>, |
| 265 A : FindimQuadraticModel<Loc<F, N>, F, PreadjointCodomain = BTFN<F, GA, BTA, N>>, |
230 for<'a> &'a A::PreadjointCodomain: Instance<A::PreadjointCodomain>, |
| 266 BTA : BTSearch<F, N, Data=usize, Agg=Bounds<F>>, |
|
| 267 // FIXME: the following *should not* be needed, they are already implied |
231 // FIXME: the following *should not* be needed, they are already implied |
| 268 RNDM<F, N> : Mapping<A::PreadjointCodomain, Codomain = F>, |
232 RNDM<N, F>: Mapping<A::PreadjointCodomain, Codomain = F>, |
| 269 DeltaMeasure<Loc<F, N>, F> : Mapping<A::PreadjointCodomain, Codomain = F>, |
233 DeltaMeasure<Loc<N, F>, F>: Mapping<A::PreadjointCodomain, Codomain = F>, |
| 270 //A : Mapping<RNDM<F, N>, Codomain = A::Observable>, |
234 { |
| 271 //A : Mapping<DeltaMeasure<Loc<F, N>, F>, Codomain = A::Observable>, |
|
| 272 { |
|
| 273 |
|
| 274 fn find_insertion( |
235 fn find_insertion( |
| 275 &self, |
236 &self, |
| 276 g : &mut A::PreadjointCodomain, |
237 g: &mut A::PreadjointCodomain, |
| 277 refinement_tolerance : F, |
238 refinement_tolerance: F, |
| 278 max_steps : usize |
239 max_steps: usize, |
| 279 ) -> (Loc<F, N>, F) { |
240 ) -> (Loc<N, F>, F) { |
| 280 let (ξmax, v_ξmax) = g.maximise(refinement_tolerance, max_steps); |
241 let (ξmax, v_ξmax) = g.maximise(refinement_tolerance, max_steps); |
| 281 let (ξmin, v_ξmin) = g.minimise(refinement_tolerance, max_steps); |
242 let (ξmin, v_ξmin) = g.minimise(refinement_tolerance, max_steps); |
| 282 if v_ξmin < 0.0 && -v_ξmin > v_ξmax { |
243 if v_ξmin < 0.0 && -v_ξmin > v_ξmax { |
| 283 (ξmin, v_ξmin) |
244 (ξmin, v_ξmin) |
| 284 } else { |
245 } else { |
| 286 } |
247 } |
| 287 } |
248 } |
| 288 |
249 |
| 289 fn relaxed_insert<'a>( |
250 fn relaxed_insert<'a>( |
| 290 &self, |
251 &self, |
| 291 μ : &mut RNDM<F, N>, |
252 μ: &mut RNDM<N, F>, |
| 292 g : &A::PreadjointCodomain, |
253 g: &A::PreadjointCodomain, |
| 293 opA : &'a A, |
254 opA: &'a A, |
| 294 ξ : Loc<F, N>, |
255 ξ: Loc<N, F>, |
| 295 v_ξ : F, |
256 v_ξ: F, |
| 296 findim_data : &FindimData<F> |
257 findim_data: &FindimData<F>, |
| 297 ) { |
258 ) { |
| 298 let α = self.0; |
259 let α = self.0; |
| 299 let m0 = findim_data.m0; |
260 let m0 = findim_data.m0; |
| 300 let φ = |t| if t <= m0 { α * t } else { α / (2.0 * m0) * (t*t + m0 * m0) }; |
261 let φ = |t| { |
| 301 let v = if v_ξ.abs() <= α { 0.0 } else { m0 / α * v_ξ }; |
262 if t <= m0 { |
| 302 let δ = DeltaMeasure { x : ξ, α : v }; |
263 α * t |
| |
264 } else { |
| |
265 α / (2.0 * m0) * (t * t + m0 * m0) |
| |
266 } |
| |
267 }; |
| |
268 let v = if v_ξ.abs() <= α { |
| |
269 0.0 |
| |
270 } else { |
| |
271 m0 / α * v_ξ |
| |
272 }; |
| |
273 let δ = DeltaMeasure { x: ξ, α: v }; |
| 303 let dp = μ.apply(g) - δ.apply(g); |
274 let dp = μ.apply(g) - δ.apply(g); |
| 304 let d = opA.apply(&*μ) - opA.apply(δ); |
275 let d = opA.apply(&*μ) - opA.apply(δ); |
| 305 let r = d.norm2_squared(); |
276 let r = d.norm2_squared(); |
| 306 let s = if r == 0.0 { |
277 let s = if r == 0.0 { |
| 307 1.0 |
278 1.0 |
| 308 } else { |
279 } else { |
| 309 1.0.min( (α * μ.norm(Radon) - φ(v.abs()) - dp) / r) |
280 1.0.min((α * μ.norm(Radon) - φ(v.abs()) - dp) / r) |
| 310 }; |
281 }; |
| 311 *μ *= 1.0 - s; |
282 *μ *= 1.0 - s; |
| 312 *μ += δ * s; |
283 *μ += δ * s; |
| 313 } |
284 } |
| 314 } |
285 } |
| 315 |
286 |
| 316 #[replace_float_literals(F::cast_from(literal))] |
287 #[replace_float_literals(F::cast_from(literal))] |
| 317 impl<F : Float + ToNalgebraRealField, A, I, const N : usize> WeightOptim<F, A, I, N> |
288 impl<F: Float + ToNalgebraRealField, A, I, const N: usize> WeightOptim<F, A, I, N> |
| 318 for NonnegRadonRegTerm<F> |
289 for NonnegRadonRegTerm<F> |
| 319 where I : AlgIteratorFactory<F>, |
290 where |
| 320 A : FindimQuadraticModel<Loc<F, N>, F> { |
291 I: AlgIteratorFactory<F>, |
| 321 |
292 A: FindimQuadraticModel<Loc<N, F>, F>, |
| 322 fn prepare_optimise_weights(&self, opA : &A, b : &A::Observable) -> FindimData<F> { |
293 { |
| 323 FindimData{ |
294 fn prepare_optimise_weights(&self, opA: &A, b: &A::Observable) -> DynResult<FindimData<F>> { |
| 324 opAnorm_squared : opA.opnorm_bound(Radon, L2).powi(2), |
295 Ok(FindimData { |
| 325 m0 : b.norm2_squared() / (2.0 * self.α()), |
296 opAnorm_squared: opA.opnorm_bound(Radon, L2)?.powi(2), |
| 326 } |
297 m0: b.norm2_squared() / (2.0 * self.α()), |
| |
298 }) |
| 327 } |
299 } |
| 328 |
300 |
| 329 fn optimise_weights<'a>( |
301 fn optimise_weights<'a>( |
| 330 &self, |
302 &self, |
| 331 μ : &mut RNDM<F, N>, |
303 μ: &mut RNDM<N, F>, |
| 332 opA : &'a A, |
304 opA: &'a A, |
| 333 b : &A::Observable, |
305 b: &A::Observable, |
| 334 findim_data : &FindimData<F>, |
306 findim_data: &FindimData<F>, |
| 335 inner : &InnerSettings<F>, |
307 inner: &InnerSettings<F>, |
| 336 iterator : I |
308 iterator: I, |
| 337 ) -> usize { |
309 ) -> usize { |
| 338 |
|
| 339 // Form and solve finite-dimensional subproblem. |
310 // Form and solve finite-dimensional subproblem. |
| 340 let (Ã, g̃) = opA.findim_quadratic_model(&μ, b); |
311 let (Ã, g̃) = opA.findim_quadratic_model(&μ, b); |
| 341 let mut x = μ.masses_dvector(); |
312 let mut x = μ.masses_dvector(); |
| 342 |
313 |
| 343 // `inner_τ1` is based on an estimate of the operator norm of $A$ from ℳ(Ω) to |
314 // `inner_τ1` is based on an estimate of the operator norm of $A$ from ℳ(Ω) to |
| 347 // ‖A‖_{2,2} := sup_{‖x‖_2 ≤ 1} ‖Ax‖_2 ≤ sup_{‖x‖_1 ≤ C} ‖Ax‖_2 |
318 // ‖A‖_{2,2} := sup_{‖x‖_2 ≤ 1} ‖Ax‖_2 ≤ sup_{‖x‖_1 ≤ C} ‖Ax‖_2 |
| 348 // = C sup_{‖x‖_1 ≤ 1} ‖Ax‖_2 = C ‖A‖_{1,2}, |
319 // = C sup_{‖x‖_1 ≤ 1} ‖Ax‖_2 = C ‖A‖_{1,2}, |
| 349 // where C = √m satisfies ‖x‖_1 ≤ C ‖x‖_2. Since we are intested in ‖A_*A‖, no |
320 // where C = √m satisfies ‖x‖_1 ≤ C ‖x‖_2. Since we are intested in ‖A_*A‖, no |
| 350 // square root is needed when we scale: |
321 // square root is needed when we scale: |
| 351 let normest = findim_data.opAnorm_squared * F::cast_from(μ.len()); |
322 let normest = findim_data.opAnorm_squared * F::cast_from(μ.len()); |
| 352 let iters = quadratic_nonneg(&Ã, &g̃, self.α(), &mut x, |
323 let iters = quadratic_nonneg(&Ã, &g̃, self.α(), &mut x, normest, inner, iterator); |
| 353 normest, inner, iterator); |
|
| 354 // Update masses of μ based on solution of finite-dimensional subproblem. |
324 // Update masses of μ based on solution of finite-dimensional subproblem. |
| 355 μ.set_masses_dvector(&x); |
325 μ.set_masses_dvector(&x); |
| 356 |
326 |
| 357 iters |
327 iters |
| 358 } |
328 } |
| 359 } |
329 } |
| 360 |
330 |
| 361 #[replace_float_literals(F::cast_from(literal))] |
331 #[replace_float_literals(F::cast_from(literal))] |
| 362 impl<F : Float + ToNalgebraRealField, A, I, S, GA, BTA, const N : usize> RegTermFW<F, A, I, N> |
332 impl<F: Float + ToNalgebraRealField, A, I, const N: usize> RegTermFW<F, A, I, N> |
| 363 for NonnegRadonRegTerm<F> |
333 for NonnegRadonRegTerm<F> |
| 364 where |
334 where |
| 365 Cube<F, N> : P2Minimise<Loc<F, N>, F>, |
335 Cube<N, F>: P2Minimise<Loc<N, F>, F>, |
| 366 I : AlgIteratorFactory<F>, |
336 I: AlgIteratorFactory<F>, |
| 367 S: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N>, |
337 A: FindimQuadraticModel<Loc<N, F>, F>, |
| 368 GA : SupportGenerator<F, N, SupportType = S, Id = usize> + Clone, |
338 A::PreadjointCodomain: MinMaxMapping<Loc<N, F>, F>, |
| 369 A : FindimQuadraticModel<Loc<F, N>, F, PreadjointCodomain = BTFN<F, GA, BTA, N>>, |
339 for<'a> &'a A::PreadjointCodomain: Instance<A::PreadjointCodomain>, |
| 370 BTA : BTSearch<F, N, Data=usize, Agg=Bounds<F>>, |
|
| 371 // FIXME: the following *should not* be needed, they are already implied |
340 // FIXME: the following *should not* be needed, they are already implied |
| 372 RNDM<F, N> : Mapping<A::PreadjointCodomain, Codomain = F>, |
341 RNDM<N, F>: Mapping<A::PreadjointCodomain, Codomain = F>, |
| 373 DeltaMeasure<Loc<F, N>, F> : Mapping<A::PreadjointCodomain, Codomain = F>, |
342 DeltaMeasure<Loc<N, F>, F>: Mapping<A::PreadjointCodomain, Codomain = F>, |
| 374 { |
343 { |
| 375 |
|
| 376 fn find_insertion( |
344 fn find_insertion( |
| 377 &self, |
345 &self, |
| 378 g : &mut A::PreadjointCodomain, |
346 g: &mut A::PreadjointCodomain, |
| 379 refinement_tolerance : F, |
347 refinement_tolerance: F, |
| 380 max_steps : usize |
348 max_steps: usize, |
| 381 ) -> (Loc<F, N>, F) { |
349 ) -> (Loc<N, F>, F) { |
| 382 g.maximise(refinement_tolerance, max_steps) |
350 g.maximise(refinement_tolerance, max_steps) |
| 383 } |
351 } |
| 384 |
352 |
| 385 |
|
| 386 fn relaxed_insert<'a>( |
353 fn relaxed_insert<'a>( |
| 387 &self, |
354 &self, |
| 388 μ : &mut RNDM<F, N>, |
355 μ: &mut RNDM<N, F>, |
| 389 g : &A::PreadjointCodomain, |
356 g: &A::PreadjointCodomain, |
| 390 opA : &'a A, |
357 opA: &'a A, |
| 391 ξ : Loc<F, N>, |
358 ξ: Loc<N, F>, |
| 392 v_ξ : F, |
359 v_ξ: F, |
| 393 findim_data : &FindimData<F> |
360 findim_data: &FindimData<F>, |
| 394 ) { |
361 ) { |
| 395 // This is just a verbatim copy of RadonRegTerm::relaxed_insert. |
362 // This is just a verbatim copy of RadonRegTerm::relaxed_insert. |
| 396 let α = self.0; |
363 let α = self.0; |
| 397 let m0 = findim_data.m0; |
364 let m0 = findim_data.m0; |
| 398 let φ = |t| if t <= m0 { α * t } else { α / (2.0 * m0) * (t*t + m0 * m0) }; |
365 let φ = |t| { |
| 399 let v = if v_ξ.abs() <= α { 0.0 } else { m0 / α * v_ξ }; |
366 if t <= m0 { |
| 400 let δ = DeltaMeasure { x : ξ, α : v }; |
367 α * t |
| |
368 } else { |
| |
369 α / (2.0 * m0) * (t * t + m0 * m0) |
| |
370 } |
| |
371 }; |
| |
372 let v = if v_ξ.abs() <= α { |
| |
373 0.0 |
| |
374 } else { |
| |
375 m0 / α * v_ξ |
| |
376 }; |
| |
377 let δ = DeltaMeasure { x: ξ, α: v }; |
| 401 let dp = μ.apply(g) - δ.apply(g); |
378 let dp = μ.apply(g) - δ.apply(g); |
| 402 let d = opA.apply(&*μ) - opA.apply(&δ); |
379 let d = opA.apply(&*μ) - opA.apply(&δ); |
| 403 let r = d.norm2_squared(); |
380 let r = d.norm2_squared(); |
| 404 let s = if r == 0.0 { |
381 let s = if r == 0.0 { |
| 405 1.0 |
382 1.0 |
| 406 } else { |
383 } else { |
| 407 1.0.min( (α * μ.norm(Radon) - φ(v.abs()) - dp) / r) |
384 1.0.min((α * μ.norm(Radon) - φ(v.abs()) - dp) / r) |
| 408 }; |
385 }; |
| 409 *μ *= 1.0 - s; |
386 *μ *= 1.0 - s; |
| 410 *μ += δ * s; |
387 *μ += δ * s; |
| 411 } |
388 } |
| 412 } |
389 } |
| 413 |
|
| 414 |
390 |
| 415 /// Solve point source localisation problem using a conditional gradient method |
391 /// Solve point source localisation problem using a conditional gradient method |
| 416 /// for the 2-norm-squared data fidelity, i.e., the problem |
392 /// for the 2-norm-squared data fidelity, i.e., the problem |
| 417 /// <div>$$ |
393 /// <div>$$ |
| 418 /// \min_μ \frac{1}{2}\|Aμ-b\|_w^2 + G(μ), |
394 /// \min_μ \frac{1}{2}\|Aμ-b\|_w^2 + G(μ), |
| 423 /// The `opA` parameter is the forward operator $A$, while `b`$ is as in the |
399 /// The `opA` parameter is the forward operator $A$, while `b`$ is as in the |
| 424 /// objective above. The method parameter are set in `config` (see [`FWConfig`]), while |
400 /// objective above. The method parameter are set in `config` (see [`FWConfig`]), while |
| 425 /// `iterator` is used to iterate the steps of the method, and `plotter` may be used to |
401 /// `iterator` is used to iterate the steps of the method, and `plotter` may be used to |
| 426 /// save intermediate iteration states as images. |
402 /// save intermediate iteration states as images. |
| 427 #[replace_float_literals(F::cast_from(literal))] |
403 #[replace_float_literals(F::cast_from(literal))] |
| 428 pub fn pointsource_fw_reg<F, I, A, GA, BTA, S, Reg, const N : usize>( |
404 pub fn pointsource_fw_reg<'a, F, I, A, Reg, Plot, const N: usize>( |
| 429 opA : &A, |
405 f: &'a QuadraticDataTerm<F, RNDM<N, F>, A>, |
| 430 b : &A::Observable, |
406 reg: &Reg, |
| 431 reg : Reg, |
407 //domain : Cube<N, F>, |
| 432 //domain : Cube<F, N>, |
408 config: &FWConfig<F>, |
| 433 config : &FWConfig<F>, |
409 iterator: I, |
| 434 iterator : I, |
410 mut plotter: Plot, |
| 435 mut plotter : SeqPlotter<F, N>, |
411 μ0 : Option<RNDM<N, F>>, |
| 436 ) -> RNDM<F, N> |
412 ) -> DynResult<RNDM<N, F>> |
| 437 where F : Float + ToNalgebraRealField, |
413 where |
| 438 I : AlgIteratorFactory<IterInfo<F, N>>, |
414 F: Float + ToNalgebraRealField, |
| 439 for<'b> &'b A::Observable : std::ops::Neg<Output=A::Observable>, |
415 I: AlgIteratorFactory<IterInfo<F>>, |
| 440 GA : SupportGenerator<F, N, SupportType = S, Id = usize> + Clone, |
416 A: ForwardModel<RNDM<N, F>, F>, |
| 441 A : ForwardModel<RNDM<F, N>, F, PreadjointCodomain = BTFN<F, GA, BTA, N>>, |
417 A::PreadjointCodomain: MinMaxMapping<Loc<N, F>, F>, |
| 442 BTA : BTSearch<F, N, Data=usize, Agg=Bounds<F>>, |
418 &'a A::PreadjointCodomain: Instance<A::PreadjointCodomain>, |
| 443 S: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N>, |
419 Cube<N, F>: P2Minimise<Loc<N, F>, F>, |
| 444 BTNodeLookup: BTNode<F, usize, Bounds<F>, N>, |
420 RNDM<N, F>: SpikeMerging<F>, |
| 445 Cube<F, N>: P2Minimise<Loc<F, N>, F>, |
421 Reg: RegTermFW<F, A, ValueIteratorFactory<F, AlgIteratorOptions>, N>, |
| 446 PlotLookup : Plotting<N>, |
422 Plot: Plotter<A::PreadjointCodomain, A::PreadjointCodomain, RNDM<N, F>>, |
| 447 RNDM<F, N> : SpikeMerging<F>, |
423 { |
| 448 Reg : RegTermFW<F, A, ValueIteratorFactory<F, AlgIteratorOptions>, N> { |
424 let opA = f.operator(); |
| |
425 let b = f.data(); |
| 449 |
426 |
| 450 // Set up parameters |
427 // Set up parameters |
| 451 // We multiply tolerance by α for all algoritms. |
428 // We multiply tolerance by α for all algoritms. |
| 452 let tolerance = config.tolerance * reg.tolerance_scaling(); |
429 let tolerance = config.tolerance * reg.tolerance_scaling(); |
| 453 let mut ε = tolerance.initial(); |
430 let mut ε = tolerance.initial(); |
| 454 let findim_data = reg.prepare_optimise_weights(opA, b); |
431 let findim_data = reg.prepare_optimise_weights(opA, b)?; |
| 455 |
432 |
| 456 // Initialise operators |
433 // Initialise operators |
| 457 let preadjA = opA.preadjoint(); |
434 let preadjA = opA.preadjoint(); |
| 458 |
435 |
| 459 // Initialise iterates |
436 // Initialise iterates |
| 460 let mut μ = DiscreteMeasure::new(); |
437 let mut μ = μ0.unwrap_or_else(|| DiscreteMeasure::new()); |
| 461 let mut residual = -b; |
438 let mut residual = f.residual(&μ); |
| 462 |
439 |
| 463 // Statistics |
440 // Statistics |
| 464 let full_stats = |residual : &A::Observable, |
441 let full_stats = |residual: &A::Observable, ν: &RNDM<N, F>, ε, stats| IterInfo { |
| 465 ν : &RNDM<F, N>, |
442 value: residual.norm2_squared_div2() + reg.apply(ν), |
| 466 ε, stats| IterInfo { |
443 n_spikes: ν.len(), |
| 467 value : residual.norm2_squared_div2() + reg.apply(ν), |
|
| 468 n_spikes : ν.len(), |
|
| 469 ε, |
444 ε, |
| 470 .. stats |
445 ..stats |
| 471 }; |
446 }; |
| 472 let mut stats = IterInfo::new(); |
447 let mut stats = IterInfo::new(); |
| 473 |
448 |
| 474 // Run the algorithm |
449 // Run the algorithm |
| 475 for state in iterator.iter_init(|| full_stats(&residual, &μ, ε, stats.clone())) { |
450 for state in iterator.iter_init(|| full_stats(&residual, &μ, ε, stats.clone())) { |
| 478 |
453 |
| 479 // Calculate smooth part of surrogate model. |
454 // Calculate smooth part of surrogate model. |
| 480 let mut g = preadjA.apply(residual * (-1.0)); |
455 let mut g = preadjA.apply(residual * (-1.0)); |
| 481 |
456 |
| 482 // Find absolute value maximising point |
457 // Find absolute value maximising point |
| 483 let (ξ, v_ξ) = reg.find_insertion(&mut g, refinement_tolerance, |
458 let (ξ, v_ξ) = |
| 484 config.refinement.max_steps); |
459 reg.find_insertion(&mut g, refinement_tolerance, config.refinement.max_steps); |
| 485 |
460 |
| 486 let inner_it = match config.variant { |
461 let inner_it = match config.variant { |
| 487 FWVariant::FullyCorrective => { |
462 FWVariant::FullyCorrective => { |
| 488 // No point in optimising the weight here: the finite-dimensional algorithm is fast. |
463 // No point in optimising the weight here: the finite-dimensional algorithm is fast. |
| 489 μ += DeltaMeasure { x : ξ, α : 0.0 }; |
464 μ += DeltaMeasure { x: ξ, α: 0.0 }; |
| 490 stats.inserted += 1; |
465 stats.inserted += 1; |
| 491 config.inner.iterator_options.stop_target(inner_tolerance) |
466 config.inner.iterator_options.stop_target(inner_tolerance) |
| 492 }, |
467 } |
| 493 FWVariant::Relaxed => { |
468 FWVariant::Relaxed => { |
| 494 // Perform a relaxed initialisation of μ |
469 // Perform a relaxed initialisation of μ |
| 495 reg.relaxed_insert(&mut μ, &g, opA, ξ, v_ξ, &findim_data); |
470 reg.relaxed_insert(&mut μ, &g, opA, ξ, v_ξ, &findim_data); |
| 496 stats.inserted += 1; |
471 stats.inserted += 1; |
| 497 // The stop_target is only needed for the type system. |
472 // The stop_target is only needed for the type system. |
| 498 AlgIteratorOptions{ max_iter : 1, .. config.inner.iterator_options}.stop_target(0.0) |
473 AlgIteratorOptions { max_iter: 1, ..config.inner.iterator_options }.stop_target(0.0) |
| 499 } |
474 } |
| 500 }; |
475 }; |
| 501 |
476 |
| 502 stats.inner_iters += reg.optimise_weights(&mut μ, opA, b, &findim_data, |
477 stats.inner_iters += |
| 503 &config.inner, inner_it); |
478 reg.optimise_weights(&mut μ, opA, b, &findim_data, &config.inner, inner_it); |
| 504 |
479 |
| 505 // Merge spikes and update residual for next step and `if_verbose` below. |
480 // Merge spikes and update residual for next step and `if_verbose` below. |
| 506 let (r, count) = μ.merge_spikes_fitness(config.merging, |
481 let (r, count) = μ.merge_spikes_fitness( |
| 507 |μ̃| opA.apply(μ̃) - b, |
482 config.merging, |
| 508 A::Observable::norm2_squared); |
483 |μ̃| f.residual(μ̃), |
| |
484 A::Observable::norm2_squared, |
| |
485 ); |
| 509 residual = r; |
486 residual = r; |
| 510 stats.merged += count; |
487 stats.merged += count; |
| 511 |
488 |
| 512 // Prune points with zero mass |
489 // Prune points with zero mass |
| 513 let n_before_prune = μ.len(); |
490 let n_before_prune = μ.len(); |