use pounce_common::types::Number;
const PROGRESS: Number = 0.99;
#[derive(Debug, Clone)]
pub struct TinyStepDualStall {
pub max_iters: usize,
count: usize,
best: Number,
released: bool,
}
impl TinyStepDualStall {
pub fn new(max_iters: usize) -> Self {
Self {
max_iters,
count: 0,
best: Number::INFINITY,
released: false,
}
}
pub fn not_tiny(&mut self) {
self.count = 0;
self.best = Number::INFINITY;
}
pub fn observe(&mut self, y_settled: bool, acceptable: bool, dual_inf: Number) -> bool {
if y_settled || acceptable || self.max_iters == 0 {
self.not_tiny();
return false;
}
if dual_inf.is_finite() && dual_inf < PROGRESS * self.best {
self.best = dual_inf;
self.count = 0;
} else {
self.count += 1;
}
let stalled = self.count >= self.max_iters;
self.released |= stalled;
stalled
}
pub fn released(&self) -> bool {
self.released
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_dual_that_stops_improving_is_released_after_max_iters() {
for duals in [
vec![1.3e29; 20],
(0..20).map(|k| 1e21 * 10f64.powi(k)).collect(),
] {
let mut s = TinyStepDualStall::new(10);
let fired: Vec<bool> = duals.iter().map(|&d| s.observe(false, false, d)).collect();
assert_eq!(fired.iter().position(|&f| f), Some(10), "{duals:?}");
assert!(s.released());
}
}
#[test]
fn converging_multipliers_are_never_released() {
let mut s = TinyStepDualStall::new(10);
for k in 0..200 {
assert!(!s.observe(false, false, 1e3 * 0.9f64.powi(k)), "step {k}");
}
assert!(!s.released());
}
#[test]
fn progress_restarts_the_count() {
let mut s = TinyStepDualStall::new(10);
for _ in 0..9 {
assert!(!s.observe(false, false, 5.0));
}
assert!(!s.observe(false, false, 1.0)); for _ in 0..9 {
assert!(!s.observe(false, false, 1.0));
}
assert!(s.observe(false, false, 1.0));
}
#[test]
fn a_settled_multiplier_step_or_a_real_step_resets() {
let mut s = TinyStepDualStall::new(3);
s.observe(false, false, 1.0);
s.observe(false, false, 1.0);
assert!(!s.observe(true, false, 1.0));
assert!(!s.observe(false, false, 1.0)); assert!(!s.observe(false, false, 1.0));
s.not_tiny();
assert!(!s.observe(false, false, 1.0));
assert!(!s.observe(false, false, 1.0));
assert!(!s.observe(false, false, 1.0));
assert!(s.observe(false, false, 1.0));
}
#[test]
fn an_acceptable_iterate_is_left_to_its_own_exit() {
let mut s = TinyStepDualStall::new(3);
for _ in 0..100 {
assert!(!s.observe(false, true, 1.3e29));
}
assert!(!s.released());
assert!(!s.observe(false, false, 1.0));
assert!(!s.observe(false, false, 1.0));
assert!(!s.observe(false, true, 1.0));
assert!(!s.observe(false, false, 1.0));
}
#[test]
fn zero_is_upstreams_rule() {
let mut s = TinyStepDualStall::new(0);
for _ in 0..5000 {
assert!(!s.observe(false, false, 1.3e29));
}
assert!(!s.released());
}
#[test]
fn a_non_finite_dual_is_not_progress() {
let mut s = TinyStepDualStall::new(2);
assert!(!s.observe(false, false, Number::NAN));
assert!(s.observe(false, false, Number::INFINITY));
}
}