Sun, 11 Dec 2022 23:25:53 +0200
Support arbitrary regularisation terms; implement non-positivity-constrained regularisation.
* Fixes the conditional gradient methods that were incorrectly solving
positivity constrained subproblems although the infinite-dimensional
versions do not include such constraints.
* Introduces the `ExperimentV2` struct that has `regularisation` in place
of `α`. The `Experiment` struct is now deprecated.
* The L^2-squared experiments were switch to be unconstrained, as the
Franke-Wolfe implementations do not support constraints. (This would
be easy to add for the “fully corrective” variant, but is not
immediate for the “relaxed” variant.)
//! Tolerance update schemes for subproblem solution quality use serde::{Serialize, Deserialize}; use numeric_literals::replace_float_literals; use crate::types::*; /// Update style for optimality system solution tolerance #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] #[allow(dead_code)] pub enum Tolerance<F : Float> { /// $ε_k = εθ^k$ for the `factor` $θ$ and initial tolerance $ε$. Exponential{ factor : F, initial : F }, /// $ε_k = ε/(1+θk)^p$ for the `factor` $θ$, `exponent` $p$, and initial tolerance $ε$. Power{ factor : F, exponent : F, initial : F}, /// $ε_k = εθ^{⌊k^p⌋}$ for the `factor` $θ$, initial tolerance $ε$, and exponent $p$. SlowExp{ factor : F, exponent : F, initial : F } } #[replace_float_literals(F::cast_from(literal))] impl<F : Float> Default for Tolerance<F> { fn default() -> Self { Tolerance::Power { initial : 0.5, factor : 0.2, exponent : 1.4 // 1.5 works but is already slower in practise on our examples. } } } #[replace_float_literals(F::cast_from(literal))] impl<F : Float> Tolerance<F> { /// Get the initial tolerance pub fn initial(&self) -> F { match self { &Tolerance::Exponential { initial, .. } => initial, &Tolerance::Power { initial, .. } => initial, &Tolerance::SlowExp { initial, .. } => initial, } } /// Get mutable reference to the initial tolerance fn initial_mut(&mut self) -> &mut F { match self { Tolerance::Exponential { ref mut initial, .. } => initial, Tolerance::Power { ref mut initial, .. } => initial, Tolerance::SlowExp { ref mut initial, .. } => initial, } } /// Set the initial tolerance pub fn set_initial(&mut self, set : F) { *self.initial_mut() = set; } /// Update `tolerance` for iteration `iter`. /// `tolerance` may or may not be used depending on the specific /// update scheme. pub fn update(&self, tolerance : F, iter : usize) -> F { match self { &Tolerance::Exponential { factor, .. } => { tolerance * factor }, &Tolerance::Power { factor, exponent, initial } => { initial /(1.0 + factor * F::cast_from(iter)).powf(exponent) }, &Tolerance::SlowExp { factor, exponent, initial } => { // let m = (speed // * factor.powi(-(iter as i32)) // * F::cast_from(iter).powf(-exponent) // ).floor().as_(); let m = F::cast_from(iter).powf(exponent).floor().as_(); initial * factor.powi(m) }, } } } impl<F: Float> std::ops::MulAssign<F> for Tolerance<F> { fn mul_assign(&mut self, factor : F) { *self.initial_mut() *= factor; } } impl<F: Float> std::ops::Mul<F> for Tolerance<F> { type Output = Tolerance<F>; fn mul(mut self, factor : F) -> Self::Output { *self.initial_mut() *= factor; self } }