src/prox_penalty.rs

Mon, 17 Feb 2025 13:51:50 -0500

author
Tuomo Valkonen <tuomov@iki.fi>
date
Mon, 17 Feb 2025 13:51:50 -0500
branch
dev
changeset 51
0693cc9ba9f0
parent 39
6316d68b58af
permissions
-rw-r--r--

Update documentation references

Also allow Zed to reindent.

/*!
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