pub const MADSEN_DAMPING_CAP: f64 = 1e12;
pub const PLATEAU_DEFAULT_WINDOW: usize = 3;
#[inline]
pub fn madsen_can_retry(damping: f64) -> bool {
damping.is_finite() && damping < MADSEN_DAMPING_CAP
}
#[inline]
pub fn madsen_retry_exhausted(damping: f64, attempts: usize, max_attempts: usize) -> bool {
attempts >= max_attempts || !damping.is_finite() || damping > MADSEN_DAMPING_CAP
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LoopVerdict {
Continue,
Plateaued,
Exhausted,
}
#[derive(Clone, Debug)]
pub struct FlatStreak {
window: usize,
streak: usize,
}
impl FlatStreak {
pub fn new(window: usize) -> Self {
Self {
window: window.max(1),
streak: 0,
}
}
pub fn note(&mut self, flat: bool) -> LoopVerdict {
if flat {
self.streak += 1;
if self.streak >= self.window {
return LoopVerdict::Plateaued;
}
} else {
self.streak = 0;
}
LoopVerdict::Continue
}
pub fn reset(&mut self) {
self.streak = 0;
}
pub fn streak(&self) -> usize {
self.streak
}
}
#[derive(Clone, Debug)]
pub struct IterationBound {
used: usize,
max: usize,
}
impl IterationBound {
pub fn new(max: usize) -> Self {
Self {
used: 0,
max: max.max(1),
}
}
pub fn tick(&mut self) {
self.used += 1;
}
pub fn used(&self) -> usize {
self.used
}
pub fn max(&self) -> usize {
self.max
}
pub fn count_exhausted(&self) -> bool {
self.used >= self.max
}
pub fn exhausted_at(&self, damping: f64) -> bool {
madsen_retry_exhausted(damping, self.used, self.max)
}
pub fn verdict_at(&self, damping: f64) -> LoopVerdict {
if self.exhausted_at(damping) {
LoopVerdict::Exhausted
} else {
LoopVerdict::Continue
}
}
}
pub const MADSEN_INITIAL_REJECT_FACTOR: f64 = 2.0;
#[derive(Clone, Debug)]
pub struct RejectEscalator {
factor: f64,
rejects: usize,
}
impl Default for RejectEscalator {
fn default() -> Self {
Self::new()
}
}
impl RejectEscalator {
pub fn new() -> Self {
Self {
factor: MADSEN_INITIAL_REJECT_FACTOR,
rejects: 0,
}
}
pub fn escalate(&mut self, damping: &mut f64) {
*damping *= self.factor;
self.factor *= 2.0;
self.rejects += 1;
}
pub fn restart(&mut self) {
self.factor = MADSEN_INITIAL_REJECT_FACTOR;
self.rejects = 0;
}
pub fn rejects(&self) -> usize {
self.rejects
}
}
#[inline]
pub fn inner_convergence_is_truthful(converged: bool, min_certified_residual: f64) -> bool {
!converged || min_certified_residual.is_finite()
}
#[inline]
pub fn slow_geometric_rate_exceeds_projection_cap(
current: f64,
window_oldest: f64,
window_cycles: usize,
residual_tol: f64,
projection_cap: usize,
) -> bool {
if window_cycles == 0 {
return false;
}
if !current.is_finite() || current <= residual_tol {
return false;
}
if !window_oldest.is_finite() || window_oldest <= 0.0 || current >= window_oldest {
return true;
}
let rate = (current / window_oldest).powf(1.0 / (window_cycles as f64));
if !rate.is_finite() || rate >= 1.0 {
return true;
}
let projected_cycles = (residual_tol / current).ln() / rate.ln();
projected_cycles.is_finite() && projected_cycles > projection_cap as f64
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reject_storm_exhausts_in_bounded_steps() {
let mut esc = RejectEscalator::new();
let mut damping = 1.0;
let mut steps = 0usize;
while madsen_can_retry(damping) {
esc.escalate(&mut damping);
steps += 1;
assert!(steps <= 64, "escalation must reach the damping cap");
}
assert!(steps <= 16, "escalation took {steps} steps");
assert_eq!(esc.rejects(), steps);
assert!(madsen_retry_exhausted(damping, 0, usize::MAX));
}
#[test]
fn continue_paths_without_rejects_still_exhaust_the_bound() {
let mut bound = IterationBound::new(5);
let esc = RejectEscalator::new();
let damping = 1e-6; let mut passes = 0usize;
while !bound.exhausted_at(damping) {
bound.tick();
passes += 1;
assert!(passes <= 5, "bound must stop a reject-free spin");
}
assert_eq!(passes, 5);
assert_eq!(esc.rejects(), 0, "no reject was ever recorded");
assert_eq!(bound.verdict_at(damping), LoopVerdict::Exhausted);
}
#[test]
fn escalations_do_not_double_count_iterations() {
let mut bound = IterationBound::new(10);
let mut esc = RejectEscalator::new();
let mut damping = 1.0;
bound.tick();
for _ in 0..3 {
esc.escalate(&mut damping);
}
assert_eq!(bound.used(), 1);
assert_eq!(esc.rejects(), 3);
assert!(!bound.count_exhausted());
}
#[test]
fn restart_rewinds_the_geometric_schedule() {
let mut esc = RejectEscalator::new();
let mut damping = 1.0;
esc.escalate(&mut damping); esc.escalate(&mut damping); assert_eq!(damping, 8.0);
esc.restart();
assert_eq!(esc.rejects(), 0);
let mut fresh = 1.0;
esc.escalate(&mut fresh);
assert_eq!(
fresh, MADSEN_INITIAL_REJECT_FACTOR,
"schedule restarts at ×2"
);
}
#[test]
fn flat_streak_pins_the_window_discipline() {
let mut streak = FlatStreak::new(3);
assert_eq!(streak.note(true), LoopVerdict::Continue); assert_eq!(streak.note(true), LoopVerdict::Continue); assert_eq!(streak.note(false), LoopVerdict::Continue); assert_eq!(streak.streak(), 0);
assert_eq!(streak.note(true), LoopVerdict::Continue); assert_eq!(streak.note(true), LoopVerdict::Continue); assert_eq!(streak.note(true), LoopVerdict::Plateaued); assert_eq!(streak.note(true), LoopVerdict::Plateaued); assert_eq!(streak.streak(), 4);
}
#[test]
fn inner_convergence_truthfulness_rejects_converged_with_nonfinite_residual() {
assert!(inner_convergence_is_truthful(true, 8.0e-6));
assert!(inner_convergence_is_truthful(true, 0.0));
assert!(!inner_convergence_is_truthful(true, f64::INFINITY));
assert!(!inner_convergence_is_truthful(true, f64::NAN));
assert!(inner_convergence_is_truthful(false, f64::INFINITY));
assert!(inner_convergence_is_truthful(false, 1.0e-3));
}
#[test]
fn policy_predicates_pin_the_reweight_semantics() {
assert!(madsen_can_retry(1e11));
assert!(!madsen_can_retry(MADSEN_DAMPING_CAP));
assert!(!madsen_can_retry(f64::INFINITY));
assert!(madsen_retry_exhausted(1.0, 5, 5));
assert!(madsen_retry_exhausted(f64::NAN, 0, 5));
assert!(madsen_retry_exhausted(1e13, 0, 5));
assert!(!madsen_retry_exhausted(1.0, 4, 5));
}
#[test]
fn slow_geometric_stall_guard_terminates_in_bounded_cycles_979() {
const LINEAR_RATE_WINDOW: usize = 16;
const LINEAR_RATE_PROJECTION_CAP: usize = 100;
const RESIDUAL_STALL_MIN_CYCLES: usize = 40;
const INNER_BUDGET: usize = 1000;
let residual_tol = 1e-6_f64;
fn run_stream(
per_cycle_rate: f64,
residual_tol: f64,
window: usize,
min_cycles: usize,
cap: usize,
budget: usize,
) -> (Option<usize>, bool) {
let mut history: std::collections::VecDeque<f64> =
std::collections::VecDeque::with_capacity(window + 1);
let mut residual = 1.0_f64; let mut reached_tol = false;
for cycle in 0..budget {
if residual <= residual_tol {
reached_tol = true;
return (None, reached_tol);
}
if history.len() > window {
history.pop_front();
}
history.push_back(residual);
if cycle + 1 >= min_cycles && history.len() > window {
let oldest = *history.front().unwrap();
if slow_geometric_rate_exceeds_projection_cap(
residual,
oldest,
window,
residual_tol,
cap,
) {
return (Some(cycle + 1), reached_tol);
}
}
residual *= per_cycle_rate;
}
(None, reached_tol)
}
let (slow_exit, slow_reached) = run_stream(
0.99,
residual_tol,
LINEAR_RATE_WINDOW,
RESIDUAL_STALL_MIN_CYCLES,
LINEAR_RATE_PROJECTION_CAP,
INNER_BUDGET,
);
assert!(
!slow_reached,
"the slow-geometric stream must not reach tol within budget (it is the hang)"
);
let slow_exit =
slow_exit.expect("slow-geometric stall guard must fire, not grind to the budget");
assert!(
slow_exit < INNER_BUDGET / 4,
"guard must terminate well below the {INNER_BUDGET}-cycle budget, fired at {slow_exit}"
);
assert!(
slow_exit >= RESIDUAL_STALL_MIN_CYCLES,
"guard must not fire before it is armed (MIN_CYCLES={RESIDUAL_STALL_MIN_CYCLES})"
);
let (fast_exit, fast_reached) = run_stream(
0.3,
residual_tol,
LINEAR_RATE_WINDOW,
RESIDUAL_STALL_MIN_CYCLES,
LINEAR_RATE_PROJECTION_CAP,
INNER_BUDGET,
);
assert!(
fast_reached,
"a fast-geometric solve must reach tol (healthy convergence)"
);
assert!(
fast_exit.is_none(),
"the slow-rate guard must NEVER fire on a healthy fast-geometric solve"
);
assert!(slow_geometric_rate_exceeds_projection_cap(
1.0,
1.0,
LINEAR_RATE_WINDOW,
residual_tol,
LINEAR_RATE_PROJECTION_CAP
));
assert!(!slow_geometric_rate_exceeds_projection_cap(
1e-7,
1.0,
LINEAR_RATE_WINDOW,
residual_tol,
LINEAR_RATE_PROJECTION_CAP
));
let brisk_oldest = 1e-3 / 0.5_f64.powi(LINEAR_RATE_WINDOW as i32);
assert!(!slow_geometric_rate_exceeds_projection_cap(
1e-3,
brisk_oldest,
LINEAR_RATE_WINDOW,
residual_tol,
LINEAR_RATE_PROJECTION_CAP
));
}
}