Sun, 26 Jan 2025 11:58:02 -0500
Fixes to Radon norm prox term inner algorithm
32 | 1 | /*! |
2 | Solver for the point source localisation problem using a sliding | |
3 | forward-backward splitting method. | |
4 | */ | |
5 | ||
6 | use numeric_literals::replace_float_literals; | |
7 | use serde::{Serialize, Deserialize}; | |
8 | //use colored::Colorize; | |
9 | //use nalgebra::{DVector, DMatrix}; | |
10 | use itertools::izip; | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
11 | use std::iter::Iterator; |
32 | 12 | |
35 | 13 | use alg_tools::iterate::AlgIteratorFactory; |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
14 | use alg_tools::euclidean::Euclidean; |
37
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
15 | use alg_tools::mapping::{Mapping, DifferentiableRealMapping, Instance}; |
35 | 16 | use alg_tools::norms::Norm; |
32 | 17 | use alg_tools::nalgebra_support::ToNalgebraRealField; |
18 | ||
19 | use crate::types::*; | |
35 | 20 | use crate::measures::{DiscreteMeasure, Radon, RNDM}; |
37
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
21 | use crate::measures::merging::SpikeMerging; |
35 | 22 | use crate::forward_model::{ |
23 | ForwardModel, | |
24 | AdjointProductBoundedBy, | |
25 | LipschitzValues, | |
26 | }; | |
32 | 27 | //use crate::tolerance::Tolerance; |
28 | use crate::plot::{ | |
29 | SeqPlotter, | |
30 | Plotting, | |
31 | PlotLookup | |
32 | }; | |
33 | use crate::fb::*; | |
34 | use crate::regularisation::SlidingRegTerm; | |
35 | use crate::dataterm::{ | |
36 | L2Squared, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
37 | DataTerm, |
32 | 38 | calculate_residual, |
39 | calculate_residual2, | |
40 | }; | |
35 | 41 | //use crate::transport::TransportLipschitz; |
42 | ||
43 | /// Transport settings for [`pointsource_sliding_fb_reg`]. | |
44 | #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] | |
45 | #[serde(default)] | |
46 | pub struct TransportConfig<F : Float> { | |
47 | /// Transport step length $θ$ normalised to $(0, 1)$. | |
48 | pub θ0 : F, | |
49 | /// Factor in $(0, 1)$ for decreasing transport to adapt to tolerance. | |
50 | pub adaptation : F, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
51 | /// A posteriori transport tolerance multiplier (C_pos) |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
52 | pub tolerance_mult_pos : F, |
35 | 53 | } |
54 | ||
55 | #[replace_float_literals(F::cast_from(literal))] | |
56 | impl <F : Float> TransportConfig<F> { | |
57 | /// Check that the parameters are ok. Panics if not. | |
58 | pub fn check(&self) { | |
59 | assert!(self.θ0 > 0.0); | |
60 | assert!(0.0 < self.adaptation && self.adaptation < 1.0); | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
61 | assert!(self.tolerance_mult_pos > 0.0); |
35 | 62 | } |
63 | } | |
64 | ||
65 | #[replace_float_literals(F::cast_from(literal))] | |
66 | impl<F : Float> Default for TransportConfig<F> { | |
67 | fn default() -> Self { | |
68 | TransportConfig { | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
69 | θ0 : 0.4, |
35 | 70 | adaptation : 0.9, |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
71 | tolerance_mult_pos : 100.0, |
35 | 72 | } |
73 | } | |
74 | } | |
32 | 75 | |
76 | /// Settings for [`pointsource_sliding_fb_reg`]. | |
77 | #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] | |
78 | #[serde(default)] | |
79 | pub struct SlidingFBConfig<F : Float> { | |
80 | /// Step length scaling | |
81 | pub τ0 : F, | |
35 | 82 | /// Transport parameters |
83 | pub transport : TransportConfig<F>, | |
32 | 84 | /// Generic parameters |
85 | pub insertion : FBGenericConfig<F>, | |
86 | } | |
87 | ||
88 | #[replace_float_literals(F::cast_from(literal))] | |
89 | impl<F : Float> Default for SlidingFBConfig<F> { | |
90 | fn default() -> Self { | |
91 | SlidingFBConfig { | |
92 | τ0 : 0.99, | |
35 | 93 | transport : Default::default(), |
32 | 94 | insertion : Default::default() |
95 | } | |
96 | } | |
97 | } | |
98 | ||
35 | 99 | /// Internal type of adaptive transport step length calculation |
100 | pub(crate) enum TransportStepLength<F : Float, G : Fn(F, F) -> F> { | |
101 | /// Fixed, known step length | |
102 | Fixed(F), | |
103 | /// Adaptive step length, only wrt. maximum transport. | |
104 | /// Content of `l` depends on use case, while `g` calculates the step length from `l`. | |
105 | AdaptiveMax{ l : F, max_transport : F, g : G }, | |
106 | /// Adaptive step length. | |
107 | /// Content of `l` depends on use case, while `g` calculates the step length from `l`. | |
108 | FullyAdaptive{ l : F, max_transport : F, g : G }, | |
109 | } | |
110 | ||
41
b6bdb6cb4d44
Remove initial transport adaptation—it is not needed, after all
Tuomo Valkonen <tuomov@iki.fi>
parents:
39
diff
changeset
|
111 | /// Constrution of initial transport `γ1` from initial measure `μ` and `v=F'(μ)` |
b6bdb6cb4d44
Remove initial transport adaptation—it is not needed, after all
Tuomo Valkonen <tuomov@iki.fi>
parents:
39
diff
changeset
|
112 | /// with step lengh τ and transport step length `θ_or_adaptive`. |
32 | 113 | #[replace_float_literals(F::cast_from(literal))] |
41
b6bdb6cb4d44
Remove initial transport adaptation—it is not needed, after all
Tuomo Valkonen <tuomov@iki.fi>
parents:
39
diff
changeset
|
114 | pub(crate) fn initial_transport<F, G, D, const N : usize>( |
35 | 115 | γ1 : &mut RNDM<F, N>, |
116 | μ : &mut RNDM<F, N>, | |
117 | τ : F, | |
118 | θ_or_adaptive : &mut TransportStepLength<F, G>, | |
119 | v : D, | |
120 | ) -> (Vec<F>, RNDM<F, N>) | |
121 | where | |
122 | F : Float + ToNalgebraRealField, | |
123 | G : Fn(F, F) -> F, | |
37
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
124 | D : DifferentiableRealMapping<F, N>, |
35 | 125 | { |
126 | ||
127 | use TransportStepLength::*; | |
128 | ||
129 | // Save current base point and shift μ to new positions. Idea is that | |
130 | // μ_base(_masses) = μ^k (vector of masses) | |
131 | // μ_base_minus_γ0 = μ^k - π_♯^0γ^{k+1} | |
132 | // γ1 = π_♯^1γ^{k+1} | |
133 | // μ = μ^{k+1} | |
134 | let μ_base_masses : Vec<F> = μ.iter_masses().collect(); | |
135 | let mut μ_base_minus_γ0 = μ.clone(); // Weights will be set in the loop below. | |
136 | // Construct μ^{k+1} and π_♯^1γ^{k+1} initial candidates | |
137 | //let mut sum_norm_dv = 0.0; | |
138 | let γ_prev_len = γ1.len(); | |
139 | assert!(μ.len() >= γ_prev_len); | |
140 | γ1.extend(μ[γ_prev_len..].iter().cloned()); | |
141 | ||
142 | // Calculate initial transport and step length. | |
143 | // First calculate initial transported weights | |
144 | for (δ, ρ) in izip!(μ.iter_spikes(), γ1.iter_spikes_mut()) { | |
145 | // If old transport has opposing sign, the new transport will be none. | |
146 | ρ.α = if (ρ.α > 0.0 && δ.α < 0.0) || (ρ.α < 0.0 && δ.α > 0.0) { | |
147 | 0.0 | |
148 | } else { | |
149 | δ.α | |
150 | }; | |
151 | }; | |
152 | ||
41
b6bdb6cb4d44
Remove initial transport adaptation—it is not needed, after all
Tuomo Valkonen <tuomov@iki.fi>
parents:
39
diff
changeset
|
153 | // Calculate transport rays. |
35 | 154 | match *θ_or_adaptive { |
155 | Fixed(θ) => { | |
156 | let θτ = τ * θ; | |
157 | for (δ, ρ) in izip!(μ.iter_spikes(), γ1.iter_spikes_mut()) { | |
158 | ρ.x = δ.x - v.differential(&δ.x) * (ρ.α.signum() * θτ); | |
159 | } | |
160 | }, | |
161 | AdaptiveMax{ l : ℓ_v, ref mut max_transport, g : ref calculate_θ } => { | |
162 | *max_transport = max_transport.max(γ1.norm(Radon)); | |
163 | let θτ = τ * calculate_θ(ℓ_v, *max_transport); | |
164 | for (δ, ρ) in izip!(μ.iter_spikes(), γ1.iter_spikes_mut()) { | |
165 | ρ.x = δ.x - v.differential(&δ.x) * (ρ.α.signum() * θτ); | |
166 | } | |
167 | }, | |
168 | FullyAdaptive{ l : ref mut adaptive_ℓ_v, ref mut max_transport, g : ref calculate_θ } => { | |
169 | *max_transport = max_transport.max(γ1.norm(Radon)); | |
170 | let mut θ = calculate_θ(*adaptive_ℓ_v, *max_transport); | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
171 | // Do two runs through the spikes to update θ, breaking if first run did not cause |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
172 | // a change. |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
173 | for _i in 0..=1 { |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
174 | let mut changes = false; |
35 | 175 | for (δ, ρ) in izip!(μ.iter_spikes(), γ1.iter_spikes_mut()) { |
176 | let dv_x = v.differential(&δ.x); | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
177 | let g = &dv_x * (ρ.α.signum() * θ * τ); |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
178 | ρ.x = δ.x - g; |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
179 | let n = g.norm2(); |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
180 | if n >= F::EPSILON { |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
181 | // Estimate Lipschitz factor of ∇v |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
182 | let this_ℓ_v = (dv_x - v.differential(&ρ.x)).norm2() / n; |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
183 | *adaptive_ℓ_v = adaptive_ℓ_v.max(this_ℓ_v); |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
184 | θ = calculate_θ(*adaptive_ℓ_v, *max_transport); |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
185 | changes = true |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
186 | } |
35 | 187 | } |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
188 | if !changes { |
35 | 189 | break |
190 | } | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
191 | } |
32 | 192 | } |
35 | 193 | } |
194 | ||
195 | // Set initial guess for μ=μ^{k+1}. | |
196 | for (δ, ρ, &β) in izip!(μ.iter_spikes_mut(), γ1.iter_spikes(), μ_base_masses.iter()) { | |
197 | if ρ.α.abs() > F::EPSILON { | |
198 | δ.x = ρ.x; | |
199 | //δ.α = ρ.α; // already set above | |
200 | } else { | |
201 | δ.α = β; | |
202 | } | |
203 | } | |
204 | // Calculate μ^k-π_♯^0γ^{k+1} and v̆ = A_*(A[μ_transported + μ_transported_base]-b) | |
205 | μ_base_minus_γ0.set_masses(μ_base_masses.iter().zip(γ1.iter_masses()) | |
206 | .map(|(&a,b)| a - b)); | |
207 | (μ_base_masses, μ_base_minus_γ0) | |
208 | } | |
209 | ||
210 | /// A posteriori transport adaptation. | |
211 | #[replace_float_literals(F::cast_from(literal))] | |
212 | pub(crate) fn aposteriori_transport<F, const N : usize>( | |
213 | γ1 : &mut RNDM<F, N>, | |
214 | μ : &mut RNDM<F, N>, | |
215 | μ_base_minus_γ0 : &mut RNDM<F, N>, | |
216 | μ_base_masses : &Vec<F>, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
217 | extra : Option<F>, |
35 | 218 | ε : F, |
219 | tconfig : &TransportConfig<F> | |
220 | ) -> bool | |
221 | where F : Float + ToNalgebraRealField { | |
222 | ||
223 | // 1. If π_♯^1γ^{k+1} = γ1 has non-zero mass at some point y, but μ = μ^{k+1} does not, | |
224 | // then the ansatz ∇w̃_x(y) = w^{k+1}(y) may not be satisfied. So set the mass of γ1 | |
225 | // at that point to zero, and retry. | |
226 | let mut all_ok = true; | |
227 | for (α_μ, α_γ1) in izip!(μ.iter_masses(), γ1.iter_masses_mut()) { | |
228 | if α_μ == 0.0 && *α_γ1 != 0.0 { | |
229 | all_ok = false; | |
230 | *α_γ1 = 0.0; | |
231 | } | |
232 | } | |
233 | ||
234 | // 2. Through bounding ∫ B_ω(y, z) dλ(x, y, z). | |
235 | // through the estimate ≤ C ‖Δ‖‖γ^{k+1}‖ for Δ := μ^{k+1}-μ^k-(π_♯^1-π_♯^0)γ^{k+1}, | |
236 | // which holds for some some C if the convolution kernel in 𝒟 has Lipschitz gradient. | |
237 | let nγ = γ1.norm(Radon); | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
238 | let nΔ = μ_base_minus_γ0.norm(Radon) + μ.dist_matching(&γ1) + extra.unwrap_or(0.0); |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
239 | let t = ε * tconfig.tolerance_mult_pos; |
35 | 240 | if nγ*nΔ > t { |
241 | // Since t/(nγ*nΔ)<1, and the constant tconfig.adaptation < 1, | |
242 | // this will guarantee that eventually ‖γ‖ decreases sufficiently that we | |
243 | // will not enter here. | |
244 | *γ1 *= tconfig.adaptation * t / ( nγ * nΔ ); | |
245 | all_ok = false | |
246 | } | |
247 | ||
248 | if !all_ok { | |
249 | // Update weights for μ_base_minus_γ0 = μ^k - π_♯^0γ^{k+1} | |
250 | μ_base_minus_γ0.set_masses(μ_base_masses.iter().zip(γ1.iter_masses()) | |
251 | .map(|(&a,b)| a - b)); | |
252 | ||
253 | } | |
254 | ||
255 | all_ok | |
32 | 256 | } |
257 | ||
258 | /// Iteratively solve the pointsource localisation problem using sliding forward-backward | |
259 | /// splitting | |
260 | /// | |
35 | 261 | /// The parametrisation is as for [`pointsource_fb_reg`]. |
32 | 262 | /// Inertia is currently not supported. |
263 | #[replace_float_literals(F::cast_from(literal))] | |
37
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
264 | pub fn pointsource_sliding_fb_reg<F, I, A, Reg, P, const N : usize>( |
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
265 | opA : &A, |
32 | 266 | b : &A::Observable, |
267 | reg : Reg, | |
37
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
268 | prox_penalty : &P, |
35 | 269 | config : &SlidingFBConfig<F>, |
32 | 270 | iterator : I, |
271 | mut plotter : SeqPlotter<F, N>, | |
35 | 272 | ) -> RNDM<F, N> |
37
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
273 | where |
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
274 | F : Float + ToNalgebraRealField, |
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
275 | I : AlgIteratorFactory<IterInfo<F, N>>, |
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
276 | A : ForwardModel<RNDM<F, N>, F> |
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
277 | + AdjointProductBoundedBy<RNDM<F, N>, P, FloatType=F>, |
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
278 | //+ TransportLipschitz<L2Squared, FloatType=F>, |
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
279 | for<'b> &'b A::Observable : std::ops::Neg<Output=A::Observable> + Instance<A::Observable>, |
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
280 | for<'b> A::Preadjoint<'b> : LipschitzValues<FloatType=F>, |
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
281 | A::PreadjointCodomain : DifferentiableRealMapping<F, N>, |
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
282 | RNDM<F, N> : SpikeMerging<F>, |
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
283 | Reg : SlidingRegTerm<F, N>, |
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
284 | P : ProxPenalty<F, A::PreadjointCodomain, Reg, N>, |
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
285 | PlotLookup : Plotting<N>, |
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
286 | { |
32 | 287 | |
35 | 288 | // Check parameters |
289 | assert!(config.τ0 > 0.0, "Invalid step length parameter"); | |
290 | config.transport.check(); | |
32 | 291 | |
292 | // Initialise iterates | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
293 | let mut μ = DiscreteMeasure::new(); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
294 | let mut γ1 = DiscreteMeasure::new(); |
35 | 295 | let mut residual = -b; // Has to equal $Aμ-b$. |
296 | ||
297 | // Set up parameters | |
41
b6bdb6cb4d44
Remove initial transport adaptation—it is not needed, after all
Tuomo Valkonen <tuomov@iki.fi>
parents:
39
diff
changeset
|
298 | // let opAnorm = opA.opnorm_bound(Radon, L2); |
35 | 299 | //let max_transport = config.max_transport.scale |
300 | // * reg.radon_norm_bound(b.norm2_squared() / 2.0); | |
301 | //let ℓ = opA.transport.lipschitz_factor(L2Squared) * max_transport; | |
302 | let ℓ = 0.0; | |
37
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
303 | let τ = config.τ0 / opA.adjoint_product_bound(prox_penalty).unwrap(); |
35 | 304 | let calculate_θ = |ℓ_v, _| config.transport.θ0 / (τ*(ℓ + ℓ_v)); |
305 | let mut θ_or_adaptive = match opA.preadjoint().value_diff_unit_lipschitz_factor() { | |
306 | // We only estimate w (the uniform Lipschitz for of v), if we also estimate ℓ_v | |
307 | // (the uniform Lipschitz factor of ∇v). | |
308 | // We assume that the residual is decreasing. | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
309 | Some(ℓ_v0) => TransportStepLength::Fixed(calculate_θ(ℓ_v0 * b.norm2(), 0.0)), |
35 | 310 | None => TransportStepLength::FullyAdaptive { |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
311 | l : 10.0 * F::EPSILON, // Start with something very small to estimate differentials |
35 | 312 | max_transport : 0.0, |
313 | g : calculate_θ | |
314 | }, | |
315 | }; | |
316 | // We multiply tolerance by τ for FB since our subproblems depending on tolerances are scaled | |
317 | // by τ compared to the conditional gradient approach. | |
318 | let tolerance = config.insertion.tolerance * τ * reg.tolerance_scaling(); | |
319 | let mut ε = tolerance.initial(); | |
320 | ||
321 | // Statistics | |
322 | let full_stats = |residual : &A::Observable, | |
323 | μ : &RNDM<F, N>, | |
324 | ε, stats| IterInfo { | |
325 | value : residual.norm2_squared_div2() + reg.apply(μ), | |
326 | n_spikes : μ.len(), | |
327 | ε, | |
328 | // postprocessing: config.insertion.postprocessing.then(|| μ.clone()), | |
329 | .. stats | |
330 | }; | |
32 | 331 | let mut stats = IterInfo::new(); |
332 | ||
333 | // Run the algorithm | |
35 | 334 | for state in iterator.iter_init(|| full_stats(&residual, &μ, ε, stats.clone())) { |
335 | // Calculate initial transport | |
336 | let v = opA.preadjoint().apply(residual); | |
337 | let (μ_base_masses, mut μ_base_minus_γ0) = initial_transport( | |
41
b6bdb6cb4d44
Remove initial transport adaptation—it is not needed, after all
Tuomo Valkonen <tuomov@iki.fi>
parents:
39
diff
changeset
|
338 | &mut γ1, &mut μ, τ, &mut θ_or_adaptive, v |
35 | 339 | ); |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
340 | |
32 | 341 | // Solve finite-dimensional subproblem several times until the dual variable for the |
342 | // regularisation term conforms to the assumptions made for the transport above. | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
343 | let (maybe_d, _within_tolerances, mut τv̆) = 'adapt_transport: loop { |
35 | 344 | // Calculate τv̆ = τA_*(A[μ_transported + μ_transported_base]-b) |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
345 | let residual_μ̆ = calculate_residual2(&γ1, &μ_base_minus_γ0, opA, b); |
37
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
346 | let mut τv̆ = opA.preadjoint().apply(residual_μ̆ * τ); |
32 | 347 | |
348 | // Construct μ^{k+1} by solving finite-dimensional subproblems and insert new spikes. | |
37
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
349 | let (maybe_d, within_tolerances) = prox_penalty.insert_and_reweigh( |
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
350 | &mut μ, &mut τv̆, &γ1, Some(&μ_base_minus_γ0), |
35 | 351 | τ, ε, &config.insertion, |
352 | ®, &state, &mut stats, | |
32 | 353 | ); |
354 | ||
35 | 355 | // A posteriori transport adaptation. |
356 | if aposteriori_transport( | |
357 | &mut γ1, &mut μ, &mut μ_base_minus_γ0, &μ_base_masses, | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
358 | None, |
35 | 359 | ε, &config.transport |
360 | ) { | |
37
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
361 | break 'adapt_transport (maybe_d, within_tolerances, τv̆) |
32 | 362 | } |
363 | }; | |
364 | ||
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
365 | stats.untransported_fraction = Some({ |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
366 | assert_eq!(μ_base_masses.len(), γ1.len()); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
367 | let (a, b) = stats.untransported_fraction.unwrap_or((0.0, 0.0)); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
368 | let source = μ_base_masses.iter().map(|v| v.abs()).sum(); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
369 | (a + μ_base_minus_γ0.norm(Radon), b + source) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
370 | }); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
371 | stats.transport_error = Some({ |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
372 | assert_eq!(μ_base_masses.len(), γ1.len()); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
373 | let (a, b) = stats.transport_error.unwrap_or((0.0, 0.0)); |
35 | 374 | (a + μ.dist_matching(&γ1), b + γ1.norm(Radon)) |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
375 | }); |
32 | 376 | |
39
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
377 | // Merge spikes. |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
378 | // This crucially expects the merge routine to be stable with respect to spike locations, |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
379 | // and not to performing any pruning. That is be to done below simultaneously for γ. |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
380 | let ins = &config.insertion; |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
381 | if ins.merge_now(&state) { |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
382 | stats.merged += prox_penalty.merge_spikes( |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
383 | &mut μ, &mut τv̆, &γ1, Some(&μ_base_minus_γ0), τ, ε, ins, ®, |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
384 | Some(|μ̃ : &RNDM<F, N>| L2Squared.calculate_fit_op(μ̃, opA, b)), |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
385 | ); |
6316d68b58af
Merging adjustments, parameter tuning, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
37
diff
changeset
|
386 | } |
35 | 387 | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
388 | // Prune spikes with zero weight. To maintain correct ordering between μ and γ1, also the |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
389 | // latter needs to be pruned when μ is. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
390 | // TODO: This could do with a two-vector Vec::retain to avoid copies. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
391 | let μ_new = DiscreteMeasure::from_iter(μ.iter_spikes().filter(|δ| δ.α != F::ZERO).cloned()); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
392 | if μ_new.len() != μ.len() { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
393 | let mut μ_iter = μ.iter_spikes(); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
394 | γ1.prune_by(|_| μ_iter.next().unwrap().α != F::ZERO); |
35 | 395 | stats.pruned += μ.len() - μ_new.len(); |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
396 | μ = μ_new; |
32 | 397 | } |
398 | ||
399 | // Update residual | |
400 | residual = calculate_residual(&μ, opA, b); | |
401 | ||
35 | 402 | let iter = state.iteration(); |
32 | 403 | stats.this_iters += 1; |
404 | ||
35 | 405 | // Give statistics if requested |
32 | 406 | state.if_verbose(|| { |
37
c5d8bd1a7728
Generic proximal penalty support
Tuomo Valkonen <tuomov@iki.fi>
parents:
35
diff
changeset
|
407 | plotter.plot_spikes(iter, maybe_d.as_ref(), Some(&τv̆), &μ); |
35 | 408 | full_stats(&residual, &μ, ε, std::mem::replace(&mut stats, IterInfo::new())) |
409 | }); | |
32 | 410 | |
35 | 411 | // Update main tolerance for next iteration |
412 | ε = tolerance.update(ε, iter); | |
413 | } | |
414 | ||
415 | postprocess(μ, &config.insertion, L2Squared, opA, b) | |
32 | 416 | } |