use crate::{SolverError, SolverResult, VectorType, DEFAULT_ITERMAX, DEFAULT_TOL};
use nalgebra::{allocator::Allocator, ComplexField, DefaultAllocator, Dim, UniformNorm, U1};
use num_traits::{Float, Signed};
use std::marker::PhantomData;
pub struct GaussNewtonFD<T, R, C, F> {
f: F,
h: T,
tolerance: T,
iter_max: usize,
r_phantom: PhantomData<R>,
c_phantom: PhantomData<C>,
}
impl<T, R, C, F> GaussNewtonFD<T, R, C, F>
where
T: Float + ComplexField<RealField = T> + Signed,
R: Dim,
C: Dim,
F: Fn(VectorType<T, C>) -> VectorType<T, R>,
DefaultAllocator: Allocator<C, R>
+ Allocator<C>
+ Allocator<R>
+ Allocator<U1, R>
+ Allocator<C, U1>
+ Allocator<U1, C>
+ Allocator<R, C>
+ Allocator<C, C>,
{
pub fn new(f: F) -> Self {
Self {
f,
h: Float::sqrt(T::epsilon()),
tolerance: T::from(DEFAULT_TOL).unwrap(),
iter_max: DEFAULT_ITERMAX,
r_phantom: PhantomData,
c_phantom: PhantomData,
}
}
pub fn with_tol(&mut self, tol: T) -> &mut Self {
self.tolerance = tol;
self
}
pub fn with_itermax(&mut self, max: usize) -> &mut Self {
self.iter_max = max;
self
}
pub fn with_fd_step_length(&mut self, h: T) -> &mut Self {
self.h = h;
self
}
pub fn solve(&self, mut x0: VectorType<T, C>) -> SolverResult<VectorType<T, C>> {
let mut dv = x0.clone().add_scalar(T::max_value()); let mut iter = 1;
let fx = (self.f)(x0.clone());
let zero = (fx * x0.transpose()).scale(T::zero());
while dv.apply_norm(&UniformNorm) > self.tolerance && iter <= self.iter_max {
let mut j = zero.clone(); let fx = (self.f)(x0.clone());
for i in 0..j.ncols() {
let mut x_h = x0.clone();
x_h[i] += self.h; let df = ((self.f)(x_h) - &fx) / self.h; for k in 0..j.nrows() {
j[(k, i)] = df[k];
}
}
let jt = j.transpose();
if let Some(jjt_inv) = (&jt * j).try_inverse() {
dv = jjt_inv * jt * fx;
x0 -= &dv;
iter += 1;
} else {
return Err(SolverError::BadJacobian);
}
}
if iter >= self.iter_max {
return Err(SolverError::MaxIterReached(iter));
}
Ok(x0)
}
}