src/prox_penalty.rs

changeset 52
f0e8704d3f0e
parent 51
0693cc9ba9f0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/prox_penalty.rs	Mon Feb 17 13:54:53 2025 -0500
@@ -0,0 +1,192 @@
+/*!
+Proximal penalty abstraction
+*/
+
+use alg_tools::types::*;
+use numeric_literals::replace_float_literals;
+use serde::{Deserialize, Serialize};
+
+use crate::measures::merging::SpikeMergingMethod;
+use crate::measures::RNDM;
+use crate::regularisation::RegTerm;
+use crate::subproblem::InnerSettings;
+use crate::tolerance::Tolerance;
+use crate::types::{IterInfo, RefinementSettings};
+use alg_tools::iterate::{AlgIterator, AlgIteratorIteration};
+use alg_tools::mapping::RealMapping;
+use alg_tools::nalgebra_support::ToNalgebraRealField;
+
+pub mod radon_squared;
+pub mod wave;
+pub use radon_squared::RadonSquared;
+
+/// Settings for the solution of the stepwise optimality condition.
+#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)]
+#[serde(default)]
+pub struct FBGenericConfig<F: Float> {
+    /// Tolerance for point insertion.
+    pub tolerance: Tolerance<F>,
+
+    /// Stop looking for predual maximum (where to isert a new point) below
+    /// `tolerance` multiplied by this factor.
+    ///
+    /// Not used by [`crate::prox_penalty::radon_squared`].
+    pub insertion_cutoff_factor: F,
+
+    /// Settings for branch and bound refinement when looking for predual maxima
+    pub refinement: RefinementSettings<F>,
+
+    /// Maximum insertions within each outer iteration
+    ///
+    /// Not used by [`crate::prox_penalty::radon_squared`].
+    pub max_insertions: usize,
+
+    /// Pair `(n, m)` for maximum insertions `m` on first `n` iterations.
+    ///
+    /// Not used by [`crate::prox_penalty::radon_squared`].
+    pub bootstrap_insertions: Option<(usize, usize)>,
+
+    /// Inner method settings
+    pub inner: InnerSettings<F>,
+
+    /// Spike merging method
+    pub merging: SpikeMergingMethod<F>,
+
+    /// Tolerance multiplier for merges
+    pub merge_tolerance_mult: F,
+
+    /// Merge spikes after last step (even if merging not generally enabled)
+    pub final_merging: bool,
+
+    /// Use fitness as merging criterion. Implies worse convergence guarantees.
+    pub fitness_merging: bool,
+
+    /// Iterations between merging heuristic tries
+    pub merge_every: usize,
+    // /// Save $μ$ for postprocessing optimisation
+    // pub postprocessing : bool
+}
+
+#[replace_float_literals(F::cast_from(literal))]
+impl<F: Float> Default for FBGenericConfig<F> {
+    fn default() -> Self {
+        FBGenericConfig {
+            tolerance: Default::default(),
+            insertion_cutoff_factor: 1.0,
+            refinement: Default::default(),
+            max_insertions: 100,
+            //bootstrap_insertions : None,
+            bootstrap_insertions: Some((10, 1)),
+            inner: Default::default(),
+            merging: Default::default(),
+            final_merging: true,
+            fitness_merging: false,
+            merge_every: 10,
+            merge_tolerance_mult: 2.0,
+            // postprocessing : false,
+        }
+    }
+}
+
+impl<F: Float> FBGenericConfig<F> {
+    /// Check if merging should be attempted this iteration
+    pub fn merge_now<I: AlgIterator>(&self, state: &AlgIteratorIteration<I>) -> bool {
+        self.merging.enabled && state.iteration() % self.merge_every == 0
+    }
+
+    /// Returns the final merging method
+    pub fn final_merging_method(&self) -> SpikeMergingMethod<F> {
+        SpikeMergingMethod {
+            enabled: self.final_merging,
+            ..self.merging
+        }
+    }
+}
+
+/// Trait for proximal penalties
+pub trait ProxPenalty<F, PreadjointCodomain, Reg, const N: usize>
+where
+    F: Float + ToNalgebraRealField,
+    Reg: RegTerm<F, N>,
+{
+    type ReturnMapping: RealMapping<F, N>;
+
+    /// Insert new spikes into `μ` to approximately satisfy optimality conditions
+    /// with the forward step term fixed to `τv`.
+    ///
+    /// May return `τv + w` for `w` a subdifferential of the regularisation term `reg`,
+    /// as well as an indication of whether the tolerance bounds `ε` are satisfied.
+    ///
+    /// `μ_base + ν_delta` is the base point, where `μ` and `μ_base` are assumed to have the same
+    /// spike locations, while `ν_delta` may have different locations.
+    ///
+    /// `τv` is mutable to allow [`alg_tools::bisection_tree::BTFN`] refinement.
+    /// Actual values of `τv` are not supposed to be mutated.
+    fn insert_and_reweigh<I>(
+        &self,
+        μ: &mut RNDM<F, N>,
+        τv: &mut PreadjointCodomain,
+        μ_base: &RNDM<F, N>,
+        ν_delta: Option<&RNDM<F, N>>,
+        τ: F,
+        ε: F,
+        config: &FBGenericConfig<F>,
+        reg: &Reg,
+        state: &AlgIteratorIteration<I>,
+        stats: &mut IterInfo<F, N>,
+    ) -> (Option<Self::ReturnMapping>, bool)
+    where
+        I: AlgIterator;
+
+    /// Merge spikes, if possible.
+    ///
+    /// Either optimality condition merging or objective value (fitness) merging
+    /// may be used, the latter only if `fitness` is provided and `config.fitness_merging`
+    /// is set.
+    fn merge_spikes(
+        &self,
+        μ: &mut RNDM<F, N>,
+        τv: &mut PreadjointCodomain,
+        μ_base: &RNDM<F, N>,
+        ν_delta: Option<&RNDM<F, N>>,
+        τ: F,
+        ε: F,
+        config: &FBGenericConfig<F>,
+        reg: &Reg,
+        fitness: Option<impl Fn(&RNDM<F, N>) -> F>,
+    ) -> usize;
+
+    /// Merge spikes, if possible.
+    ///
+    /// Unlike [`Self::merge_spikes`], this variant only supports optimality condition based merging
+    #[inline]
+    fn merge_spikes_no_fitness(
+        &self,
+        μ: &mut RNDM<F, N>,
+        τv: &mut PreadjointCodomain,
+        μ_base: &RNDM<F, N>,
+        ν_delta: Option<&RNDM<F, N>>,
+        τ: F,
+        ε: F,
+        config: &FBGenericConfig<F>,
+        reg: &Reg,
+    ) -> usize {
+        /// This is a hack to create a `None` of same type as a `Some`
+        // for the `impl Fn` parameter of `merge_spikes`.
+        #[inline]
+        fn into_none<T>(_: Option<T>) -> Option<T> {
+            None
+        }
+        self.merge_spikes(
+            μ,
+            τv,
+            μ_base,
+            ν_delta,
+            τ,
+            ε,
+            config,
+            reg,
+            into_none(Some(|_: &RNDM<F, N>| F::ZERO)),
+        )
+    }
+}

mercurial