src/frank_wolfe.rs

changeset 25
79943be70720
parent 24
d29d1fcf5423
equal deleted inserted replaced
24:d29d1fcf5423 25:79943be70720
19 19
20 use alg_tools::iterate::{ 20 use alg_tools::iterate::{
21 AlgIteratorFactory, 21 AlgIteratorFactory,
22 AlgIteratorState, 22 AlgIteratorState,
23 AlgIteratorOptions, 23 AlgIteratorOptions,
24 ValueIteratorFactory,
24 }; 25 };
25 use alg_tools::euclidean::Euclidean; 26 use alg_tools::euclidean::Euclidean;
26 use alg_tools::norms::Norm; 27 use alg_tools::norms::Norm;
27 use alg_tools::linops::Apply; 28 use alg_tools::linops::Apply;
28 use alg_tools::sets::Cube; 29 use alg_tools::sets::Cube;
52 }; 53 };
53 use crate::forward_model::ForwardModel; 54 use crate::forward_model::ForwardModel;
54 #[allow(unused_imports)] // Used in documentation 55 #[allow(unused_imports)] // Used in documentation
55 use crate::subproblem::{ 56 use crate::subproblem::{
56 unconstrained::quadratic_unconstrained, 57 unconstrained::quadratic_unconstrained,
58 nonneg::quadratic_nonneg,
57 InnerSettings, 59 InnerSettings,
58 InnerMethod, 60 InnerMethod,
59 }; 61 };
60 use crate::tolerance::Tolerance; 62 use crate::tolerance::Tolerance;
61 use crate::plot::{ 63 use crate::plot::{
62 SeqPlotter, 64 SeqPlotter,
63 Plotting, 65 Plotting,
64 PlotLookup 66 PlotLookup
65 }; 67 };
68 use crate::regularisation::{
69 NonnegRadonRegTerm,
70 RadonRegTerm,
71 };
72 use crate::fb::RegTerm;
66 73
67 /// Settings for [`pointsource_fw`]. 74 /// Settings for [`pointsource_fw`].
68 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] 75 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)]
69 #[serde(default)] 76 #[serde(default)]
70 pub struct FWConfig<F : Float> { 77 pub struct FWConfig<F : Float> {
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>
203 BTA : BTSearch<F, N, Data=usize, Agg=Bounds<F>>, 420 BTA : BTSearch<F, N, Data=usize, Agg=Bounds<F>>,
204 S: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N>, 421 S: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N>,
205 BTNodeLookup: BTNode<F, usize, Bounds<F>, N>, 422 BTNodeLookup: BTNode<F, usize, Bounds<F>, N>,
206 Cube<F, N>: P2Minimise<Loc<F, N>, F>, 423 Cube<F, N>: P2Minimise<Loc<F, N>, F>,
207 PlotLookup : Plotting<N>, 424 PlotLookup : Plotting<N>,
208 DiscreteMeasure<Loc<F, N>, F> : SpikeMerging<F> { 425 DiscreteMeasure<Loc<F, N>, F> : SpikeMerging<F>,
426 Reg : RegTermFW<F, A, ValueIteratorFactory<F, AlgIteratorOptions>, N> {
209 427
210 // Set up parameters 428 // Set up parameters
211 // We multiply tolerance by α for all algoritms. 429 // We multiply tolerance by α for all algoritms.
212 let tolerance = config.tolerance * α; 430 let tolerance = config.tolerance * reg.tolerance_scaling();
213 let mut ε = tolerance.initial(); 431 let mut ε = tolerance.initial();
214 let findim_data = prepare_optimise_weights(opA); 432 let findim_data = reg.prepare_optimise_weights(opA, b);
215 let m0 = b.norm2_squared() / (2.0 * α);
216 let φ = |t| if t <= m0 { α * t } else { α / (2.0 * m0) * (t*t + m0 * m0) };
217 433
218 // Initialise operators 434 // Initialise operators
219 let preadjA = opA.preadjoint(); 435 let preadjA = opA.preadjoint();
220 436
221 // Initialise iterates 437 // Initialise iterates
242 // the residual and replacing it below before the end of this closure. 458 // the residual and replacing it below before the end of this closure.
243 let r = std::mem::replace(&mut residual, opA.empty_observable()); 459 let r = std::mem::replace(&mut residual, opA.empty_observable());
244 let mut g = -preadjA.apply(r); 460 let mut g = -preadjA.apply(r);
245 461
246 // Find absolute value maximising point 462 // Find absolute value maximising point
247 let (ξmax, v_ξmax) = g.maximise(refinement_tolerance, 463 let (ξ, v_ξ) = reg.find_insertion(&mut g, refinement_tolerance,
248 config.refinement.max_steps); 464 config.refinement.max_steps);
249 let (ξmin, v_ξmin) = g.minimise(refinement_tolerance,
250 config.refinement.max_steps);
251 let (ξ, v_ξ) = if v_ξmin < 0.0 && -v_ξmin > v_ξmax {
252 (ξmin, v_ξmin)
253 } else {
254 (ξmax, v_ξmax)
255 };
256 465
257 let inner_it = match config.variant { 466 let inner_it = match config.variant {
258 FWVariant::FullyCorrective => { 467 FWVariant::FullyCorrective => {
259 // No point in optimising the weight here: the finite-dimensional algorithm is fast. 468 // No point in optimising the weight here: the finite-dimensional algorithm is fast.
260 μ += DeltaMeasure { x : ξ, α : 0.0 }; 469 μ += DeltaMeasure { x : ξ, α : 0.0 };
261 config.inner.iterator_options.stop_target(inner_tolerance) 470 config.inner.iterator_options.stop_target(inner_tolerance)
262 }, 471 },
263 FWVariant::Relaxed => { 472 FWVariant::Relaxed => {
264 // Perform a relaxed initialisation of μ 473 // Perform a relaxed initialisation of μ
265 let v = if v_ξ.abs() <= α { 0.0 } else { m0 / α * v_ξ }; 474 reg.relaxed_insert(&mut μ, &g, opA, ξ, v_ξ, &findim_data);
266 let δ = DeltaMeasure { x : ξ, α : v };
267 let dp = μ.apply(&g) - δ.apply(&g);
268 let d = opA.apply(&μ) - opA.apply(&δ);
269 let r = d.norm2_squared();
270 let s = if r == 0.0 {
271 1.0
272 } else {
273 1.0.min( (α * μ.norm(Radon) - φ(v.abs()) - dp) / r)
274 };
275 μ *= 1.0 - s;
276 μ += δ * s;
277 // The stop_target is only needed for the type system. 475 // The stop_target is only needed for the type system.
278 AlgIteratorOptions{ max_iter : 1, .. config.inner.iterator_options}.stop_target(0.0) 476 AlgIteratorOptions{ max_iter : 1, .. config.inner.iterator_options}.stop_target(0.0)
279 } 477 }
280 }; 478 };
281 479
282 inner_iters += optimise_weights(&mut μ, opA, b, α, &findim_data, &config.inner, inner_it); 480 inner_iters += reg.optimise_weights(&mut μ, opA, b, &findim_data, &config.inner, inner_it);
283 481
284 // Merge spikes and update residual for next step and `if_verbose` below. 482 // Merge spikes and update residual for next step and `if_verbose` below.
285 let n_before_merge = μ.len(); 483 let n_before_merge = μ.len();
286 residual = μ.merge_spikes_fitness(config.merging, 484 residual = μ.merge_spikes_fitness(config.merging,
287 |μ̃| opA.apply(μ̃) - b, 485 |μ̃| opA.apply(μ̃) - b,
304 format!("iter {} start", state.iteration()), &g, 502 format!("iter {} start", state.iteration()), &g,
305 "".to_string(), None::<&A::PreadjointCodomain>, 503 "".to_string(), None::<&A::PreadjointCodomain>,
306 None, &μ 504 None, &μ
307 ); 505 );
308 let res = IterInfo { 506 let res = IterInfo {
309 value : residual.norm2_squared_div2() + α * μ.norm(Radon), 507 value : residual.norm2_squared_div2() + reg.apply(&μ),
310 n_spikes : μ.len(), 508 n_spikes : μ.len(),
311 inner_iters, 509 inner_iters,
312 this_iters, 510 this_iters,
313 merged, 511 merged,
314 pruned, 512 pruned,
325 523
326 // Return final iterate 524 // Return final iterate
327 μ 525 μ
328 } 526 }
329 527
330 528 //
331 529 // Deprecated interface
332 530 //
531
532 #[deprecated(note = "Use `pointsource_fw_reg`")]
533 pub fn pointsource_fw<'a, F, I, A, GA, BTA, S, const N : usize>(
534 opA : &'a A,
535 b : &A::Observable,
536 α : F,
537 //domain : Cube<F, N>,
538 config : &FWConfig<F>,
539 iterator : I,
540 plotter : SeqPlotter<F, N>,
541 ) -> DiscreteMeasure<Loc<F, N>, F>
542 where F : Float + ToNalgebraRealField,
543 I : AlgIteratorFactory<IterInfo<F, N>>,
544 for<'b> &'b A::Observable : std::ops::Neg<Output=A::Observable>,
545 //+ std::ops::Mul<F, Output=A::Observable>, <-- FIXME: compiler overflow
546 A::Observable : std::ops::MulAssign<F>,
547 GA : SupportGenerator<F, N, SupportType = S, Id = usize> + Clone,
548 A : ForwardModel<Loc<F, N>, F, PreadjointCodomain = BTFN<F, GA, BTA, N>>,
549 BTA : BTSearch<F, N, Data=usize, Agg=Bounds<F>>,
550 S: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N>,
551 BTNodeLookup: BTNode<F, usize, Bounds<F>, N>,
552 Cube<F, N>: P2Minimise<Loc<F, N>, F>,
553 PlotLookup : Plotting<N>,
554 DiscreteMeasure<Loc<F, N>, F> : SpikeMerging<F> {
555
556 pointsource_fw_reg(opA, b, NonnegRadonRegTerm(α), config, iterator, plotter)
557 }
558
559 #[deprecated(note = "Use `WeightOptim::optimise_weights`")]
560 pub fn optimise_weights<'a, F, A, I, const N : usize>(
561 μ : &mut DiscreteMeasure<Loc<F, N>, F>,
562 opA : &'a A,
563 b : &A::Observable,
564 α : F,
565 findim_data : &FindimData<F>,
566 inner : &InnerSettings<F>,
567 iterator : I
568 ) -> usize
569 where F : Float + ToNalgebraRealField,
570 I : AlgIteratorFactory<F>,
571 A : ForwardModel<Loc<F, N>, F>
572 {
573 NonnegRadonRegTerm(α).optimise_weights(μ, opA, b, findim_data, inner, iterator)
574 }

mercurial