src/run.rs

changeset 72
e9a460a0e638
parent 63
7a8a55fd41c0
child 73
9c6432200aba
--- 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<F>| 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<F: Float>(&self) -> AlgorithmConfig<F> {
+    pub fn default_config<F: Float>(
+        &self,
+        regularisation_hint: Option<&Regularisation<F>>,
+    ) -> AlgorithmConfig<F> {
         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<F: Float>(&self) -> Named<AlgorithmConfig<F>> {
-        self.to_named(self.default_config())
-    }
-
     pub fn to_named<F: Float>(self, alg: AlgorithmConfig<F>) -> Named<AlgorithmConfig<F>> {
         Named { name: self.name(), data: alg }
     }
@@ -485,12 +496,9 @@
     fn runall(
         &self,
         cli: &CommandLineArgs,
-        algs: Option<Vec<Named<AlgorithmConfig<F>>>>,
+        cli_algorithm_overrides: &AlgorithmOverrides<F>,
     ) -> DynError;
 
-    /// Return algorithm default config
-    fn algorithm_overrides(&self, alg: DefaultAlgorithm) -> AlgorithmOverrides<F>;
-
     /// Experiment name
     fn name(&self) -> &str;
 }
@@ -511,6 +519,7 @@
     TimingIteratorFactory<BasicAlgIteratorFactory<IterInfo<F>>>,
 >;
 
+/// Trait for things used by many a [`RunnableExperiment`], but that are not dyn-compatible.
 pub trait RunnableExperimentExtras<F: ClapFloat>:
     RunnableExperiment<F> + 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<F>,
+        algorithm_overrides_fn: impl Fn(DefaultAlgorithm) -> Option<AlgorithmOverrides<F>>,
+        regularisation_hint: Option<&Regularisation<F>>,
+        dflt: &[DefaultAlgorithm],
+    ) -> DynResult<Vec<Named<AlgorithmConfig<F>>>>
+    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<Named<AlgorithmConfig<F>>> = 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<P, Z, Plot, const N: usize>(
         &self,
@@ -707,22 +752,10 @@
         self.name.as_ref()
     }
 
-    fn algorithm_overrides(&self, alg: DefaultAlgorithm) -> AlgorithmOverrides<F> {
-        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<Vec<Named<AlgorithmConfig<F>>>>,
+        cli_algorithm_overrides: &AlgorithmOverrides<F>,
     ) -> 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(&regularisation),
+            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<IterInfo<F>>,
     Dat: DifferentiableMapping<RNDM<N, F>, Codomain = F> + BoundedCurvature<F>,
-    Dat::DerivativeDomain: DifferentiableRealMapping<N, F> + ClosedMul<F>,
+    Dat::DerivativeDomain:
+        DifferentiableRealMapping<N, F> + ClosedMul<F> + MinMaxMapping<Loc<N, F>, F>,
     RNDM<N, F>: SpikeMerging<F>,
     Reg: SlidingRegTerm<Loc<N, F>, F>,
-    P: ProxPenalty<Loc<N, F>, Dat::DerivativeDomain, Reg, F> + StepLengthBound<F, Dat>,
+    P: TransportProxPenalty<Loc<N, F>, Dat::DerivativeDomain, Reg, F> + StepLengthBound<F, Dat>,
     Plot: Plotter<P::ReturnMapping, Dat::DerivativeDomain, RNDM<N, F>>,
 {
     let pt = P::prox_type();
@@ -1067,23 +1124,10 @@
         self.name.as_ref()
     }
 
-    fn algorithm_overrides(&self, alg: DefaultAlgorithm) -> AlgorithmOverrides<F> {
-        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<Vec<Named<AlgorithmConfig<F>>>>,
+        cli_algorithm_overrides: &AlgorithmOverrides<F>,
     ) -> 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(&regularisation),
+            &[
+                DefaultAlgorithm::SlidingPDPS,
+                DefaultAlgorithm::ForwardPDPS,
+                DefaultAlgorithm::RadonSlidingPDPS,
+                DefaultAlgorithm::RadonForwardPDPS,
+            ],
+        )?;
 
         // Set up operators
         let depth = DynamicDepth(8);
@@ -1259,19 +1322,19 @@
     I: AlgIteratorFactory<IterInfo<F>>,
     Dat: DifferentiableMapping<MeasureZ<F, Z, N>, Codomain = F, DerivativeDomain = Pair<S, Z>>
         + BoundedCurvature<F>,
-    S: DifferentiableRealMapping<N, F> + ClosedMul<F>,
+    S: DifferentiableRealMapping<N, F> + ClosedMul<F> + MinMaxMapping<Loc<N, F>, F>,
     for<'a> Pair<&'a P, &'a IdOp<Z>>: StepLengthBoundPair<F, Dat>,
     //Pair<S, Z>: ClosedMul<F>,
     RNDM<N, F>: SpikeMerging<F>,
     Reg: SlidingRegTerm<Loc<N, F>, F>,
-    P: ProxPenalty<Loc<N, F>, S, Reg, F>,
+    P: TransportProxPenalty<Loc<N, F>, S, Reg, F>,
     KOpZ: BoundedLinear<Z, L2, L2, F, Codomain = Y>
         + GEMV<F, Z>
         + SimplyAdjointable<Z, Y, AdjointCodomain = Z>,
     KOpZ::SimpleAdjoint: GEMV<F, Y>,
     Y: ClosedEuclidean<F> + Clone,
     for<'b> &'b Y: Instance<Y>,
-    Z: ClosedEuclidean<F> + Clone + ClosedMul<F>,
+    Z: ClosedEuclidean<F> + Clone + ClosedMul<F> + AXPY,
     for<'b> &'b Z: Instance<Z>,
     R: Prox<Z, Codomain = F>,
     H: Conjugable<Y, F, Codomain = F>,
@@ -1342,10 +1405,10 @@
     I: AlgIteratorFactory<IterInfo<F>>,
     Dat: DifferentiableMapping<MeasureZ<F, Z, N>, Codomain = F, DerivativeDomain = Pair<S, Z>>
         + BoundedCurvature<F>,
-    S: DifferentiableRealMapping<N, F> + ClosedMul<F>,
+    S: DifferentiableRealMapping<N, F> + ClosedMul<F> + MinMaxMapping<Loc<N, F>, F>,
     RNDM<N, F>: SpikeMerging<F>,
     Reg: SlidingRegTerm<Loc<N, F>, F>,
-    P: ProxPenalty<Loc<N, F>, S, Reg, F>,
+    P: TransportProxPenalty<Loc<N, F>, S, Reg, F>,
     for<'a> Pair<&'a P, &'a IdOp<Z>>: StepLengthBoundPair<F, Dat>,
     Z: ClosedEuclidean<F> + AXPY + Clone,
     for<'b> &'b Z: Instance<Z>,

mercurial