| |
1 /*! |
| |
2 Proximal penalty abstraction |
| |
3 */ |
| |
4 |
| |
5 use alg_tools::types::*; |
| |
6 use numeric_literals::replace_float_literals; |
| |
7 use serde::{Deserialize, Serialize}; |
| |
8 |
| |
9 use crate::measures::merging::SpikeMergingMethod; |
| |
10 use crate::measures::RNDM; |
| |
11 use crate::regularisation::RegTerm; |
| |
12 use crate::subproblem::InnerSettings; |
| |
13 use crate::tolerance::Tolerance; |
| |
14 use crate::types::{IterInfo, RefinementSettings}; |
| |
15 use alg_tools::iterate::{AlgIterator, AlgIteratorIteration}; |
| |
16 use alg_tools::mapping::RealMapping; |
| |
17 use alg_tools::nalgebra_support::ToNalgebraRealField; |
| |
18 |
| |
19 pub mod radon_squared; |
| |
20 pub mod wave; |
| |
21 pub use radon_squared::RadonSquared; |
| |
22 |
| |
23 /// Settings for the solution of the stepwise optimality condition. |
| |
24 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] |
| |
25 #[serde(default)] |
| |
26 pub struct FBGenericConfig<F: Float> { |
| |
27 /// Tolerance for point insertion. |
| |
28 pub tolerance: Tolerance<F>, |
| |
29 |
| |
30 /// Stop looking for predual maximum (where to isert a new point) below |
| |
31 /// `tolerance` multiplied by this factor. |
| |
32 /// |
| |
33 /// Not used by [`crate::prox_penalty::radon_squared`]. |
| |
34 pub insertion_cutoff_factor: F, |
| |
35 |
| |
36 /// Settings for branch and bound refinement when looking for predual maxima |
| |
37 pub refinement: RefinementSettings<F>, |
| |
38 |
| |
39 /// Maximum insertions within each outer iteration |
| |
40 /// |
| |
41 /// Not used by [`crate::prox_penalty::radon_squared`]. |
| |
42 pub max_insertions: usize, |
| |
43 |
| |
44 /// Pair `(n, m)` for maximum insertions `m` on first `n` iterations. |
| |
45 /// |
| |
46 /// Not used by [`crate::prox_penalty::radon_squared`]. |
| |
47 pub bootstrap_insertions: Option<(usize, usize)>, |
| |
48 |
| |
49 /// Inner method settings |
| |
50 pub inner: InnerSettings<F>, |
| |
51 |
| |
52 /// Spike merging method |
| |
53 pub merging: SpikeMergingMethod<F>, |
| |
54 |
| |
55 /// Tolerance multiplier for merges |
| |
56 pub merge_tolerance_mult: F, |
| |
57 |
| |
58 /// Merge spikes after last step (even if merging not generally enabled) |
| |
59 pub final_merging: bool, |
| |
60 |
| |
61 /// Use fitness as merging criterion. Implies worse convergence guarantees. |
| |
62 pub fitness_merging: bool, |
| |
63 |
| |
64 /// Iterations between merging heuristic tries |
| |
65 pub merge_every: usize, |
| |
66 // /// Save $μ$ for postprocessing optimisation |
| |
67 // pub postprocessing : bool |
| |
68 } |
| |
69 |
| |
70 #[replace_float_literals(F::cast_from(literal))] |
| |
71 impl<F: Float> Default for FBGenericConfig<F> { |
| |
72 fn default() -> Self { |
| |
73 FBGenericConfig { |
| |
74 tolerance: Default::default(), |
| |
75 insertion_cutoff_factor: 1.0, |
| |
76 refinement: Default::default(), |
| |
77 max_insertions: 100, |
| |
78 //bootstrap_insertions : None, |
| |
79 bootstrap_insertions: Some((10, 1)), |
| |
80 inner: Default::default(), |
| |
81 merging: Default::default(), |
| |
82 final_merging: true, |
| |
83 fitness_merging: false, |
| |
84 merge_every: 10, |
| |
85 merge_tolerance_mult: 2.0, |
| |
86 // postprocessing : false, |
| |
87 } |
| |
88 } |
| |
89 } |
| |
90 |
| |
91 impl<F: Float> FBGenericConfig<F> { |
| |
92 /// Check if merging should be attempted this iteration |
| |
93 pub fn merge_now<I: AlgIterator>(&self, state: &AlgIteratorIteration<I>) -> bool { |
| |
94 self.merging.enabled && state.iteration() % self.merge_every == 0 |
| |
95 } |
| |
96 |
| |
97 /// Returns the final merging method |
| |
98 pub fn final_merging_method(&self) -> SpikeMergingMethod<F> { |
| |
99 SpikeMergingMethod { |
| |
100 enabled: self.final_merging, |
| |
101 ..self.merging |
| |
102 } |
| |
103 } |
| |
104 } |
| |
105 |
| |
106 /// Trait for proximal penalties |
| |
107 pub trait ProxPenalty<F, PreadjointCodomain, Reg, const N: usize> |
| |
108 where |
| |
109 F: Float + ToNalgebraRealField, |
| |
110 Reg: RegTerm<F, N>, |
| |
111 { |
| |
112 type ReturnMapping: RealMapping<F, N>; |
| |
113 |
| |
114 /// Insert new spikes into `μ` to approximately satisfy optimality conditions |
| |
115 /// with the forward step term fixed to `τv`. |
| |
116 /// |
| |
117 /// May return `τv + w` for `w` a subdifferential of the regularisation term `reg`, |
| |
118 /// as well as an indication of whether the tolerance bounds `ε` are satisfied. |
| |
119 /// |
| |
120 /// `μ_base + ν_delta` is the base point, where `μ` and `μ_base` are assumed to have the same |
| |
121 /// spike locations, while `ν_delta` may have different locations. |
| |
122 /// |
| |
123 /// `τv` is mutable to allow [`alg_tools::bisection_tree::BTFN`] refinement. |
| |
124 /// Actual values of `τv` are not supposed to be mutated. |
| |
125 fn insert_and_reweigh<I>( |
| |
126 &self, |
| |
127 μ: &mut RNDM<F, N>, |
| |
128 τv: &mut PreadjointCodomain, |
| |
129 μ_base: &RNDM<F, N>, |
| |
130 ν_delta: Option<&RNDM<F, N>>, |
| |
131 τ: F, |
| |
132 ε: F, |
| |
133 config: &FBGenericConfig<F>, |
| |
134 reg: &Reg, |
| |
135 state: &AlgIteratorIteration<I>, |
| |
136 stats: &mut IterInfo<F, N>, |
| |
137 ) -> (Option<Self::ReturnMapping>, bool) |
| |
138 where |
| |
139 I: AlgIterator; |
| |
140 |
| |
141 /// Merge spikes, if possible. |
| |
142 /// |
| |
143 /// Either optimality condition merging or objective value (fitness) merging |
| |
144 /// may be used, the latter only if `fitness` is provided and `config.fitness_merging` |
| |
145 /// is set. |
| |
146 fn merge_spikes( |
| |
147 &self, |
| |
148 μ: &mut RNDM<F, N>, |
| |
149 τv: &mut PreadjointCodomain, |
| |
150 μ_base: &RNDM<F, N>, |
| |
151 ν_delta: Option<&RNDM<F, N>>, |
| |
152 τ: F, |
| |
153 ε: F, |
| |
154 config: &FBGenericConfig<F>, |
| |
155 reg: &Reg, |
| |
156 fitness: Option<impl Fn(&RNDM<F, N>) -> F>, |
| |
157 ) -> usize; |
| |
158 |
| |
159 /// Merge spikes, if possible. |
| |
160 /// |
| |
161 /// Unlike [`Self::merge_spikes`], this variant only supports optimality condition based merging |
| |
162 #[inline] |
| |
163 fn merge_spikes_no_fitness( |
| |
164 &self, |
| |
165 μ: &mut RNDM<F, N>, |
| |
166 τv: &mut PreadjointCodomain, |
| |
167 μ_base: &RNDM<F, N>, |
| |
168 ν_delta: Option<&RNDM<F, N>>, |
| |
169 τ: F, |
| |
170 ε: F, |
| |
171 config: &FBGenericConfig<F>, |
| |
172 reg: &Reg, |
| |
173 ) -> usize { |
| |
174 /// This is a hack to create a `None` of same type as a `Some` |
| |
175 // for the `impl Fn` parameter of `merge_spikes`. |
| |
176 #[inline] |
| |
177 fn into_none<T>(_: Option<T>) -> Option<T> { |
| |
178 None |
| |
179 } |
| |
180 self.merge_spikes( |
| |
181 μ, |
| |
182 τv, |
| |
183 μ_base, |
| |
184 ν_delta, |
| |
185 τ, |
| |
186 ε, |
| |
187 config, |
| |
188 reg, |
| |
189 into_none(Some(|_: &RNDM<F, N>| F::ZERO)), |
| |
190 ) |
| |
191 } |
| |
192 } |