use num_traits::{One, Zero};
use crate::core::{Objective, Problem};
use super::StopCriterion;
#[derive(Debug, Clone)]
pub struct ImprovementCriterion<P: Problem> {
last_improvement: usize,
best: P::Value,
max_without_improvement: usize,
current_iter: usize,
}
impl<P: Problem> ImprovementCriterion<P> {
pub fn new(initial: P::Value, max_iters: usize) -> Self {
Self {
best: initial,
last_improvement: 0,
current_iter: 0,
max_without_improvement: max_iters,
}
}
fn improvement_took_too_long(&self) -> bool {
let iterations_without_improvement = self.current_iter() - self.last_improvement;
iterations_without_improvement > self.max_without_improvement
}
fn best_solution_improved(&self, other: P::Value) -> bool {
match P::OBJECTIVE {
Objective::Min => other < self.best,
Objective::Max => other > self.best,
}
}
}
impl<P: Problem> StopCriterion<P> for ImprovementCriterion<P> {
fn progress(&self) -> f64 {
if self.improvement_took_too_long() {
f64::one()
} else {
f64::zero()
}
}
fn update(&mut self, new_value: <P as Problem>::Value) {
self.current_iter += 1;
if self.best_solution_improved(new_value) {
self.best = new_value;
self.last_improvement = self.current_iter();
}
}
fn current_iter(&self) -> usize {
self.current_iter
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let max = 10;
let mut criterion = ImprovementCriterion::<()>::new(0, max);
(0..max).for_each(|_| criterion.update(0));
assert_ne!(criterion.progress(), f64::one());
criterion.update(0);
assert_eq!(criterion.progress(), f64::one());
}
}