use crate::linear_algebra::qr::enorm;
use crate::linear_algebra::{Matrix, Vector};
use crate::numerical_derivative::autodiff::AutoDiffMulti;
use crate::numerical_derivative::derivator::DerivatorMultiVariable;
use crate::numerical_derivative::jacobian::Jacobian;
use crate::root_finding::{RootReportN, RootTermination, all_finite};
use crate::scalar::{Numeric, VectorFn};
use crate::utils::error_codes::CalcError;
const MAX_BACKTRACK: usize = 20;
pub struct NewtonSystem<D: DerivatorMultiVariable = AutoDiffMulti> {
derivator: D,
xtol: D::Scalar,
ftol: D::Scalar,
max_iterations: usize,
backtracking: bool,
}
impl<D: DerivatorMultiVariable + Default> Default for NewtonSystem<D> {
fn default() -> Self {
Self::from_derivator(D::default())
}
}
impl<D: DerivatorMultiVariable> NewtonSystem<D> {
pub fn from_derivator(derivator: D) -> Self {
let tol = D::Scalar::EPSILON * D::Scalar::from_f64(30.0);
NewtonSystem {
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, const N: usize>(
&self,
f: &F,
x0: &[D::Scalar; N],
) -> Result<RootReportN<N, D::Scalar>, CalcError>
where
D: Clone,
F: VectorFn<N, N>,
{
let one = D::Scalar::ONE;
let half = D::Scalar::HALF;
let jacobian = Jacobian::from_derivator(self.derivator.clone());
let mut x = *x0;
let mut r = f.eval(&x);
if !all_finite(&r) {
return Err(CalcError::NonFiniteValue);
}
let mut fnorm = enorm(&r);
for iter in 1..=self.max_iterations {
if fnorm <= self.ftol {
return Ok(RootReportN {
root: x,
residual_norm: fnorm,
iterations: iter,
termination: RootTermination::ResidualTolerance,
});
}
let jac = jacobian.get(f, &x)?;
if jac.iter().any(|row| !all_finite(row)) {
return Err(CalcError::NonFiniteValue);
}
let j: Matrix<N, N, D::Scalar> = Matrix::from_fn(|ri, c| jac[ri][c]);
let neg_r: [D::Scalar; N] = core::array::from_fn(|i| -r[i]);
let step = j.solve(Vector::new(neg_r))?;
let mut alpha = one;
let mut tries = 0usize;
let (x_new, r_new, fnorm_new) = loop {
let candidate: [D::Scalar; N] = core::array::from_fn(|k| x[k] + alpha * step[k]);
let trial = f.eval(&candidate);
let trial_finite = all_finite(&trial);
let trial_fnorm = if trial_finite {
enorm(&trial)
} else {
D::Scalar::INFINITY
};
if !self.backtracking {
if !trial_finite {
return Err(CalcError::NonFiniteValue);
}
break (candidate, trial, trial_fnorm);
}
if (trial_finite && trial_fnorm < fnorm) || tries >= MAX_BACKTRACK {
if !trial_finite {
return Err(CalcError::NonFiniteValue);
}
break (candidate, trial, trial_fnorm);
}
alpha *= half;
tries += 1;
};
let step_norm = alpha * enorm(step.as_array());
let xnorm = enorm(&x_new);
x = x_new;
r = r_new;
fnorm = fnorm_new;
if step_norm <= self.xtol * (one + xnorm) {
return Ok(RootReportN {
root: x,
residual_norm: fnorm,
iterations: iter,
termination: RootTermination::StepTolerance,
});
}
}
Err(CalcError::DidNotConverge)
}
}