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> |