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