src/regularisation.rs

branch
dev
changeset 61
4f468d35fa29
parent 51
0693cc9ba9f0
child 63
7a8a55fd41c0
--- 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<F: Float>(pub F /* α */);
+pub struct NonnegRadonRegTerm<F: Float = f64>(pub F /* α */);
 
 impl<'a, F: Float> NonnegRadonRegTerm<F> {
     /// Returns the regularisation parameter
@@ -44,12 +42,12 @@
     }
 }
 
-impl<'a, F: Float, const N: usize> Mapping<RNDM<F, N>> for NonnegRadonRegTerm<F> {
+impl<'a, F: Float, const N: usize> Mapping<RNDM<N, F>> for NonnegRadonRegTerm<F> {
     type Codomain = F;
 
     fn apply<I>(&self, μ: I) -> F
     where
-        I: Instance<RNDM<F, N>>,
+        I: Instance<RNDM<N, F>>,
     {
         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<F: Float>(pub F /* α */);
+pub struct RadonRegTerm<F: Float = f64>(pub F /* α */);
 
 impl<'a, F: Float> RadonRegTerm<F> {
     /// Returns the regularisation parameter
@@ -69,12 +67,12 @@
     }
 }
 
-impl<'a, F: Float, const N: usize> Mapping<RNDM<F, N>> for RadonRegTerm<F> {
+impl<'a, F: Float, const N: usize> Mapping<RNDM<N, F>> for RadonRegTerm<F> {
     type Codomain = F;
 
     fn apply<I>(&self, μ: I) -> F
     where
-        I: Instance<RNDM<F, N>>,
+        I: Instance<RNDM<N, F>>,
     {
         self.α() * μ.eval(|x| x.norm(Radon))
     }
@@ -82,19 +80,19 @@
 
 /// Regularisation term configuration
 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)]
