| 75 |
75 |
| 76 We solve this with either SSN or FB as determined by |
76 We solve this with either SSN or FB as determined by |
| 77 [`crate::subproblem::InnerSettings`] in [`InsertionConfig::inner`]. |
77 [`crate::subproblem::InnerSettings`] in [`InsertionConfig::inner`]. |
| 78 */ |
78 */ |
| 79 |
79 |
| |
80 use crate::domain::{AdmissibleDomain, FullSpace}; |
| 80 use crate::measures::merging::SpikeMerging; |
81 use crate::measures::merging::SpikeMerging; |
| 81 use crate::measures::{DiscreteMeasure, RNDM}; |
82 use crate::measures::{DiscreteMeasure, RNDM}; |
| 82 use crate::plot::Plotter; |
83 use crate::plot::Plotter; |
| 83 pub use crate::prox_penalty::{InsertionConfig, ProxPenalty, StepLengthBound}; |
84 pub use crate::prox_penalty::{InsertionConfig, ProxPenalty, StepLengthBound}; |
| 84 use crate::regularisation::RegTerm; |
85 use crate::regularisation::RegTerm; |
| 87 use alg_tools::instance::Instance; |
88 use alg_tools::instance::Instance; |
| 88 use alg_tools::iterate::AlgIteratorFactory; |
89 use alg_tools::iterate::AlgIteratorFactory; |
| 89 use alg_tools::mapping::DifferentiableMapping; |
90 use alg_tools::mapping::DifferentiableMapping; |
| 90 use alg_tools::nalgebra_support::ToNalgebraRealField; |
91 use alg_tools::nalgebra_support::ToNalgebraRealField; |
| 91 use colored::Colorize; |
92 use colored::Colorize; |
| |
93 use measures::DeltaMeasure; |
| 92 use numeric_literals::replace_float_literals; |
94 use numeric_literals::replace_float_literals; |
| 93 use serde::{Deserialize, Serialize}; |
95 use serde::{Deserialize, Serialize}; |
| 94 |
96 |
| 95 /// Settings for [`pointsource_fb_reg`]. |
97 /// Settings for [`pointsource_fb_reg`]. |
| 96 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] |
98 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] |
| 144 /// |
146 /// |
| 145 /// For details on the mathematical formulation, see the [module level](self) documentation. |
147 /// For details on the mathematical formulation, see the [module level](self) documentation. |
| 146 /// |
148 /// |
| 147 /// Returns the final iterate. |
149 /// Returns the final iterate. |
| 148 #[replace_float_literals(F::cast_from(literal))] |
150 #[replace_float_literals(F::cast_from(literal))] |
| 149 pub fn pointsource_fb_reg<F, I, Dat, Reg, P, Plot, const N: usize>( |
151 pub fn pointsource_fb_dom<F, I, Dat, Reg, P, Plot, const N: usize>( |
| 150 f: &Dat, |
152 f: &Dat, |
| 151 reg: &Reg, |
153 reg: &Reg, |
| 152 prox_penalty: &P, |
154 prox_penalty: &P, |
| 153 fbconfig: &FBConfig<F>, |
155 fbconfig: &FBConfig<F>, |
| 154 iterator: I, |
156 iterator: I, |
| 155 mut plotter: Plot, |
157 mut plotter: Plot, |
| 156 μ0: Option<RNDM<N, F>>, |
158 μ0: Option<RNDM<N, F>>, |
| |
159 domain: &mut impl AdmissibleDomain<Loc<N, F>>, |
| 157 ) -> DynResult<RNDM<N, F>> |
160 ) -> DynResult<RNDM<N, F>> |
| 158 where |
161 where |
| 159 F: Float + ToNalgebraRealField, |
162 F: Float + ToNalgebraRealField, |
| 160 I: AlgIteratorFactory<IterInfo<F>>, |
163 I: AlgIteratorFactory<IterInfo<F>>, |
| 161 RNDM<N, F>: SpikeMerging<F>, |
164 RNDM<N, F>: SpikeMerging<F>, |
| 186 }; |
189 }; |
| 187 let mut stats = IterInfo::new(); |
190 let mut stats = IterInfo::new(); |
| 188 |
191 |
| 189 // Run the algorithm |
192 // Run the algorithm |
| 190 for state in iterator.iter_init(|| full_stats(&μ, ε, stats.clone())) { |
193 for state in iterator.iter_init(|| full_stats(&μ, ε, stats.clone())) { |
| 191 let maybe_μ_base = config.if_merge_now(&state, |_| μ.clone()); |
194 let maybe_μ_base = config.if_merge_now_check(&state, || μ.clone()); |
| 192 let μ_base_len = μ.len(); |
195 let μ_base_len = μ.len(); |
| 193 |
196 |
| 194 // Calculate smooth part of surrogate model. |
197 // Calculate smooth part of surrogate model. |
| 195 let mut τv = f.differential(&μ) * τ; |
198 let mut τv = f.differential(&μ) * τ; |
| 196 |
199 |
| 198 prox_penalty.insert_and_reweigh(&mut μ, &mut τv, τ, ε, config, ®, &state, &mut stats)?; |
201 prox_penalty.insert_and_reweigh(&mut μ, &mut τv, τ, ε, config, ®, &state, &mut stats)?; |
| 199 |
202 |
| 200 stats.inserted += μ.len() - μ_base_len; |
203 stats.inserted += μ.len() - μ_base_len; |
| 201 |
204 |
| 202 // Prune and possibly merge spikes |
205 // Prune and possibly merge spikes |
| 203 if let Some(μ_base) = maybe_μ_base { |
206 let did_merge = if let Some(μ_base) = maybe_μ_base { |
| |
207 // Merge spikes. |
| |
208 // This crucially expects the merge routine to be stable with respect to spike locations, |
| |
209 // and not to performing any pruning. That is be to done below simultaneously for γ. |
| 204 config.if_merge_now(&state, |ins| { |
210 config.if_merge_now(&state, |ins| { |
| 205 stats.merged += prox_penalty.merge_spikes( |
211 stats.merged += prox_penalty.merge_spikes( |
| 206 &mut μ, |
212 &mut μ, |
| 207 &mut τv, |
213 &mut τv, |
| 208 &μ_base, |
214 &μ_base, |
| 209 τ, |
215 τ, |
| 210 ε, |
216 ε, |
| 211 ins, |
217 ins, |
| 212 ®, |
218 ®, |
| 213 Some(|μ̃: &RNDM<N, F>| f.apply(μ̃) + reg.apply(μ̃)), |
219 Some(|μ̃: &RNDM<N, F>| f.apply(μ̃) + reg.apply(μ̃)), |
| 214 ) |
220 ); |
| 215 }); |
221 stats.pruned += prune_with_stats(&mut μ); |
| 216 } |
222 |
| 217 |
223 if config.random_insertions { |
| 218 stats.pruned += prune_with_stats(&mut μ); |
224 if let Some(x) = domain.random_point() { |
| 219 |
225 μ += DeltaMeasure { x, α: 0.0 }; |
| 220 // Do extra weight optimisation step heuristic |
226 stats.inserted += 1; |
| 221 for _ in 0..config.extra_weight_optimisation_steps { |
227 } |
| 222 τv = f.differential(&μ) * τ; |
228 } |
| 223 prox_penalty.reweigh(&mut μ, &mut τv, τ, ε, config, ®, &state, &mut stats)?; |
229 |
| 224 } |
230 // Do extra weight optimisation step heuristic |
| 225 |
231 for _ in 1..config.extra_weight_optimisation_steps { |
| 226 if config.extra_weight_optimisation_steps > 0 { |
232 τv = f.differential(&μ) * τ; |
| |
233 prox_penalty |
| |
234 .reweigh(&mut μ, &mut τv, τ, ε, &config, ®, &state, &mut stats)?; |
| |
235 } |
| |
236 Ok(()) |
| |
237 })? |
| |
238 } else { |
| |
239 None |
| |
240 }; |
| |
241 |
| |
242 if did_merge.is_none() { |
| 227 stats.pruned += prune_with_stats(&mut μ); |
243 stats.pruned += prune_with_stats(&mut μ); |
| 228 } |
244 } |
| 229 |
245 |
| 230 let iter = state.iteration(); |
246 let iter = state.iteration(); |
| 231 stats.this_iters += 1; |
247 stats.this_iters += 1; |
| 240 ε = tolerance.update(ε, iter); |
256 ε = tolerance.update(ε, iter); |
| 241 } |
257 } |
| 242 |
258 |
| 243 //postprocess(μ_prev, config, f) |
259 //postprocess(μ_prev, config, f) |
| 244 postprocess(μ, config, |μ̃| f.apply(μ̃) + reg.apply(μ̃)) |
260 postprocess(μ, config, |μ̃| f.apply(μ̃) + reg.apply(μ̃)) |
| |
261 } |
| |
262 |
| |
263 #[deprecated] |
| |
264 pub fn pointsource_fb_reg<F, I, Dat, Reg, P, Plot, const N: usize>( |
| |
265 f: &Dat, |
| |
266 reg: &Reg, |
| |
267 prox_penalty: &P, |
| |
268 fbconfig: &FBConfig<F>, |
| |
269 iterator: I, |
| |
270 plotter: Plot, |
| |
271 μ0: Option<RNDM<N, F>>, |
| |
272 ) -> DynResult<RNDM<N, F>> |
| |
273 where |
| |
274 F: Float + ToNalgebraRealField, |
| |
275 I: AlgIteratorFactory<IterInfo<F>>, |
| |
276 RNDM<N, F>: SpikeMerging<F>, |
| |
277 Dat: DifferentiableMapping<RNDM<N, F>, Codomain = F>, |
| |
278 Dat::DerivativeDomain: ClosedMul<F>, |
| |
279 Reg: RegTerm<Loc<N, F>, F>, |
| |
280 P: ProxPenalty<Loc<N, F>, Dat::DerivativeDomain, Reg, F> + StepLengthBound<F, Dat>, |
| |
281 Plot: Plotter<P::ReturnMapping, Dat::DerivativeDomain, RNDM<N, F>>, |
| |
282 { |
| |
283 pointsource_fb_dom( |
| |
284 f, |
| |
285 reg, |
| |
286 prox_penalty, |
| |
287 fbconfig, |
| |
288 iterator, |
| |
289 plotter, |
| |
290 μ0, |
| |
291 &mut FullSpace, |
| |
292 ) |
| 245 } |
293 } |
| 246 |
294 |
| 247 /// Iteratively solve the pointsource localisation problem using inertial forward-backward splitting. |
295 /// Iteratively solve the pointsource localisation problem using inertial forward-backward splitting. |
| 248 /// |
296 /// |
| 249 /// The settings in `config` have their [respective documentation](FBConfig). `opA` is the |
297 /// The settings in `config` have their [respective documentation](FBConfig). `opA` is the |
| 310 |
358 |
| 311 stats.inserted += μ.len() - μ_base_len; |
359 stats.inserted += μ.len() - μ_base_len; |
| 312 |
360 |
| 313 // (Do not) merge spikes. |
361 // (Do not) merge spikes. |
| 314 if !warned_merging { |
362 if !warned_merging { |
| 315 config.if_merge_now(&state, |_| { |
363 config.if_merge_now_check(&state, || { |
| 316 let err = format!("Merging not supported for μFISTA"); |
364 let err = format!("Merging not supported for μFISTA"); |
| 317 println!("{}", err.red()); |
365 println!("{}", err.red()); |
| 318 warned_merging = true; |
366 warned_merging = true; |
| 319 }); |
367 }); |
| 320 } |
368 } |