Thu, 29 Aug 2024 00:00:00 -0500
Radon FB + sliding improvements
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 | |
13 | use alg_tools::iterate::{ | |
14 | AlgIteratorFactory, | |
15 | AlgIteratorState | |
16 | }; | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
17 | use alg_tools::euclidean::Euclidean; |
32 | 18 | use alg_tools::sets::Cube; |
19 | use alg_tools::loc::Loc; | |
20 | use alg_tools::mapping::{Apply, Differentiable}; | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
21 | use alg_tools::norms::{Norm, L2}; |
32 | 22 | use alg_tools::bisection_tree::{ |
23 | BTFN, | |
24 | PreBTFN, | |
25 | Bounds, | |
26 | BTNodeLookup, | |
27 | BTNode, | |
28 | BTSearch, | |
29 | P2Minimise, | |
30 | SupportGenerator, | |
31 | LocalAnalysis, | |
32 | //Bounded, | |
33 | }; | |
34 | use alg_tools::mapping::RealMapping; | |
35 | use alg_tools::nalgebra_support::ToNalgebraRealField; | |
36 | ||
37 | use crate::types::*; | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
38 | use crate::measures::{DeltaMeasure, DiscreteMeasure, Radon}; |
32 | 39 | use crate::measures::merging::{ |
40 | //SpikeMergingMethod, | |
41 | SpikeMerging, | |
42 | }; | |
43 | use crate::forward_model::ForwardModel; | |
44 | use crate::seminorms::DiscreteMeasureOp; | |
45 | //use crate::tolerance::Tolerance; | |
46 | use crate::plot::{ | |
47 | SeqPlotter, | |
48 | Plotting, | |
49 | PlotLookup | |
50 | }; | |
51 | use crate::fb::*; | |
52 | use crate::regularisation::SlidingRegTerm; | |
53 | use crate::dataterm::{ | |
54 | L2Squared, | |
55 | //DataTerm, | |
56 | calculate_residual, | |
57 | calculate_residual2, | |
58 | }; | |
59 | use crate::transport::TransportLipschitz; | |
60 | ||
61 | /// Settings for [`pointsource_sliding_fb_reg`]. | |
62 | #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] | |
63 | #[serde(default)] | |
64 | pub struct SlidingFBConfig<F : Float> { | |
65 | /// Step length scaling | |
66 | pub τ0 : F, | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
67 | /// Transport step length $θ$ normalised to $(0, 1)$. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
68 | pub θ0 : F, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
69 | /// Maximum transport mass scaling. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
70 | // /// The maximum transported mass is this factor times $\norm{b}^2/(2α)$. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
71 | // pub max_transport_scale : F, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
72 | /// Transport tolerance wrt. ω |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
73 | pub transport_tolerance_ω : F, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
74 | /// Transport tolerance wrt. ∇v |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
75 | pub transport_tolerance_dv : F, |
32 | 76 | /// Generic parameters |
77 | pub insertion : FBGenericConfig<F>, | |
78 | } | |
79 | ||
80 | #[replace_float_literals(F::cast_from(literal))] | |
81 | impl<F : Float> Default for SlidingFBConfig<F> { | |
82 | fn default() -> Self { | |
83 | SlidingFBConfig { | |
84 | τ0 : 0.99, | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
85 | θ0 : 0.99, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
86 | //max_transport_scale : 10.0, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
87 | transport_tolerance_ω : 1.0, // TODO: no idea what this should be |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
88 | transport_tolerance_dv : 1.0, // TODO: no idea what this should be |
32 | 89 | insertion : Default::default() |
90 | } | |
91 | } | |
92 | } | |
93 | ||
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
94 | /// Scale each |γ|_i ≠ 0 by q_i=q̄/g(γ_i) |
32 | 95 | #[replace_float_literals(F::cast_from(literal))] |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
96 | fn scale_down<'a, I, F, G, const N : usize>( |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
97 | iter : I, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
98 | q̄ : F, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
99 | mut g : G |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
100 | ) where F : Float, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
101 | I : Iterator<Item = &'a mut DeltaMeasure<Loc<F,N>, F>>, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
102 | G : FnMut(&DeltaMeasure<Loc<F,N>, F>) -> F { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
103 | iter.for_each(|δ| { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
104 | if δ.α != 0.0 { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
105 | let b = g(δ); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
106 | if b * δ.α > 0.0 { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
107 | δ.α *= q̄/b; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
108 | } |
32 | 109 | } |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
110 | }); |
32 | 111 | } |
112 | ||
113 | /// Iteratively solve the pointsource localisation problem using sliding forward-backward | |
114 | /// splitting | |
115 | /// | |
116 | /// The parametrisatio is as for [`pointsource_fb_reg`]. | |
117 | /// Inertia is currently not supported. | |
118 | #[replace_float_literals(F::cast_from(literal))] | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
119 | pub fn pointsource_sliding_fb_reg<'a, F, I, A, GA, 𝒟, BTA, BT𝒟, G𝒟, S, K, Reg, const N : usize>( |
32 | 120 | opA : &'a A, |
121 | b : &A::Observable, | |
122 | reg : Reg, | |
123 | op𝒟 : &'a 𝒟, | |
124 | sfbconfig : &SlidingFBConfig<F>, | |
125 | iterator : I, | |
126 | mut plotter : SeqPlotter<F, N>, | |
127 | ) -> DiscreteMeasure<Loc<F, N>, F> | |
128 | where F : Float + ToNalgebraRealField, | |
129 | I : AlgIteratorFactory<IterInfo<F, N>>, | |
130 | for<'b> &'b A::Observable : std::ops::Neg<Output=A::Observable>, | |
131 | //+ std::ops::Mul<F, Output=A::Observable>, <-- FIXME: compiler overflow | |
132 | A::Observable : std::ops::MulAssign<F>, | |
133 | A::PreadjointCodomain : for<'b> Differentiable<&'b Loc<F, N>, Output=Loc<F, N>>, | |
134 | GA : SupportGenerator<F, N, SupportType = S, Id = usize> + Clone, | |
135 | A : ForwardModel<Loc<F, N>, F, PreadjointCodomain = BTFN<F, GA, BTA, N>> | |
136 | + Lipschitz<&'a 𝒟, FloatType=F> + TransportLipschitz<L2Squared, FloatType=F>, | |
137 | BTA : BTSearch<F, N, Data=usize, Agg=Bounds<F>>, | |
138 | G𝒟 : SupportGenerator<F, N, SupportType = K, Id = usize> + Clone, | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
139 | 𝒟 : DiscreteMeasureOp<Loc<F, N>, F, PreCodomain = PreBTFN<F, G𝒟, N>, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
140 | Codomain = BTFN<F, G𝒟, BT𝒟, N>>, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
141 | BT𝒟 : BTSearch<F, N, Data=usize, Agg=Bounds<F>>, |
32 | 142 | S: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N> |
143 | + Differentiable<Loc<F, N>, Output=Loc<F,N>>, | |
144 | K: RealMapping<F, N> + LocalAnalysis<F, Bounds<F>, N>, | |
145 | //+ Differentiable<Loc<F, N>, Output=Loc<F,N>>, | |
146 | BTNodeLookup: BTNode<F, usize, Bounds<F>, N>, | |
147 | Cube<F, N>: P2Minimise<Loc<F, N>, F>, | |
148 | PlotLookup : Plotting<N>, | |
149 | DiscreteMeasure<Loc<F, N>, F> : SpikeMerging<F>, | |
150 | Reg : SlidingRegTerm<F, N> { | |
151 | ||
152 | assert!(sfbconfig.τ0 > 0.0 && | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
153 | sfbconfig.θ0 > 0.0); |
32 | 154 | |
155 | // Set up parameters | |
156 | let config = &sfbconfig.insertion; | |
157 | let op𝒟norm = op𝒟.opnorm_bound(); | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
158 | //let max_transport = sfbconfig.max_transport_scale |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
159 | // * reg.radon_norm_bound(b.norm2_squared() / 2.0); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
160 | //let tlip = opA.transport_lipschitz_factor(L2Squared) * max_transport; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
161 | //let ℓ = 0.0; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
162 | let θ = sfbconfig.θ0; // (ℓ + tlip); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
163 | let τ = sfbconfig.τ0/opA.lipschitz_factor(&op𝒟).unwrap(); |
32 | 164 | // We multiply tolerance by τ for FB since our subproblems depending on tolerances are scaled |
165 | // by τ compared to the conditional gradient approach. | |
166 | let tolerance = config.tolerance * τ * reg.tolerance_scaling(); | |
167 | let mut ε = tolerance.initial(); | |
168 | ||
169 | // Initialise iterates | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
170 | let mut μ = DiscreteMeasure::new(); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
171 | let mut γ1 = DiscreteMeasure::new(); |
32 | 172 | let mut residual = -b; |
173 | let mut stats = IterInfo::new(); | |
174 | ||
175 | // Run the algorithm | |
176 | iterator.iterate(|state| { | |
177 | // Calculate smooth part of surrogate model. | |
178 | // Using `std::mem::replace` here is not ideal, and expects that `empty_observable` | |
179 | // has no significant overhead. For some reosn Rust doesn't allow us simply moving | |
180 | // the residual and replacing it below before the end of this closure. | |
181 | let r = std::mem::replace(&mut residual, opA.empty_observable()); | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
182 | let v = opA.preadjoint().apply(r); |
32 | 183 | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
184 | // Save current base point and shift μ to new positions. Idea is that |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
185 | // μ_base(_masses) = μ^k (vector of masses) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
186 | // μ_base_minus_γ0 = μ^k - π_♯^0γ^{k+1} |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
187 | // γ1 = π_♯^1γ^{k+1} |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
188 | // μ = μ^{k+1} |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
189 | let μ_base_masses : Vec<F> = μ.iter_masses().collect(); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
190 | let mut μ_base_minus_γ0 = μ.clone(); // Weights will be set in the loop below. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
191 | // Construct μ^{k+1} and π_♯^1γ^{k+1} initial candidates |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
192 | let mut sum_norm_dv_times_γinit = 0.0; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
193 | let mut sum_abs_γinit = 0.0; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
194 | //let mut sum_norm_dv = 0.0; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
195 | let γ_prev_len = γ1.len(); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
196 | assert!(μ.len() >= γ_prev_len); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
197 | γ1.extend(μ[γ_prev_len..].iter().cloned()); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
198 | for (δ, ρ) in izip!(μ.iter_spikes_mut(), γ1.iter_spikes_mut()) { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
199 | let d_v_x = v.differential(&δ.x); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
200 | // If old transport has opposing sign, the new transport will be none. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
201 | ρ.α = if (ρ.α > 0.0 && δ.α < 0.0) || (ρ.α < 0.0 && δ.α > 0.0) { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
202 | 0.0 |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
203 | } else { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
204 | δ.α |
32 | 205 | }; |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
206 | δ.x -= d_v_x * (θ * δ.α.signum()); // This is δ.α.signum() when δ.α ≠ 0. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
207 | ρ.x = δ.x; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
208 | let nrm = d_v_x.norm(L2); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
209 | let a = ρ.α.abs(); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
210 | let v = nrm * a; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
211 | if v > 0.0 { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
212 | sum_norm_dv_times_γinit += v; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
213 | sum_abs_γinit += a; |
32 | 214 | } |
215 | } | |
216 | ||
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
217 | // A priori transport adaptation based on bounding ∫ ⟨∇v(x), z-y⟩ dλ(x, y, z). |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
218 | // This is just one option, there are many. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
219 | let t = ε * sfbconfig.transport_tolerance_dv; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
220 | if sum_norm_dv_times_γinit > t { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
221 | // Scale each |γ|_i by q_i=q̄/‖vx‖_i such that ∑_i |γ|_i q_i ‖vx‖_i = t |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
222 | // TODO: store the closure values above? |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
223 | scale_down(γ1.iter_spikes_mut(), |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
224 | t / sum_abs_γinit, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
225 | |δ| v.differential(&δ.x).norm(L2)); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
226 | } |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
227 | //println!("|γ| = {}, |μ| = {}", γ1.norm(crate::measures::Radon), μ.norm(crate::measures::Radon)); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
228 | |
32 | 229 | // Solve finite-dimensional subproblem several times until the dual variable for the |
230 | // regularisation term conforms to the assumptions made for the transport above. | |
231 | let (d, within_tolerances) = 'adapt_transport: loop { | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
232 | // Update weights for μ_base_minus_γ0 = μ^k - π_♯^0γ^{k+1} |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
233 | for (δ_γ1, δ_μ_base_minus_γ0, &α_μ_base) in izip!(γ1.iter_spikes(), |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
234 | μ_base_minus_γ0.iter_spikes_mut(), |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
235 | μ_base_masses.iter()) { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
236 | δ_μ_base_minus_γ0.set_mass(α_μ_base - δ_γ1.get_mass()); |
32 | 237 | } |
238 | ||
239 | // Calculate transported_minus_τv = -τA_*(A[μ_transported + μ_transported_base]-b) | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
240 | let residual_μ̆ = calculate_residual2(&γ1, &μ_base_minus_γ0, opA, b); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
241 | let transported_minus_τv̆ = opA.preadjoint().apply(residual_μ̆ * (-τ)); |
32 | 242 | |
243 | // Construct μ^{k+1} by solving finite-dimensional subproblems and insert new spikes. | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
244 | let (d, within_tolerances) = insert_and_reweigh( |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
245 | &mut μ, &transported_minus_τv̆, &γ1, Some(&μ_base_minus_γ0), |
32 | 246 | op𝒟, op𝒟norm, |
247 | τ, ε, | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
248 | config, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
249 | ®, state, &mut stats, |
32 | 250 | ); |
251 | ||
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
252 | // A posteriori transport adaptation based on bounding (1/τ)∫ ω(z) - ω(y) dλ(x, y, z). |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
253 | let all_ok = if false { // Basic check |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
254 | // If π_♯^1γ^{k+1} = γ1 has non-zero mass at some point y, but μ = μ^{k+1} does not, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
255 | // then the ansatz ∇w̃_x(y) = w^{k+1}(y) may not be satisfied. So set the mass of γ1 |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
256 | // at that point to zero, and retry. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
257 | let mut all_ok = true; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
258 | for (α_μ, α_γ1) in izip!(μ.iter_masses(), γ1.iter_masses_mut()) { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
259 | if α_μ == 0.0 && *α_γ1 != 0.0 { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
260 | all_ok = false; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
261 | *α_γ1 = 0.0; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
262 | } |
32 | 263 | } |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
264 | all_ok |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
265 | } else { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
266 | // TODO: Could maybe optimise, as this is also formed in insert_and_reweigh above. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
267 | let mut minus_ω = op𝒟.apply(γ1.sub_matching(&μ) + &μ_base_minus_γ0); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
268 | |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
269 | // let vpos = γ1.iter_spikes() |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
270 | // .filter(|δ| δ.α > 0.0) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
271 | // .map(|δ| minus_ω.apply(&δ.x)) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
272 | // .reduce(F::max) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
273 | // .and_then(|threshold| { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
274 | // minus_ω.minimise_below(threshold, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
275 | // ε * config.refinement.tolerance_mult, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
276 | // config.refinement.max_steps) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
277 | // .map(|(_z, minus_ω_z)| minus_ω_z) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
278 | // }); |
32 | 279 | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
280 | // let vneg = γ1.iter_spikes() |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
281 | // .filter(|δ| δ.α < 0.0) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
282 | // .map(|δ| minus_ω.apply(&δ.x)) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
283 | // .reduce(F::min) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
284 | // .and_then(|threshold| { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
285 | // minus_ω.maximise_above(threshold, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
286 | // ε * config.refinement.tolerance_mult, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
287 | // config.refinement.max_steps) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
288 | // .map(|(_z, minus_ω_z)| minus_ω_z) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
289 | // }); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
290 | let (_, vpos) = minus_ω.minimise(ε * config.refinement.tolerance_mult, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
291 | config.refinement.max_steps); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
292 | let (_, vneg) = minus_ω.maximise(ε * config.refinement.tolerance_mult, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
293 | config.refinement.max_steps); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
294 | |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
295 | let t = τ * ε * sfbconfig.transport_tolerance_ω; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
296 | let val = |δ : &DeltaMeasure<Loc<F, N>, F>| { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
297 | δ.α * (minus_ω.apply(&δ.x) - if δ.α >= 0.0 { vpos } else { vneg }) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
298 | // match if δ.α >= 0.0 { vpos } else { vneg } { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
299 | // None => 0.0, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
300 | // Some(v) => δ.α * (minus_ω.apply(&δ.x) - v) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
301 | // } |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
302 | }; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
303 | // Calculate positive/bad (rp) values under the integral. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
304 | // Also store sum of masses for the positive entries. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
305 | let (rp, w) = γ1.iter_spikes().fold((0.0, 0.0), |(p, w), δ| { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
306 | let v = val(δ); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
307 | if v <= 0.0 { (p, w) } else { (p + v, w + δ.α.abs()) } |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
308 | }); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
309 | |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
310 | if rp > t { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
311 | // TODO: store v above? |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
312 | scale_down(γ1.iter_spikes_mut(), t / w, val); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
313 | false |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
314 | } else { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
315 | true |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
316 | } |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
317 | }; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
318 | |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
319 | if all_ok { |
32 | 320 | break 'adapt_transport (d, within_tolerances) |
321 | } | |
322 | }; | |
323 | ||
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
324 | stats.untransported_fraction = Some({ |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
325 | assert_eq!(μ_base_masses.len(), γ1.len()); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
326 | 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
|
327 | let source = μ_base_masses.iter().map(|v| v.abs()).sum(); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
328 | (a + μ_base_minus_γ0.norm(Radon), b + source) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
329 | }); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
330 | stats.transport_error = Some({ |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
331 | assert_eq!(μ_base_masses.len(), γ1.len()); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
332 | let (a, b) = stats.transport_error.unwrap_or((0.0, 0.0)); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
333 | let err = izip!(μ.iter_masses(), γ1.iter_masses()).map(|(v,w)| (v-w).abs()).sum(); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
334 | (a + err, b + γ1.norm(Radon)) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
335 | }); |
32 | 336 | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
337 | // 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
|
338 | // latter needs to be pruned when μ is. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
339 | // 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
|
340 | 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
|
341 | if μ_new.len() != μ.len() { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
342 | let mut μ_iter = μ.iter_spikes(); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
343 | γ1.prune_by(|_| μ_iter.next().unwrap().α != F::ZERO); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
344 | μ = μ_new; |
32 | 345 | } |
346 | ||
347 | // TODO: how to merge? | |
348 | ||
349 | // Update residual | |
350 | residual = calculate_residual(&μ, opA, b); | |
351 | ||
352 | // Update main tolerance for next iteration | |
353 | let ε_prev = ε; | |
354 | ε = tolerance.update(ε, state.iteration()); | |
355 | stats.this_iters += 1; | |
356 | ||
357 | // Give function value if needed | |
358 | state.if_verbose(|| { | |
359 | // Plot if so requested | |
360 | plotter.plot_spikes( | |
361 | format!("iter {} end; {}", state.iteration(), within_tolerances), &d, | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
32
diff
changeset
|
362 | "start".to_string(), None::<&A::PreadjointCodomain>, // TODO: Should be Some(&((-τ) * v)), but not implemented |
32 | 363 | reg.target_bounds(τ, ε_prev), &μ, |
364 | ); | |
365 | // Calculate mean inner iterations and reset relevant counters. | |
366 | // Return the statistics | |
367 | let res = IterInfo { | |
368 | value : residual.norm2_squared_div2() + reg.apply(&μ), | |
369 | n_spikes : μ.len(), | |
370 | ε : ε_prev, | |
371 | postprocessing: config.postprocessing.then(|| μ.clone()), | |
372 | .. stats | |
373 | }; | |
374 | stats = IterInfo::new(); | |
375 | res | |
376 | }) | |
377 | }); | |
378 | ||
379 | postprocess(μ, config, L2Squared, opA, b) | |
380 | } |