|
1 //! Tolerance update schemes for subproblem solution quality |
|
2 use serde::{Serialize, Deserialize}; |
|
3 use numeric_literals::replace_float_literals; |
|
4 use crate::types::*; |
|
5 |
|
6 /// Update style for optimality system solution tolerance |
|
7 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] |
|
8 #[allow(dead_code)] |
|
9 pub enum Tolerance<F : Float> { |
|
10 /// $ε_k = εθ^k$ for the `factor` $θ$ and initial tolerance $ε$. |
|
11 Exponential{ factor : F, initial : F }, |
|
12 /// $ε_k = ε/(1+θk)^p$ for the `factor` $θ$, `exponent` $p$, and initial tolerance $ε$. |
|
13 Power{ factor : F, exponent : F, initial : F}, |
|
14 /// $ε_k = εθ^{⌊k^p⌋}$ for the `factor` $θ$, initial tolerance $ε$, and exponent $p$. |
|
15 SlowExp{ factor : F, exponent : F, initial : F } |
|
16 } |
|
17 |
|
18 #[replace_float_literals(F::cast_from(literal))] |
|
19 impl<F : Float> Default for Tolerance<F> { |
|
20 fn default() -> Self { |
|
21 Tolerance::Power { |
|
22 initial : 0.5, |
|
23 factor : 0.2, |
|
24 exponent : 1.4 // 1.5 works but is already slower in practise on our examples. |
|
25 } |
|
26 } |
|
27 } |
|
28 |
|
29 #[replace_float_literals(F::cast_from(literal))] |
|
30 impl<F : Float> Tolerance<F> { |
|
31 /// Get the initial tolerance |
|
32 pub fn initial(&self) -> F { |
|
33 match self { |
|
34 &Tolerance::Exponential { initial, .. } => initial, |
|
35 &Tolerance::Power { initial, .. } => initial, |
|
36 &Tolerance::SlowExp { initial, .. } => initial, |
|
37 } |
|
38 } |
|
39 |
|
40 /// Get mutable reference to the initial tolerance |
|
41 fn initial_mut(&mut self) -> &mut F { |
|
42 match self { |
|
43 Tolerance::Exponential { ref mut initial, .. } => initial, |
|
44 Tolerance::Power { ref mut initial, .. } => initial, |
|
45 Tolerance::SlowExp { ref mut initial, .. } => initial, |
|
46 } |
|
47 } |
|
48 |
|
49 /// Set the initial tolerance |
|
50 pub fn set_initial(&mut self, set : F) { |
|
51 *self.initial_mut() = set; |
|
52 } |
|
53 |
|
54 /// Update `tolerance` for iteration `iter`. |
|
55 /// `tolerance` may or may not be used depending on the specific |
|
56 /// update scheme. |
|
57 pub fn update(&self, tolerance : F, iter : usize) -> F { |
|
58 match self { |
|
59 &Tolerance::Exponential { factor, .. } => { |
|
60 tolerance * factor |
|
61 }, |
|
62 &Tolerance::Power { factor, exponent, initial } => { |
|
63 initial /(1.0 + factor * F::cast_from(iter)).powf(exponent) |
|
64 }, |
|
65 &Tolerance::SlowExp { factor, exponent, initial } => { |
|
66 // let m = (speed |
|
67 // * factor.powi(-(iter as i32)) |
|
68 // * F::cast_from(iter).powf(-exponent) |
|
69 // ).floor().as_(); |
|
70 let m = F::cast_from(iter).powf(exponent).floor().as_(); |
|
71 initial * factor.powi(m) |
|
72 }, |
|
73 } |
|
74 } |
|
75 } |
|
76 |
|
77 impl<F: Float> std::ops::MulAssign<F> for Tolerance<F> { |
|
78 fn mul_assign(&mut self, factor : F) { |
|
79 *self.initial_mut() *= factor; |
|
80 } |
|
81 } |
|
82 |
|
83 impl<F: Float> std::ops::Mul<F> for Tolerance<F> { |
|
84 type Output = Tolerance<F>; |
|
85 fn mul(mut self, factor : F) -> Self::Output { |
|
86 *self.initial_mut() *= factor; |
|
87 self |
|
88 } |
|
89 } |