--- a/src/experiments.rs Tue Aug 01 10:25:09 2023 +0300 +++ b/src/experiments.rs Mon Feb 17 13:54:53 2025 -0500 @@ -13,21 +13,24 @@ use alg_tools::error::DynResult; use alg_tools::norms::Linfinity; -use crate::ExperimentOverrides; +use crate::{ExperimentOverrides, AlgorithmOverrides}; use crate::kernels::*; -use crate::kernels::{SupportProductFirst as Prod}; -use crate::pdps::PDPSConfig; +use crate::kernels::SupportProductFirst as Prod; use crate::types::*; use crate::run::{ RunnableExperiment, ExperimentV2, + ExperimentBiased, Named, DefaultAlgorithm, - AlgorithmConfig }; //use crate::fb::FBGenericConfig; use crate::rand_distr::{SerializableNormal, SaltAndPepper}; use crate::regularisation::Regularisation; +use alg_tools::euclidean::Euclidean; +use alg_tools::instance::Instance; +use alg_tools::mapping::Mapping; +use alg_tools::operator_arithmetic::{MappingSum, Weighted}; /// Experiments shorthands, to be used with the command line parser @@ -58,6 +61,12 @@ /// Two dimensions, “fast” spread, 1-norm data fidelity #[clap(name = "2d_l1_fast")] Experiment2D_L1_Fast, + /// One dimension, “fast” spread, 2-norm-squared data fidelity with extra TV-regularised bias + #[clap(name = "1d_tv_fast")] + Experiment1D_TV_Fast, + /// Two dimensions, “fast” spread, 2-norm-squared data fidelity with extra TV-regularised bias + #[clap(name = "2d_tv_fast")] + Experiment2D_TV_Fast, } macro_rules! make_float_constant { @@ -92,6 +101,25 @@ ([0.30, 0.70], 5.0) ]; +/// The $\{0,1\}$-valued characteristic function of a ball as a [`Mapping`]. +#[derive(Debug,Copy,Clone,Serialize,PartialEq)] +struct BallCharacteristic<F : Float, const N : usize> { + pub center : Loc<F, N>, + pub radius : F, +} + +impl<F : Float, const N : usize> Mapping<Loc<F, N>> for BallCharacteristic<F, N> { + type Codomain =F; + + fn apply<I : Instance<Loc<F, N>>>(&self, i : I) -> F { + if self.center.dist2(i) <= self.radius { + F::ONE + } else { + F::ZERO + } + } +} + //#[replace_float_literals(F::cast_from(literal))] impl DefaultExperiment { /// Convert the experiment shorthand into a runnable experiment configuration. @@ -115,23 +143,70 @@ make_float_constant!(Variance1 = 0.05.powi(2)); make_float_constant!(CutOff1 = 0.15); make_float_constant!(Hat1 = 0.16); + make_float_constant!(HatBias = 0.05); // We use a different step length for PDPS in 2D experiments - let pdps_2d = || { - let τ0 = 3.0; - PDPSConfig { - τ0, - σ0 : 0.99 / τ0, + // let pdps_2d = (DefaultAlgorithm::PDPS, + // AlgorithmOverrides { + // tau0 : Some(3.0), + // sigma0 : Some(0.99 / 3.0), + // .. Default::default() + // } + // ); + // let radon_pdps_2d = (DefaultAlgorithm::RadonPDPS, + // AlgorithmOverrides { + // tau0 : Some(3.0), + // sigma0 : Some(0.99 / 3.0), + // .. Default::default() + // } + // ); + let sliding_fb_cut_gaussian = (DefaultAlgorithm::SlidingFB, + AlgorithmOverrides { + theta0 : Some(0.3), .. Default::default() } - }; - + ); + // let higher_cpos = |alg| (alg, + // AlgorithmOverrides { + // transport_tolerance_pos : Some(1000.0), + // .. Default::default() + // } + // ); + let higher_cpos_merging = |alg| (alg, + AlgorithmOverrides { + transport_tolerance_pos : Some(1000.0), + merge : Some(true), + fitness_merging : Some(true), + .. Default::default() + } + ); + let higher_cpos_merging_steptune = |alg| (alg, + AlgorithmOverrides { + transport_tolerance_pos : Some(1000.0), + theta0 : Some(0.3), + merge : Some(true), + fitness_merging : Some(true), + .. Default::default() + } + ); + let much_higher_cpos_merging_steptune = |alg| (alg, + AlgorithmOverrides { + transport_tolerance_pos : Some(10000.0), + sigma0 : Some(0.15), + theta0 : Some(0.3), + merge : Some(true), + fitness_merging : Some(true), + .. Default::default() + } + ); // We add a hash of the experiment name to the configured // noise seed to not use the same noise for different experiments. let mut h = DefaultHasher::new(); name.hash(&mut h); let noise_seed = cli.noise_seed.unwrap_or(BASE_SEED) + h.finish(); + let default_merge_radius = 0.01; + use DefaultExperiment::*; Ok(match self { Experiment1D => { @@ -140,7 +215,7 @@ Box::new(Named { name, data : ExperimentV2 { domain : [[0.0, 1.0]].into(), sensor_count : [N_SENSORS_1D], - regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.09)), + regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.08)), noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.2))?, dataterm : DataTerm::L2Squared, μ_hat : MU_TRUE_1D_BASIC.into(), @@ -149,7 +224,12 @@ kernel : Prod(AutoConvolution(spread_cutoff), base_spread), kernel_plot_width, noise_seed, - algorithm_defaults: HashMap::new(), + default_merge_radius, + algorithm_overrides: HashMap::from([ + sliding_fb_cut_gaussian, + higher_cpos_merging(DefaultAlgorithm::RadonFB), + higher_cpos_merging(DefaultAlgorithm::RadonSlidingFB), + ]), }}) }, Experiment1DFast => { @@ -166,7 +246,11 @@ kernel : base_spread, kernel_plot_width, noise_seed, - algorithm_defaults: HashMap::new(), + default_merge_radius, + algorithm_overrides: HashMap::from([ + higher_cpos_merging(DefaultAlgorithm::RadonFB), + higher_cpos_merging(DefaultAlgorithm::RadonSlidingFB), + ]), }}) }, Experiment2D => { @@ -184,8 +268,11 @@ kernel : Prod(AutoConvolution(spread_cutoff), base_spread), kernel_plot_width, noise_seed, - algorithm_defaults: HashMap::from([ - (DefaultAlgorithm::PDPS, AlgorithmConfig::PDPS(pdps_2d())) + default_merge_radius, + algorithm_overrides: HashMap::from([ + sliding_fb_cut_gaussian, + higher_cpos_merging(DefaultAlgorithm::RadonFB), + higher_cpos_merging(DefaultAlgorithm::RadonSlidingFB), ]), }}) }, @@ -203,8 +290,10 @@ kernel : base_spread, kernel_plot_width, noise_seed, - algorithm_defaults: HashMap::from([ - (DefaultAlgorithm::PDPS, AlgorithmConfig::PDPS(pdps_2d())) + default_merge_radius, + algorithm_overrides: HashMap::from([ + higher_cpos_merging(DefaultAlgorithm::RadonFB), + higher_cpos_merging(DefaultAlgorithm::RadonSlidingFB), ]), }}) }, @@ -226,7 +315,8 @@ kernel : Prod(AutoConvolution(spread_cutoff), base_spread), kernel_plot_width, noise_seed, - algorithm_defaults: HashMap::new(), + default_merge_radius, + algorithm_overrides: HashMap::new(), }}) }, Experiment1D_L1_Fast => { @@ -246,7 +336,8 @@ kernel : base_spread, kernel_plot_width, noise_seed, - algorithm_defaults: HashMap::new(), + default_merge_radius, + algorithm_overrides: HashMap::new(), }}) }, Experiment2D_L1 => { @@ -267,8 +358,8 @@ kernel : Prod(AutoConvolution(spread_cutoff), base_spread), kernel_plot_width, noise_seed, - algorithm_defaults: HashMap::from([ - (DefaultAlgorithm::PDPS, AlgorithmConfig::PDPS(pdps_2d())) + default_merge_radius, + algorithm_overrides: HashMap::from([ ]), }}) }, @@ -289,9 +380,65 @@ kernel : base_spread, kernel_plot_width, noise_seed, - algorithm_defaults: HashMap::from([ - (DefaultAlgorithm::PDPS, AlgorithmConfig::PDPS(pdps_2d())) + default_merge_radius, + algorithm_overrides: HashMap::from([ + ]), + }}) + }, + Experiment1D_TV_Fast => { + let base_spread = HatConv { radius : HatBias }; + Box::new(Named { name, data : ExperimentBiased { + λ : 0.02, + bias : MappingSum::new([ + Weighted::new(1.0, BallCharacteristic{ center : 0.3.into(), radius : 0.2 }), + Weighted::new(0.5, BallCharacteristic{ center : 0.6.into(), radius : 0.3 }), ]), + base : ExperimentV2 { + domain : [[0.0, 1.0]].into(), + sensor_count : [N_SENSORS_1D], + regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.2)), + noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.1))?, + dataterm : DataTerm::L2Squared, + μ_hat : MU_TRUE_1D_BASIC.into(), + sensor : BallIndicator { r : SensorWidth1D, exponent : Linfinity }, + spread : base_spread, + kernel : base_spread, + kernel_plot_width, + noise_seed, + default_merge_radius, + algorithm_overrides: HashMap::from([ + higher_cpos_merging_steptune(DefaultAlgorithm::RadonForwardPDPS), + higher_cpos_merging_steptune(DefaultAlgorithm::RadonSlidingPDPS), + ]), + }, + }}) + }, + Experiment2D_TV_Fast => { + let base_spread = HatConv { radius : Hat1 }; + Box::new(Named { name, data : ExperimentBiased { + λ : 0.005, + bias : MappingSum::new([ + Weighted::new(1.0, BallCharacteristic{ center : [0.3, 0.3].into(), radius : 0.2 }), + Weighted::new(0.5, BallCharacteristic{ center : [0.6, 0.6].into(), radius : 0.3 }), + ]), + base : ExperimentV2 { + domain : [[0.0, 1.0]; 2].into(), + sensor_count : [N_SENSORS_2D; 2], + regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.06)), + noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.15))?, //0.25 + dataterm : DataTerm::L2Squared, + μ_hat : MU_TRUE_2D_BASIC.into(), + sensor : BallIndicator { r : SensorWidth2D, exponent : Linfinity }, + spread : base_spread, + kernel : base_spread, + kernel_plot_width, + noise_seed, + default_merge_radius, + algorithm_overrides: HashMap::from([ + much_higher_cpos_merging_steptune(DefaultAlgorithm::RadonForwardPDPS), + much_higher_cpos_merging_steptune(DefaultAlgorithm::RadonSlidingPDPS), + ]), + }, }}) }, })