# HG changeset patch # User Tuomo Valkonen # Date 1785512507 18000 # Node ID 66b8ac0ebe009384a0433106a4e7b946c0221635 # Parent afb3ce40cff69a10ecafa81988b7104b49148677 Sliding methods now support domain restrictions diff -r afb3ce40cff6 -r 66b8ac0ebe00 Cargo.lock --- a/Cargo.lock Thu Jul 30 09:27:44 2026 -0500 +++ b/Cargo.lock Fri Jul 31 10:41:47 2026 -0500 @@ -816,7 +816,7 @@ [[package]] name = "pointsource_algs" -version = "3.1.1" +version = "3.2.0" dependencies = [ "GSL", "alg_tools", diff -r afb3ce40cff6 -r 66b8ac0ebe00 Cargo.toml --- a/Cargo.toml Thu Jul 30 09:27:44 2026 -0500 +++ b/Cargo.toml Fri Jul 31 10:41:47 2026 -0500 @@ -1,6 +1,6 @@ [package] name = "pointsource_algs" -version = "3.1.1" +version = "3.2.0" edition = "2021" rust-version = "1.85" authors = ["Tuomo Valkonen "] diff -r afb3ce40cff6 -r 66b8ac0ebe00 src/regularisation.rs --- a/src/regularisation.rs Thu Jul 30 09:27:44 2026 -0500 +++ b/src/regularisation.rs Fri Jul 31 10:41:47 2026 -0500 @@ -7,7 +7,7 @@ use crate::fb::InsertionConfig; use crate::measures::{DeltaMeasure, DiscreteMeasure, Radon, RNDM}; #[allow(unused_imports)] // Used by documentation. -use crate::sliding_fb::pointsource_sliding_fb_reg; +use crate::sliding_fb::pointsource_sliding_fb_dom; use crate::types::*; use alg_tools::instance::{Instance, Space}; use alg_tools::linops::Mapping; @@ -213,7 +213,7 @@ M: MinMaxMapping; } -/// Abstraction of regularisation terms for [`pointsource_sliding_fb_reg`]. +/// Abstraction of regularisation terms for [`pointsource_sliding_fb_dom`]. pub trait SlidingRegTerm: RegTerm where Domain: Space + Clone, diff -r afb3ce40cff6 -r 66b8ac0ebe00 src/run.rs --- a/src/run.rs Thu Jul 30 09:27:44 2026 -0500 +++ b/src/run.rs Fri Jul 31 10:41:47 2026 -0500 @@ -24,10 +24,10 @@ use crate::regularisation::{NonnegRadonRegTerm, RadonRegTerm, Regularisation, SlidingRegTerm}; use crate::seminorms::*; use crate::sliding_fb::{ - pointsource_sliding_fb_reg, SlidingFBConfig, TransportConfig, TransportProxPenalty, + pointsource_sliding_fb_dom, SlidingFBConfig, TransportConfig, TransportProxPenalty, }; use crate::sliding_pdps::{ - pointsource_sliding_fb_pair, pointsource_sliding_pdps_pair, SlidingPDPSConfig, + pointsource_sliding_fb_pair_dom, pointsource_sliding_pdps_pair_dom, SlidingPDPSConfig, }; use crate::subproblem::{InnerMethod, InnerSettings}; use crate::tolerance::Tolerance; @@ -989,6 +989,38 @@ f: &Dat, reg: &Reg, prox_penalty: &P, + init: ( + &AlgorithmConfig, + I, + Plot, + Option>, + String, + ), + cont: impl FnOnce( + (&AlgorithmConfig, I, Plot, Option>, String), + ) -> DynResult>, +) -> DynResult> +where + F: Float + ToNalgebraRealField, + I: AlgIteratorFactory>, + Dat: DifferentiableMapping, Codomain = F> + BoundedCurvature, + Dat::DerivativeDomain: + DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, + RNDM: SpikeMerging, + Reg: SlidingRegTerm, F>, + P: TransportProxPenalty, Dat::DerivativeDomain, Reg, F> + StepLengthBound, + Plot: Plotter>, +{ + run_fb_dom(f, reg, prox_penalty, init, None, cont) +} + +/// Runs FB-style algorithms if `alg` so requests and `prox_penalty` matches. +/// +/// `cont` gives a continuation attempt to find the algorithm matching the description `alg`. +pub fn run_fb_dom( + f: &Dat, + reg: &Reg, + prox_penalty: &P, (alg, iterator, plotter, μ0, running): ( &AlgorithmConfig, I, @@ -996,6 +1028,7 @@ Option>, String, ), + domain: Option<&P::Dom>, cont: impl FnOnce( (&AlgorithmConfig, I, Plot, Option>, String), ) -> DynResult>, @@ -1024,7 +1057,7 @@ } &AlgorithmConfig::SlidingFB(ref algconfig, prox_type) if prox_type == pt => { print!("{running}"); - pointsource_sliding_fb_reg(f, reg, prox_penalty, algconfig, iterator, plotter, μ0) + pointsource_sliding_fb_dom(f, reg, prox_penalty, algconfig, iterator, plotter, μ0, domain) } _ => cont((alg, iterator, plotter, μ0, running)), } @@ -1261,8 +1294,8 @@ save_extra, (μ0, z, y), |p| { - run_pdps_pair(&f, ®, &RadonSquared, &opKz, &fnR, &fnH, p, |q| { - run_pdps_pair(&f, ®, &op𝒟, &opKz, &fnR, &fnH, q, |_| { + run_pdps_pair_dom(&f, ®, &RadonSquared, &opKz, &fnR, &fnH, p, None, |q| { + run_pdps_pair_dom(&f, ®, &op𝒟, &opKz, &fnR, &fnH, q, None, |_| { Err(NotImplemented.into()) }) }) @@ -1281,8 +1314,8 @@ save_extra, (μ0, z, y), |p| { - run_pdps_pair(&f, ®, &RadonSquared, &opKz, &fnR, &fnH, p, |q| { - run_pdps_pair(&f, ®, &op𝒟, &opKz, &fnR, &fnH, q, |_| { + run_pdps_pair_dom(&f, ®, &RadonSquared, &opKz, &fnR, &fnH, p, None, |q| { + run_pdps_pair_dom(&f, ®, &op𝒟, &opKz, &fnR, &fnH, q, None, |_| { Err(NotImplemented.into()) }) }) @@ -1297,6 +1330,7 @@ type MeasureZ = Pair, Z>; +#[deprecated] pub fn run_pdps_pair( f: &Dat, reg: &Reg, @@ -1304,6 +1338,57 @@ opKz: &KOpZ, fnR: &R, fnH: &H, + init: ( + &AlgorithmConfig, + I, + Plot, + (Option>, Z, Y), + String, + ), + cont: impl FnOnce( + ( + &AlgorithmConfig, + I, + Plot, + (Option>, Z, Y), + String, + ), + ) -> DynResult, Z>>, +) -> DynResult, Z>> +where + F: Float + ToNalgebraRealField, + I: AlgIteratorFactory>, + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + + BoundedCurvature, + S: DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, + for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, + //Pair: ClosedMul, + RNDM: SpikeMerging, + Reg: SlidingRegTerm, 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 + AXPY, + for<'b> &'b Z: Instance, + R: Prox, + H: Conjugable, + for<'b> H::Conjugate<'b>: Prox, + Plot: Plotter>, +{ + run_pdps_pair_dom(f, reg, prox_penalty, opKz, fnR, fnH, init, None, cont) +} + +pub fn run_pdps_pair_dom( + f: &Dat, + reg: &Reg, + prox_penalty: &P, + opKz: &KOpZ, + fnR: &R, + fnH: &H, (alg, iterator, plotter, μ0zy, running): ( &AlgorithmConfig, I, @@ -1311,6 +1396,7 @@ (Option>, Z, Y), String, ), + domain: Option<&P::Dom>, cont: impl FnOnce( ( &AlgorithmConfig, @@ -1365,7 +1451,7 @@ } &AlgorithmConfig::SlidingPDPS(ref algconfig, prox_type) if prox_type == pt => { print!("{running}"); - pointsource_sliding_pdps_pair( + pointsource_sliding_pdps_pair_dom( f, reg, prox_penalty, @@ -1376,6 +1462,7 @@ opKz, fnR, fnH, + domain, ) } _ => cont((alg, iterator, plotter, μ0zy, running)), @@ -1387,6 +1474,48 @@ reg: &Reg, prox_penalty: &P, fnR: &R, + init: ( + &AlgorithmConfig, + I, + Plot, + (Option>, Z), + String, + ), + cont: impl FnOnce( + ( + &AlgorithmConfig, + I, + Plot, + (Option>, Z), + String, + ), + ) -> DynResult, Z>>, +) -> DynResult, Z>> +where + F: Float + ToNalgebraRealField, + I: AlgIteratorFactory>, + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + + BoundedCurvature, + S: DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, + RNDM: SpikeMerging, + Reg: SlidingRegTerm, F>, + P: TransportProxPenalty, S, Reg, F>, + for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, + Z: ClosedEuclidean + AXPY + Clone, + for<'b> &'b Z: Instance, + R: Prox, + 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, None, cont) +} + +pub fn run_fb_pair_dom( + f: &Dat, + reg: &Reg, + prox_penalty: &P, + fnR: &R, (alg, iterator, plotter, μ0z, running): ( &AlgorithmConfig, I, @@ -1394,6 +1523,7 @@ (Option>, Z), String, ), + domain: Option<&P::Dom>, cont: impl FnOnce( ( &AlgorithmConfig, @@ -1430,7 +1560,7 @@ } &AlgorithmConfig::SlidingFB(ref algconfig, prox_type) if prox_type == pt => { print!("{running}"); - pointsource_sliding_fb_pair( + pointsource_sliding_fb_pair_dom( f, reg, prox_penalty, @@ -1439,6 +1569,7 @@ plotter, μ0z, fnR, + domain ) } _ => cont((alg, iterator, plotter, μ0z, running)), diff -r afb3ce40cff6 -r 66b8ac0ebe00 src/sliding_fb.rs --- a/src/sliding_fb.rs Thu Jul 30 09:27:44 2026 -0500 +++ b/src/sliding_fb.rs Fri Jul 31 10:41:47 2026 -0500 @@ -28,6 +28,7 @@ use alg_tools::mapping::{DifferentiableMapping, DifferentiableRealMapping, Mapping, RealMapping}; use alg_tools::nalgebra_support::ToNalgebraRealField; use alg_tools::norms::Norm; +use alg_tools::sets::{Set, Cube}; use anyhow::ensure; use std::ops::ControlFlow; @@ -230,6 +231,7 @@ Domain: Space + Clone, { type TransportStepLength; + type Dom : Set; /// Constrution of initial transport `γ1` from initial measure `μ` and `v=F'(μ)` /// with step lengh τ and transport step length `θ_or_adaptive`. @@ -242,6 +244,7 @@ τθ_or_adaptive: &mut Self::TransportStepLength, v: &PreadjointCodomain, tconfig: &TransportConfig, + domain: Option<&Self::Dom>, ); /// A posteriori transport adaptation. @@ -281,6 +284,7 @@ RNDM: SpikeMerging, { type TransportStepLength = TransportStepLength; + type Dom = Cube; fn get_transport_steplength( &self, @@ -306,8 +310,9 @@ τθ_or_adaptive: &mut TransportStepLength, v: &M, tconfig: &TransportConfig, + domain: Option<&Cube>, ) { - γ.do_init_transport(v, μ, τθ_or_adaptive, tconfig); + γ.do_init_transport(v, μ, τθ_or_adaptive, tconfig, domain); } fn aposteriori_transport( @@ -387,6 +392,7 @@ RNDM: SpikeMerging, { type TransportStepLength = TransportStepLength; + type Dom = Cube; fn get_transport_steplength( &self, @@ -412,8 +418,9 @@ τθ_or_adaptive: &mut TransportStepLength, v: &M, tconfig: &TransportConfig, + domain: Option<&Cube>, ) { - γ.do_init_transport(v, μ, τθ_or_adaptive, tconfig); + γ.do_init_transport(v, μ, τθ_or_adaptive, tconfig, domain); } fn aposteriori_transport( @@ -744,6 +751,7 @@ μ: &RNDM, τθ_or_adaptive: &mut TransportStepLength, tconfig: &TransportConfig, + domain: Option<&Cube>, ) where D: DifferentiableRealMapping, { @@ -810,7 +818,8 @@ if ρ.fail_count < tconfig.max_fail { let dv_x = v.differential(&ρ.x); let g = &dv_x * (ρ.α_γ.signum() * τθ); - ρ.y = ρ.x - g; + let tmp = ρ.x - g; + ρ.y = if domain.is_none_or(|d| d.contains(&tmp)) { tmp } else { ρ.x }; let n = g.norm2(); if n >= F::EPSILON { // Estimate Lipschitz factor of ∇v @@ -835,7 +844,8 @@ if let Some(τθ) = simple_τθ { for ρ in self.iter_mut() { if ρ.fail_count <= tconfig.max_fail { - ρ.y = ρ.x - v.differential(&ρ.x) * (ρ.α_γ.signum() * τθ); + let tmp = ρ.x - v.differential(&ρ.x) * (ρ.α_γ.signum() * τθ); + ρ.y = if domain.is_none_or(|d| d.contains(&tmp)) { tmp } else { ρ.x }; } } } @@ -964,10 +974,11 @@ /// Iteratively solve the pointsource localisation problem using sliding forward-backward /// splitting /// -/// The parametrisation is as for [`pointsource_fb_reg`]. +/// The parametrisation is as for [`pointsource_fb_reg`], except an additional domain restriction +/// may be given. /// Inertia is currently not supported. #[replace_float_literals(F::cast_from(literal))] -pub fn pointsource_sliding_fb_reg( +pub fn pointsource_sliding_fb_dom( f: &Dat, reg: &Reg, prox_penalty: &P, @@ -975,6 +986,7 @@ iterator: I, mut plotter: Plot, μ0: Option>, + domain: Option<&P::Dom>, ) -> DynResult> where F: Float + ToNalgebraRealField, @@ -1037,6 +1049,7 @@ &mut τθ_or_adaptive, &v, &config.transport, + domain ); let mut attempts = 0; @@ -1148,3 +1161,29 @@ //postprocess(μ, &config.insertion, f) postprocess(μ, &config.insertion, |μ̃| f.apply(μ̃)) } + +#[deprecated] +/// Version of `pointsource_sliding_fb` without domain restriction +pub fn pointsource_sliding_fb_reg( + f: &Dat, + reg: &Reg, + prox_penalty: &P, + config: &SlidingFBConfig, + iterator: I, + plotter: Plot, + μ0: Option>, +) -> DynResult> +where + F: Float + ToNalgebraRealField, + I: AlgIteratorFactory>, + Dat: DifferentiableMapping, Codomain = F> + BoundedCurvature, + Dat::DerivativeDomain: + DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, + //for<'a> Dat::Differential<'a>: Lipschitz<&'a P, FloatType = F>, + RNDM: SpikeMerging, + Reg: SlidingRegTerm, F>, + P: TransportProxPenalty, Dat::DerivativeDomain, Reg, F> + StepLengthBound, + Plot: Plotter>, +{ + pointsource_sliding_fb_dom(f, reg, prox_penalty, config, iterator, plotter, μ0, None) +} diff -r afb3ce40cff6 -r 66b8ac0ebe00 src/sliding_pdps.rs --- a/src/sliding_pdps.rs Thu Jul 30 09:27:44 2026 -0500 +++ b/src/sliding_pdps.rs Fri Jul 31 10:41:47 2026 -0500 @@ -67,9 +67,10 @@ /// Iteratively solve the pointsource localisation with an additional variable /// using sliding primal-dual proximal splitting /// -/// The parametrisation is as for [`crate::forward_pdps::pointsource_forward_pdps_pair`]. +/// The parametrisation is as for [`crate::forward_pdps::pointsource_forward_pdps_pair`], +/// except an additional domain restriction may be given. #[replace_float_literals(F::cast_from(literal))] -pub fn pointsource_sliding_pdps_pair< +pub fn pointsource_sliding_pdps_pair_dom< F, I, S, @@ -95,6 +96,7 @@ opKz: &KOpZ, fnR: &R, fnH: &H, + domain: Option<&P::Dom>, ) -> DynResult> where F: Float + ToNalgebraRealField, @@ -236,6 +238,7 @@ &mut τθ_or_adaptive, &v, &config.transport, + domain, ); let mut attempts = 0; @@ -382,6 +385,144 @@ /// 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( + f: &Dat, + reg: &Reg, + prox_penalty: &P, + config: &SlidingFBConfig, + iterator: I, + plotter: Plot, + (μ0, z): (Option>, Z), + //opKμ : KOpM, + fnR: &R, + domain: Option<&P::Dom> +) -> DynResult> +where + F: Float + ToNalgebraRealField, + I: AlgIteratorFactory>, + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + + BoundedCurvature, + S: DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, + RNDM: SpikeMerging, + Reg: SlidingRegTerm, F>, + P: TransportProxPenalty, S, Reg, F>, + for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, + Z: ClosedEuclidean + AXPY + Clone, + for<'b> &'b Z: Instance, + R: Prox, + Plot: Plotter>, + // We should not need to explicitly require this: + for<'b> &'b Loc<0, F>: Instance>, + // Loc<0, F>: StaticEuclidean> + // + Instance> + // + VectorSpace, +{ + let opKz: ZeroOp, _, _, F> = + ZeroOp::new_dualisable(StaticEuclideanOriginGenerator, z.dual_origin()); + let fnH = Zero::new(); + // Convert config. We don't implement From (that could be done with the o2o crate), as σd0 + // needs to be chosen in a general case; for the problem of this fucntion, anything is valid. + let &SlidingFBConfig { τ0, σp0, insertion, transport, guess } = config; + let pdps_config = SlidingPDPSConfig { τ0, σp0, insertion, transport, guess, σd0: 0.0 }; + + pointsource_sliding_pdps_pair_dom( + f, + reg, + prox_penalty, + &pdps_config, + iterator, + plotter, + (μ0, z, Loc([])), + &opKz, + fnR, + &fnH, + domain + ) +} + + +#[deprecated] +/// Version of [`pointsource_sliding_pdps_pair`] without domain specification. +#[replace_float_literals(F::cast_from(literal))] +pub fn pointsource_sliding_pdps_pair< + F, + I, + S, + Dat, + Reg, + P, + Z, + R, + Y, + Plot, + /*KOpM, */ KOpZ, + H, + const N: usize, +>( + f: &Dat, + reg: &Reg, + prox_penalty: &P, + config: &SlidingPDPSConfig, + iterator: I, + plotter: Plot, + init: (Option>, Z, Y), + //opKμ : KOpM, + opKz: &KOpZ, + fnR: &R, + fnH: &H, +) -> DynResult> +where + F: Float + ToNalgebraRealField, + I: AlgIteratorFactory>, + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair> + + BoundedCurvature, + S: DifferentiableRealMapping + ClosedMul + MinMaxMapping, F>, + 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> + // + Preadjointable< + // RNDM, Y, + // PreadjointCodomain = S, + // > + // + TransportLipschitz + // + 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 + + GEMV + + SimplyAdjointable, + KOpZ::SimpleAdjoint: GEMV, + Y: ClosedEuclidean, + for<'b> &'b Y: Instance, + Z: ClosedEuclidean + AXPY, + for<'b> &'b Z: Instance, + R: Prox, + H: Conjugable, + for<'b> H::Conjugate<'b>: Prox, + Plot: Plotter>, +{ + pointsource_sliding_pdps_pair_dom( + f, + reg, + prox_penalty, + config, + iterator, + plotter, + init, + opKz, + fnR, + fnH, + None, + ) +} + +#[deprecated] +/// Version of [`pointsource_sliding_fb_pair_dom`] without domain specification +#[replace_float_literals(F::cast_from(literal))] pub fn pointsource_sliding_fb_pair( f: &Dat, reg: &Reg, @@ -421,7 +562,7 @@ let &SlidingFBConfig { τ0, σp0, insertion, transport, guess } = config; let pdps_config = SlidingPDPSConfig { τ0, σp0, insertion, transport, guess, σd0: 0.0 }; - pointsource_sliding_pdps_pair( + pointsource_sliding_pdps_pair_dom( f, reg, prox_penalty, @@ -432,5 +573,6 @@ &opKz, fnR, &fnH, + None ) }