diff -r 51dbb2cc25d9 -r 8d1c959432c8 src/forward_pdps.rs --- a/src/forward_pdps.rs Tue Aug 04 13:46:29 2026 -0500 +++ b/src/forward_pdps.rs Tue Aug 04 14:12:18 2026 -0500 @@ -3,6 +3,7 @@ primal-dual proximal splitting with a forward step. */ +use crate::domain::{AdmissibleDomain, FullSpace}; use crate::fb::*; use crate::measures::merging::SpikeMerging; use crate::measures::{DiscreteMeasure, RNDM}; @@ -20,6 +21,7 @@ use alg_tools::nalgebra_support::ToNalgebraRealField; use alg_tools::norms::L2; use anyhow::ensure; +use measures::DeltaMeasure; use numeric_literals::replace_float_literals; use serde::{Deserialize, Serialize}; @@ -85,7 +87,7 @@ /// τ = τ_0 \frac{1 - σ_{p,0}}{(σ_d M (1-σ_p L_z) + (1 - σ_{p,0} L)}. /// $$ #[replace_float_literals(F::cast_from(literal))] -pub fn pointsource_forward_pdps_pair< +pub fn pointsource_forward_pdps_pair_dom< F, I, S, @@ -111,6 +113,7 @@ opKz: &KOpZ, fnR: &R, fnH: &H, + domain: &mut impl AdmissibleDomain>, ) -> DynResult> where F: Float + ToNalgebraRealField, @@ -237,10 +240,7 @@ // Merge spikes. // This crucially expects the merge routine to be stable with respect to spike locations, // and not to performing any pruning. That is be to done below simultaneously for γ. - // Merge spikes. - // This crucially expects the merge routine to be stable with respect to spike locations, - // and not to performing any pruning. That is be to done below simultaneously for γ. - config.insertion.if_merge_now(&state, |ins| { + let did_merge = config.insertion.if_merge_now(&state, |ins| { stats.merged += prox_penalty.merge_spikes( &mut μ, &mut τv, @@ -249,32 +249,45 @@ ε, ins, ®, - Some(|μ̃: &RNDM| f.apply(Pair(μ̃, &z))+ reg.apply(μ̃)), + Some(|μ̃: &RNDM| f.apply(Pair(μ̃, &z)) + reg.apply(μ̃)), ); - }); + stats.pruned += prune_with_stats(&mut μ); - // Prune spikes with zero weight. - stats.pruned += prune_with_stats(&mut μ); + if config.insertion.random_insertions { + if let Some(x) = domain.random_point() { + μ += DeltaMeasure { x, α: 0.0 }; + stats.inserted += 1; + } + } - for _ in 0..config.insertion.extra_weight_optimisation_steps { - // Do extra weight optimisation step heuristic - let Pair(v, _z_tmp) = f.differential(Pair(&μ, &z)); - τv = v * τ; - prox_penalty.reweigh( - &mut μ, - &mut τv, - τ, - ε, - &config.insertion, - ®, - &state, - &mut stats, - )?; - //(z, y) = zy_step(z, y, z_tmp); - } + for _ in 0..config.insertion.extra_weight_optimisation_steps { + // Do extra weight optimisation step heuristic + let Pair(v, mut _z_tmp) = f.differential(Pair(&μ, &z)); + τv = v * τ; + prox_penalty.reweigh( + &mut μ, + &mut τv, + τ, + ε, + &config.insertion, + ®, + &state, + &mut stats, + )?; - if config.insertion.extra_weight_optimisation_steps > 0 { - // Prune spikes with zero weight. + // opKz_adj.apply_add(&mut z_tmp, &y); + // z_tmp.axpy(1.0, &z, -σ_p); + // let z_new = fnR.prox(σ_p, z_tmp); + // z.axpy(1.0 + ω, &z_new, -ω); + // //z = (z - &z_new) * (-ω) + &z_new; + // opKz.gemv(&mut y, σ_d, z, 1.0); + // y = starH.prox(σ_d, y); + // z = z_new; + } + Ok(()) + })?; + + if did_merge.is_none() { stats.pruned += prune_with_stats(&mut μ); } @@ -305,13 +318,79 @@ Ok(Pair(μ, z)) } +#[deprecated] +pub fn pointsource_forward_pdps_pair< + F, + I, + S, + Dat, + Reg, + P, + Z, + R, + Y, + /*KOpM, */ KOpZ, + H, + Plot, + const N: usize, +>( + f: &Dat, + reg: &Reg, + prox_penalty: &P, + config: &ForwardPDPSConfig, + 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>, + //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>, + for<'a> Pair<&'a P, &'a IdOp>: StepLengthBoundPair, + 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_forward_pdps_pair_dom( + f, + reg, + prox_penalty, + config, + iterator, + plotter, + init, + opKz, + fnR, + fnH, + &mut FullSpace, + ) +} + /// Iteratively solve the pointsource localisation with an additional variable /// using forward-backward splitting. /// /// 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( +pub fn pointsource_fb_pair_dom( f: &Dat, reg: &Reg, prox_penalty: &P, @@ -321,6 +400,7 @@ (μ0, z): (Option>, Z), //opKμ : KOpM, fnR: &R, + domain: &mut impl AdmissibleDomain>, ) -> DynResult> where F: Float + ToNalgebraRealField, @@ -345,7 +425,7 @@ let &FBConfig { τ0, σp0, insertion } = config; let pdps_config = ForwardPDPSConfig { τ0, σp0, insertion, σd0: 0.0 }; - pointsource_forward_pdps_pair( + pointsource_forward_pdps_pair_dom( f, reg, prox_penalty, @@ -356,5 +436,48 @@ &opKz, fnR, &fnH, + domain, ) } + +#[deprecated] +#[replace_float_literals(F::cast_from(literal))] +pub fn pointsource_fb_pair( + f: &Dat, + reg: &Reg, + prox_penalty: &P, + config: &FBConfig, + iterator: I, + plotter: Plot, + init: (Option>, Z), + //opKμ : KOpM, + fnR: &R, +) -> DynResult> +where + F: Float + ToNalgebraRealField, + I: AlgIteratorFactory>, + Dat: DifferentiableMapping, Codomain = F, DerivativeDomain = Pair>, + S: DifferentiableRealMapping + ClosedMul, + RNDM: SpikeMerging, + Reg: RegTerm, F>, + P: ProxPenalty, 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>, +{ + pointsource_fb_pair_dom( + f, + reg, + prox_penalty, + config, + iterator, + plotter, + init, + fnR, + &mut FullSpace, + ) +}