Skip to main content

forge_core/
termination.rs

1//! Stopping criteria shared by every optimizer.
2//!
3//! All forge algorithms count objective **evaluations** as their budget unit
4//! (not iterations or generations), so runs are comparable across algorithms
5//! with very different inner loops. An optional target value enables early exit
6//! once a good-enough solution is reached.
7
8use crate::solution::StopReason;
9
10/// When an optimizer should stop.
11#[derive(Debug, Clone, Copy)]
12pub struct Termination {
13    /// Maximum number of objective evaluations.
14    pub max_evaluations: usize,
15    /// Optional target: stop as soon as the minimized objective drops to or
16    /// below this value. `None` disables early exit.
17    pub target: Option<f64>,
18}
19
20impl Termination {
21    /// A pure evaluation budget with no target.
22    pub fn budget(max_evaluations: usize) -> Self {
23        Termination {
24            max_evaluations,
25            target: None,
26        }
27    }
28
29    /// Adds an early-exit target (minimized sense).
30    pub fn with_target(mut self, target: f64) -> Self {
31        self.target = Some(target);
32        self
33    }
34
35    /// True once `best` meets the target, if any.
36    #[inline]
37    pub fn target_met(&self, best: f64) -> bool {
38        matches!(self.target, Some(t) if best <= t)
39    }
40
41    /// The stop reason implied by the current state.
42    #[inline]
43    pub fn reason(&self, evaluations: usize, best: f64) -> Option<StopReason> {
44        if self.target_met(best) {
45            Some(StopReason::TargetReached)
46        } else if evaluations >= self.max_evaluations {
47            Some(StopReason::BudgetExhausted)
48        } else {
49            None
50        }
51    }
52}
53
54impl Default for Termination {
55    fn default() -> Self {
56        Termination::budget(1000)
57    }
58}