# HG changeset patch # User Tuomo Valkonen # Date 1786032855 18000 # Node ID 8f1e57821d7657eb4ab09ac6c05492f6fc742c2a # Parent 759634edb5365efdac7b6241126c98a4ca6fe950 Generalise algorithms to arbitrary domains diff -r 759634edb536 -r 8f1e57821d76 src/domain.rs --- a/src/domain.rs Tue Aug 04 20:11:10 2026 -0500 +++ b/src/domain.rs Thu Aug 06 11:14:15 2026 -0500 @@ -28,7 +28,7 @@ } } -impl AdmissibleDomain> for FullSpace {} +impl AdmissibleDomain for FullSpace {} pub struct UniformRandom(pub S, pub R); diff -r 759634edb536 -r 8f1e57821d76 src/fb.rs --- a/src/fb.rs Tue Aug 04 20:11:10 2026 -0500 +++ b/src/fb.rs Thu Aug 06 11:14:15 2026 -0500 @@ -79,7 +79,7 @@ use crate::domain::{AdmissibleDomain, FullSpace}; use crate::measures::merging::SpikeMerging; -use crate::measures::{DiscreteMeasure, RNDM}; +use crate::measures::DiscreteMeasure; use crate::plot::Plotter; pub use crate::prox_penalty::{InsertionConfig, ProxPenalty, StepLengthBound}; use crate::regularisation::RegTerm; @@ -87,6 +87,7 @@ use alg_tools::error::DynResult; use alg_tools::instance::Instance; use alg_tools::iterate::AlgIteratorFactory; +use alg_tools::linops::Space; use alg_tools::mapping::DifferentiableMapping; use alg_tools::nalgebra_support::ToNalgebraRealField; use colored::Colorize; @@ -113,8 +114,8 @@ } } -pub(crate) fn prune_with_stats( - μ: &mut RNDM, +pub(crate) fn prune_with_stats( + μ: &mut DiscreteMeasure, until: usize, ) -> usize { let n_before_prune = μ.len(); @@ -124,14 +125,16 @@ } #[replace_float_literals(F::cast_from(literal))] -pub(crate) fn postprocess) -> F, const N: usize>( - mut μ: RNDM, +pub(crate) fn postprocess( + mut μ: DiscreteMeasure, config: &InsertionConfig, f: Dat, -) -> DynResult> +) -> DynResult> where - RNDM: SpikeMerging, - for<'a> &'a RNDM: Instance>, + Dat: Fn(&DiscreteMeasure) -> F, + U: Space + Clone, + DiscreteMeasure: SpikeMerging, + for<'a> &'a DiscreteMeasure: Instance>, { //μ.merge_spikes_fitness(config.final_merging_method(), |μ̃| f.apply(μ̃), |&v| v); μ.merge_spikes_fitness(config.final_merging_method(), f, |&v| v); @@ -151,25 +154,26 @@ /// /// Returns the final iterate. #[replace_float_literals(F::cast_from(literal))] -pub fn pointsource_fb_dom( +pub fn pointsource_fb_dom( f: &Dat, reg: &Reg, prox_penalty: &P, fbconfig: &FBConfig, iterator: I, mut plotter: Plot, - μ0: Option>, - domain: &mut impl AdmissibleDomain>, -) -> DynResult> + μ0: Option>, + domain: &mut impl AdmissibleDomain, +) -> DynResult> where + U: Space + Clone, F: Float + ToNalgebraRealField, I: AlgIteratorFactory>, - RNDM: SpikeMerging, - Dat: DifferentiableMapping, Codomain = F>, + DiscreteMeasure: SpikeMerging, + Dat: DifferentiableMapping, Codomain = F>, Dat::DerivativeDomain: ClosedMul, - Reg: RegTerm, F>, - P: ProxPenalty, Dat::DerivativeDomain, Reg, F> + StepLengthBound, - Plot: Plotter>, + Reg: RegTerm, + P: ProxPenalty + StepLengthBound, + Plot: Plotter>, { // Set up parameters let config = &fbconfig.insertion; @@ -183,7 +187,7 @@ let mut μ = μ0.unwrap_or_else(|| DiscreteMeasure::new()); // Statistics - let full_stats = |μ: &RNDM, ε, stats| IterInfo { + let full_stats = |μ: &DiscreteMeasure, ε, stats| IterInfo { value: f.apply(μ) + reg.apply(μ), n_spikes: μ.len(), ε, @@ -219,7 +223,7 @@ ε, ins, ®, - Some(|μ̃: &RNDM| f.apply(μ̃) + reg.apply(μ̃)), + Some(|μ̃: &DiscreteMeasure| f.apply(μ̃) + reg.apply(μ̃)), ); // Prune. Do not prune new insertions on first round. @@ -267,24 +271,25 @@ } #[deprecated] -pub fn pointsource_fb_reg( +pub fn pointsource_fb_reg( f: &Dat, reg: &Reg, prox_penalty: &P, fbconfig: &FBConfig, iterator: I, plotter: Plot, - μ0: Option>, -) -> DynResult> + μ0: Option>, +) -> DynResult> where + U: Space + Clone, F: Float + ToNalgebraRealField, I: AlgIteratorFactory>, - RNDM: SpikeMerging, - Dat: DifferentiableMapping, Codomain = F>, + DiscreteMeasure: SpikeMerging, + Dat: DifferentiableMapping, Codomain = F>, Dat::DerivativeDomain: ClosedMul, - Reg: RegTerm, F>, - P: ProxPenalty, Dat::DerivativeDomain, Reg, F> + StepLengthBound, - Plot: Plotter>, + Reg: RegTerm, + P: ProxPenalty + StepLengthBound, + Plot: Plotter>, { pointsource_fb_dom( f, @@ -310,24 +315,25 @@ /// /// Returns the final iterate. #[replace_float_literals(F::cast_from(literal))] -pub fn pointsource_fista_reg( +pub fn pointsource_fista_reg( f: &Dat, reg: &Reg, prox_penalty: &P, fbconfig: &FBConfig, iterator: I, mut plotter: Plot, - μ0: Option>, -) -> DynResult> + μ0: Option>, +) -> DynResult> where + U: Space + Clone, F: Float + ToNalgebraRealField, I: AlgIteratorFactory>, - RNDM: SpikeMerging, - Dat: DifferentiableMapping, Codomain = F>, + DiscreteMeasure: SpikeMerging, + Dat: DifferentiableMapping, Codomain = F>, Dat::DerivativeDomain: ClosedMul, - Reg: RegTerm, F>, - P: ProxPenalty, Dat::DerivativeDomain, Reg, F> + StepLengthBound, - Plot: Plotter>, + Reg: RegTerm, + P: ProxPenalty + StepLengthBound, + Plot: Plotter>, { // Set up parameters let config = &fbconfig.insertion; @@ -344,7 +350,7 @@ let mut warned_merging = false; // Statistics - let full_stats = |ν: &RNDM, ε, stats| IterInfo { + let full_stats = |ν: &DiscreteMeasure, ε, stats| IterInfo { value: f.apply(ν) + reg.apply(ν), n_spikes: ν.len(), ε, diff -r 759634edb536 -r 8f1e57821d76 src/forward_pdps.rs --- a/src/forward_pdps.rs Tue Aug 04 20:11:10 2026 -0500 +++ b/src/forward_pdps.rs Thu Aug 06 11:14:15 2026 -0500 @@ -6,7 +6,7 @@ use crate::domain::{AdmissibleDomain, FullSpace}; use crate::fb::*; use crate::measures::merging::SpikeMerging; -use crate::measures::{DiscreteMeasure, RNDM}; +use crate::measures::DiscreteMeasure; use crate::plot::Plotter; use crate::prox_penalty::{ProxPenalty, StepLengthBoundPair}; use crate::regularisation::RegTerm; @@ -16,8 +16,8 @@ use alg_tools::error::DynResult; use alg_tools::euclidean::ClosedEuclidean; use alg_tools::iterate::AlgIteratorFactory; -use alg_tools::linops::{BoundedLinear, IdOp, SimplyAdjointable, ZeroOp, AXPY, GEMV}; -use alg_tools::mapping::{DifferentiableMapping, DifferentiableRealMapping, Instance}; +use alg_tools::linops::{BoundedLinear, IdOp, SimplyAdjointable, Space, ZeroOp, AXPY, GEMV}; +use alg_tools::mapping::{DifferentiableMapping, Instance}; use alg_tools::nalgebra_support::ToNalgebraRealField; use alg_tools::norms::L2; use anyhow::ensure; @@ -48,7 +48,7 @@ } } -type MeasureZ = Pair, Z>; +type MeasureZ = Pair, Z>; /// Iteratively solve the pointsource localisation with an additional variable /// using primal-dual proximal splitting with a forward step. @@ -89,6 +89,7 @@ #[replace_float_literals(F::cast_from(literal))] pub fn pointsource_forward_pdps_pair_dom< F, + U, I, S, Dat, @@ -100,7 +101,6 @@ /*KOpM, */ KOpZ, H, Plot, - const N: usize, >( f: &Dat, reg: &Reg, @@ -108,22 +108,23 @@ config: &ForwardPDPSConfig, iterator: I, mut plotter: Plot, - (μ0, mut z, mut y): (Option>, Z, Y), + (μ0, mut z, mut y): (Option>, Z, Y), //opKμ : KOpM, opKz: &KOpZ, fnR: &R, fnH: &H, - domain: &mut impl AdmissibleDomain>, -) -> DynResult> + domain: &mut impl AdmissibleDomain, +) -> DynResult> where + U: Space + Clone, F: Float + ToNalgebraRealField, I: AlgIteratorFactory>, - Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair>, + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair>, //Pair: ClosedMul, // Doesn't really need to be closed, if make this signature more complex… - S: DifferentiableRealMapping + ClosedMul, - RNDM: SpikeMerging, - Reg: RegTerm, F>, - P: ProxPenalty, S, Reg, F>, + S: ClosedMul, + DiscreteMeasure: SpikeMerging, + Reg: RegTerm, + P: ProxPenalty, for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, KOpZ: BoundedLinear + GEMV @@ -136,7 +137,7 @@ R: Prox, H: Conjugable, for<'b> H::Conjugate<'b>: Prox, - Plot: Plotter>, + Plot: Plotter>, { // Check parameters ensure!( @@ -188,7 +189,7 @@ let starH = fnH.conjugate(); // Statistics - let full_stats = |μ: &RNDM, z: &Z, ε, stats| IterInfo { + let full_stats = |μ: &DiscreteMeasure, z: &Z, ε, stats| IterInfo { value: f.apply(Pair(μ, z)) + fnR.apply(z) + reg.apply(μ) @@ -250,7 +251,7 @@ ε, ins, ®, - Some(|μ̃: &RNDM| f.apply(Pair(μ̃, &z)) + reg.apply(μ̃)), + Some(|μ̃: &DiscreteMeasure| f.apply(Pair(μ̃, &z)) + reg.apply(μ̃)), ); // Prune. Do not prune new insertions on first round. stats.pruned += prune_with_stats(&mut μ, μ_base_len); @@ -310,7 +311,7 @@ ε = tolerance.update(ε, iter); } - let fit = |μ̃: &RNDM| { + let fit = |μ̃: &DiscreteMeasure| { f.apply(Pair(μ̃, &z)) /*+ fnR.apply(z) + reg.apply(μ)*/ + fnH.apply(/* opKμ.apply(&μ̃) + */ opKz.apply(&z)) + reg.apply(μ̃) @@ -324,6 +325,7 @@ #[deprecated] pub fn pointsource_forward_pdps_pair< F, + U, I, S, Dat, @@ -335,7 +337,6 @@ /*KOpM, */ KOpZ, H, Plot, - const N: usize, >( f: &Dat, reg: &Reg, @@ -343,21 +344,22 @@ config: &ForwardPDPSConfig, iterator: I, plotter: Plot, - init: (Option>, Z, Y), + init: (Option>, Z, Y), //opKμ : KOpM, opKz: &KOpZ, fnR: &R, fnH: &H, -) -> DynResult> +) -> DynResult> where + U: Space + Clone, F: Float + ToNalgebraRealField, I: AlgIteratorFactory>, - Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair>, + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair>, //Pair: ClosedMul, // Doesn't really need to be closed, if make this signature more complex… - S: DifferentiableRealMapping + ClosedMul, - RNDM: SpikeMerging, - Reg: RegTerm, F>, - P: ProxPenalty, S, Reg, F>, + S: ClosedMul, + DiscreteMeasure: SpikeMerging, + Reg: RegTerm, + P: ProxPenalty, for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, KOpZ: BoundedLinear + GEMV @@ -370,7 +372,7 @@ R: Prox, H: Conjugable, for<'b> H::Conjugate<'b>: Prox, - Plot: Plotter>, + Plot: Plotter>, { pointsource_forward_pdps_pair_dom( f, @@ -393,31 +395,32 @@ /// The implementation uses [`pointsource_forward_pdps_pair`] with appropriate dummy /// variables, operators, and functions. #[replace_float_literals(F::cast_from(literal))] -pub fn pointsource_fb_pair_dom( +pub fn pointsource_fb_pair_dom( f: &Dat, reg: &Reg, prox_penalty: &P, config: &FBConfig, iterator: I, plotter: Plot, - (μ0, z): (Option>, Z), + (μ0, z): (Option>, Z), //opKμ : KOpM, fnR: &R, - domain: &mut impl AdmissibleDomain>, -) -> DynResult> + domain: &mut impl AdmissibleDomain, +) -> DynResult> where + U: Space + Clone, F: Float + ToNalgebraRealField, I: AlgIteratorFactory>, - Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair>, - S: DifferentiableRealMapping + ClosedMul, - RNDM: SpikeMerging, - Reg: RegTerm, F>, - P: ProxPenalty, S, Reg, F>, + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair>, + S: ClosedMul, + DiscreteMeasure: SpikeMerging, + Reg: RegTerm, + P: ProxPenalty, for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, Z: ClosedEuclidean + AXPY + Clone, for<'b> &'b Z: Instance, R: Prox, - Plot: Plotter>, + Plot: Plotter>, // We should not need to explicitly require this: for<'b> &'b Loc<0, F>: Instance>, { @@ -445,30 +448,31 @@ #[deprecated] #[replace_float_literals(F::cast_from(literal))] -pub fn pointsource_fb_pair( +pub fn pointsource_fb_pair( f: &Dat, reg: &Reg, prox_penalty: &P, config: &FBConfig, iterator: I, plotter: Plot, - init: (Option>, Z), + init: (Option>, Z), //opKμ : KOpM, fnR: &R, -) -> DynResult> +) -> DynResult> where + U: Space + Clone, F: Float + ToNalgebraRealField, I: AlgIteratorFactory>, - Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair>, - S: DifferentiableRealMapping + ClosedMul, - RNDM: SpikeMerging, - Reg: RegTerm, F>, - P: ProxPenalty, S, Reg, F>, + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair>, + S: DifferentiableMapping + ClosedMul, + DiscreteMeasure: SpikeMerging, + Reg: RegTerm, + P: ProxPenalty, for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, Z: ClosedEuclidean + AXPY + Clone, for<'b> &'b Z: Instance, R: Prox, - Plot: Plotter>, + Plot: Plotter>, // We should not need to explicitly require this: for<'b> &'b Loc<0, F>: Instance>, { diff -r 759634edb536 -r 8f1e57821d76 src/frank_wolfe.rs --- a/src/frank_wolfe.rs Tue Aug 04 20:11:10 2026 -0500 +++ b/src/frank_wolfe.rs Thu Aug 06 11:14:15 2026 -0500 @@ -20,7 +20,7 @@ use crate::dataterm::QuadraticDataTerm; use crate::forward_model::ForwardModel; use crate::measures::merging::{SpikeMerging, SpikeMergingMethod}; -use crate::measures::{DeltaMeasure, DiscreteMeasure, Radon, RNDM}; +use crate::measures::{DeltaMeasure, DiscreteMeasure, Radon}; use crate::plot::Plotter; use crate::regularisation::{NonnegRadonRegTerm, RadonRegTerm, RegTerm}; #[allow(unused_imports)] // Used in documentation @@ -29,18 +29,15 @@ }; use crate::tolerance::Tolerance; use crate::types::*; -use alg_tools::bisection_tree::P2Minimise; use alg_tools::bounds::MinMaxMapping; use alg_tools::error::DynResult; use alg_tools::euclidean::Euclidean; use alg_tools::instance::Instance; use alg_tools::iterate::{AlgIteratorFactory, AlgIteratorOptions, ValueIteratorFactory}; -use alg_tools::linops::Mapping; -use alg_tools::loc::Loc; +use alg_tools::linops::{Mapping, Space}; use alg_tools::nalgebra_support::ToNalgebraRealField; use alg_tools::norms::Norm; use alg_tools::norms::L2; -use alg_tools::sets::Cube; /// Settings for [`pointsource_fw_reg`]. #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] @@ -106,9 +103,9 @@ /// Trait for finite dimensional weight optimisation. pub trait WeightOptim< F: Float + ToNalgebraRealField, - A: ForwardModel, F>, + U: Space + Clone + PartialEq, + A: ForwardModel, F>, I: AlgIteratorFactory, - const N: usize, > { /// Return a pre-initialisation struct for [`Self::optimise_weights`]. @@ -135,7 +132,7 @@ /// Returns the number of iterations taken by the method configured in `inner`. fn optimise_weights<'a>( &self, - μ: &mut RNDM, + μ: &mut DiscreteMeasure, opA: &'a A, b: &A::Observable, findim_data: &FindimData, @@ -147,10 +144,10 @@ /// Trait for regularisation terms supported by [`pointsource_fw_reg`]. pub trait RegTermFW< F: Float + ToNalgebraRealField, - A: ForwardModel, F>, + U: Space + Clone + PartialEq, + A: ForwardModel, F>, I: AlgIteratorFactory, - const N: usize, ->: RegTerm, F> + WeightOptim + Mapping, Codomain = F> +>: RegTerm + WeightOptim + Mapping, Codomain = F> { /// With $g = A\_\*(Aμ-b)$, returns $(x, g(x))$ for $x$ a new point to be inserted /// into $μ$, as determined by the regulariser. @@ -162,26 +159,26 @@ g: &mut A::PreadjointCodomain, refinement_tolerance: F, max_steps: usize, - ) -> (Loc, F); + ) -> (U, F); /// Insert point `ξ` into `μ` for the relaxed algorithm from Bredies–Pikkarainen. fn relaxed_insert<'a>( &self, - μ: &mut RNDM, + μ: &mut DiscreteMeasure, g: &A::PreadjointCodomain, opA: &'a A, - ξ: Loc, + ξ: U, v_ξ: F, findim_data: &FindimData, ); } #[replace_float_literals(F::cast_from(literal))] -impl WeightOptim - for RadonRegTerm +impl WeightOptim for RadonRegTerm where + U: Space + Clone + PartialEq, I: AlgIteratorFactory, - A: FindimQuadraticModel, F>, + A: FindimQuadraticModel, { fn prepare_optimise_weights(&self, opA: &A, b: &A::Observable) -> DynResult> { Ok(FindimData { @@ -192,7 +189,7 @@ fn optimise_weights<'a>( &self, - μ: &mut RNDM, + μ: &mut DiscreteMeasure, opA: &'a A, b: &A::Observable, findim_data: &FindimData, @@ -221,23 +218,24 @@ } #[replace_float_literals(F::cast_from(literal))] -impl RegTermFW for RadonRegTerm +impl RegTermFW for RadonRegTerm where - Cube: P2Minimise, F>, + U: Space + Clone + PartialEq, + RadonRegTerm: RegTerm, I: AlgIteratorFactory, - A: FindimQuadraticModel, F>, - A::PreadjointCodomain: MinMaxMapping, F>, + A: FindimQuadraticModel, + A::PreadjointCodomain: MinMaxMapping, for<'a> &'a A::PreadjointCodomain: Instance, // FIXME: the following *should not* be needed, they are already implied - RNDM: Mapping, - DeltaMeasure, F>: Mapping, + DiscreteMeasure: Mapping, + DeltaMeasure: Copy + Mapping, { fn find_insertion( &self, g: &mut A::PreadjointCodomain, refinement_tolerance: F, max_steps: usize, - ) -> (Loc, F) { + ) -> (U, F) { let (ξmax, v_ξmax) = g.maximise(refinement_tolerance, max_steps); let (ξmin, v_ξmin) = g.minimise(refinement_tolerance, max_steps); if v_ξmin < 0.0 && -v_ξmin > v_ξmax { @@ -249,10 +247,10 @@ fn relaxed_insert<'a>( &self, - μ: &mut RNDM, + μ: &mut DiscreteMeasure, g: &A::PreadjointCodomain, opA: &'a A, - ξ: Loc, + ξ: U, v_ξ: F, findim_data: &FindimData, ) { @@ -285,11 +283,11 @@ } #[replace_float_literals(F::cast_from(literal))] -impl WeightOptim - for NonnegRadonRegTerm +impl WeightOptim for NonnegRadonRegTerm where + U: Space + Clone + PartialEq, I: AlgIteratorFactory, - A: FindimQuadraticModel, F>, + A: FindimQuadraticModel, { fn prepare_optimise_weights(&self, opA: &A, b: &A::Observable) -> DynResult> { Ok(FindimData { @@ -300,7 +298,7 @@ fn optimise_weights<'a>( &self, - μ: &mut RNDM, + μ: &mut DiscreteMeasure, opA: &'a A, b: &A::Observable, findim_data: &FindimData, @@ -329,33 +327,33 @@ } #[replace_float_literals(F::cast_from(literal))] -impl RegTermFW - for NonnegRadonRegTerm +impl RegTermFW for NonnegRadonRegTerm where - Cube: P2Minimise, F>, + U: Space + Clone + PartialEq, + NonnegRadonRegTerm: RegTerm, I: AlgIteratorFactory, - A: FindimQuadraticModel, F>, - A::PreadjointCodomain: MinMaxMapping, F>, + A: FindimQuadraticModel, + A::PreadjointCodomain: MinMaxMapping, for<'a> &'a A::PreadjointCodomain: Instance, // FIXME: the following *should not* be needed, they are already implied - RNDM: Mapping, - DeltaMeasure, F>: Mapping, + DiscreteMeasure: Mapping, + DeltaMeasure: Mapping, { fn find_insertion( &self, g: &mut A::PreadjointCodomain, refinement_tolerance: F, max_steps: usize, - ) -> (Loc, F) { + ) -> (U, F) { g.maximise(refinement_tolerance, max_steps) } fn relaxed_insert<'a>( &self, - μ: &mut RNDM, + μ: &mut DiscreteMeasure, g: &A::PreadjointCodomain, opA: &'a A, - ξ: Loc, + ξ: U, v_ξ: F, findim_data: &FindimData, ) { @@ -401,25 +399,25 @@ /// `iterator` is used to iterate the steps of the method, and `plotter` may be used to /// save intermediate iteration states as images. #[replace_float_literals(F::cast_from(literal))] -pub fn pointsource_fw_reg<'a, F, I, A, Reg, Plot, const N: usize>( - f: &'a QuadraticDataTerm, A>, +pub fn pointsource_fw_reg<'a, F, U, I, A, Reg, Plot>( + f: &'a QuadraticDataTerm, A>, reg: &Reg, - //domain : Cube, config: &FWConfig, iterator: I, mut plotter: Plot, - μ0: Option>, -) -> DynResult> + μ0: Option>, +) -> DynResult> where F: Float + ToNalgebraRealField, + U: Space + Clone + PartialEq, I: AlgIteratorFactory>, - A: ForwardModel, F>, - A::PreadjointCodomain: MinMaxMapping, F>, + A: ForwardModel, F>, + A::PreadjointCodomain: MinMaxMapping, &'a A::PreadjointCodomain: Instance, - Cube: P2Minimise, F>, - RNDM: SpikeMerging, - Reg: RegTermFW, N>, - Plot: Plotter>, + //Cube: P2Minimise, + DiscreteMeasure: SpikeMerging, + Reg: RegTermFW>, + Plot: Plotter>, { let opA = f.operator(); let b = f.data(); @@ -438,7 +436,7 @@ let mut residual = f.residual(&μ); // Statistics - let full_stats = |residual: &A::Observable, ν: &RNDM, ε, stats| IterInfo { + let full_stats = |residual: &A::Observable, ν: &DiscreteMeasure, ε, stats| IterInfo { value: residual.norm2_squared_div2() + reg.apply(ν), n_spikes: ν.len(), ε, diff -r 759634edb536 -r 8f1e57821d76 src/pdps.rs --- a/src/pdps.rs Tue Aug 04 20:11:10 2026 -0500 +++ b/src/pdps.rs Thu Aug 06 11:14:15 2026 -0500 @@ -42,7 +42,7 @@ use crate::forward_model::ForwardModel; use crate::measures::merging::SpikeMerging; use crate::measures::merging::SpikeMergingMethod; -use crate::measures::{DiscreteMeasure, RNDM}; +use crate::measures::DiscreteMeasure; use crate::plot::Plotter; pub use crate::prox_penalty::{InsertionConfig, ProxPenalty, StepLengthBoundPD}; use crate::regularisation::RegTerm; @@ -50,7 +50,7 @@ use alg_tools::convex::{Conjugable, ConvexMapping, Prox}; use alg_tools::error::DynResult; use alg_tools::iterate::AlgIteratorFactory; -use alg_tools::linops::{Mapping, AXPY}; +use alg_tools::linops::{Mapping, Space, AXPY}; use alg_tools::mapping::{DataTerm, Instance}; use alg_tools::nalgebra_support::ToNalgebraRealField; use anyhow::ensure; @@ -140,27 +140,29 @@ /// /// Returns the final iterate. #[replace_float_literals(F::cast_from(literal))] -pub fn pointsource_pdps_reg<'a, F, I, A, Phi, Reg, Plot, P, const N: usize>( - f: &'a DataTerm, A, Phi>, +pub fn pointsource_pdps_reg<'a, F, U, I, A, Phi, Reg, Plot, P>( + f: &'a DataTerm, A, Phi>, reg: &Reg, prox_penalty: &P, pdpsconfig: &PDPSConfig, iterator: I, mut plotter: Plot, - μ0: Option>, -) -> DynResult> + μ0: Option>, +) -> DynResult> where + U: Space + Clone + PartialEq, F: Float + ToNalgebraRealField, I: AlgIteratorFactory>, - A: ForwardModel, F>, + A: ForwardModel, F>, for<'b> &'b A::Observable: Instance, A::Observable: AXPY, - RNDM: SpikeMerging, - Reg: RegTerm, F>, + DiscreteMeasure: SpikeMerging, + Reg: RegTerm, Phi: Conjugable, for<'b> Phi::Conjugate<'b>: Prox, - P: ProxPenalty, A::PreadjointCodomain, Reg, F> + StepLengthBoundPD>, - Plot: Plotter>, + P: ProxPenalty + + StepLengthBoundPD>, + Plot: Plotter>, { // Check parameters ensure!( @@ -187,7 +189,7 @@ // Initialise iterates let mut μ = μ0.unwrap_or_else(|| DiscreteMeasure::new()); let mut y = f.residual(&μ); - let full_stats = |μ: &RNDM, ε, stats| IterInfo { + let full_stats = |μ: &DiscreteMeasure, ε, stats| IterInfo { value: f.apply(μ) + reg.apply(μ), n_spikes: μ.len(), ε, diff -r 759634edb536 -r 8f1e57821d76 src/run.rs --- a/src/run.rs Tue Aug 04 20:11:10 2026 -0500 +++ b/src/run.rs Thu Aug 06 11:14:15 2026 -0500 @@ -55,7 +55,7 @@ }; use alg_tools::maputil::map3; use alg_tools::nalgebra_support::ToNalgebraRealField; -use alg_tools::norms::{NormExponent, L1, L2}; +use alg_tools::norms::{Dist, NormExponent, L1, L2}; use alg_tools::operator_arithmetic::Weighted; use alg_tools::sets::Cube; use alg_tools::sets::SetOrd; @@ -1086,30 +1086,36 @@ /// $L^2$-squared data term is enabled through the type signatures. /// /// `cont` gives a continuation attempt to find the algorithm matching the description `alg`. -pub fn run_fw<'a, F, A, Reg, I, Plot, const N: usize>( - f: &'a DataTerm, A, Norm222>, +pub fn run_fw<'a, F, U, A, Reg, I, Plot>( + f: &'a DataTerm, A, Norm222>, reg: &Reg, (alg, iterator, plotter, μ0, running): ( &AlgorithmConfig, I, Plot, - Option>, + Option>, String, ), cont: impl FnOnce( - (&AlgorithmConfig, I, Plot, Option>, String), - ) -> DynResult>, -) -> DynResult> + ( + &AlgorithmConfig, + I, + Plot, + Option>, + String, + ), + ) -> DynResult>, +) -> DynResult> where F: Float + ToNalgebraRealField, + U: Space + Clone + PartialEq, I: AlgIteratorFactory>, - A: ForwardModel, F>, - A::PreadjointCodomain: MinMaxMapping, F>, + A: ForwardModel, F>, + A::PreadjointCodomain: MinMaxMapping, for<'b> &'b A::PreadjointCodomain: Instance, - Cube: P2Minimise, F>, - RNDM: SpikeMerging, - Reg: RegTermFW, N>, - Plot: Plotter>, + DiscreteMeasure: SpikeMerging, + Reg: RegTermFW>, + Plot: Plotter>, { match alg { &AlgorithmConfig::FW(ref algconfig) => { @@ -1381,10 +1387,10 @@ } } -type MeasureZ = Pair, Z>; +type MeasureZ = Pair, Z>; #[deprecated] -pub fn run_pdps_pair( +pub fn run_pdps_pair( f: &Dat, reg: &Reg, prox_penalty: &P, @@ -1395,7 +1401,7 @@ &AlgorithmConfig, I, Plot, - (Option>, Z, Y), + (Option>, Z, Y), String, ), cont: impl FnOnce( @@ -1403,22 +1409,24 @@ &AlgorithmConfig, I, Plot, - (Option>, Z, Y), + (Option>, Z, Y), String, ), - ) -> DynResult, Z>>, -) -> DynResult, Z>> + ) -> DynResult, Z>>, +) -> DynResult, Z>> where F: Float + ToNalgebraRealField, + U: Space + Clone + Dist, + for<'a> &'a U: Instance, I: AlgIteratorFactory>, - Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + BoundedCurvature, - S: DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, + S: DifferentiableMapping + ClosedMul + MinMaxMapping, for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, //Pair: ClosedMul, - RNDM: SpikeMerging, - Reg: SlidingRegTerm, F>, - P: TransportProxPenalty, S, Reg, F>, + DiscreteMeasure: SpikeMerging, + Reg: SlidingRegTerm, + P: TransportProxPenalty, KOpZ: BoundedLinear + GEMV + SimplyAdjointable, @@ -1430,7 +1438,7 @@ R: Prox, H: Conjugable, for<'b> H::Conjugate<'b>: Prox, - Plot: Plotter>, + Plot: Plotter>, { run_pdps_pair_dom( f, @@ -1445,7 +1453,7 @@ ) } -pub fn run_pdps_pair_dom( +pub fn run_pdps_pair_dom( f: &Dat, reg: &Reg, prox_penalty: &P, @@ -1456,31 +1464,33 @@ &AlgorithmConfig, I, Plot, - (Option>, Z, Y), + (Option>, Z, Y), String, ), - domain: &mut impl AdmissibleDomain>, + domain: &mut impl AdmissibleDomain, cont: impl FnOnce( ( &AlgorithmConfig, I, Plot, - (Option>, Z, Y), + (Option>, Z, Y), String, ), - ) -> DynResult, Z>>, -) -> DynResult, Z>> + ) -> DynResult, Z>>, +) -> DynResult, Z>> where F: Float + ToNalgebraRealField, + U: Space + Clone + Dist, + for<'a> &'a U: Instance, I: AlgIteratorFactory>, - Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + BoundedCurvature, - S: DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, + S: DifferentiableMapping + ClosedMul + MinMaxMapping, for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, //Pair: ClosedMul, - RNDM: SpikeMerging, - Reg: SlidingRegTerm, F>, - P: TransportProxPenalty, S, Reg, F>, + DiscreteMeasure: SpikeMerging, + Reg: SlidingRegTerm, + P: TransportProxPenalty, KOpZ: BoundedLinear + GEMV + SimplyAdjointable, @@ -1492,7 +1502,7 @@ R: Prox, H: Conjugable, for<'b> H::Conjugate<'b>: Prox, - Plot: Plotter>, + Plot: Plotter>, { let pt = P::prox_type(); @@ -1533,7 +1543,7 @@ } } -pub fn run_fb_pair( +pub fn run_fb_pair( f: &Dat, reg: &Reg, prox_penalty: &P, @@ -1542,7 +1552,7 @@ &AlgorithmConfig, I, Plot, - (Option>, Z), + (Option>, Z), String, ), cont: impl FnOnce( @@ -1550,32 +1560,34 @@ &AlgorithmConfig, I, Plot, - (Option>, Z), + (Option>, Z), String, ), - ) -> DynResult, Z>>, -) -> DynResult, Z>> + ) -> DynResult, Z>>, +) -> DynResult, Z>> where F: Float + ToNalgebraRealField, + U: Space + Clone + Dist, + for<'a> &'a U: Instance, I: AlgIteratorFactory>, - Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + BoundedCurvature, - S: DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, - RNDM: SpikeMerging, - Reg: SlidingRegTerm, F>, - P: TransportProxPenalty, S, Reg, F>, + S: DifferentiableMapping + ClosedMul + MinMaxMapping, + DiscreteMeasure: SpikeMerging, + Reg: SlidingRegTerm, + P: TransportProxPenalty, for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, Z: ClosedEuclidean + AXPY + Clone, for<'b> &'b Z: Instance, R: Prox, - Plot: Plotter>, + Plot: Plotter>, // We should not need to explicitly require this: for<'b> &'b Loc<0, F>: Instance>, { run_fb_pair_dom(f, reg, prox_penalty, fnR, init, &mut FullSpace, cont) } -pub fn run_fb_pair_dom( +pub fn run_fb_pair_dom( f: &Dat, reg: &Reg, prox_penalty: &P, @@ -1584,34 +1596,36 @@ &AlgorithmConfig, I, Plot, - (Option>, Z), + (Option>, Z), String, ), - domain: &mut impl AdmissibleDomain>, + domain: &mut impl AdmissibleDomain, cont: impl FnOnce( ( &AlgorithmConfig, I, Plot, - (Option>, Z), + (Option>, Z), String, ), - ) -> DynResult, Z>>, -) -> DynResult, Z>> + ) -> DynResult, Z>>, +) -> DynResult, Z>> where F: Float + ToNalgebraRealField, + U: Space + Clone + Dist, + for<'a> &'a U: Instance, I: AlgIteratorFactory>, - Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + BoundedCurvature, - S: DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, - RNDM: SpikeMerging, - Reg: SlidingRegTerm, F>, - P: TransportProxPenalty, S, Reg, F>, + S: DifferentiableMapping + ClosedMul + MinMaxMapping, + DiscreteMeasure: SpikeMerging, + Reg: SlidingRegTerm, + P: TransportProxPenalty, for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, Z: ClosedEuclidean + AXPY + Clone, for<'b> &'b Z: Instance, R: Prox, - Plot: Plotter>, + Plot: Plotter>, // We should not need to explicitly require this: for<'b> &'b Loc<0, F>: Instance>, { diff -r 759634edb536 -r 8f1e57821d76 src/sliding_fb.rs --- a/src/sliding_fb.rs Tue Aug 04 20:11:10 2026 -0500 +++ b/src/sliding_fb.rs Thu Aug 06 11:14:15 2026 -0500 @@ -24,11 +24,11 @@ use alg_tools::bounds::{Bounds, MinMaxMapping}; use alg_tools::error::DynResult; use alg_tools::euclidean::Euclidean; -use alg_tools::instance::Space; +use alg_tools::instance::{Instance, Space}; use alg_tools::iterate::AlgIteratorFactory; use alg_tools::mapping::{DifferentiableMapping, DifferentiableRealMapping, Mapping, RealMapping}; use alg_tools::nalgebra_support::ToNalgebraRealField; -use alg_tools::norms::Norm; +use alg_tools::norms::{Dist, Norm, L2}; use anyhow::ensure; use std::ops::ControlFlow; @@ -193,11 +193,11 @@ } #[derive(Clone, Debug, Serialize)] -pub struct SingleTransport { +pub struct SingleTransport { /// Source point - x: Domain, + x: U, /// Target point - y: Domain, + y: U, /// Original mass α_μ_orig: F, /// Transported mass @@ -211,8 +211,8 @@ } #[derive(Clone, Debug, Serialize)] -pub struct Transport { - vec: Vec>, +pub struct Transport { + vec: Vec>, } /// Whether partially transported points are allowed. @@ -223,21 +223,21 @@ const MINIMAL_PARTIAL_TRANSPORT: bool = true; const NEW_APPROACH: bool = true; -pub trait TransportProxPenalty: - ProxPenalty +pub trait TransportProxPenalty: + ProxPenalty where F: Float + ToNalgebraRealField, - Reg: SlidingRegTerm, - Point: Space + Clone, + Reg: SlidingRegTerm, + U: Space + Clone, { type TransportStepLength; /// Constrution of initial transport `γ1` from initial measure `μ` and `v=F'(μ)` /// with step lengh τ and transport step length `θ_or_adaptive`. - fn initial_transport>( + fn initial_transport>( &self, - γ: &mut Transport, - μ: &DiscreteMeasure, + γ: &mut Transport, + μ: &DiscreteMeasure, ε: F, τ: F, τθ_or_adaptive: &mut Self::TransportStepLength, @@ -249,9 +249,9 @@ /// A posteriori transport adaptation. fn aposteriori_transport( &self, - γ: &mut Transport, - μ: &DiscreteMeasure, - μ̆: &DiscreteMeasure, + γ: &mut Transport, + μ: &DiscreteMeasure, + μ̆: &DiscreteMeasure, τv̆: &mut PreadjointCodomain, v: &mut PreadjointCodomain, extra: Option, @@ -377,14 +377,12 @@ } #[replace_float_literals(F::cast_from(literal))] -impl TransportProxPenalty, M, Reg, F> for 𝒟 +impl TransportProxPenalty, M, Reg, F> for 𝒟 where F: Float + ToNalgebraRealField, 𝒟: DiscreteMeasureOp, F>, 𝒟::Codomain: RealMapping, M: MinMaxMapping, F> + DifferentiableRealMapping, - for<'a> &'a M: std::ops::Add<𝒟::PreCodomain, Output = O>, - O: MinMaxMapping, F>, Reg: SlidingRegTerm, F>, Self: ProxPenalty, M, Reg, F>, RNDM: SpikeMerging, @@ -608,25 +606,37 @@ } } +impl Norm for Transport { + fn norm(&self, _: Radon) -> F { + self.iter().map(|ρ| ρ.α_γ.abs()).sum() + } +} + +impl MulAssign for Transport { + fn mul_assign(&mut self, factor: F) { + for ρ in self.iter_mut() { + ρ.α_γ *= factor; + } + } +} + #[replace_float_literals(F::cast_from(literal))] -impl Transport, F> { +impl Transport { pub(crate) fn new() -> Self { Transport { vec: Vec::new() } } - pub(crate) fn iter(&self) -> impl Iterator, F>> { + pub(crate) fn iter(&self) -> impl Iterator> { self.vec.iter() } - pub(crate) fn iter_mut( - &mut self, - ) -> impl Iterator, F>> { + pub(crate) fn iter_mut(&mut self) -> impl Iterator> { self.vec.iter_mut() } pub(crate) fn extend(&mut self, it: I) where - I: IntoIterator, F>>, + I: IntoIterator>, { self.vec.extend(it) } @@ -635,62 +645,44 @@ self.vec.len() } - // pub(crate) fn dist_matching(&self, μ: &RNDM) -> F { - // self.iter() - // .zip(μ.iter_spikes()) - // .map(|(ρ, δ)| (ρ.α_γ - δ.α).abs()) - // .sum() - // } - - /// Construct `μ̆`, replacing the contents of `μ`. - pub(crate) fn μ̆_into(&self, μ: &mut RNDM) { - assert!(self.len() <= μ.len()); - - // First transported points - for (δ, ρ) in izip!(μ.iter_spikes_mut(), self.iter()) { - if ρ.α_γ.abs() > 0.0 { - // Transport – transported point - δ.α = ρ.α_γ; - δ.x = ρ.y; - } else { - // No transport – original point - δ.α = ρ.α_μ_orig; - δ.x = ρ.x; - } - } - - // Then source points with partial transport - let mut i = self.len(); - // This can cause the number of points to explode, so cannot have partial transport. - for ρ in self.iter() { - let α = ρ.α_μ_orig - ρ.α_γ; - if ρ.α_γ.abs() > F::EPSILON && α.abs() > F::EPSILON { - let δ = DeltaMeasure { α, x: ρ.x }; - if i < μ.len() { - μ[i] = δ; - } else { - μ.push(δ) - } - i += 1; - } - } - μ.truncate(i); - } - /// Returns $‖μ\^k - π\_♯\^0γ\^{k+1}‖$ pub(crate) fn μ0_minus_γ0_radon(&self) -> F { self.vec.iter().map(|ρ| (ρ.α_μ_orig - ρ.α_γ).abs()).sum() } + /// Prune spikes with zero weight. To maintain correct ordering between μ and γ, also the + /// latter needs to be pruned when μ is. + pub(crate) fn prune_compat(&mut self, μ: &mut DiscreteMeasure, stats: &mut IterInfo) { + assert!(self.vec.len() <= μ.len()); + let old_len = μ.len(); + for (ρ, δ) in self.vec.iter_mut().zip(μ.iter_spikes()) { + ρ.retain = δ.α != F::ZERO; + } + // Do not prune new insertions on first round. + μ.prune_until(self.vec.len()); + stats.pruned += old_len - μ.len(); + self.vec.retain(|ρ| ρ.retain); + assert!(self.vec.len() <= μ.len()); + } +} + +#[replace_float_literals(F::cast_from(literal))] +impl Transport +where + U: Dist, + F: Float, + for<'a> &'a U: Instance, +{ /// Returns $∫ c_2 d|γ|$ pub(crate) fn c2integral(&self) -> F { + let sq2 = |x| (x * x) / 2.0; self.vec .iter() - .map(|ρ| ρ.y.dist2_squared(&ρ.x) / 2.0 * ρ.α_γ.abs()) + .map(|ρ| sq2(ρ.y.dist(&ρ.x, L2)) * ρ.α_γ.abs()) .sum() } - pub(crate) fn get_transport_stats(&self, stats: &mut IterInfo, μ: &RNDM) { + pub(crate) fn get_transport_stats(&self, stats: &mut IterInfo, μ: &DiscreteMeasure) { // TODO: This doesn't take into account μ[i].α becoming zero in the latest tranport // attempt, for i < self.len(), when a corresponding source term also exists with index // j ≥ self.len(). For now, we let that be reflected in the prune count. @@ -726,22 +718,55 @@ }) }; } +} - /// Prune spikes with zero weight. To maintain correct ordering between μ and γ, also the - /// latter needs to be pruned when μ is. - pub(crate) fn prune_compat(&mut self, μ: &mut RNDM, stats: &mut IterInfo) { - assert!(self.vec.len() <= μ.len()); - let old_len = μ.len(); - for (ρ, δ) in self.vec.iter_mut().zip(μ.iter_spikes()) { - ρ.retain = δ.α != F::ZERO; +#[replace_float_literals(F::cast_from(literal))] +impl Transport { + // pub(crate) fn dist_matching(&self, μ: &RNDM) -> F { + // self.iter() + // .zip(μ.iter_spikes()) + // .map(|(ρ, δ)| (ρ.α_γ - δ.α).abs()) + // .sum() + // } + + /// Construct `μ̆`, replacing the contents of `μ`. + pub(crate) fn μ̆_into(&self, μ: &mut DiscreteMeasure) { + assert!(self.len() <= μ.len()); + + // First transported points + for (δ, ρ) in izip!(μ.iter_spikes_mut(), self.iter()) { + if ρ.α_γ.abs() > 0.0 { + // Transport – transported point + δ.α = ρ.α_γ; + δ.x = ρ.y.clone(); + } else { + // No transport – original point + δ.α = ρ.α_μ_orig; + δ.x = ρ.x.clone(); + } } - // Do not prune new insertions on first round. - μ.prune_until(self.vec.len()); - stats.pruned += old_len - μ.len(); - self.vec.retain(|ρ| ρ.retain); - assert!(self.vec.len() <= μ.len()); + + // Then source points with partial transport + let mut i = self.len(); + // This can cause the number of points to explode, so cannot have partial transport. + for ρ in self.iter() { + let α = ρ.α_μ_orig - ρ.α_γ; + if ρ.α_γ.abs() > F::EPSILON && α.abs() > F::EPSILON { + let δ = DeltaMeasure { α, x: ρ.x.clone() }; + if i < μ.len() { + μ[i] = δ; + } else { + μ.push(δ) + } + i += 1; + } + } + μ.truncate(i); } +} +#[replace_float_literals(F::cast_from(literal))] +impl Transport, F> { /// Helper for initial transport. Called from [`TransportProxPenalty::initial_transport`]. pub(super) fn do_init_transport>>( &mut self, @@ -955,20 +980,6 @@ } } -impl Norm for Transport, F> { - fn norm(&self, _: Radon) -> F { - self.iter().map(|ρ| ρ.α_γ.abs()).sum() - } -} - -impl MulAssign for Transport, F> { - fn mul_assign(&mut self, factor: F) { - for ρ in self.iter_mut() { - ρ.α_γ *= factor; - } - } -} - /// Iteratively solve the pointsource localisation problem using sliding forward-backward /// splitting /// @@ -976,27 +987,29 @@ /// may be given. /// Inertia is currently not supported. #[replace_float_literals(F::cast_from(literal))] -pub fn pointsource_sliding_fb( +pub fn pointsource_sliding_fb( f: &Dat, reg: &Reg, prox_penalty: &P, config: &SlidingFBConfig, iterator: I, mut plotter: Plot, - μ0: Option>, - domain: &mut impl AdmissibleDomain>, -) -> DynResult> + μ0: Option>, + domain: &mut impl AdmissibleDomain, +) -> DynResult> where F: Float + ToNalgebraRealField, + U: Space + Clone + Dist, + for<'a> &'a U: Instance, I: AlgIteratorFactory>, - Dat: DifferentiableMapping, Codomain = F> + BoundedCurvature, + Dat: DifferentiableMapping, Codomain = F> + BoundedCurvature, Dat::DerivativeDomain: - DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, + DifferentiableMapping + ClosedMul + MinMaxMapping, //for<'a> Dat::Differential<'a>: Lipschitz<&'a P, FloatType = F>, - RNDM: SpikeMerging, - Reg: SlidingRegTerm, F>, - P: TransportProxPenalty, Dat::DerivativeDomain, Reg, F> + StepLengthBound, - Plot: Plotter>, + DiscreteMeasure: SpikeMerging, + Reg: SlidingRegTerm, + P: TransportProxPenalty + StepLengthBound, + Plot: Plotter>, { // Check parameters ensure!(config.τ0 > 0.0, "Invalid step length parameter"); @@ -1025,7 +1038,7 @@ let mut ε = tolerance.initial(); // Statistics - let full_stats = |μ: &RNDM, ε, stats| IterInfo { + let full_stats = |μ: &DiscreteMeasure, ε, stats| IterInfo { value: f.apply(μ) + reg.apply(μ), n_spikes: μ.len(), ε, @@ -1114,7 +1127,7 @@ ε, ins, ®, - Some(|μ̃: &RNDM| f.apply(μ̃) + reg.apply(μ̃)), + Some(|μ̃: &DiscreteMeasure| f.apply(μ̃) + reg.apply(μ̃)), ); // Prune. Do not prune new insertions on first round. γ.prune_compat(&mut μ, &mut stats); @@ -1167,26 +1180,28 @@ #[deprecated] /// Version of `pointsource_sliding_fb` without domain restriction -pub fn pointsource_sliding_fb_reg( +pub fn pointsource_sliding_fb_reg( f: &Dat, reg: &Reg, prox_penalty: &P, config: &SlidingFBConfig, iterator: I, plotter: Plot, - μ0: Option>, -) -> DynResult> + μ0: Option>, +) -> DynResult> where F: Float + ToNalgebraRealField, + U: Space + Clone + Dist, + for<'a> &'a U: Instance, I: AlgIteratorFactory>, - Dat: DifferentiableMapping, Codomain = F> + BoundedCurvature, + Dat: DifferentiableMapping, Codomain = F> + BoundedCurvature, Dat::DerivativeDomain: - DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, + DifferentiableMapping + ClosedMul + MinMaxMapping, //for<'a> Dat::Differential<'a>: Lipschitz<&'a P, FloatType = F>, - RNDM: SpikeMerging, - Reg: SlidingRegTerm, F>, - P: TransportProxPenalty, Dat::DerivativeDomain, Reg, F> + StepLengthBound, - Plot: Plotter>, + DiscreteMeasure: SpikeMerging, + Reg: SlidingRegTerm, + P: TransportProxPenalty + StepLengthBound, + Plot: Plotter>, { pointsource_sliding_fb( f, diff -r 759634edb536 -r 8f1e57821d76 src/sliding_pdps.rs --- a/src/sliding_pdps.rs Tue Aug 04 20:11:10 2026 -0500 +++ b/src/sliding_pdps.rs Thu Aug 06 11:14:15 2026 -0500 @@ -7,7 +7,7 @@ use crate::fb::*; use crate::forward_model::{BoundedCurvature, BoundedCurvatureGuess}; use crate::measures::merging::SpikeMerging; -use crate::measures::{DiscreteMeasure, RNDM}; +use crate::measures::DiscreteMeasure; use crate::plot::Plotter; use crate::prox_penalty::StepLengthBoundPair; use crate::regularisation::SlidingRegTerm; @@ -18,13 +18,14 @@ use alg_tools::direct_product::Pair; use alg_tools::error::DynResult; use alg_tools::euclidean::ClosedEuclidean; +use alg_tools::instance::{Instance, Space}; use alg_tools::iterate::AlgIteratorFactory; use alg_tools::linops::{ BoundedLinear, IdOp, SimplyAdjointable, StaticEuclideanOriginGenerator, ZeroOp, AXPY, GEMV, }; -use alg_tools::mapping::{DifferentiableMapping, DifferentiableRealMapping, Instance}; +use alg_tools::mapping::DifferentiableMapping; use alg_tools::nalgebra_support::ToNalgebraRealField; -use alg_tools::norms::L2; +use alg_tools::norms::{Dist, L2}; use anyhow::ensure; use measures::DeltaMeasure; use numeric_literals::replace_float_literals; @@ -64,7 +65,7 @@ } } -type MeasureZ = Pair, Z>; +type MeasureZ = Pair, Z>; /// Iteratively solve the pointsource localisation with an additional variable /// using sliding primal-dual proximal splitting @@ -85,7 +86,7 @@ Plot, /*KOpM, */ KOpZ, H, - const N: usize, + U, >( f: &Dat, reg: &Reg, @@ -93,32 +94,34 @@ config: &SlidingPDPSConfig, iterator: I, mut plotter: Plot, - (μ0, mut z, mut y): (Option>, Z, Y), + (μ0, mut z, mut y): (Option>, Z, Y), //opKμ : KOpM, opKz: &KOpZ, fnR: &R, fnH: &H, - domain: &mut impl AdmissibleDomain>, -) -> DynResult> + domain: &mut impl AdmissibleDomain, +) -> DynResult> where F: Float + ToNalgebraRealField, + U: Space + Clone + Dist, + for<'a> &'a U: Instance, I: AlgIteratorFactory>, - Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + BoundedCurvature, - S: DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, + S: DifferentiableMapping + ClosedMul + MinMaxMapping, for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, //Pair: ClosedMul, - RNDM: SpikeMerging, - Reg: SlidingRegTerm, F>, - P: TransportProxPenalty, S, Reg, F>, - // KOpM : Linear, Codomain=Y> - // + GEMV> + DiscreteMeasure: SpikeMerging, + Reg: SlidingRegTerm, + P: TransportProxPenalty, + // KOpM : Linear, Codomain=Y> + // + GEMV> // + Preadjointable< - // RNDM, Y, + // DiscreteMeasure, Y, // PreadjointCodomain = S, // > // + TransportLipschitz - // + AdjointProductBoundedBy, 𝒟, FloatType=F>, + // + AdjointProductBoundedBy, 𝒟, FloatType=F>, // for<'b> KOpM::Preadjoint<'b> : GEMV, // Since Z is Hilbert, we may just as well use adjoints for K_z. KOpZ: BoundedLinear @@ -132,7 +135,7 @@ R: Prox, H: Conjugable, for<'b> H::Conjugate<'b>: Prox, - Plot: Plotter>, + Plot: Plotter>, { // Check parameters /*ensure!( @@ -206,7 +209,7 @@ let starH = fnH.conjugate(); // Statistics - let full_stats = |μ: &RNDM, z: &Z, ε, stats| IterInfo { + let full_stats = |μ: &DiscreteMeasure, z: &Z, ε, stats| IterInfo { value: f.apply(Pair(μ, z)) + fnR.apply(z) + reg.apply(μ) @@ -317,7 +320,7 @@ ε, ins, ®, - Some(|μ̃: &RNDM| f.apply(Pair(μ̃, &z)) + reg.apply(μ̃)), + Some(|μ̃: &DiscreteMeasure| f.apply(Pair(μ̃, &z)) + reg.apply(μ̃)), ); γ.prune_compat(&mut μ, &mut stats); @@ -375,7 +378,7 @@ ε = tolerance.update(ε, iter); } - let fit = |μ̃: &RNDM| { + let fit = |μ̃: &DiscreteMeasure| { f.apply(Pair(μ̃, &z)) /*+ fnR.apply(z) + reg.apply(μ)*/ + fnH.apply(/* opKμ.apply(&μ̃) + */ opKz.apply(&z)) + reg.apply(μ̃) @@ -392,32 +395,34 @@ /// The implementation uses [`pointsource_sliding_pdps_pair`] with appropriate dummy /// variables, operators, and functions. #[replace_float_literals(F::cast_from(literal))] -pub fn pointsource_sliding_fb_pair_dom( +pub fn pointsource_sliding_fb_pair_dom( f: &Dat, reg: &Reg, prox_penalty: &P, config: &SlidingFBConfig, iterator: I, plotter: Plot, - (μ0, z): (Option>, Z), + (μ0, z): (Option>, Z), //opKμ : KOpM, fnR: &R, - domain: &mut impl AdmissibleDomain>, -) -> DynResult> + domain: &mut impl AdmissibleDomain, +) -> DynResult> where F: Float + ToNalgebraRealField, + U: Space + Clone + Dist, + for<'a> &'a U: Instance, I: AlgIteratorFactory>, - Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + BoundedCurvature, - S: DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, - RNDM: SpikeMerging, - Reg: SlidingRegTerm, F>, - P: TransportProxPenalty, S, Reg, F>, + S: DifferentiableMapping + ClosedMul + MinMaxMapping, + DiscreteMeasure: SpikeMerging, + Reg: SlidingRegTerm, + P: TransportProxPenalty, for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, Z: ClosedEuclidean + AXPY + Clone, for<'b> &'b Z: Instance, R: Prox, - Plot: Plotter>, + Plot: Plotter>, // We should not need to explicitly require this: for<'b> &'b Loc<0, F>: Instance>, // Loc<0, F>: StaticEuclidean> @@ -463,7 +468,7 @@ Plot, /*KOpM, */ KOpZ, H, - const N: usize, + U, >( f: &Dat, reg: &Reg, @@ -471,31 +476,33 @@ config: &SlidingPDPSConfig, iterator: I, plotter: Plot, - init: (Option>, Z, Y), + init: (Option>, Z, Y), //opKμ : KOpM, opKz: &KOpZ, fnR: &R, fnH: &H, -) -> DynResult> +) -> DynResult> where F: Float + ToNalgebraRealField, + U: Space + Clone + Dist, + for<'a> &'a U: Instance, I: AlgIteratorFactory>, - Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + BoundedCurvature, - S: DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, + S: DifferentiableMapping + ClosedMul + MinMaxMapping, for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, //Pair: ClosedMul, - RNDM: SpikeMerging, - Reg: SlidingRegTerm, F>, - P: TransportProxPenalty, S, Reg, F>, - // KOpM : Linear, Codomain=Y> - // + GEMV> + DiscreteMeasure: SpikeMerging, + Reg: SlidingRegTerm, + P: TransportProxPenalty, + // KOpM : Linear, Codomain=Y> + // + GEMV> // + Preadjointable< - // RNDM, Y, + // DiscreteMeasure, Y, // PreadjointCodomain = S, // > // + TransportLipschitz - // + AdjointProductBoundedBy, 𝒟, FloatType=F>, + // + AdjointProductBoundedBy, 𝒟, FloatType=F>, // for<'b> KOpM::Preadjoint<'b> : GEMV, // Since Z is Hilbert, we may just as well use adjoints for K_z. KOpZ: BoundedLinear @@ -509,7 +516,7 @@ R: Prox, H: Conjugable, for<'b> H::Conjugate<'b>: Prox, - Plot: Plotter>, + Plot: Plotter>, { pointsource_sliding_pdps_pair_dom( f, @@ -529,31 +536,33 @@ #[deprecated] /// Version of [`pointsource_sliding_fb_pair_dom`] without domain specification #[replace_float_literals(F::cast_from(literal))] -pub fn pointsource_sliding_fb_pair( +pub fn pointsource_sliding_fb_pair( f: &Dat, reg: &Reg, prox_penalty: &P, config: &SlidingFBConfig, iterator: I, plotter: Plot, - (μ0, z): (Option>, Z), + (μ0, z): (Option>, Z), //opKμ : KOpM, fnR: &R, -) -> DynResult> +) -> DynResult> where F: Float + ToNalgebraRealField, + U: Space + Clone + Dist, + for<'a> &'a U: Instance, I: AlgIteratorFactory>, - Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + BoundedCurvature, - S: DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, - RNDM: SpikeMerging, - Reg: SlidingRegTerm, F>, - P: TransportProxPenalty, S, Reg, F>, + S: DifferentiableMapping + ClosedMul + MinMaxMapping, + DiscreteMeasure: SpikeMerging, + Reg: SlidingRegTerm, + P: TransportProxPenalty, for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, Z: ClosedEuclidean + AXPY + Clone, for<'b> &'b Z: Instance, R: Prox, - Plot: Plotter>, + Plot: Plotter>, // We should not need to explicitly require this: for<'b> &'b Loc<0, F>: Instance>, // Loc<0, F>: StaticEuclidean>