pub trait IsConverged {
fn is_converged(&self, x_pre: f64, x_cur: f64, f_cur: f64) -> bool;
}
pub struct DeltaX {
epsilon_abs: f64,
}
impl DeltaX {
pub fn new(epsilon_abs: f64) -> DeltaX {
assert!(epsilon_abs > 0.0);
assert!(epsilon_abs.is_finite());
DeltaX { epsilon_abs }
}
}
impl IsConverged for DeltaX {
fn is_converged(&self, x_pre: f64, x_cur: f64, _f_cur: f64) -> bool {
(x_pre - x_cur).abs() < self.epsilon_abs
}
}
pub struct FnResidual {
epsilon_abs: f64,
}
impl FnResidual {
pub fn new(epsilon_abs: f64) -> FnResidual {
assert!(epsilon_abs >= 0.0);
assert!(epsilon_abs.is_finite());
FnResidual { epsilon_abs }
}
}
impl IsConverged for FnResidual {
fn is_converged(&self, _x_pre: f64, _x_cur: f64, f_cur: f64) -> bool {
f_cur.abs() < self.epsilon_abs
}
}
pub struct DualCriteria<'a, C1: 'a + IsConverged, C2: 'a + IsConverged> {
c1: &'a C1,
c2: &'a C2,
}
impl<'a, C1: 'a + IsConverged, C2: 'a + IsConverged> DualCriteria<'a, C1, C2> {
pub fn new(c1: &'a C1, c2: &'a C2) -> DualCriteria<'a, C1, C2> {
DualCriteria { c1, c2 }
}
}
impl<'a, C1: IsConverged, C2: IsConverged> IsConverged for DualCriteria<'a, C1, C2> {
fn is_converged(&self, x_pre: f64, x_cur: f64, f_cur: f64) -> bool {
self.c1.is_converged(x_pre, x_cur, f_cur) && self.c2.is_converged(x_pre, x_cur, f_cur)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::f64;
#[test]
fn test_delta_x_convergence() {
let c = DeltaX::new(1e-9);
let x_0 = 10.2;
assert_eq!(false, c.is_converged(x_0, x_0 + 1e-8, 10.0));
assert_eq!(true, c.is_converged(x_0, x_0 + 5e-10, 10.0));
}
#[test]
#[should_panic]
fn test_delta_x_epsabs_zero() {
let _ = DeltaX::new(0.0);
}
#[test]
#[should_panic]
fn test_delta_x_epsabs_negative() {
let _ = DeltaX::new(-1.0);
}
#[test]
#[should_panic]
fn test_delta_x_epsabs_nan() {
let _ = DeltaX::new(f64::NAN);
}
#[test]
fn test_fn_residual_convergence() {
let c = FnResidual::new(1e-3);
assert_eq!(false, c.is_converged(0.0, 1e-10, 2e-3));
assert_eq!(false, c.is_converged(0.0, 1e-10, -2e-3));
assert_eq!(true, c.is_converged(0.0, 1e-10, 9e-4));
assert_eq!(true, c.is_converged(0.0, 1e-10, -9e-4));
}
#[test]
#[should_panic]
fn test_fn_residual_epsabs_negative() {
let _ = FnResidual::new(-1.0);
}
#[test]
#[should_panic]
fn test_fn_residual_epsabs_nan() {
let _ = FnResidual::new(f64::NAN);
}
#[test]
fn test_dual_convergence() {
let c1 = FnResidual::new(1e-4);
let c2 = DeltaX::new(1e-9);
let c = DualCriteria::new(&c1, &c2);
let x_0 = -3.7;
assert_eq!(false, c.is_converged(x_0, x_0 + 1.0, 113456.987));
assert_eq!(false, c.is_converged(x_0, x_0 + 5e-10, 113456.987));
assert_eq!(false, c.is_converged(x_0, x_0 + 1.0, 0.00008));
assert_eq!(true, c.is_converged(0.0, 1e-10, 0.00008));
}
}