diff -r 9738b51d90d7 -r 4f468d35fa29 src/regularisation.rs --- a/src/regularisation.rs Sun Apr 27 15:03:51 2025 -0500 +++ b/src/regularisation.rs Thu Feb 26 11:38:43 2026 -0500 @@ -4,12 +4,12 @@ #[allow(unused_imports)] // Used by documentation. use crate::fb::pointsource_fb_reg; -use crate::fb::FBGenericConfig; -use crate::measures::{DeltaMeasure, Radon, RNDM}; +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::types::*; -use alg_tools::instance::Instance; +use alg_tools::instance::{Instance, Space}; use alg_tools::linops::Mapping; use alg_tools::loc::Loc; use alg_tools::norms::Norm; @@ -20,9 +20,7 @@ l1squared_nonneg::l1squared_nonneg, l1squared_unconstrained::l1squared_unconstrained, nonneg::quadratic_nonneg, unconstrained::quadratic_unconstrained, }; -use alg_tools::bisection_tree::{ - BTSearch, Bounded, Bounds, LocalAnalysis, P2Minimise, SupportGenerator, BTFN, -}; +use alg_tools::bounds::{Bounds, MinMaxMapping}; use alg_tools::iterate::AlgIteratorFactory; use alg_tools::nalgebra_support::ToNalgebraRealField; use nalgebra::{DMatrix, DVector}; @@ -34,7 +32,7 @@ /// /// The only member of the struct is the regularisation parameter α. #[derive(Copy, Clone, Debug, Serialize, Deserialize)] -pub struct NonnegRadonRegTerm(pub F /* α */); +pub struct NonnegRadonRegTerm(pub F /* α */); impl<'a, F: Float> NonnegRadonRegTerm { /// Returns the regularisation parameter @@ -44,12 +42,12 @@ } } -impl<'a, F: Float, const N: usize> Mapping> for NonnegRadonRegTerm { +impl<'a, F: Float, const N: usize> Mapping> for NonnegRadonRegTerm { type Codomain = F; fn apply(&self, μ: I) -> F where - I: Instance>, + I: Instance>, { self.α() * μ.eval(|x| x.norm(Radon)) } @@ -59,7 +57,7 @@ /// /// The only member of the struct is the regularisation parameter α. #[derive(Copy, Clone, Debug, Serialize, Deserialize)] -pub struct RadonRegTerm(pub F /* α */); +pub struct RadonRegTerm(pub F /* α */); impl<'a, F: Float> RadonRegTerm { /// Returns the regularisation parameter @@ -69,12 +67,12 @@ } } -impl<'a, F: Float, const N: usize> Mapping> for RadonRegTerm { +impl<'a, F: Float, const N: usize> Mapping> for RadonRegTerm { type Codomain = F; fn apply(&self, μ: I) -> F where - I: Instance>, + I: Instance>, { self.α() * μ.eval(|x| x.norm(Radon)) } @@ -82,19 +80,19 @@ /// Regularisation term configuration #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] -pub enum Regularisation { +pub enum Regularisation { /// $α \\|μ\\|\_{ℳ(Ω)}$ Radon(F), /// $α\\|μ\\|\_{ℳ(Ω)} + δ_{≥ 0}(μ)$ NonnegRadon(F), } -impl<'a, F: Float, const N: usize> Mapping> for Regularisation { +impl<'a, F: Float, const N: usize> Mapping> for Regularisation { type Codomain = F; fn apply(&self, μ: I) -> F where - I: Instance>, + I: Instance>, { match *self { Self::Radon(α) => RadonRegTerm(α).apply(μ), @@ -104,8 +102,10 @@ } /// Abstraction of regularisation terms. -pub trait RegTerm: - Mapping, Codomain = F> +pub trait RegTerm: Mapping, Codomain = F> +where + Domain: Space + Clone, + F: Float + ToNalgebraRealField, { /// Approximately solve the problem ///
$$ @@ -125,7 +125,7 @@ x: &mut DVector, mA_normest: F, ε: F, - config: &FBGenericConfig, + config: &InsertionConfig, ) -> usize; /// Approximately solve the problem @@ -142,7 +142,7 @@ τ: F, x: &mut DVector, ε: F, - config: &FBGenericConfig, + config: &InsertionConfig, ) -> usize; /// Find a point where `d` may violate the tolerance `ε`. @@ -154,18 +154,16 @@ /// Returns `None` if `d` is in bounds either based on the rough check, or a more precise check /// terminating early. Otherwise returns a possibly violating point, the value of `d` there, /// and a boolean indicating whether the found point is in bounds. - fn find_tolerance_violation( + fn find_tolerance_violation( &self, - d: &mut BTFN, + d: &mut M, τ: F, ε: F, skip_by_rough_check: bool, - config: &FBGenericConfig, - ) -> Option<(Loc, F, bool)> + config: &InsertionConfig, + ) -> Option<(Domain, F, bool)> where - BT: BTSearch>, - G: SupportGenerator, - G::SupportType: Mapping, Codomain = F> + LocalAnalysis, N>, + M: MinMaxMapping, { self.find_tolerance_violation_slack(d, τ, ε, skip_by_rough_check, config, F::ZERO) } @@ -182,35 +180,31 @@ /// Returns `None` if `d` is in bounds either based on the rough check, or a more precise check /// terminating early. Otherwise returns a possibly violating point, the value of `d` there, /// and a boolean indicating whether the found point is in bounds. - fn find_tolerance_violation_slack( + fn find_tolerance_violation_slack( &self, - d: &mut BTFN, + d: &mut M, τ: F, ε: F, skip_by_rough_check: bool, - config: &FBGenericConfig, + config: &InsertionConfig, slack: F, - ) -> Option<(Loc, F, bool)> + ) -> Option<(Domain, F, bool)> where - BT: BTSearch>, - G: SupportGenerator, - G::SupportType: Mapping, Codomain = F> + LocalAnalysis, N>; + M: MinMaxMapping; /// Verify that `d` is in bounds `ε` for a merge candidate `μ` /// /// `ε` is the current main tolerance and `τ` a scaling factor for the regulariser. - fn verify_merge_candidate( + fn verify_merge_candidate( &self, - d: &mut BTFN, - μ: &RNDM, + d: &mut M, + μ: &DiscreteMeasure, τ: F, ε: F, - config: &FBGenericConfig, + config: &InsertionConfig, ) -> bool where - BT: BTSearch>, - G: SupportGenerator, - G::SupportType: Mapping, Codomain = F> + LocalAnalysis, N>; + M: MinMaxMapping; /// Verify that `d` is in bounds `ε` for a merge candidate `μ` /// @@ -220,19 +214,17 @@ /// same coordinates at same agreeing indices. /// /// `ε` is the current main tolerance and `τ` a scaling factor for the regulariser. - fn verify_merge_candidate_radonsq( + fn verify_merge_candidate_radonsq( &self, - d: &mut BTFN, - μ: &RNDM, + d: &mut M, + μ: &DiscreteMeasure, τ: F, ε: F, - config: &FBGenericConfig, - radon_μ: &RNDM, + config: &InsertionConfig, + radon_μ: &DiscreteMeasure, ) -> bool where - BT: BTSearch>, - G: SupportGenerator, - G::SupportType: Mapping, Codomain = F> + LocalAnalysis, N>; + M: MinMaxMapping; /// TODO: document this fn target_bounds(&self, τ: F, ε: F) -> Option>; @@ -244,32 +236,33 @@ } /// Abstraction of regularisation terms for [`pointsource_sliding_fb_reg`]. -pub trait SlidingRegTerm: RegTerm { +pub trait SlidingRegTerm: RegTerm +where + Domain: Space + Clone, + F: Float + ToNalgebraRealField, +{ /// Calculate $τ[w(z) - w(y)]$ for some w in the subdifferential of the regularisation /// term, such that $-ε ≤ τw - d ≤ ε$. - fn goodness( + fn goodness( &self, - d: &mut BTFN, - μ: &RNDM, - y: &Loc, - z: &Loc, + d: &mut M, + μ: &DiscreteMeasure, + y: &Domain, + z: &Domain, τ: F, ε: F, - config: &FBGenericConfig, + config: &InsertionConfig, ) -> F where - BT: BTSearch>, - G: SupportGenerator, - G::SupportType: Mapping, Codomain = F> + LocalAnalysis, N>; + M: MinMaxMapping; /// Convert bound on the regulariser to a bond on the Radon norm fn radon_norm_bound(&self, b: F) -> F; } #[replace_float_literals(F::cast_from(literal))] -impl RegTerm for NonnegRadonRegTerm -where - Cube: P2Minimise, F>, +impl RegTerm, F> + for NonnegRadonRegTerm { fn solve_findim( &self, @@ -279,7 +272,7 @@ x: &mut DVector, mA_normest: F, ε: F, - config: &FBGenericConfig, + config: &InsertionConfig, ) -> usize { let inner_tolerance = ε * config.inner.tolerance_mult; let inner_it = config.inner.iterator_options.stop_target(inner_tolerance); @@ -293,7 +286,7 @@ τ: F, x: &mut DVector, ε: F, - config: &FBGenericConfig, + config: &InsertionConfig, ) -> usize { let inner_tolerance = ε * config.inner.tolerance_mult; let inner_it = config.inner.iterator_options.stop_target(inner_tolerance); @@ -301,52 +294,54 @@ } #[inline] - fn find_tolerance_violation_slack( + fn find_tolerance_violation_slack( &self, - d: &mut BTFN, + d: &mut M, τ: F, ε: F, skip_by_rough_check: bool, - config: &FBGenericConfig, + config: &InsertionConfig, slack: F, - ) -> Option<(Loc, F, bool)> + ) -> Option<(Loc, F, bool)> where - BT: BTSearch>, - G: SupportGenerator, - G::SupportType: Mapping, Codomain = F> + LocalAnalysis, N>, + M: MinMaxMapping, F>, { let τα = τ * self.α(); let keep_above = -τα - slack - ε; let minimise_below = -τα - slack - ε * config.insertion_cutoff_factor; let refinement_tolerance = ε * config.refinement.tolerance_mult; + // println!( + // "keep_above: {keep_above}, rough lower bound: {}, tolerance: {ε}, slack: {slack}, τα: {τα}", + // d.bounds().lower() + // ); + // If preliminary check indicates that we are in bounds, and if it otherwise matches // the insertion strategy, skip insertion. if skip_by_rough_check && d.bounds().lower() >= keep_above { None } else { // If the rough check didn't indicate no insertion needed, find minimising point. - d.minimise_below( + let res = d.minimise_below( minimise_below, refinement_tolerance, config.refinement.max_steps, - ) - .map(|(ξ, v_ξ)| (ξ, v_ξ, v_ξ >= keep_above)) + ); + + res.map(|(ξ, v_ξ)| (ξ, v_ξ, v_ξ >= keep_above)) } } - fn verify_merge_candidate( + fn verify_merge_candidate( &self, - d: &mut BTFN, - μ: &RNDM, + d: &mut M, + μ: &RNDM, τ: F, ε: F, - config: &FBGenericConfig, + config: &InsertionConfig, ) -> bool where - BT: BTSearch>, - G: SupportGenerator, - G::SupportType: Mapping, Codomain = F> + LocalAnalysis, N>, + M: MinMaxMapping, F>, { let τα = τ * self.α(); let refinement_tolerance = ε * config.refinement.tolerance_mult; @@ -367,19 +362,17 @@ )); } - fn verify_merge_candidate_radonsq( + fn verify_merge_candidate_radonsq( &self, - d: &mut BTFN, - μ: &RNDM, + d: &mut M, + μ: &RNDM, τ: F, ε: F, - config: &FBGenericConfig, - radon_μ: &RNDM, + config: &InsertionConfig, + radon_μ: &RNDM, ) -> bool where - BT: BTSearch>, - G: SupportGenerator, - G::SupportType: Mapping, Codomain = F> + LocalAnalysis, N>, + M: MinMaxMapping, F>, { let τα = τ * self.α(); let refinement_tolerance = ε * config.refinement.tolerance_mult; @@ -426,24 +419,21 @@ } #[replace_float_literals(F::cast_from(literal))] -impl SlidingRegTerm for NonnegRadonRegTerm -where - Cube: P2Minimise, F>, +impl SlidingRegTerm, F> + for NonnegRadonRegTerm { - fn goodness( + fn goodness( &self, - d: &mut BTFN, - _μ: &RNDM, - y: &Loc, - z: &Loc, + d: &mut M, + _μ: &RNDM, + y: &Loc, + z: &Loc, τ: F, ε: F, - _config: &FBGenericConfig, + _config: &InsertionConfig, ) -> F where - BT: BTSearch>, - G: SupportGenerator, - G::SupportType: Mapping, Codomain = F> + LocalAnalysis, N>, + M: MinMaxMapping, F>, { let w = |x| 1.0.min((ε + d.apply(x)) / (τ * self.α())); w(z) - w(y) @@ -455,10 +445,7 @@ } #[replace_float_literals(F::cast_from(literal))] -impl RegTerm for RadonRegTerm -where - Cube: P2Minimise, F>, -{ +impl RegTerm, F> for RadonRegTerm { fn solve_findim( &self, mA: &DMatrix, @@ -467,7 +454,7 @@ x: &mut DVector, mA_normest: F, ε: F, - config: &FBGenericConfig, + config: &InsertionConfig, ) -> usize { let inner_tolerance = ε * config.inner.tolerance_mult; let inner_it = config.inner.iterator_options.stop_target(inner_tolerance); @@ -481,26 +468,24 @@ τ: F, x: &mut DVector, ε: F, - config: &FBGenericConfig, + config: &InsertionConfig, ) -> usize { let inner_tolerance = ε * config.inner.tolerance_mult; let inner_it = config.inner.iterator_options.stop_target(inner_tolerance); l1squared_unconstrained(y, g, τ * self.α(), 1.0, x, &config.inner, inner_it) } - fn find_tolerance_violation_slack( + fn find_tolerance_violation_slack( &self, - d: &mut BTFN, + d: &mut M, τ: F, ε: F, skip_by_rough_check: bool, - config: &FBGenericConfig, + config: &InsertionConfig, slack: F, - ) -> Option<(Loc, F, bool)> + ) -> Option<(Loc, F, bool)> where - BT: BTSearch>, - G: SupportGenerator, - G::SupportType: Mapping, Codomain = F> + LocalAnalysis, N>, + M: MinMaxMapping, F>, { let τα = τ * self.α(); let keep_below = τα + slack + ε; @@ -541,18 +526,16 @@ } } - fn verify_merge_candidate( + fn verify_merge_candidate( &self, - d: &mut BTFN, - μ: &RNDM, + d: &mut M, + μ: &RNDM, τ: F, ε: F, - config: &FBGenericConfig, + config: &InsertionConfig, ) -> bool where - BT: BTSearch>, - G: SupportGenerator, - G::SupportType: Mapping, Codomain = F> + LocalAnalysis, N>, + M: MinMaxMapping, F>, { let τα = τ * self.α(); let refinement_tolerance = ε * config.refinement.tolerance_mult; @@ -585,19 +568,17 @@ )); } - fn verify_merge_candidate_radonsq( + fn verify_merge_candidate_radonsq( &self, - d: &mut BTFN, - μ: &RNDM, + d: &mut M, + μ: &RNDM, τ: F, ε: F, - config: &FBGenericConfig, - radon_μ: &RNDM, + config: &InsertionConfig, + radon_μ: &RNDM, ) -> bool where - BT: BTSearch>, - G: SupportGenerator, - G::SupportType: Mapping, Codomain = F> + LocalAnalysis, N>, + M: MinMaxMapping, F>, { let τα = τ * self.α(); let refinement_tolerance = ε * config.refinement.tolerance_mult; @@ -650,24 +631,21 @@ } #[replace_float_literals(F::cast_from(literal))] -impl SlidingRegTerm for RadonRegTerm -where - Cube: P2Minimise, F>, +impl SlidingRegTerm, F> + for RadonRegTerm { - fn goodness( + fn goodness( &self, - d: &mut BTFN, - _μ: &RNDM, - y: &Loc, - z: &Loc, + d: &mut M, + _μ: &RNDM, + y: &Loc, + z: &Loc, τ: F, ε: F, - _config: &FBGenericConfig, + _config: &InsertionConfig, ) -> F where - BT: BTSearch>, - G: SupportGenerator, - G::SupportType: Mapping, Codomain = F> + LocalAnalysis, N>, + M: MinMaxMapping, F>, { let α = self.α(); let w = |x| {