-pub enum Regularisation<F: Float> {
+pub enum Regularisation<F: Float = f64> {
     /// $α \\|μ\\|\_{ℳ(Ω)}$
     Radon(F),
     /// $α\\|μ\\|\_{ℳ(Ω)} + δ_{≥ 0}(μ)$
     NonnegRadon(F),
 }
 
-impl<'a, F: Float, const N: usize> Mapping<RNDM<F, N>> for Regularisation<F> {
+impl<'a, F: Float, const N: usize> Mapping<RNDM<N, F>> for Regularisation<F> {
     type Codomain = F;
 
     fn apply<I>(&self, μ: I) -> F
     where
-        I: Instance<RNDM<F, N>>,
+        I: Instance<RNDM<N, F>>,
     {
         match *self {
             Self::Radon(α) => RadonRegTerm(α).apply(μ),
@@ -104,8 +102,10 @@
 }
 
 /// Abstraction of regularisation terms.
-pub trait RegTerm<F: Float + ToNalgebraRealField, const N: usize>:
-    Mapping<RNDM<F, N>, Codomain = F>
+pub trait RegTerm<Domain, F = f64>: Mapping<DiscreteMeasure<Domain, F>, Codomain = F>
+where
+    Domain: Space + Clone,
+    F: Float + ToNalgebraRealField,
 {
     /// Approximately solve the problem
     /// <div>$$
@@ -125,7 +125,7 @@
         x: &mut DVector<F::MixedType>,
         mA_normest: F,
         ε: F,
-        config: &FBGenericConfig<F>,
+        config: &InsertionConfig<F>,
     ) -> usize;
 
     /// Approximately solve the problem
@@ -142,7 +142,7 @@
         τ: F,
         x: &mut DVector<F::MixedType>,
         ε: F,
-        config: &FBGenericConfig<F>,
+        config: &InsertionConfig<F>,
     ) -> 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<G, BT>(
+    fn find_tolerance_violation<M>(
         &self,
-        d: &mut BTFN<F, G, BT, N>,
+        d: &mut M,
         τ: F,
         ε: F,
         skip_by_rough_check: bool,
-        config: &FBGenericConfig<F>,
-    ) -> Option<(Loc<F, N>, F, bool)>
+        config: &InsertionConfig<F>,
+    ) -> Option<(Domain, F, bool)>
     where
-        BT: BTSearch<F, N, Agg = Bounds<F>>,
-        G: SupportGenerator<F, N, Id = BT::Data>,
-        G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
+        M: MinMaxMapping<Domain, F>,
     {
         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<G, BT>(
+    fn find_tolerance_violation_slack<M>(
         &self,
-        d: &mut BTFN<F, G, BT, N>,
+        d: &mut M,
         τ: F,
         ε: F,
         skip_by_rough_check: bool,
-        config: &FBGenericConfig<F>,
+        config: &InsertionConfig<F>,
         slack: F,
-    ) -> Option<(Loc<F, N>, F, bool)>
+    ) -> Option<(Domain, F, bool)>
     where
-        BT: BTSearch<F, N, Agg = Bounds<F>>,
-        G: SupportGenerator<F, N, Id = BT::Data>,
-        G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>;
+        M: MinMaxMapping<Domain, F>;
 
     /// 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<G, BT>(
+    fn verify_merge_candidate<M>(
         &self,
-        d: &mut BTFN<F, G, BT, N>,
-        μ: &RNDM<F, N>,
+        d: &mut M,
+        μ: &DiscreteMeasure<Domain, F>,
         τ: F,
         ε: F,
-        config: &FBGenericConfig<F>,
+        config: &InsertionConfig<F>,
     ) -> bool
     where
-        BT: BTSearch<F, N, Agg = Bounds<F>>,
-        G: SupportGenerator<F, N, Id = BT::Data>,
-        G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>;
+        M: MinMaxMapping<Domain, F>;
 
     /// 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<G, BT>(
+    fn verify_merge_candidate_radonsq<M>(
         &self,
-        d: &mut BTFN<F, G, BT, N>,
-        μ: &RNDM<F, N>,
+        d: &mut M,
+        μ: &DiscreteMeasure<Domain, F>,
         τ: F,
         ε: F,
-        config: &FBGenericConfig<F>,
-        radon_μ: &RNDM<F, N>,
+        config: &InsertionConfig<F>,
+        radon_μ: &DiscreteMeasure<Domain, F>,
     ) -> bool
     where
-        BT: BTSearch<F, N, Agg = Bounds<F>>,
-        G: SupportGenerator<F, N, Id = BT::Data>,
-        G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>;
+        M: MinMaxMapping<Domain, F>;
 
     /// TODO: document this
     fn target_bounds(&self, τ: F, ε: F) -> Option<Bounds<F>>;
@@ -244,32 +236,33 @@
 }
 
 /// Abstraction of regularisation terms for [`pointsource_sliding_fb_reg`].
-pub trait SlidingRegTerm<F: Float + ToNalgebraRealField, const N: usize>: RegTerm<F, N> {
+pub trait SlidingRegTerm<Domain, F = f64>: RegTerm<Domain, F>
+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<G, BT>(
+    fn goodness<M>(
         &self,
-        d: &mut BTFN<F, G, BT, N>,
-        μ: &RNDM<F, N>,
-        y: &Loc<F, N>,
-        z: &Loc<F, N>,
+        d: &mut M,
+        μ: &DiscreteMeasure<Domain, F>,
+        y: &Domain,
+        z: &Domain,
         τ: F,
         ε: F,
-        config: &FBGenericConfig<F>,
+        config: &InsertionConfig<F>,
     ) -> F
     where
-        BT: BTSearch<F, N, Agg = Bounds<F>>,
-        G: SupportGenerator<F, N, Id = BT::Data>,
-        G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>;
+        M: MinMaxMapping<Domain, F>;
 
     /// 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<F: Float + ToNalgebraRealField, const N: usize> RegTerm<F, N> for NonnegRadonRegTerm<F>
-where
-    Cube<F, N>: P2Minimise<Loc<F, N>, F>,
+impl<F: Float + ToNalgebraRealField, const N: usize> RegTerm<Loc<N, F>, F>
+    for NonnegRadonRegTerm<F>
 {
     fn solve_findim(
         &self,
@@ -279,7 +272,7 @@
         x: &mut DVector<F::MixedType>,
         mA_normest: F,
         ε: F,
-        config: &FBGenericConfig<F>,
+        config: &InsertionConfig<F>,
     ) -> 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::MixedType>,
         ε: F,
-        config: &FBGenericConfig<F>,
+        config: &InsertionConfig<F>,
     ) -> 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<G, BT>(
+    fn find_tolerance_violation_slack<M>(
         &self,
-        d: &mut BTFN<F, G, BT, N>,
+        d: &mut M,
         τ: F,
         ε: F,
         skip_by_rough_check: bool,
-        config: &FBGenericConfig<F>,
+        config: &InsertionConfig<F>,
         slack: F,
-    ) -> Option<(Loc<F, N>, F, bool)>
+    ) -> Option<(Loc<N, F>, F, bool)>
     where
-        BT: BTSearch<F, N, Agg = Bounds<F>>,
-        G: SupportGenerator<F, N, Id = BT::Data>,
-        G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
+        M: MinMaxMapping<Loc<N, F>, 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<G, BT>(
+    fn verify_merge_candidate<M>(
         &self,
-        d: &mut BTFN<F, G, BT, N>,
-        μ: &RNDM<F, N>,
+        d: &mut M,
+        μ: &RNDM<N, F>,
         τ: F,
         ε: F,
-        config: &FBGenericConfig<F>,
+        config: &InsertionConfig<F>,
     ) -> bool
     where
-        BT: BTSearch<F, N, Agg = Bounds<F>>,
-        G: SupportGenerator<F, N, Id = BT::Data>,
-        G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
+        M: MinMaxMapping<Loc<N, F>, F>,
     {
         let τα = τ * self.α();
         let refinement_tolerance = ε * config.refinement.tolerance_mult;
@@ -367,19 +362,17 @@
                 ));
     }
 
-    fn verify_merge_candidate_radonsq<G, BT>(
+    fn verify_merge_candidate_radonsq<M>(
         &self,
-        d: &mut BTFN<F, G, BT, N>,
-        μ: &RNDM<F, N>,
+        d: &mut M,
+        μ: &RNDM<N, F>,
         τ: F,
         ε: F,
-        config: &FBGenericConfig<F>,
-        radon_μ: &RNDM<F, N>,
+        config: &InsertionConfig<F>,
+        radon_μ: &RNDM<N, F>,
     ) -> bool
     where
-        BT: BTSearch<F, N, Agg = Bounds<F>>,
-        G: SupportGenerator<F, N, Id = BT::Data>,
-        G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
+        M: MinMaxMapping<Loc<N, F>, F>,
     {
         let τα = τ * self.α();
         let refinement_tolerance = ε * config.refinement.tolerance_mult;
@@ -426,24 +419,21 @@
 }
 
 #[replace_float_literals(F::cast_from(literal))]
-impl<F: Float + ToNalgebraRealField, const N: usize> SlidingRegTerm<F, N> for NonnegRadonRegTerm<F>
-where
-    Cube<F, N>: P2Minimise<Loc<F, N>, F>,
+impl<F: Float + ToNalgebraRealField, const N: usize> SlidingRegTerm<Loc<N, F>, F>
+    for NonnegRadonRegTerm<F>
 {
-    fn goodness<G, BT>(
+    fn goodness<M>(
         &self,
-        d: &mut BTFN<F, G, BT, N>,
-        _μ: &RNDM<F, N>,
-        y: &Loc<F, N>,
-        z: &Loc<F, N>,
+        d: &mut M,
+        _μ: &RNDM<N, F>,
+        y: &Loc<N, F>,
+        z: &Loc<N, F>,
         τ: F,
         ε: F,
-        _config: &FBGenericConfig<F>,
+        _config: &InsertionConfig<F>,
     ) -> F
     where
-        BT: BTSearch<F, N, Agg = Bounds<F>>,
-        G: SupportGenerator<F, N, Id = BT::Data>,
-        G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
+        M: MinMaxMapping<Loc<N, F>, 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<F: Float + ToNalgebraRealField, const N: usize> RegTerm<F, N> for RadonRegTerm<F>
-where
-    Cube<F, N>: P2Minimise<Loc<F, N>, F>,
-{
+impl<F: Float + ToNalgebraRealField, const N: usize> RegTerm<Loc<N, F>, F> for RadonRegTerm<F> {
     fn solve_findim(
         &self,
         mA: &DMatrix<F::MixedType>,
@@ -467,7 +454,7 @@
         x: &mut DVector<F::MixedType>,
         mA_normest: F,
         ε: F,
-        config: &FBGenericConfig<F>,
+        config: &InsertionConfig<F>,
     ) -> 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::MixedType>,
         ε: F,
-        config: &FBGenericConfig<F>,
+        config: &InsertionConfig<F>,
     ) -> 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<G, BT>(
+    fn find_tolerance_violation_slack<M>(
         &self,
-        d: &mut BTFN<F, G, BT, N>,
+        d: &mut M,
         τ: F,
         ε: F,
         skip_by_rough_check: bool,
-        config: &FBGenericConfig<F>,
+        config: &InsertionConfig<F>,
         slack: F,
-    ) -> Option<(Loc<F, N>, F, bool)>
+    ) -> Option<(Loc<N, F>, F, bool)>
     where
-        BT: BTSearch<F, N, Agg = Bounds<F>>,
-        G: SupportGenerator<F, N, Id = BT::Data>,
-        G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
+        M: MinMaxMapping<Loc<N, F>, F>,
     {
         let τα = τ * self.α();
         let keep_below = τα + slack + ε;
@@ -541,18 +526,16 @@
         }
     }
 
-    fn verify_merge_candidate<G, BT>(
+    fn verify_merge_candidate<M>(
         &self,
-        d: &mut BTFN<F, G, BT, N>,
-        μ: &RNDM<F, N>,
+        d: &mut M,
+        μ: &RNDM<N, F>,
         τ: F,
         ε: F,
-        config: &FBGenericConfig<F>,
+        config: &InsertionConfig<F>,
     ) -> bool
     where
-        BT: BTSearch<F, N, Agg = Bounds<F>>,
-        G: SupportGenerator<F, N, Id = BT::Data>,
-        G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
+        M: MinMaxMapping<Loc<N, F>, F>,
     {
         let τα = τ * self.α();
         let refinement_tolerance = ε * config.refinement.tolerance_mult;
@@ -585,19 +568,17 @@
                 ));
     }
 
-    fn verify_merge_candidate_radonsq<G, BT>(
+    fn verify_merge_candidate_radonsq<M>(
         &self,
-        d: &mut BTFN<F, G, BT, N>,
-        μ: &RNDM<F, N>,
+        d: &mut M,
+        μ: &RNDM<N, F>,
         τ: F,
         ε: F,
-        config: &FBGenericConfig<F>,
-        radon_μ: &RNDM<F, N>,
+        config: &InsertionConfig<F>,
+        radon_μ: &RNDM<N, F>,
     ) -> bool
     where
-        BT: BTSearch<F, N, Agg = Bounds<F>>,
-        G: SupportGenerator<F, N, Id = BT::Data>,
-        G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
+        M: MinMaxMapping<Loc<N, F>, F>,
     {
         let τα = τ * self.α();
         let refinement_tolerance = ε * config.refinement.tolerance_mult;
@@ -650,24 +631,21 @@
 }
 
 #[replace_float_literals(F::cast_from(literal))]
-impl<F: Float + ToNalgebraRealField, const N: usize> SlidingRegTerm<F, N> for RadonRegTerm<F>
-where
-    Cube<F, N>: P2Minimise<Loc<F, N>, F>,
+impl<F: Float + ToNalgebraRealField, const N: usize> SlidingRegTerm<Loc<N, F>, F>
+    for RadonRegTerm<F>
 {
-    fn goodness<G, BT>(
+    fn goodness<M>(
         &self,
-        d: &mut BTFN<F, G, BT, N>,
-        _μ: &RNDM<F, N>,
-        y: &Loc<F, N>,
-        z: &Loc<F, N>,
+        d: &mut M,
+        _μ: &RNDM<N, F>,
+        y: &Loc<N, F>,
+        z: &Loc<N, F>,
         τ: F,
         ε: F,
-        _config: &FBGenericConfig<F>,
+        _config: &InsertionConfig<F>,
     ) -> F
     where
-        BT: BTSearch<F, N, Agg = Bounds<F>>,
-        G: SupportGenerator<F, N, Id = BT::Data>,
-        G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>,
+        M: MinMaxMapping<Loc<N, F>, F>,
     {
         let α = self.α();
         let w = |x| {

mercurial