--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tolerance.rs Thu Dec 01 23:07:35 2022 +0200 @@ -0,0 +1,89 @@ +//! 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 + } +}