diff -r e2953ffd4e0b -r e9a460a0e638 src/run.rs --- a/src/run.rs Fri May 15 14:40:02 2026 -0500 +++ b/src/run.rs Sun Jul 19 07:34:39 2026 +0200 @@ -23,7 +23,9 @@ }; use crate::regularisation::{NonnegRadonRegTerm, RadonRegTerm, Regularisation, SlidingRegTerm}; use crate::seminorms::*; -use crate::sliding_fb::{pointsource_sliding_fb_reg, SlidingFBConfig, TransportConfig}; +use crate::sliding_fb::{ + pointsource_sliding_fb_reg, SlidingFBConfig, TransportConfig, TransportProxPenalty, +}; use crate::sliding_pdps::{ pointsource_sliding_fb_pair, pointsource_sliding_pdps_pair, SlidingPDPSConfig, }; @@ -129,8 +131,14 @@ }; let override_transport = |g: TransportConfig| TransportConfig { θ0: cli.theta0.unwrap_or(g.θ0), - tolerance_mult_con: cli.transport_tolerance_pos.unwrap_or(g.tolerance_mult_con), + τθ: cli.tautheta.or(g.τθ), + tolerance_mult: cli.transport_tolerance.unwrap_or(g.tolerance_mult), + ℓ_gradv_mult: cli.gradv_lipest_mult.unwrap_or(g.ℓ_gradv_mult), adaptation: cli.transport_adaptation.unwrap_or(g.adaptation), + allow_partial_transport: cli + .allow_partial_transport + .unwrap_or(g.allow_partial_transport), + alt_remainder_control: cli.alt_remainder_control.unwrap_or(g.alt_remainder_control), ..g }; @@ -270,12 +278,20 @@ impl DefaultAlgorithm { /// Returns the algorithm configuration corresponding to the algorithm shorthand - pub fn default_config(&self) -> AlgorithmConfig { + pub fn default_config( + &self, + regularisation_hint: Option<&Regularisation>, + ) -> AlgorithmConfig { use DefaultAlgorithm::*; let radon_insertion = InsertionConfig { - merging: SpikeMergingMethod { interp: false, ..Default::default() }, + merging: SpikeMergingMethod { enabled: true, interp: false, ..Default::default() }, + fitness_merging: true, inner: InnerSettings { - method: InnerMethod::PDPS, // SSN not implemented + method: if let Some(Regularisation::NonnegRadon(_)) = regularisation_hint { + InnerMethod::Exact + } else { + InnerMethod::PDPS // SSN not implemented + }, ..Default::default() }, ..Default::default() @@ -320,11 +336,6 @@ } } - /// Returns the [`Named`] algorithm corresponding to the algorithm shorthand - pub fn get_named(&self) -> Named> { - self.to_named(self.default_config()) - } - pub fn to_named(self, alg: AlgorithmConfig) -> Named> { Named { name: self.name(), data: alg } } @@ -485,12 +496,9 @@ fn runall( &self, cli: &CommandLineArgs, - algs: Option>>>, + cli_algorithm_overrides: &AlgorithmOverrides, ) -> DynError; - /// Return algorithm default config - fn algorithm_overrides(&self, alg: DefaultAlgorithm) -> AlgorithmOverrides; - /// Experiment name fn name(&self) -> &str; } @@ -511,6 +519,7 @@ TimingIteratorFactory>>, >; +/// Trait for things used by many a [`RunnableExperiment`], but that are not dyn-compatible. pub trait RunnableExperimentExtras: RunnableExperiment + Serialize + Sized { @@ -545,6 +554,42 @@ Ok(prefix) } + /// Collect algorithms to run based on command line args. + fn collect_algs( + &self, + cli: &CommandLineArgs, + algorithm_overrides: &AlgorithmOverrides, + algorithm_overrides_fn: impl Fn(DefaultAlgorithm) -> Option>, + regularisation_hint: Option<&Regularisation>, + dflt: &[DefaultAlgorithm], + ) -> DynResult>>> + where + F: for<'a> Deserialize<'a>, + { + let proc_alg = |alg: &DefaultAlgorithm| { + let default_cfg = alg.default_config(regularisation_hint); + let cfg = if let Some(over) = &algorithm_overrides_fn(*alg) { + default_cfg.cli_override(over) + } else { + default_cfg + } + .cli_override(algorithm_overrides); + alg.to_named(cfg) + }; + + let mut algs: Vec>> = cli.algorithm.iter().map(proc_alg).collect(); + for filename in cli.saved_algorithm.iter() { + let f = std::fs::File::open(filename)?; + let alg = serde_json::from_reader(f)?; + algs.push(alg); + } + if algs.is_empty() { + Ok(dflt.iter().map(proc_alg).collect()) + } else { + Ok(algs) + } + } + /// Helper function to run all algorithms on an experiment. fn do_runall( &self, @@ -707,22 +752,10 @@ self.name.as_ref() } - fn algorithm_overrides(&self, alg: DefaultAlgorithm) -> AlgorithmOverrides { - AlgorithmOverrides { - merge_radius: Some(self.data.default_merge_radius), - ..self - .data - .algorithm_overrides - .get(&alg) - .cloned() - .unwrap_or(Default::default()) - } - } - fn runall( &self, cli: &CommandLineArgs, - algs: Option>>>, + cli_algorithm_overrides: &AlgorithmOverrides, ) -> DynError { // Get experiment configuration let &ExperimentV2 { @@ -741,11 +774,34 @@ } = &self.data; // Set up algorithms - let algorithms = match (algs, dataterm) { - (Some(algs), _) => algs, - (None, DataTermType::L222) => vec![DefaultAlgorithm::FB.get_named()], - (None, DataTermType::L1) => vec![DefaultAlgorithm::PDPS.get_named()], - }; + let algorithms = self.collect_algs( + cli, + cli_algorithm_overrides, + |alg| { + Some(AlgorithmOverrides { + merge_radius: Some(self.data.default_merge_radius), + ..self + .data + .algorithm_overrides + .get(&alg) + .cloned() + .unwrap_or_else(Default::default) + }) + }, + Some(®ularisation), + match dataterm { + DataTermType::L222 => &[ + DefaultAlgorithm::FB, + DefaultAlgorithm::RadonFB, + DefaultAlgorithm::SlidingFB, + DefaultAlgorithm::RadonSlidingFB, + DefaultAlgorithm::FW, + DefaultAlgorithm::FWRelax, + DefaultAlgorithm::PDPS, + ], + DataTermType::L1 => &[DefaultAlgorithm::PDPS], + }, + )?; // Set up operators let depth = DynamicDepth(8); @@ -944,10 +1000,11 @@ F: Float + ToNalgebraRealField, I: AlgIteratorFactory>, Dat: DifferentiableMapping, Codomain = F> + BoundedCurvature, - Dat::DerivativeDomain: DifferentiableRealMapping + ClosedMul, + Dat::DerivativeDomain: + DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, RNDM: SpikeMerging, Reg: SlidingRegTerm, F>, - P: ProxPenalty, Dat::DerivativeDomain, Reg, F> + StepLengthBound, + P: TransportProxPenalty, Dat::DerivativeDomain, Reg, F> + StepLengthBound, Plot: Plotter>, { let pt = P::prox_type(); @@ -1067,23 +1124,10 @@ self.name.as_ref() } - fn algorithm_overrides(&self, alg: DefaultAlgorithm) -> AlgorithmOverrides { - AlgorithmOverrides { - merge_radius: Some(self.data.base.default_merge_radius), - ..self - .data - .base - .algorithm_overrides - .get(&alg) - .cloned() - .unwrap_or(Default::default()) - } - } - fn runall( &self, cli: &CommandLineArgs, - algs: Option>>>, + cli_algorithm_overrides: &AlgorithmOverrides, ) -> DynError { // Get experiment configuration let &ExperimentBiased { @@ -1107,10 +1151,29 @@ } = &self.data; // Set up algorithms - let algorithms = match (algs, dataterm) { - (Some(algs), _) => algs, - _ => vec![DefaultAlgorithm::SlidingPDPS.get_named()], - }; + let algorithms = self.collect_algs( + cli, + cli_algorithm_overrides, + |alg| { + Some(AlgorithmOverrides { + merge_radius: Some(self.data.base.default_merge_radius), + ..self + .data + .base + .algorithm_overrides + .get(&alg) + .cloned() + .unwrap_or_else(Default::default) + }) + }, + Some(®ularisation), + &[ + DefaultAlgorithm::SlidingPDPS, + DefaultAlgorithm::ForwardPDPS, + DefaultAlgorithm::RadonSlidingPDPS, + DefaultAlgorithm::RadonForwardPDPS, + ], + )?; // Set up operators let depth = DynamicDepth(8); @@ -1259,19 +1322,19 @@ I: AlgIteratorFactory>, Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + BoundedCurvature, - S: DifferentiableRealMapping + ClosedMul, + S: DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, //Pair: ClosedMul, RNDM: SpikeMerging, Reg: SlidingRegTerm, F>, - P: ProxPenalty, S, Reg, F>, + P: TransportProxPenalty, S, Reg, F>, KOpZ: BoundedLinear + GEMV + SimplyAdjointable, KOpZ::SimpleAdjoint: GEMV, Y: ClosedEuclidean + Clone, for<'b> &'b Y: Instance, - Z: ClosedEuclidean + Clone + ClosedMul, + Z: ClosedEuclidean + Clone + ClosedMul + AXPY, for<'b> &'b Z: Instance, R: Prox, H: Conjugable, @@ -1342,10 +1405,10 @@ I: AlgIteratorFactory>, Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + BoundedCurvature, - S: DifferentiableRealMapping + ClosedMul, + S: DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, RNDM: SpikeMerging, Reg: SlidingRegTerm, F>, - P: ProxPenalty, S, Reg, F>, + P: TransportProxPenalty, S, Reg, F>, for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, Z: ClosedEuclidean + AXPY + Clone, for<'b> &'b Z: Instance,