Tue, 31 Dec 2024 09:25:45 -0500
New version of sliding.
0 | 1 | /*! |
2 | Experimental setups. | |
3 | */ | |
4 | ||
5 | //use numeric_literals::replace_float_literals; | |
6 | use serde::{Serialize, Deserialize}; | |
7 | use clap::ValueEnum; | |
8 | use std::collections::HashMap; | |
9 | use std::hash::{Hash, Hasher}; | |
10 | use std::collections::hash_map::DefaultHasher; | |
11 | ||
12 | use alg_tools::bisection_tree::*; | |
13 | use alg_tools::error::DynResult; | |
14 | use alg_tools::norms::Linfinity; | |
15 | ||
16 | use crate::ExperimentOverrides; | |
17 | use crate::kernels::*; | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
25
diff
changeset
|
18 | use crate::kernels::SupportProductFirst as Prod; |
0 | 19 | use crate::pdps::PDPSConfig; |
20 | use crate::types::*; | |
21 | use crate::run::{ | |
22 | RunnableExperiment, | |
24
d29d1fcf5423
Support arbitrary regularisation terms; implement non-positivity-constrained regularisation.
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
23 | ExperimentV2, |
35 | 24 | ExperimentBiased, |
0 | 25 | Named, |
26 | DefaultAlgorithm, | |
27 | AlgorithmConfig | |
28 | }; | |
29 | //use crate::fb::FBGenericConfig; | |
30 | use crate::rand_distr::{SerializableNormal, SaltAndPepper}; | |
24
d29d1fcf5423
Support arbitrary regularisation terms; implement non-positivity-constrained regularisation.
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
31 | use crate::regularisation::Regularisation; |
35 | 32 | use alg_tools::euclidean::Euclidean; |
33 | use alg_tools::instance::Instance; | |
34 | use alg_tools::mapping::Mapping; | |
35 | use alg_tools::operator_arithmetic::{MappingSum, Weighted}; | |
0 | 36 | |
37 | /// Experiments shorthands, to be used with the command line parser | |
38 | ||
39 | #[derive(ValueEnum, Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] | |
40 | #[allow(non_camel_case_types)] | |
41 | pub enum DefaultExperiment { | |
42 | /// One dimension, cut gaussian spread, 2-norm-squared data fidelity | |
43 | #[clap(name = "1d")] | |
44 | Experiment1D, | |
45 | /// One dimension, “fast” spread, 2-norm-squared data fidelity | |
46 | #[clap(name = "1d_fast")] | |
47 | Experiment1DFast, | |
48 | /// Two dimensions, cut gaussian spread, 2-norm-squared data fidelity | |
49 | #[clap(name = "2d")] | |
50 | Experiment2D, | |
51 | /// Two dimensions, “fast” spread, 2-norm-squared data fidelity | |
52 | #[clap(name = "2d_fast")] | |
53 | Experiment2DFast, | |
54 | /// One dimension, cut gaussian spread, 1-norm data fidelity | |
55 | #[clap(name = "1d_l1")] | |
56 | Experiment1D_L1, | |
57 | /// One dimension, ‘“fast” spread, 1-norm data fidelity | |
58 | #[clap(name = "1d_l1_fast")] | |
59 | Experiment1D_L1_Fast, | |
60 | /// Two dimensions, cut gaussian spread, 1-norm data fidelity | |
61 | #[clap(name = "2d_l1")] | |
62 | Experiment2D_L1, | |
63 | /// Two dimensions, “fast” spread, 1-norm data fidelity | |
64 | #[clap(name = "2d_l1_fast")] | |
65 | Experiment2D_L1_Fast, | |
35 | 66 | /// One dimension, “fast” spread, 2-norm-squared data fidelity with extra TV-regularised bias |
67 | #[clap(name = "1d_tv_fast")] | |
68 | Experiment1D_TV_Fast, | |
0 | 69 | } |
70 | ||
71 | macro_rules! make_float_constant { | |
72 | ($name:ident = $value:expr) => { | |
73 | #[derive(Debug, Copy, Eq, PartialEq, Clone, Serialize, Deserialize)] | |
74 | #[serde(into = "float")] | |
75 | struct $name; | |
76 | impl Into<float> for $name { | |
77 | #[inline] | |
78 | fn into(self) -> float { $value } | |
79 | } | |
80 | impl Constant for $name { | |
81 | type Type = float; | |
82 | fn value(&self) -> float { $value } | |
83 | } | |
84 | } | |
85 | } | |
86 | ||
87 | /// Ground-truth measure spike locations and magnitudes for 1D experiments | |
88 | static MU_TRUE_1D_BASIC : [(float, float); 4] = [ | |
89 | (0.10, 10.0), | |
90 | (0.30, 2.0), | |
91 | (0.70, 3.0), | |
92 | (0.80, 5.0) | |
93 | ]; | |
94 | ||
95 | /// Ground-truth measure spike locations and magnitudes for 2D experiments | |
96 | static MU_TRUE_2D_BASIC : [([float; 2], float); 4] = [ | |
97 | ([0.15, 0.15], 10.0), | |
98 | ([0.75, 0.45], 2.0), | |
99 | ([0.80, 0.50], 4.0), | |
100 | ([0.30, 0.70], 5.0) | |
101 | ]; | |
102 | ||
35 | 103 | /// The $\{0,1\}$-valued characteristic function of a ball as a [`Mapping`]. |
104 | #[derive(Debug,Copy,Clone,Serialize,PartialEq)] | |
105 | struct BallCharacteristic<F : Float, const N : usize> { | |
106 | pub center : Loc<F, N>, | |
107 | pub radius : F, | |
108 | } | |
109 | ||
110 | impl<F : Float, const N : usize> Mapping<Loc<F, N>> for BallCharacteristic<F, N> { | |
111 | type Codomain =F; | |
112 | ||
113 | fn apply<I : Instance<Loc<F, N>>>(&self, i : I) -> F { | |
114 | if self.center.dist2(i) <= self.radius { | |
115 | F::ONE | |
116 | } else { | |
117 | F::ZERO | |
118 | } | |
119 | } | |
120 | } | |
121 | ||
0 | 122 | //#[replace_float_literals(F::cast_from(literal))] |
123 | impl DefaultExperiment { | |
124 | /// Convert the experiment shorthand into a runnable experiment configuration. | |
125 | pub fn get_experiment(&self, cli : &ExperimentOverrides<float>) -> DynResult<Box<dyn RunnableExperiment<float>>> { | |
126 | let name = "pointsource".to_string() | |
127 | + self.to_possible_value().unwrap().get_name(); | |
128 | ||
129 | let kernel_plot_width = 0.2; | |
130 | ||
131 | const BASE_SEED : u64 = 915373234; | |
132 | ||
133 | const N_SENSORS_1D : usize = 100; | |
134 | make_float_constant!(SensorWidth1D = 0.4/(N_SENSORS_1D as float)); | |
135 | ||
136 | const N_SENSORS_2D : usize = 16; | |
137 | make_float_constant!(SensorWidth2D = 0.4/(N_SENSORS_2D as float)); | |
138 | ||
139 | const N_SENSORS_2D_MORE : usize = 32; | |
140 | make_float_constant!(SensorWidth2DMore = 0.4/(N_SENSORS_2D_MORE as float)); | |
141 | ||
142 | make_float_constant!(Variance1 = 0.05.powi(2)); | |
143 | make_float_constant!(CutOff1 = 0.15); | |
144 | make_float_constant!(Hat1 = 0.16); | |
35 | 145 | make_float_constant!(HatBias = 0.05); |
0 | 146 | |
147 | // We use a different step length for PDPS in 2D experiments | |
148 | let pdps_2d = || { | |
149 | let τ0 = 3.0; | |
150 | PDPSConfig { | |
151 | τ0, | |
152 | σ0 : 0.99 / τ0, | |
153 | .. Default::default() | |
154 | } | |
155 | }; | |
156 | ||
157 | // We add a hash of the experiment name to the configured | |
158 | // noise seed to not use the same noise for different experiments. | |
159 | let mut h = DefaultHasher::new(); | |
160 | name.hash(&mut h); | |
161 | let noise_seed = cli.noise_seed.unwrap_or(BASE_SEED) + h.finish(); | |
162 | ||
163 | use DefaultExperiment::*; | |
164 | Ok(match self { | |
165 | Experiment1D => { | |
166 | let base_spread = Gaussian { variance : Variance1 }; | |
167 | let spread_cutoff = BallIndicator { r : CutOff1, exponent : Linfinity }; | |
24
d29d1fcf5423
Support arbitrary regularisation terms; implement non-positivity-constrained regularisation.
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
168 | Box::new(Named { name, data : ExperimentV2 { |
0 | 169 | domain : [[0.0, 1.0]].into(), |
170 | sensor_count : [N_SENSORS_1D], | |
25
79943be70720
Implement non-negativity constraints for the conditional gradient methods
Tuomo Valkonen <tuomov@iki.fi>
parents:
24
diff
changeset
|
171 | regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.09)), |
0 | 172 | noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.2))?, |
173 | dataterm : DataTerm::L2Squared, | |
174 | μ_hat : MU_TRUE_1D_BASIC.into(), | |
175 | sensor : BallIndicator { r : SensorWidth1D, exponent : Linfinity }, | |
176 | spread : Prod(spread_cutoff, base_spread), | |
177 | kernel : Prod(AutoConvolution(spread_cutoff), base_spread), | |
178 | kernel_plot_width, | |
179 | noise_seed, | |
180 | algorithm_defaults: HashMap::new(), | |
181 | }}) | |
182 | }, | |
183 | Experiment1DFast => { | |
184 | let base_spread = HatConv { radius : Hat1 }; | |
24
d29d1fcf5423
Support arbitrary regularisation terms; implement non-positivity-constrained regularisation.
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
185 | Box::new(Named { name, data : ExperimentV2 { |
0 | 186 | domain : [[0.0, 1.0]].into(), |
187 | sensor_count : [N_SENSORS_1D], | |
25
79943be70720
Implement non-negativity constraints for the conditional gradient methods
Tuomo Valkonen <tuomov@iki.fi>
parents:
24
diff
changeset
|
188 | regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.06)), |
0 | 189 | noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.2))?, |
190 | dataterm : DataTerm::L2Squared, | |
191 | μ_hat : MU_TRUE_1D_BASIC.into(), | |
192 | sensor : BallIndicator { r : SensorWidth1D, exponent : Linfinity }, | |
193 | spread : base_spread, | |
194 | kernel : base_spread, | |
195 | kernel_plot_width, | |
196 | noise_seed, | |
197 | algorithm_defaults: HashMap::new(), | |
198 | }}) | |
199 | }, | |
200 | Experiment2D => { | |
201 | let base_spread = Gaussian { variance : Variance1 }; | |
202 | let spread_cutoff = BallIndicator { r : CutOff1, exponent : Linfinity }; | |
24
d29d1fcf5423
Support arbitrary regularisation terms; implement non-positivity-constrained regularisation.
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
203 | Box::new(Named { name, data : ExperimentV2 { |
0 | 204 | domain : [[0.0, 1.0]; 2].into(), |
205 | sensor_count : [N_SENSORS_2D; 2], | |
25
79943be70720
Implement non-negativity constraints for the conditional gradient methods
Tuomo Valkonen <tuomov@iki.fi>
parents:
24
diff
changeset
|
206 | regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.19)), |
0 | 207 | noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.25))?, |
208 | dataterm : DataTerm::L2Squared, | |
209 | μ_hat : MU_TRUE_2D_BASIC.into(), | |
210 | sensor : BallIndicator { r : SensorWidth2D, exponent : Linfinity }, | |
211 | spread : Prod(spread_cutoff, base_spread), | |
212 | kernel : Prod(AutoConvolution(spread_cutoff), base_spread), | |
213 | kernel_plot_width, | |
214 | noise_seed, | |
215 | algorithm_defaults: HashMap::from([ | |
216 | (DefaultAlgorithm::PDPS, AlgorithmConfig::PDPS(pdps_2d())) | |
217 | ]), | |
218 | }}) | |
219 | }, | |
220 | Experiment2DFast => { | |
221 | let base_spread = HatConv { radius : Hat1 }; | |
24
d29d1fcf5423
Support arbitrary regularisation terms; implement non-positivity-constrained regularisation.
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
222 | Box::new(Named { name, data : ExperimentV2 { |
0 | 223 | domain : [[0.0, 1.0]; 2].into(), |
224 | sensor_count : [N_SENSORS_2D; 2], | |
25
79943be70720
Implement non-negativity constraints for the conditional gradient methods
Tuomo Valkonen <tuomov@iki.fi>
parents:
24
diff
changeset
|
225 | regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.12)), |
0 | 226 | noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.15))?, //0.25 |
227 | dataterm : DataTerm::L2Squared, | |
228 | μ_hat : MU_TRUE_2D_BASIC.into(), | |
229 | sensor : BallIndicator { r : SensorWidth2D, exponent : Linfinity }, | |
230 | spread : base_spread, | |
231 | kernel : base_spread, | |
232 | kernel_plot_width, | |
233 | noise_seed, | |
234 | algorithm_defaults: HashMap::from([ | |
235 | (DefaultAlgorithm::PDPS, AlgorithmConfig::PDPS(pdps_2d())) | |
236 | ]), | |
237 | }}) | |
238 | }, | |
239 | Experiment1D_L1 => { | |
240 | let base_spread = Gaussian { variance : Variance1 }; | |
241 | let spread_cutoff = BallIndicator { r : CutOff1, exponent : Linfinity }; | |
24
d29d1fcf5423
Support arbitrary regularisation terms; implement non-positivity-constrained regularisation.
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
242 | Box::new(Named { name, data : ExperimentV2 { |
0 | 243 | domain : [[0.0, 1.0]].into(), |
244 | sensor_count : [N_SENSORS_1D], | |
24
d29d1fcf5423
Support arbitrary regularisation terms; implement non-positivity-constrained regularisation.
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
245 | regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.1)), |
0 | 246 | noise_distr : SaltAndPepper::new( |
247 | cli.salt_and_pepper.as_ref().map_or(0.6, |v| v[0]), | |
248 | cli.salt_and_pepper.as_ref().map_or(0.4, |v| v[1]) | |
249 | )?, | |
250 | dataterm : DataTerm::L1, | |
251 | μ_hat : MU_TRUE_1D_BASIC.into(), | |
252 | sensor : BallIndicator { r : SensorWidth1D, exponent : Linfinity }, | |
253 | spread : Prod(spread_cutoff, base_spread), | |
254 | kernel : Prod(AutoConvolution(spread_cutoff), base_spread), | |
255 | kernel_plot_width, | |
256 | noise_seed, | |
257 | algorithm_defaults: HashMap::new(), | |
258 | }}) | |
259 | }, | |
260 | Experiment1D_L1_Fast => { | |
261 | let base_spread = HatConv { radius : Hat1 }; | |
24
d29d1fcf5423
Support arbitrary regularisation terms; implement non-positivity-constrained regularisation.
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
262 | Box::new(Named { name, data : ExperimentV2 { |
0 | 263 | domain : [[0.0, 1.0]].into(), |
264 | sensor_count : [N_SENSORS_1D], | |
24
d29d1fcf5423
Support arbitrary regularisation terms; implement non-positivity-constrained regularisation.
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
265 | regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.12)), |
0 | 266 | noise_distr : SaltAndPepper::new( |
267 | cli.salt_and_pepper.as_ref().map_or(0.6, |v| v[0]), | |
268 | cli.salt_and_pepper.as_ref().map_or(0.4, |v| v[1]) | |
269 | )?, | |
270 | dataterm : DataTerm::L1, | |
271 | μ_hat : MU_TRUE_1D_BASIC.into(), | |
272 | sensor : BallIndicator { r : SensorWidth1D, exponent : Linfinity }, | |
273 | spread : base_spread, | |
274 | kernel : base_spread, | |
275 | kernel_plot_width, | |
276 | noise_seed, | |
277 | algorithm_defaults: HashMap::new(), | |
278 | }}) | |
279 | }, | |
280 | Experiment2D_L1 => { | |
281 | let base_spread = Gaussian { variance : Variance1 }; | |
282 | let spread_cutoff = BallIndicator { r : CutOff1, exponent : Linfinity }; | |
24
d29d1fcf5423
Support arbitrary regularisation terms; implement non-positivity-constrained regularisation.
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
283 | Box::new(Named { name, data : ExperimentV2 { |
0 | 284 | domain : [[0.0, 1.0]; 2].into(), |
285 | sensor_count : [N_SENSORS_2D; 2], | |
24
d29d1fcf5423
Support arbitrary regularisation terms; implement non-positivity-constrained regularisation.
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
286 | regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.35)), |
0 | 287 | noise_distr : SaltAndPepper::new( |
288 | cli.salt_and_pepper.as_ref().map_or(0.8, |v| v[0]), | |
289 | cli.salt_and_pepper.as_ref().map_or(0.2, |v| v[1]) | |
290 | )?, | |
291 | dataterm : DataTerm::L1, | |
292 | μ_hat : MU_TRUE_2D_BASIC.into(), | |
293 | sensor : BallIndicator { r : SensorWidth2D, exponent : Linfinity }, | |
294 | spread : Prod(spread_cutoff, base_spread), | |
295 | kernel : Prod(AutoConvolution(spread_cutoff), base_spread), | |
296 | kernel_plot_width, | |
297 | noise_seed, | |
298 | algorithm_defaults: HashMap::from([ | |
299 | (DefaultAlgorithm::PDPS, AlgorithmConfig::PDPS(pdps_2d())) | |
300 | ]), | |
301 | }}) | |
302 | }, | |
303 | Experiment2D_L1_Fast => { | |
304 | let base_spread = HatConv { radius : Hat1 }; | |
24
d29d1fcf5423
Support arbitrary regularisation terms; implement non-positivity-constrained regularisation.
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
305 | Box::new(Named { name, data : ExperimentV2 { |
0 | 306 | domain : [[0.0, 1.0]; 2].into(), |
307 | sensor_count : [N_SENSORS_2D; 2], | |
24
d29d1fcf5423
Support arbitrary regularisation terms; implement non-positivity-constrained regularisation.
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
308 | regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.40)), |
0 | 309 | noise_distr : SaltAndPepper::new( |
310 | cli.salt_and_pepper.as_ref().map_or(0.8, |v| v[0]), | |
311 | cli.salt_and_pepper.as_ref().map_or(0.2, |v| v[1]) | |
312 | )?, | |
313 | dataterm : DataTerm::L1, | |
314 | μ_hat : MU_TRUE_2D_BASIC.into(), | |
315 | sensor : BallIndicator { r : SensorWidth2D, exponent : Linfinity }, | |
316 | spread : base_spread, | |
317 | kernel : base_spread, | |
318 | kernel_plot_width, | |
319 | noise_seed, | |
320 | algorithm_defaults: HashMap::from([ | |
321 | (DefaultAlgorithm::PDPS, AlgorithmConfig::PDPS(pdps_2d())) | |
322 | ]), | |
323 | }}) | |
324 | }, | |
35 | 325 | Experiment1D_TV_Fast => { |
326 | let base_spread = HatConv { radius : HatBias }; | |
327 | Box::new(Named { name, data : ExperimentBiased { | |
328 | λ : 0.02, | |
329 | bias : MappingSum::new([ | |
330 | Weighted::new(1.0, BallCharacteristic{ center : 0.3.into(), radius : 0.2 }), | |
331 | Weighted::new(0.5, BallCharacteristic{ center : 0.6.into(), radius : 0.3 }), | |
332 | ]), | |
333 | base : ExperimentV2 { | |
334 | domain : [[0.0, 1.0]].into(), | |
335 | sensor_count : [N_SENSORS_1D], | |
336 | regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.2)), | |
337 | noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.1))?, | |
338 | dataterm : DataTerm::L2Squared, | |
339 | μ_hat : MU_TRUE_1D_BASIC.into(), | |
340 | sensor : BallIndicator { r : SensorWidth1D, exponent : Linfinity }, | |
341 | spread : base_spread, | |
342 | kernel : base_spread, | |
343 | kernel_plot_width, | |
344 | noise_seed, | |
345 | algorithm_defaults: HashMap::new(), | |
346 | }, | |
347 | }}) | |
348 | }, | |
0 | 349 | }) |
350 | } | |
351 | } | |
352 |