| 107 /// Helper struct for pre-initialising the finite-dimensional subproblems solver |
114 /// Helper struct for pre-initialising the finite-dimensional subproblems solver |
| 108 /// [`prepare_optimise_weights`]. |
115 /// [`prepare_optimise_weights`]. |
| 109 /// |
116 /// |
| 110 /// The pre-initialisation is done by [`prepare_optimise_weights`]. |
117 /// The pre-initialisation is done by [`prepare_optimise_weights`]. |
| 111 pub struct FindimData<F : Float> { |
118 pub struct FindimData<F : Float> { |
| 112 opAnorm_squared : F |
119 /// ‖A‖^2 |
| 113 } |
120 opAnorm_squared : F, |
| 114 |
121 /// Bound $M_0$ from the Bredies–Pikkarainen article. |
| 115 /// Return a pre-initialisation struct for [`prepare_optimise_weights`]. |
122 m0 : F |
| 116 /// |
123 } |
| 117 /// The parameter `opA` is the forward operator $A$. |
124 |
| 118 pub fn prepare_optimise_weights<F, A, const N : usize>(opA : &A) -> FindimData<F> |
125 /// Trait for finite dimensional weight optimisation. |
| 119 where F : Float + ToNalgebraRealField, |
126 pub trait WeightOptim< |
| |
127 F : Float + ToNalgebraRealField, |
| |
128 A : ForwardModel<Loc<F, N>, F>, |
| |
129 I : AlgIteratorFactory<F>, |
| |
130 const N : usize |
| |
131 > { |
| |
132 |
| |
133 /// Return a pre-initialisation struct for [`Self::optimise_weights`]. |
| |
134 /// |
| |
135 /// The parameter `opA` is the forward operator $A$. |
| |
136 fn prepare_optimise_weights(&self, opA : &A, b : &A::Observable) -> FindimData<F>; |
| |
137 |
| |
138 /// Solve the finite-dimensional weight optimisation problem for the 2-norm-squared data fidelity |
| |
139 /// point source localisation problem. |
| |
140 /// |
| |
141 /// That is, we minimise |
| |
142 /// <div>$$ |
| |
143 /// μ ↦ \frac{1}{2}\|Aμ-b\|_w^2 + G(μ) |
| |
144 /// $$</div> |
| |
145 /// only with respect to the weights of $μ$. Here $G$ is a regulariser modelled by `Self`. |
| |
146 /// |
| |
147 /// The parameter `μ` is the discrete measure whose weights are to be optimised. |
| |
148 /// The `opA` parameter is the forward operator $A$, while `b`$ and `α` are as in the |
| |
149 /// objective above. The method parameter are set in `inner` (see [`InnerSettings`]), while |
| |
150 /// `iterator` is used to iterate the steps of the method, and `plotter` may be used to |
| |
151 /// save intermediate iteration states as images. The parameter `findim_data` should be |
| |
152 /// prepared using [`Self::prepare_optimise_weights`]: |
| |
153 /// |
| |
154 /// Returns the number of iterations taken by the method configured in `inner`. |
| |
155 fn optimise_weights<'a>( |
| |
156 &self, |
| |
157 μ : &mut DiscreteMeasure<Loc<F, N>, F>, |
| |
158 opA : &'a A, |
| |
159 b : &A::Observable, |
| |
160 findim_data : &FindimData<F>, |
| |
161 inner : &InnerSettings<F>, |
| |
162 iterator : I |
| |
163 ) -> usize; |
| |
164 } |
| |
165 |
| |
166 /// Trait for regularisation terms supported by [`pointsource_fw_reg`]. |
| |
167 pub trait RegTermFW< |
| |
168 F : Float + ToNalgebraRealField, |
| |
169 A : ForwardModel<Loc<F, N>, F>, |
| |
170 I : AlgIteratorFactory<F>, |
| |
171 const N : usize |
| |
172 > : RegTerm<F, N> |
| |
173 + WeightOptim<F, A, I, N> |
| |
174 + for<'a> Apply<&'a DiscreteMeasure<Loc<F, N>, F>, Output = F> { |
| |
175 |
| |
176 /// With $g = A\_\*(Aμ-b)$, returns $(x, g(x))$ for $x$ a new point to be inserted |
| |
177 /// into $μ$, as determined by the regulariser. |
| |
178 /// |
| |
179 /// The parameters `refinement_tolerance` and `max_steps` are passed to relevant |
| |
180 /// [`BTFN`] minimisation and maximisation routines. |
| |
181 fn find_insertion( |
| |
182 &self, |
| |
183 g : &mut A::PreadjointCodomain, |
| |
184 refinement_tolerance : F, |
| |
185 max_steps : usize |
| |
186 ) -> (Loc<F, N>, F); |
| |
187 |
| |
188 /// Insert point `ξ` into `μ` for the relaxed algorithm from Bredies–Pikkarainen. |
| |
189 fn relaxed_insert<'a>( |
| |
190 &self, |
| |
191 μ : &mut DiscreteMeasure<Loc<F, N>, F>, |
| |
192 g : &A::PreadjointCodomain, |
| |
193 opA : &'a A, |
| |
194 ξ : Loc<F, N>, |
| |
195 v_ξ : F, |
| |
196 findim_data : &FindimData<F> |
| |
197 ); |
| |
198 } |
| |
199 |
| |
200 #[replace_float_literals(F::cast_from(literal))] |
| |
201 impl<F : Float + ToNalgebraRealField, A, I, const N : usize> WeightOptim<F, A, I, N> |
| |
202 for RadonRegTerm<F> |
| |
203 where I : AlgIteratorFactory<F>, |
| 120 A : ForwardModel<Loc<F, N>, F> { |
204 A : ForwardModel<Loc<F, N>, F> { |
| 121 FindimData{ |
205 |
| 122 opAnorm_squared : opA.opnorm_bound().powi(2) |
206 fn prepare_optimise_weights(&self, opA : &A, b : &A::Observable) -> FindimData<F> { |
| 123 } |
207 FindimData{ |
| 124 } |
208 opAnorm_squared : opA.opnorm_bound().powi(2), |
| 125 |
209 m0 : b.norm2_squared() / (2.0 * self.α()), |
| 126 /// Solve the finite-dimensional weight optimisation problem for the 2-norm-squared data fidelity |
210 } |
| 127 /// point source localisation problem. |
211 } |
| 128 /// |
212 |
| 129 /// That is, we minimise |
213 fn optimise_weights<'a>( |
| 130 /// <div>$$ |
214 &self, |
| 131 /// μ ↦ \frac{1}{2}\|Aμ-b\|_w^2 + α\|μ\|_ℳ + δ_{≥ 0}(μ) |
215 μ : &mut DiscreteMeasure<Loc<F, N>, F>, |
| 132 /// $$</div> |
216 opA : &'a A, |
| 133 /// only with respect to the weights of $μ$. |
217 b : &A::Observable, |
| 134 /// |
218 findim_data : &FindimData<F>, |
| 135 /// The parameter `μ` is the discrete measure whose weights are to be optimised. |
219 inner : &InnerSettings<F>, |
| 136 /// The `opA` parameter is the forward operator $A$, while `b`$ and `α` are as in the |
220 iterator : I |
| 137 /// objective above. The method parameter are set in `inner` (see [`InnerSettings`]), while |
221 ) -> usize { |
| 138 /// `iterator` is used to iterate the steps of the method, and `plotter` may be used to |
222 |
| 139 /// save intermediate iteration states as images. The parameter `findim_data` should be |
223 // Form and solve finite-dimensional subproblem. |
| 140 /// prepared using [`prepare_optimise_weights`]: |
224 let (Ã, g̃) = opA.findim_quadratic_model(&μ, b); |
| 141 /// |
225 let mut x = μ.masses_dvector(); |
| 142 /// Returns the number of iterations taken by the method configured in `inner`. |
226 |
| 143 pub fn optimise_weights<'a, F, A, I, const N : usize>( |
227 // `inner_τ1` is based on an estimate of the operator norm of $A$ from ℳ(Ω) to |
| 144 μ : &mut DiscreteMeasure<Loc<F, N>, F>, |
228 // ℝ^n. This estimate is a good one for the matrix norm from ℝ^m to ℝ^n when the |
| 145 opA : &'a A, |
229 // former is equipped with the 1-norm. We need the 2-norm. To pass from 1-norm to |
| 146 b : &A::Observable, |
230 // 2-norm, we estimate |
| 147 α : F, |
231 // ‖A‖_{2,2} := sup_{‖x‖_2 ≤ 1} ‖Ax‖_2 ≤ sup_{‖x‖_1 ≤ C} ‖Ax‖_2 |
| 148 findim_data : &FindimData<F>, |
232 // = C sup_{‖x‖_1 ≤ 1} ‖Ax‖_2 = C ‖A‖_{1,2}, |
| 149 inner : &InnerSettings<F>, |
233 // where C = √m satisfies ‖x‖_1 ≤ C ‖x‖_2. Since we are intested in ‖A_*A‖, no |
| 150 iterator : I |
234 // square root is needed when we scale: |
| 151 ) -> usize |
235 let inner_τ = inner.τ0 / (findim_data.opAnorm_squared * F::cast_from(μ.len())); |
| 152 where F : Float + ToNalgebraRealField, |
236 let iters = quadratic_unconstrained(inner.method, &Ã, &g̃, self.α(), |
| |
237 &mut x, inner_τ, iterator); |
| |
238 // Update masses of μ based on solution of finite-dimensional subproblem. |
| |
239 μ.set_masses_dvector(&x); |
| |
240 |
| |
241 iters |
| |
242 } |
| |
243 } |
| |
244 |
| |
245 #[replace_float_literals(F::cast_from(literal))] |
| |
246 impl<F : Float + ToNalgebraRealField, A, I, S, GA, BTA, const N : usize> RegTermFW<F, A, I, N> |
| |
247 for RadonRegTerm<F> |
| |
248 where Cube<F, N> : P2Minimise<Loc<F, N>, F>, |
| 153 I : AlgIteratorFactory<F>, |
249 I : AlgIteratorFactory<F>, |
| 154 A : ForwardModel<Loc<F, N>, F> |
250 S: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N>, |
| 155 { |
251 GA : SupportGenerator<F, N, SupportType = S, Id = usize> + Clone, |
| 156 // Form and solve finite-dimensional subproblem. |
252 A : ForwardModel<Loc<F, N>, F, PreadjointCodomain = BTFN<F, GA, BTA, N>>, |
| 157 let (Ã, g̃) = opA.findim_quadratic_model(&μ, b); |
253 BTA : BTSearch<F, N, Data=usize, Agg=Bounds<F>> { |
| 158 let mut x = μ.masses_dvector(); |
254 |
| 159 |
255 fn find_insertion( |
| 160 // `inner_τ1` is based on an estimate of the operator norm of $A$ from ℳ(Ω) to |
256 &self, |
| 161 // ℝ^n. This estimate is a good one for the matrix norm from ℝ^m to ℝ^n when the |
257 g : &mut A::PreadjointCodomain, |
| 162 // former is equipped with the 1-norm. We need the 2-norm. To pass from 1-norm to |
258 refinement_tolerance : F, |
| 163 // 2-norm, we estimate |
259 max_steps : usize |
| 164 // ‖A‖_{2,2} := sup_{‖x‖_2 ≤ 1} ‖Ax‖_2 ≤ sup_{‖x‖_1 ≤ C} ‖Ax‖_2 |
260 ) -> (Loc<F, N>, F) { |
| 165 // = C sup_{‖x‖_1 ≤ 1} ‖Ax‖_2 = C ‖A‖_{1,2}, |
261 let (ξmax, v_ξmax) = g.maximise(refinement_tolerance, max_steps); |
| 166 // where C = √m satisfies ‖x‖_1 ≤ C ‖x‖_2. Since we are intested in ‖A_*A‖, no |
262 let (ξmin, v_ξmin) = g.minimise(refinement_tolerance, max_steps); |
| 167 // square root is needed when we scale: |
263 if v_ξmin < 0.0 && -v_ξmin > v_ξmax { |
| 168 let inner_τ = inner.τ0 / (findim_data.opAnorm_squared * F::cast_from(μ.len())); |
264 (ξmin, v_ξmin) |
| 169 let iters = quadratic_unconstrained(inner.method, &Ã, &g̃, α, &mut x, inner_τ, iterator); |
265 } else { |
| 170 // Update masses of μ based on solution of finite-dimensional subproblem. |
266 (ξmax, v_ξmax) |
| 171 μ.set_masses_dvector(&x); |
267 } |
| 172 |
268 } |
| 173 iters |
269 |
| 174 } |
270 fn relaxed_insert<'a>( |
| |
271 &self, |
| |
272 μ : &mut DiscreteMeasure<Loc<F, N>, F>, |
| |
273 g : &A::PreadjointCodomain, |
| |
274 opA : &'a A, |
| |
275 ξ : Loc<F, N>, |
| |
276 v_ξ : F, |
| |
277 findim_data : &FindimData<F> |
| |
278 ) { |
| |
279 let α = self.0; |
| |
280 let m0 = findim_data.m0; |
| |
281 let φ = |t| if t <= m0 { α * t } else { α / (2.0 * m0) * (t*t + m0 * m0) }; |
| |
282 let v = if v_ξ.abs() <= α { 0.0 } else { m0 / α * v_ξ }; |
| |
283 let δ = DeltaMeasure { x : ξ, α : v }; |
| |
284 let dp = μ.apply(g) - δ.apply(g); |
| |
285 let d = opA.apply(&*μ) - opA.apply(&δ); |
| |
286 let r = d.norm2_squared(); |
| |
287 let s = if r == 0.0 { |
| |
288 1.0 |
| |
289 } else { |
| |
290 1.0.min( (α * μ.norm(Radon) - φ(v.abs()) - dp) / r) |
| |
291 }; |
| |
292 *μ *= 1.0 - s; |
| |
293 *μ += δ * s; |
| |
294 } |
| |
295 } |
| |
296 |
| |
297 #[replace_float_literals(F::cast_from(literal))] |
| |
298 impl<F : Float + ToNalgebraRealField, A, I, const N : usize> WeightOptim<F, A, I, N> |
| |
299 for NonnegRadonRegTerm<F> |
| |
300 where I : AlgIteratorFactory<F>, |
| |
301 A : ForwardModel<Loc<F, N>, F> { |
| |
302 |
| |
303 fn prepare_optimise_weights(&self, opA : &A, b : &A::Observable) -> FindimData<F> { |
| |
304 FindimData{ |
| |
305 opAnorm_squared : opA.opnorm_bound().powi(2), |
| |
306 m0 : b.norm2_squared() / (2.0 * self.α()), |
| |
307 } |
| |
308 } |
| |
309 |
| |
310 fn optimise_weights<'a>( |
| |
311 &self, |
| |
312 μ : &mut DiscreteMeasure<Loc<F, N>, F>, |
| |
313 opA : &'a A, |
| |
314 b : &A::Observable, |
| |
315 findim_data : &FindimData<F>, |
| |
316 inner : &InnerSettings<F>, |
| |
317 iterator : I |
| |
318 ) -> usize { |
| |
319 |
| |
320 // Form and solve finite-dimensional subproblem. |
| |
321 let (Ã, g̃) = opA.findim_quadratic_model(&μ, b); |
| |
322 let mut x = μ.masses_dvector(); |
| |
323 |
| |
324 // `inner_τ1` is based on an estimate of the operator norm of $A$ from ℳ(Ω) to |
| |
325 // ℝ^n. This estimate is a good one for the matrix norm from ℝ^m to ℝ^n when the |
| |
326 // former is equipped with the 1-norm. We need the 2-norm. To pass from 1-norm to |
| |
327 // 2-norm, we estimate |
| |
328 // ‖A‖_{2,2} := sup_{‖x‖_2 ≤ 1} ‖Ax‖_2 ≤ sup_{‖x‖_1 ≤ C} ‖Ax‖_2 |
| |
329 // = C sup_{‖x‖_1 ≤ 1} ‖Ax‖_2 = C ‖A‖_{1,2}, |
| |
330 // where C = √m satisfies ‖x‖_1 ≤ C ‖x‖_2. Since we are intested in ‖A_*A‖, no |
| |
331 // square root is needed when we scale: |
| |
332 let inner_τ = inner.τ0 / (findim_data.opAnorm_squared * F::cast_from(μ.len())); |
| |
333 let iters = quadratic_nonneg(inner.method, &Ã, &g̃, self.α(), |
| |
334 &mut x, inner_τ, iterator); |
| |
335 // Update masses of μ based on solution of finite-dimensional subproblem. |
| |
336 μ.set_masses_dvector(&x); |
| |
337 |
| |
338 iters |
| |
339 } |
| |
340 } |
| |
341 |
| |
342 #[replace_float_literals(F::cast_from(literal))] |
| |
343 impl<F : Float + ToNalgebraRealField, A, I, S, GA, BTA, const N : usize> RegTermFW<F, A, I, N> |
| |
344 for NonnegRadonRegTerm<F> |
| |
345 where Cube<F, N> : P2Minimise<Loc<F, N>, F>, |
| |
346 I : AlgIteratorFactory<F>, |
| |
347 S: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N>, |
| |
348 GA : SupportGenerator<F, N, SupportType = S, Id = usize> + Clone, |
| |
349 A : ForwardModel<Loc<F, N>, F, PreadjointCodomain = BTFN<F, GA, BTA, N>>, |
| |
350 BTA : BTSearch<F, N, Data=usize, Agg=Bounds<F>> { |
| |
351 |
| |
352 fn find_insertion( |
| |
353 &self, |
| |
354 g : &mut A::PreadjointCodomain, |
| |
355 refinement_tolerance : F, |
| |
356 max_steps : usize |
| |
357 ) -> (Loc<F, N>, F) { |
| |
358 g.maximise(refinement_tolerance, max_steps) |
| |
359 } |
| |
360 |
| |
361 |
| |
362 fn relaxed_insert<'a>( |
| |
363 &self, |
| |
364 μ : &mut DiscreteMeasure<Loc<F, N>, F>, |
| |
365 g : &A::PreadjointCodomain, |
| |
366 opA : &'a A, |
| |
367 ξ : Loc<F, N>, |
| |
368 v_ξ : F, |
| |
369 findim_data : &FindimData<F> |
| |
370 ) { |
| |
371 // This is just a verbatim copy of RadonRegTerm::relaxed_insert. |
| |
372 let α = self.0; |
| |
373 let m0 = findim_data.m0; |
| |
374 let φ = |t| if t <= m0 { α * t } else { α / (2.0 * m0) * (t*t + m0 * m0) }; |
| |
375 let v = if v_ξ.abs() <= α { 0.0 } else { m0 / α * v_ξ }; |
| |
376 let δ = DeltaMeasure { x : ξ, α : v }; |
| |
377 let dp = μ.apply(g) - δ.apply(g); |
| |
378 let d = opA.apply(&*μ) - opA.apply(&δ); |
| |
379 let r = d.norm2_squared(); |
| |
380 let s = if r == 0.0 { |
| |
381 1.0 |
| |
382 } else { |
| |
383 1.0.min( (α * μ.norm(Radon) - φ(v.abs()) - dp) / r) |
| |
384 }; |
| |
385 *μ *= 1.0 - s; |
| |
386 *μ += δ * s; |
| |
387 } |
| |
388 } |
| |
389 |
| 175 |
390 |
| 176 /// Solve point source localisation problem using a conditional gradient method |
391 /// Solve point source localisation problem using a conditional gradient method |
| 177 /// for the 2-norm-squared data fidelity, i.e., the problem |
392 /// for the 2-norm-squared data fidelity, i.e., the problem |
| 178 /// <div>$$ |
393 /// <div>$$ |
| 179 /// \min_μ \frac{1}{2}\|Aμ-b\|_w^2 + α\|μ\|_ℳ + δ_{≥ 0}(μ). |
394 /// \min_μ \frac{1}{2}\|Aμ-b\|_w^2 + G(μ), |
| 180 /// $$</div> |
395 /// $$ |
| |
396 /// where $G$ is the regularisation term modelled by `reg`. |
| |
397 /// </div> |
| 181 /// |
398 /// |
| 182 /// The `opA` parameter is the forward operator $A$, while `b`$ and `α` are as in the |
399 /// The `opA` parameter is the forward operator $A$, while `b`$ is as in the |
| 183 /// 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 |
| 184 /// `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 |
| 185 /// save intermediate iteration states as images. |
402 /// save intermediate iteration states as images. |
| 186 #[replace_float_literals(F::cast_from(literal))] |
403 #[replace_float_literals(F::cast_from(literal))] |
| 187 pub fn pointsource_fw<'a, F, I, A, GA, BTA, S, const N : usize>( |
404 pub fn pointsource_fw_reg<'a, F, I, A, GA, BTA, S, Reg, const N : usize>( |
| 188 opA : &'a A, |
405 opA : &'a A, |
| 189 b : &A::Observable, |
406 b : &A::Observable, |
| 190 α : F, |
407 reg : Reg, |
| 191 //domain : Cube<F, N>, |
408 //domain : Cube<F, N>, |
| 192 config : &FWConfig<F>, |
409 config : &FWConfig<F>, |
| 193 iterator : I, |
410 iterator : I, |
| 194 mut plotter : SeqPlotter<F, N>, |
411 mut plotter : SeqPlotter<F, N>, |
| 195 ) -> DiscreteMeasure<Loc<F, N>, F> |
412 ) -> DiscreteMeasure<Loc<F, N>, F> |