use crate::numerical_derivative::autodiff::AutoDiffSingle;
use crate::numerical_derivative::derivator::DerivatorSingleVariable;
use crate::root_finding::{RootReport, RootTermination};
use crate::scalar::{Numeric, ScalarFn};
use crate::utils::error_codes::CalcError;
const MAX_BACKTRACK: usize = 20;
pub struct Newton<D: DerivatorSingleVariable = AutoDiffSingle> {
derivator: D,
xtol: D::Scalar,
ftol: D::Scalar,
max_iterations: usize,
backtracking: bool,
}
impl<D: DerivatorSingleVariable + Default> Default for Newton<D> {
fn default() -> Self {
Self::from_derivator(D::default())
}
}
impl<D: DerivatorSingleVariable> Newton<D> {
pub fn from_derivator(derivator: D) -> Self {
let tol = D::Scalar::EPSILON * D::Scalar::from_f64(30.0);
Newton {
derivator,
xtol: tol,
ftol: tol,
max_iterations: 100,
backtracking: false,
}
}
#[must_use]
pub fn with_xtol(mut self, xtol: D::Scalar) -> Self {
self.xtol = xtol;
self
}
#[must_use]
pub fn with_ftol(mut self, ftol: D::Scalar) -> Self {
self.ftol = ftol;
self
}
#[must_use]
pub fn with_max_iterations(mut self, max_iterations: usize) -> Self {
self.max_iterations = max_iterations;
self
}
#[must_use]
pub fn with_backtracking(mut self, backtracking: bool) -> Self {
self.backtracking = backtracking;
self
}
pub fn solve<F: ScalarFn>(
&self,
f: &F,
x0: D::Scalar,
) -> Result<RootReport<D::Scalar>, CalcError> {
let one = D::Scalar::ONE;
let half = D::Scalar::HALF;
let mut x = x0;
let mut fx = f.eval(x);
if !fx.is_finite() {
return Err(CalcError::NonFiniteValue);
}
for iter in 1..=self.max_iterations {
if fx.abs() <= self.ftol {
return Ok(RootReport {
root: x,
residual: fx,
iterations: iter,
termination: RootTermination::ResidualTolerance,
});
}
let dfx = self.derivator.get_single(f, x)?;
if !dfx.is_finite() {
return Err(CalcError::NonFiniteValue);
}
if dfx == D::Scalar::ZERO {
return Err(CalcError::SingularMatrix);
}
let step = fx / dfx;
let mut alpha = one;
let mut tries = 0usize;
let (x_new, fx_new) = loop {
let candidate = x - alpha * step;
let trial = f.eval(candidate);
if !self.backtracking {
if !trial.is_finite() {
return Err(CalcError::NonFiniteValue);
}
break (candidate, trial);
}
if (trial.is_finite() && trial.abs() < fx.abs()) || tries >= MAX_BACKTRACK {
if !trial.is_finite() {
return Err(CalcError::NonFiniteValue);
}
break (candidate, trial);
}
alpha *= half;
tries += 1;
};
let step_taken = (x_new - x).abs();
x = x_new;
fx = fx_new;
if step_taken <= self.xtol * (one + x.abs()) {
return Ok(RootReport {
root: x,
residual: fx,
iterations: iter,
termination: RootTermination::StepTolerance,
});
}
}
Err(CalcError::DidNotConverge)
}
}