78 */ |
78 */ |
79 |
79 |
80 use numeric_literals::replace_float_literals; |
80 use numeric_literals::replace_float_literals; |
81 use serde::{Serialize, Deserialize}; |
81 use serde::{Serialize, Deserialize}; |
82 use colored::Colorize; |
82 use colored::Colorize; |
83 use nalgebra::DVector; |
83 |
84 |
84 use alg_tools::iterate::AlgIteratorFactory; |
85 use alg_tools::iterate::{ |
|
86 AlgIteratorFactory, |
|
87 AlgIteratorIteration, |
|
88 AlgIterator, |
|
89 }; |
|
90 use alg_tools::euclidean::Euclidean; |
85 use alg_tools::euclidean::Euclidean; |
91 use alg_tools::linops::{Mapping, GEMV}; |
86 use alg_tools::linops::{Mapping, GEMV}; |
92 use alg_tools::sets::Cube; |
|
93 use alg_tools::loc::Loc; |
|
94 use alg_tools::bisection_tree::{ |
|
95 BTFN, |
|
96 PreBTFN, |
|
97 Bounds, |
|
98 BTNodeLookup, |
|
99 BTNode, |
|
100 BTSearch, |
|
101 P2Minimise, |
|
102 SupportGenerator, |
|
103 LocalAnalysis, |
|
104 BothGenerators, |
|
105 }; |
|
106 use alg_tools::mapping::RealMapping; |
87 use alg_tools::mapping::RealMapping; |
107 use alg_tools::nalgebra_support::ToNalgebraRealField; |
88 use alg_tools::nalgebra_support::ToNalgebraRealField; |
108 use alg_tools::instance::Instance; |
89 use alg_tools::instance::Instance; |
109 use alg_tools::norms::Linfinity; |
|
110 |
90 |
111 use crate::types::*; |
91 use crate::types::*; |
112 use crate::measures::{ |
92 use crate::measures::{ |
113 DiscreteMeasure, |
93 DiscreteMeasure, |
114 RNDM, |
94 RNDM, |
115 DeltaMeasure, |
|
116 Radon, |
|
117 }; |
95 }; |
118 use crate::measures::merging::{ |
96 use crate::measures::merging::{ |
119 SpikeMergingMethod, |
97 SpikeMergingMethod, |
120 SpikeMerging, |
98 SpikeMerging, |
121 }; |
99 }; |
122 use crate::forward_model::{ |
100 use crate::forward_model::{ |
123 ForwardModel, |
101 ForwardModel, |
124 AdjointProductBoundedBy |
102 AdjointProductBoundedBy, |
125 }; |
103 }; |
126 use crate::seminorms::DiscreteMeasureOp; |
|
127 use crate::subproblem::{ |
|
128 InnerSettings, |
|
129 InnerMethod, |
|
130 }; |
|
131 use crate::tolerance::Tolerance; |
|
132 use crate::plot::{ |
104 use crate::plot::{ |
133 SeqPlotter, |
105 SeqPlotter, |
134 Plotting, |
106 Plotting, |
135 PlotLookup |
107 PlotLookup |
136 }; |
108 }; |
137 use crate::regularisation::RegTerm; |
109 use crate::regularisation::RegTerm; |
138 use crate::dataterm::{ |
110 use crate::dataterm::{ |
139 calculate_residual, |
111 calculate_residual, |
140 L2Squared, |
112 L2Squared, |
141 DataTerm, |
113 DataTerm, |
|
114 }; |
|
115 pub use crate::prox_penalty::{ |
|
116 FBGenericConfig, |
|
117 ProxPenalty |
142 }; |
118 }; |
143 |
119 |
144 /// Settings for [`pointsource_fb_reg`]. |
120 /// Settings for [`pointsource_fb_reg`]. |
145 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] |
121 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] |
146 #[serde(default)] |
122 #[serde(default)] |
149 pub τ0 : F, |
125 pub τ0 : F, |
150 /// Generic parameters |
126 /// Generic parameters |
151 pub generic : FBGenericConfig<F>, |
127 pub generic : FBGenericConfig<F>, |
152 } |
128 } |
153 |
129 |
154 /// Settings for the solution of the stepwise optimality condition. |
|
155 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] |
|
156 #[serde(default)] |
|
157 pub struct FBGenericConfig<F : Float> { |
|
158 /// Tolerance for point insertion. |
|
159 pub tolerance : Tolerance<F>, |
|
160 |
|
161 /// Stop looking for predual maximum (where to isert a new point) below |
|
162 /// `tolerance` multiplied by this factor. |
|
163 /// |
|
164 /// Not used by [`super::radon_fb`]. |
|
165 pub insertion_cutoff_factor : F, |
|
166 |
|
167 /// Settings for branch and bound refinement when looking for predual maxima |
|
168 pub refinement : RefinementSettings<F>, |
|
169 |
|
170 /// Maximum insertions within each outer iteration |
|
171 /// |
|
172 /// Not used by [`super::radon_fb`]. |
|
173 pub max_insertions : usize, |
|
174 |
|
175 /// Pair `(n, m)` for maximum insertions `m` on first `n` iterations. |
|
176 /// |
|
177 /// Not used by [`super::radon_fb`]. |
|
178 pub bootstrap_insertions : Option<(usize, usize)>, |
|
179 |
|
180 /// Inner method settings |
|
181 pub inner : InnerSettings<F>, |
|
182 |
|
183 /// Spike merging method |
|
184 pub merging : SpikeMergingMethod<F>, |
|
185 |
|
186 /// Tolerance multiplier for merges |
|
187 pub merge_tolerance_mult : F, |
|
188 |
|
189 /// Spike merging method after the last step |
|
190 pub final_merging : SpikeMergingMethod<F>, |
|
191 |
|
192 /// Iterations between merging heuristic tries |
|
193 pub merge_every : usize, |
|
194 |
|
195 // /// Save $μ$ for postprocessing optimisation |
|
196 // pub postprocessing : bool |
|
197 } |
|
198 |
|
199 #[replace_float_literals(F::cast_from(literal))] |
130 #[replace_float_literals(F::cast_from(literal))] |
200 impl<F : Float> Default for FBConfig<F> { |
131 impl<F : Float> Default for FBConfig<F> { |
201 fn default() -> Self { |
132 fn default() -> Self { |
202 FBConfig { |
133 FBConfig { |
203 τ0 : 0.99, |
134 τ0 : 0.99, |
204 generic : Default::default(), |
135 generic : Default::default(), |
205 } |
136 } |
206 } |
137 } |
207 } |
|
208 |
|
209 #[replace_float_literals(F::cast_from(literal))] |
|
210 impl<F : Float> Default for FBGenericConfig<F> { |
|
211 fn default() -> Self { |
|
212 FBGenericConfig { |
|
213 tolerance : Default::default(), |
|
214 insertion_cutoff_factor : 1.0, |
|
215 refinement : Default::default(), |
|
216 max_insertions : 100, |
|
217 //bootstrap_insertions : None, |
|
218 bootstrap_insertions : Some((10, 1)), |
|
219 inner : InnerSettings { |
|
220 method : InnerMethod::Default, |
|
221 .. Default::default() |
|
222 }, |
|
223 merging : SpikeMergingMethod::None, |
|
224 //merging : Default::default(), |
|
225 final_merging : Default::default(), |
|
226 merge_every : 10, |
|
227 merge_tolerance_mult : 2.0, |
|
228 // postprocessing : false, |
|
229 } |
|
230 } |
|
231 } |
|
232 |
|
233 impl<F : Float> FBGenericConfig<F> { |
|
234 /// Check if merging should be attempted this iteration |
|
235 pub fn merge_now<I : AlgIterator>(&self, state : &AlgIteratorIteration<I>) -> bool { |
|
236 state.iteration() % self.merge_every == 0 |
|
237 } |
|
238 } |
|
239 |
|
240 /// TODO: document. |
|
241 /// `μ_base + ν_delta` is the base point, where `μ` and `μ_base` are assumed to have the same spike |
|
242 /// locations, while `ν_delta` may have different locations. |
|
243 #[replace_float_literals(F::cast_from(literal))] |
|
244 pub(crate) fn insert_and_reweigh< |
|
245 'a, F, GA, 𝒟, BTA, G𝒟, S, K, Reg, I, const N : usize |
|
246 >( |
|
247 μ : &mut RNDM<F, N>, |
|
248 τv : &BTFN<F, GA, BTA, N>, |
|
249 μ_base : &RNDM<F, N>, |
|
250 ν_delta: Option<&RNDM<F, N>>, |
|
251 op𝒟 : &'a 𝒟, |
|
252 op𝒟norm : F, |
|
253 τ : F, |
|
254 ε : F, |
|
255 config : &FBGenericConfig<F>, |
|
256 reg : &Reg, |
|
257 state : &AlgIteratorIteration<I>, |
|
258 stats : &mut IterInfo<F, N>, |
|
259 ) -> (BTFN<F, BothGenerators<GA, G𝒟>, BTA, N>, bool) |
|
260 where F : Float + ToNalgebraRealField, |
|
261 GA : SupportGenerator<F, N, SupportType = S, Id = usize> + Clone, |
|
262 BTA : BTSearch<F, N, Data=usize, Agg=Bounds<F>>, |
|
263 G𝒟 : SupportGenerator<F, N, SupportType = K, Id = usize> + Clone, |
|
264 𝒟 : DiscreteMeasureOp<Loc<F, N>, F, PreCodomain = PreBTFN<F, G𝒟, N>>, |
|
265 𝒟::Codomain : RealMapping<F, N>, |
|
266 S: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N>, |
|
267 K: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N>, |
|
268 BTNodeLookup: BTNode<F, usize, Bounds<F>, N>, |
|
269 Reg : RegTerm<F, N>, |
|
270 I : AlgIterator { |
|
271 |
|
272 // Maximum insertion count and measure difference calculation depend on insertion style. |
|
273 let (max_insertions, warn_insertions) = match (state.iteration(), config.bootstrap_insertions) { |
|
274 (i, Some((l, k))) if i <= l => (k, false), |
|
275 _ => (config.max_insertions, !state.is_quiet()), |
|
276 }; |
|
277 |
|
278 let ω0 = match ν_delta { |
|
279 None => op𝒟.apply(μ_base), |
|
280 Some(ν) => op𝒟.apply(μ_base + ν), |
|
281 }; |
|
282 |
|
283 // Add points to support until within error tolerance or maximum insertion count reached. |
|
284 let mut count = 0; |
|
285 let (within_tolerances, d) = 'insertion: loop { |
|
286 if μ.len() > 0 { |
|
287 // Form finite-dimensional subproblem. The subproblem references to the original μ^k |
|
288 // from the beginning of the iteration are all contained in the immutable c and g. |
|
289 // TODO: observe negation of -τv after switch from minus_τv: finite-dimensional |
|
290 // problems have not yet been updated to sign change. |
|
291 let à = op𝒟.findim_matrix(μ.iter_locations()); |
|
292 let g̃ = DVector::from_iterator(μ.len(), |
|
293 μ.iter_locations() |
|
294 .map(|ζ| ω0.apply(ζ) - τv.apply(ζ)) |
|
295 .map(F::to_nalgebra_mixed)); |
|
296 let mut x = μ.masses_dvector(); |
|
297 |
|
298 // The gradient of the forward component of the inner objective is C^*𝒟Cx - g̃. |
|
299 // We have |C^*𝒟Cx|_2 = sup_{|z|_2 ≤ 1} ⟨z, C^*𝒟Cx⟩ = sup_{|z|_2 ≤ 1} ⟨Cz|𝒟Cx⟩ |
|
300 // ≤ sup_{|z|_2 ≤ 1} |Cz|_ℳ |𝒟Cx|_∞ ≤ sup_{|z|_2 ≤ 1} |Cz|_ℳ |𝒟| |Cx|_ℳ |
|
301 // ≤ sup_{|z|_2 ≤ 1} |z|_1 |𝒟| |x|_1 ≤ sup_{|z|_2 ≤ 1} n |z|_2 |𝒟| |x|_2 |
|
302 // = n |𝒟| |x|_2, where n is the number of points. Therefore |
|
303 let Ã_normest = op𝒟norm * F::cast_from(μ.len()); |
|
304 |
|
305 // Solve finite-dimensional subproblem. |
|
306 stats.inner_iters += reg.solve_findim(&Ã, &g̃, τ, &mut x, Ã_normest, ε, config); |
|
307 |
|
308 // Update masses of μ based on solution of finite-dimensional subproblem. |
|
309 μ.set_masses_dvector(&x); |
|
310 } |
|
311 |
|
312 // Form d = τv + 𝒟μ - ω0 = τv + 𝒟(μ - μ^k) for checking the proximate optimality |
|
313 // conditions in the predual space, and finding new points for insertion, if necessary. |
|
314 let mut d = τv + match ν_delta { |
|
315 None => op𝒟.preapply(μ.sub_matching(μ_base)), |
|
316 Some(ν) => op𝒟.preapply(μ.sub_matching(μ_base) - ν) |
|
317 }; |
|
318 |
|
319 // If no merging heuristic is used, let's be more conservative about spike insertion, |
|
320 // and skip it after first round. If merging is done, being more greedy about spike |
|
321 // insertion also seems to improve performance. |
|
322 let skip_by_rough_check = if let SpikeMergingMethod::None = config.merging { |
|
323 false |
|
324 } else { |
|
325 count > 0 |
|
326 }; |
|
327 |
|
328 // Find a spike to insert, if needed |
|
329 let (ξ, _v_ξ, in_bounds) = match reg.find_tolerance_violation( |
|
330 &mut d, τ, ε, skip_by_rough_check, config |
|
331 ) { |
|
332 None => break 'insertion (true, d), |
|
333 Some(res) => res, |
|
334 }; |
|
335 |
|
336 // Break if maximum insertion count reached |
|
337 if count >= max_insertions { |
|
338 break 'insertion (in_bounds, d) |
|
339 } |
|
340 |
|
341 // No point in optimising the weight here; the finite-dimensional algorithm is fast. |
|
342 *μ += DeltaMeasure { x : ξ, α : 0.0 }; |
|
343 count += 1; |
|
344 stats.inserted += 1; |
|
345 }; |
|
346 |
|
347 if !within_tolerances && warn_insertions { |
|
348 // Complain (but continue) if we failed to get within tolerances |
|
349 // by inserting more points. |
|
350 let err = format!("Maximum insertions reached without achieving \ |
|
351 subproblem solution tolerance"); |
|
352 println!("{}", err.red()); |
|
353 } |
|
354 |
|
355 (d, within_tolerances) |
|
356 } |
138 } |
357 |
139 |
358 pub(crate) fn prune_with_stats<F : Float, const N : usize>( |
140 pub(crate) fn prune_with_stats<F : Float, const N : usize>( |
359 μ : &mut RNDM<F, N>, |
141 μ : &mut RNDM<F, N>, |
360 ) -> usize { |
142 ) -> usize { |
407 /// of [`std::sync::Arc`] to only update relevant parts of the bisection tree when adding functions. |
189 /// of [`std::sync::Arc`] to only update relevant parts of the bisection tree when adding functions. |
408 /// |
190 /// |
409 /// Returns the final iterate. |
191 /// Returns the final iterate. |
410 #[replace_float_literals(F::cast_from(literal))] |
192 #[replace_float_literals(F::cast_from(literal))] |
411 pub fn pointsource_fb_reg< |
193 pub fn pointsource_fb_reg< |
412 'a, F, I, A, GA, 𝒟, BTA, G𝒟, S, K, Reg, const N : usize |
194 F, I, A, Reg, P, const N : usize |
413 >( |
195 >( |
414 opA : &'a A, |
196 opA : &A, |
415 b : &A::Observable, |
197 b : &A::Observable, |
416 reg : Reg, |
198 reg : Reg, |
417 op𝒟 : &'a 𝒟, |
199 prox_penalty : &P, |
418 fbconfig : &FBConfig<F>, |
200 fbconfig : &FBConfig<F>, |
419 iterator : I, |
201 iterator : I, |
420 mut plotter : SeqPlotter<F, N>, |
202 mut plotter : SeqPlotter<F, N>, |
421 ) -> RNDM<F, N> |
203 ) -> RNDM<F, N> |
422 where F : Float + ToNalgebraRealField, |
204 where |
423 I : AlgIteratorFactory<IterInfo<F, N>>, |
205 F : Float + ToNalgebraRealField, |
424 for<'b> &'b A::Observable : std::ops::Neg<Output=A::Observable>, |
206 I : AlgIteratorFactory<IterInfo<F, N>>, |
425 GA : SupportGenerator<F, N, SupportType = S, Id = usize> + Clone, |
207 for<'b> &'b A::Observable : std::ops::Neg<Output=A::Observable>, |
426 A : ForwardModel<RNDM<F, N>, F, PreadjointCodomain = BTFN<F, GA, BTA, N>> |
208 A : ForwardModel<RNDM<F, N>, F> |
427 + AdjointProductBoundedBy<RNDM<F, N>, 𝒟, FloatType=F>, |
209 + AdjointProductBoundedBy<RNDM<F, N>, P, FloatType=F>, |
428 BTA : BTSearch<F, N, Data=usize, Agg=Bounds<F>>, |
210 A::PreadjointCodomain : RealMapping<F, N>, |
429 G𝒟 : SupportGenerator<F, N, SupportType = K, Id = usize> + Clone, |
211 PlotLookup : Plotting<N>, |
430 𝒟 : DiscreteMeasureOp<Loc<F, N>, F, PreCodomain = PreBTFN<F, G𝒟, N>>, |
212 RNDM<F, N> : SpikeMerging<F>, |
431 𝒟::Codomain : RealMapping<F, N>, |
213 Reg : RegTerm<F, N>, |
432 S: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N>, |
214 P : ProxPenalty<F, A::PreadjointCodomain, Reg, N>, |
433 K: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N>, |
215 { |
434 BTNodeLookup: BTNode<F, usize, Bounds<F>, N>, |
|
435 Cube<F, N>: P2Minimise<Loc<F, N>, F>, |
|
436 PlotLookup : Plotting<N>, |
|
437 RNDM<F, N> : SpikeMerging<F>, |
|
438 Reg : RegTerm<F, N> { |
|
439 |
216 |
440 // Set up parameters |
217 // Set up parameters |
441 let config = &fbconfig.generic; |
218 let config = &fbconfig.generic; |
442 let op𝒟norm = op𝒟.opnorm_bound(Radon, Linfinity); |
219 let τ = fbconfig.τ0/opA.adjoint_product_bound(prox_penalty).unwrap(); |
443 let τ = fbconfig.τ0/opA.adjoint_product_bound(&op𝒟).unwrap(); |
|
444 // We multiply tolerance by τ for FB since our subproblems depending on tolerances are scaled |
220 // We multiply tolerance by τ for FB since our subproblems depending on tolerances are scaled |
445 // by τ compared to the conditional gradient approach. |
221 // by τ compared to the conditional gradient approach. |
446 let tolerance = config.tolerance * τ * reg.tolerance_scaling(); |
222 let tolerance = config.tolerance * τ * reg.tolerance_scaling(); |
447 let mut ε = tolerance.initial(); |
223 let mut ε = tolerance.initial(); |
448 |
224 |
463 let mut stats = IterInfo::new(); |
239 let mut stats = IterInfo::new(); |
464 |
240 |
465 // Run the algorithm |
241 // Run the algorithm |
466 for state in iterator.iter_init(|| full_stats(&residual, &μ, ε, stats.clone())) { |
242 for state in iterator.iter_init(|| full_stats(&residual, &μ, ε, stats.clone())) { |
467 // Calculate smooth part of surrogate model. |
243 // Calculate smooth part of surrogate model. |
468 let τv = opA.preadjoint().apply(residual * τ); |
244 let mut τv = opA.preadjoint().apply(residual * τ); |
469 |
245 |
470 // Save current base point |
246 // Save current base point |
471 let μ_base = μ.clone(); |
247 let μ_base = μ.clone(); |
472 |
248 |
473 // Insert and reweigh |
249 // Insert and reweigh |
474 let (d, _within_tolerances) = insert_and_reweigh( |
250 let (maybe_d, _within_tolerances) = prox_penalty.insert_and_reweigh( |
475 &mut μ, &τv, &μ_base, None, |
251 &mut μ, &mut τv, &μ_base, None, |
476 op𝒟, op𝒟norm, |
|
477 τ, ε, |
252 τ, ε, |
478 config, ®, &state, &mut stats |
253 config, ®, &state, &mut stats |
479 ); |
254 ); |
480 |
255 |
481 // Prune and possibly merge spikes |
256 // Prune and possibly merge spikes |
482 if config.merge_now(&state) { |
257 if config.merge_now(&state) { |
483 stats.merged += μ.merge_spikes(config.merging, |μ_candidate| { |
258 stats.merged += prox_penalty.merge_spikes(&mut μ, &mut τv, &μ_base, τ, ε, config, ®); |
484 let mut d = &τv + op𝒟.preapply(μ_candidate.sub_matching(&μ_base)); |
|
485 reg.verify_merge_candidate(&mut d, μ_candidate, τ, ε, &config) |
|
486 }); |
|
487 } |
259 } |
|
260 |
488 stats.pruned += prune_with_stats(&mut μ); |
261 stats.pruned += prune_with_stats(&mut μ); |
489 |
262 |
490 // Update residual |
263 // Update residual |
491 residual = calculate_residual(&μ, opA, b); |
264 residual = calculate_residual(&μ, opA, b); |
492 |
265 |
493 let iter = state.iteration(); |
266 let iter = state.iteration(); |
494 stats.this_iters += 1; |
267 stats.this_iters += 1; |
495 |
268 |
496 // Give statistics if needed |
269 // Give statistics if needed |
497 state.if_verbose(|| { |
270 state.if_verbose(|| { |
498 plotter.plot_spikes(iter, Some(&d), Some(&τv), &μ); |
271 plotter.plot_spikes(iter, maybe_d.as_ref(), Some(&τv), &μ); |
499 full_stats(&residual, &μ, ε, std::mem::replace(&mut stats, IterInfo::new())) |
272 full_stats(&residual, &μ, ε, std::mem::replace(&mut stats, IterInfo::new())) |
500 }); |
273 }); |
501 |
274 |
502 // Update main tolerance for next iteration |
275 // Update main tolerance for next iteration |
503 ε = tolerance.update(ε, iter); |
276 ε = tolerance.update(ε, iter); |
524 /// of [`std::sync::Arc`] to only update relevant parts of the bisection tree when adding functions. |
297 /// of [`std::sync::Arc`] to only update relevant parts of the bisection tree when adding functions. |
525 /// |
298 /// |
526 /// Returns the final iterate. |
299 /// Returns the final iterate. |
527 #[replace_float_literals(F::cast_from(literal))] |
300 #[replace_float_literals(F::cast_from(literal))] |
528 pub fn pointsource_fista_reg< |
301 pub fn pointsource_fista_reg< |
529 'a, F, I, A, GA, 𝒟, BTA, G𝒟, S, K, Reg, const N : usize |
302 F, I, A, Reg, P, const N : usize |
530 >( |
303 >( |
531 opA : &'a A, |
304 opA : &A, |
532 b : &A::Observable, |
305 b : &A::Observable, |
533 reg : Reg, |
306 reg : Reg, |
534 op𝒟 : &'a 𝒟, |
307 prox_penalty : &P, |
535 fbconfig : &FBConfig<F>, |
308 fbconfig : &FBConfig<F>, |
536 iterator : I, |
309 iterator : I, |
537 mut plotter : SeqPlotter<F, N>, |
310 mut plotter : SeqPlotter<F, N>, |
538 ) -> RNDM<F, N> |
311 ) -> RNDM<F, N> |
539 where F : Float + ToNalgebraRealField, |
312 where |
540 I : AlgIteratorFactory<IterInfo<F, N>>, |
313 F : Float + ToNalgebraRealField, |
541 for<'b> &'b A::Observable : std::ops::Neg<Output=A::Observable>, |
314 I : AlgIteratorFactory<IterInfo<F, N>>, |
542 GA : SupportGenerator<F, N, SupportType = S, Id = usize> + Clone, |
315 for<'b> &'b A::Observable : std::ops::Neg<Output=A::Observable>, |
543 A : ForwardModel<RNDM<F, N>, F, PreadjointCodomain = BTFN<F, GA, BTA, N>> |
316 A : ForwardModel<RNDM<F, N>, F> |
544 + AdjointProductBoundedBy<RNDM<F, N>, 𝒟, FloatType=F>, |
317 + AdjointProductBoundedBy<RNDM<F, N>, P, FloatType=F>, |
545 BTA : BTSearch<F, N, Data=usize, Agg=Bounds<F>>, |
318 A::PreadjointCodomain : RealMapping<F, N>, |
546 G𝒟 : SupportGenerator<F, N, SupportType = K, Id = usize> + Clone, |
319 PlotLookup : Plotting<N>, |
547 𝒟 : DiscreteMeasureOp<Loc<F, N>, F, PreCodomain = PreBTFN<F, G𝒟, N>>, |
320 RNDM<F, N> : SpikeMerging<F>, |
548 𝒟::Codomain : RealMapping<F, N>, |
321 Reg : RegTerm<F, N>, |
549 S: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N>, |
322 P : ProxPenalty<F, A::PreadjointCodomain, Reg, N>, |
550 K: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N>, |
323 { |
551 BTNodeLookup: BTNode<F, usize, Bounds<F>, N>, |
|
552 Cube<F, N>: P2Minimise<Loc<F, N>, F>, |
|
553 PlotLookup : Plotting<N>, |
|
554 RNDM<F, N> : SpikeMerging<F>, |
|
555 Reg : RegTerm<F, N> { |
|
556 |
324 |
557 // Set up parameters |
325 // Set up parameters |
558 let config = &fbconfig.generic; |
326 let config = &fbconfig.generic; |
559 let op𝒟norm = op𝒟.opnorm_bound(Radon, Linfinity); |
327 let τ = fbconfig.τ0/opA.adjoint_product_bound(prox_penalty).unwrap(); |
560 let τ = fbconfig.τ0/opA.adjoint_product_bound(&op𝒟).unwrap(); |
|
561 let mut λ = 1.0; |
328 let mut λ = 1.0; |
562 // We multiply tolerance by τ for FB since our subproblems depending on tolerances are scaled |
329 // We multiply tolerance by τ for FB since our subproblems depending on tolerances are scaled |
563 // by τ compared to the conditional gradient approach. |
330 // by τ compared to the conditional gradient approach. |
564 let tolerance = config.tolerance * τ * reg.tolerance_scaling(); |
331 let tolerance = config.tolerance * τ * reg.tolerance_scaling(); |
565 let mut ε = tolerance.initial(); |
332 let mut ε = tolerance.initial(); |