Tue, 06 Dec 2022 14:12:38 +0200
Added tag v1.0.0-pre-arxiv for changeset b71edfd403aa
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::*; | |
18 | use crate::kernels::{SupportProductFirst as Prod}; | |
19 | use crate::pdps::PDPSConfig; | |
20 | use crate::types::*; | |
21 | use crate::run::{ | |
22 | RunnableExperiment, | |
23 | Experiment, | |
24 | Named, | |
25 | DefaultAlgorithm, | |
26 | AlgorithmConfig | |
27 | }; | |
28 | //use crate::fb::FBGenericConfig; | |
29 | use crate::rand_distr::{SerializableNormal, SaltAndPepper}; | |
30 | ||
31 | /// Experiments shorthands, to be used with the command line parser | |
32 | ||
33 | #[derive(ValueEnum, Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] | |
34 | #[allow(non_camel_case_types)] | |
35 | pub enum DefaultExperiment { | |
36 | /// One dimension, cut gaussian spread, 2-norm-squared data fidelity | |
37 | #[clap(name = "1d")] | |
38 | Experiment1D, | |
39 | /// One dimension, “fast” spread, 2-norm-squared data fidelity | |
40 | #[clap(name = "1d_fast")] | |
41 | Experiment1DFast, | |
42 | /// Two dimensions, cut gaussian spread, 2-norm-squared data fidelity | |
43 | #[clap(name = "2d")] | |
44 | Experiment2D, | |
45 | /// Two dimensions, “fast” spread, 2-norm-squared data fidelity | |
46 | #[clap(name = "2d_fast")] | |
47 | Experiment2DFast, | |
48 | /// One dimension, cut gaussian spread, 1-norm data fidelity | |
49 | #[clap(name = "1d_l1")] | |
50 | Experiment1D_L1, | |
51 | /// One dimension, ‘“fast” spread, 1-norm data fidelity | |
52 | #[clap(name = "1d_l1_fast")] | |
53 | Experiment1D_L1_Fast, | |
54 | /// Two dimensions, cut gaussian spread, 1-norm data fidelity | |
55 | #[clap(name = "2d_l1")] | |
56 | Experiment2D_L1, | |
57 | /// Two dimensions, “fast” spread, 1-norm data fidelity | |
58 | #[clap(name = "2d_l1_fast")] | |
59 | Experiment2D_L1_Fast, | |
60 | } | |
61 | ||
62 | macro_rules! make_float_constant { | |
63 | ($name:ident = $value:expr) => { | |
64 | #[derive(Debug, Copy, Eq, PartialEq, Clone, Serialize, Deserialize)] | |
65 | #[serde(into = "float")] | |
66 | struct $name; | |
67 | impl Into<float> for $name { | |
68 | #[inline] | |
69 | fn into(self) -> float { $value } | |
70 | } | |
71 | impl Constant for $name { | |
72 | type Type = float; | |
73 | fn value(&self) -> float { $value } | |
74 | } | |
75 | } | |
76 | } | |
77 | ||
78 | /// Ground-truth measure spike locations and magnitudes for 1D experiments | |
79 | static MU_TRUE_1D_BASIC : [(float, float); 4] = [ | |
80 | (0.10, 10.0), | |
81 | (0.30, 2.0), | |
82 | (0.70, 3.0), | |
83 | (0.80, 5.0) | |
84 | ]; | |
85 | ||
86 | /// Ground-truth measure spike locations and magnitudes for 2D experiments | |
87 | static MU_TRUE_2D_BASIC : [([float; 2], float); 4] = [ | |
88 | ([0.15, 0.15], 10.0), | |
89 | ([0.75, 0.45], 2.0), | |
90 | ([0.80, 0.50], 4.0), | |
91 | ([0.30, 0.70], 5.0) | |
92 | ]; | |
93 | ||
94 | //#[replace_float_literals(F::cast_from(literal))] | |
95 | impl DefaultExperiment { | |
96 | /// Convert the experiment shorthand into a runnable experiment configuration. | |
97 | pub fn get_experiment(&self, cli : &ExperimentOverrides<float>) -> DynResult<Box<dyn RunnableExperiment<float>>> { | |
98 | let name = "pointsource".to_string() | |
99 | + self.to_possible_value().unwrap().get_name(); | |
100 | ||
101 | let kernel_plot_width = 0.2; | |
102 | ||
103 | const BASE_SEED : u64 = 915373234; | |
104 | ||
105 | const N_SENSORS_1D : usize = 100; | |
106 | make_float_constant!(SensorWidth1D = 0.4/(N_SENSORS_1D as float)); | |
107 | ||
108 | const N_SENSORS_2D : usize = 16; | |
109 | make_float_constant!(SensorWidth2D = 0.4/(N_SENSORS_2D as float)); | |
110 | ||
111 | const N_SENSORS_2D_MORE : usize = 32; | |
112 | make_float_constant!(SensorWidth2DMore = 0.4/(N_SENSORS_2D_MORE as float)); | |
113 | ||
114 | make_float_constant!(Variance1 = 0.05.powi(2)); | |
115 | make_float_constant!(CutOff1 = 0.15); | |
116 | make_float_constant!(Hat1 = 0.16); | |
117 | ||
118 | // We use a different step length for PDPS in 2D experiments | |
119 | let pdps_2d = || { | |
120 | let τ0 = 3.0; | |
121 | PDPSConfig { | |
122 | τ0, | |
123 | σ0 : 0.99 / τ0, | |
124 | .. Default::default() | |
125 | } | |
126 | }; | |
127 | ||
128 | // We add a hash of the experiment name to the configured | |
129 | // noise seed to not use the same noise for different experiments. | |
130 | let mut h = DefaultHasher::new(); | |
131 | name.hash(&mut h); | |
132 | let noise_seed = cli.noise_seed.unwrap_or(BASE_SEED) + h.finish(); | |
133 | ||
134 | use DefaultExperiment::*; | |
135 | Ok(match self { | |
136 | Experiment1D => { | |
137 | let base_spread = Gaussian { variance : Variance1 }; | |
138 | let spread_cutoff = BallIndicator { r : CutOff1, exponent : Linfinity }; | |
139 | Box::new(Named { name, data : Experiment { | |
140 | domain : [[0.0, 1.0]].into(), | |
141 | sensor_count : [N_SENSORS_1D], | |
142 | α : cli.alpha.unwrap_or(0.09), | |
143 | noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.2))?, | |
144 | dataterm : DataTerm::L2Squared, | |
145 | μ_hat : MU_TRUE_1D_BASIC.into(), | |
146 | sensor : BallIndicator { r : SensorWidth1D, exponent : Linfinity }, | |
147 | spread : Prod(spread_cutoff, base_spread), | |
148 | kernel : Prod(AutoConvolution(spread_cutoff), base_spread), | |
149 | kernel_plot_width, | |
150 | noise_seed, | |
151 | algorithm_defaults: HashMap::new(), | |
152 | }}) | |
153 | }, | |
154 | Experiment1DFast => { | |
155 | let base_spread = HatConv { radius : Hat1 }; | |
156 | Box::new(Named { name, data : Experiment { | |
157 | domain : [[0.0, 1.0]].into(), | |
158 | sensor_count : [N_SENSORS_1D], | |
159 | α : cli.alpha.unwrap_or(0.06), | |
160 | noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.2))?, | |
161 | dataterm : DataTerm::L2Squared, | |
162 | μ_hat : MU_TRUE_1D_BASIC.into(), | |
163 | sensor : BallIndicator { r : SensorWidth1D, exponent : Linfinity }, | |
164 | spread : base_spread, | |
165 | kernel : base_spread, | |
166 | kernel_plot_width, | |
167 | noise_seed, | |
168 | algorithm_defaults: HashMap::new(), | |
169 | }}) | |
170 | }, | |
171 | Experiment2D => { | |
172 | let base_spread = Gaussian { variance : Variance1 }; | |
173 | let spread_cutoff = BallIndicator { r : CutOff1, exponent : Linfinity }; | |
174 | Box::new(Named { name, data : Experiment { | |
175 | domain : [[0.0, 1.0]; 2].into(), | |
176 | sensor_count : [N_SENSORS_2D; 2], | |
177 | α : cli.alpha.unwrap_or(0.19), // 0.18, //0.17, //0.16, | |
178 | noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.25))?, | |
179 | dataterm : DataTerm::L2Squared, | |
180 | μ_hat : MU_TRUE_2D_BASIC.into(), | |
181 | sensor : BallIndicator { r : SensorWidth2D, exponent : Linfinity }, | |
182 | spread : Prod(spread_cutoff, base_spread), | |
183 | kernel : Prod(AutoConvolution(spread_cutoff), base_spread), | |
184 | kernel_plot_width, | |
185 | noise_seed, | |
186 | algorithm_defaults: HashMap::from([ | |
187 | (DefaultAlgorithm::PDPS, AlgorithmConfig::PDPS(pdps_2d())) | |
188 | ]), | |
189 | }}) | |
190 | }, | |
191 | Experiment2DFast => { | |
192 | let base_spread = HatConv { radius : Hat1 }; | |
193 | Box::new(Named { name, data : Experiment { | |
194 | domain : [[0.0, 1.0]; 2].into(), | |
195 | sensor_count : [N_SENSORS_2D; 2], | |
196 | α : cli.alpha.unwrap_or(0.12), //0.10, //0.14, | |
197 | noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.15))?, //0.25 | |
198 | dataterm : DataTerm::L2Squared, | |
199 | μ_hat : MU_TRUE_2D_BASIC.into(), | |
200 | sensor : BallIndicator { r : SensorWidth2D, exponent : Linfinity }, | |
201 | spread : base_spread, | |
202 | kernel : base_spread, | |
203 | kernel_plot_width, | |
204 | noise_seed, | |
205 | algorithm_defaults: HashMap::from([ | |
206 | (DefaultAlgorithm::PDPS, AlgorithmConfig::PDPS(pdps_2d())) | |
207 | ]), | |
208 | }}) | |
209 | }, | |
210 | Experiment1D_L1 => { | |
211 | let base_spread = Gaussian { variance : Variance1 }; | |
212 | let spread_cutoff = BallIndicator { r : CutOff1, exponent : Linfinity }; | |
213 | Box::new(Named { name, data : Experiment { | |
214 | domain : [[0.0, 1.0]].into(), | |
215 | sensor_count : [N_SENSORS_1D], | |
216 | α : cli.alpha.unwrap_or(0.1), | |
217 | noise_distr : SaltAndPepper::new( | |
218 | cli.salt_and_pepper.as_ref().map_or(0.6, |v| v[0]), | |
219 | cli.salt_and_pepper.as_ref().map_or(0.4, |v| v[1]) | |
220 | )?, | |
221 | dataterm : DataTerm::L1, | |
222 | μ_hat : MU_TRUE_1D_BASIC.into(), | |
223 | sensor : BallIndicator { r : SensorWidth1D, exponent : Linfinity }, | |
224 | spread : Prod(spread_cutoff, base_spread), | |
225 | kernel : Prod(AutoConvolution(spread_cutoff), base_spread), | |
226 | kernel_plot_width, | |
227 | noise_seed, | |
228 | algorithm_defaults: HashMap::new(), | |
229 | }}) | |
230 | }, | |
231 | Experiment1D_L1_Fast => { | |
232 | let base_spread = HatConv { radius : Hat1 }; | |
233 | Box::new(Named { name, data : Experiment { | |
234 | domain : [[0.0, 1.0]].into(), | |
235 | sensor_count : [N_SENSORS_1D], | |
236 | α : cli.alpha.unwrap_or(0.12), | |
237 | noise_distr : SaltAndPepper::new( | |
238 | cli.salt_and_pepper.as_ref().map_or(0.6, |v| v[0]), | |
239 | cli.salt_and_pepper.as_ref().map_or(0.4, |v| v[1]) | |
240 | )?, | |
241 | dataterm : DataTerm::L1, | |
242 | μ_hat : MU_TRUE_1D_BASIC.into(), | |
243 | sensor : BallIndicator { r : SensorWidth1D, exponent : Linfinity }, | |
244 | spread : base_spread, | |
245 | kernel : base_spread, | |
246 | kernel_plot_width, | |
247 | noise_seed, | |
248 | algorithm_defaults: HashMap::new(), | |
249 | }}) | |
250 | }, | |
251 | Experiment2D_L1 => { | |
252 | let base_spread = Gaussian { variance : Variance1 }; | |
253 | let spread_cutoff = BallIndicator { r : CutOff1, exponent : Linfinity }; | |
254 | Box::new(Named { name, data : Experiment { | |
255 | domain : [[0.0, 1.0]; 2].into(), | |
256 | sensor_count : [N_SENSORS_2D; 2], | |
257 | α : cli.alpha.unwrap_or(0.35), | |
258 | noise_distr : SaltAndPepper::new( | |
259 | cli.salt_and_pepper.as_ref().map_or(0.8, |v| v[0]), | |
260 | cli.salt_and_pepper.as_ref().map_or(0.2, |v| v[1]) | |
261 | )?, | |
262 | dataterm : DataTerm::L1, | |
263 | μ_hat : MU_TRUE_2D_BASIC.into(), | |
264 | sensor : BallIndicator { r : SensorWidth2D, exponent : Linfinity }, | |
265 | spread : Prod(spread_cutoff, base_spread), | |
266 | kernel : Prod(AutoConvolution(spread_cutoff), base_spread), | |
267 | kernel_plot_width, | |
268 | noise_seed, | |
269 | algorithm_defaults: HashMap::from([ | |
270 | (DefaultAlgorithm::PDPS, AlgorithmConfig::PDPS(pdps_2d())) | |
271 | ]), | |
272 | }}) | |
273 | }, | |
274 | Experiment2D_L1_Fast => { | |
275 | let base_spread = HatConv { radius : Hat1 }; | |
276 | Box::new(Named { name, data : Experiment { | |
277 | domain : [[0.0, 1.0]; 2].into(), | |
278 | sensor_count : [N_SENSORS_2D; 2], | |
279 | α : cli.alpha.unwrap_or(0.40), | |
280 | noise_distr : SaltAndPepper::new( | |
281 | cli.salt_and_pepper.as_ref().map_or(0.8, |v| v[0]), | |
282 | cli.salt_and_pepper.as_ref().map_or(0.2, |v| v[1]) | |
283 | )?, | |
284 | dataterm : DataTerm::L1, | |
285 | μ_hat : MU_TRUE_2D_BASIC.into(), | |
286 | sensor : BallIndicator { r : SensorWidth2D, exponent : Linfinity }, | |
287 | spread : base_spread, | |
288 | kernel : base_spread, | |
289 | kernel_plot_width, | |
290 | noise_seed, | |
291 | algorithm_defaults: HashMap::from([ | |
292 | (DefaultAlgorithm::PDPS, AlgorithmConfig::PDPS(pdps_2d())) | |
293 | ]), | |
294 | }}) | |
295 | }, | |
296 | }) | |
297 | } | |
298 | } | |
299 |