use anyhow::Result;
use crate::{op::Op, solver::SolverProblem};
pub struct NonLinearSolveSolution<V> {
pub x0: V,
pub x: V,
}
impl<V> NonLinearSolveSolution<V> {
pub fn new(x0: V, x: V) -> Self {
Self { x0, x }
}
}
pub trait NonLinearSolver<C: Op> {
fn problem(&self) -> &SolverProblem<C>;
fn set_problem(&mut self, problem: &SolverProblem<C>);
fn reset_jacobian(&mut self, x: &C::V, t: C::T);
fn solve(&mut self, x: &C::V, t: C::T) -> Result<C::V> {
let mut x = x.clone();
self.solve_in_place(&mut x, t)?;
Ok(x)
}
fn solve_in_place(&mut self, x: &mut C::V, t: C::T) -> Result<()>;
fn solve_linearised_in_place(&self, x: &mut C::V) -> Result<()>;
fn set_max_iter(&mut self, max_iter: usize);
fn max_iter(&self) -> usize;
fn niter(&self) -> usize;
}
pub mod convergence;
pub mod newton;
pub mod root;
#[cfg(test)]
pub mod tests {
use std::rc::Rc;
use self::newton::NewtonNonlinearSolver;
use crate::{
linear_solver::nalgebra::lu::LU,
matrix::MatrixCommon,
op::{closure::Closure, NonLinearOp},
scale, DenseMatrix, Vector,
};
use super::*;
use num_traits::{One, Zero};
pub fn get_square_problem<M>() -> (
SolverProblem<impl NonLinearOp<M = M, V = M::V, T = M::T>>,
Vec<NonLinearSolveSolution<M::V>>,
)
where
M: DenseMatrix + 'static,
{
let jac1 = M::from_diagonal(&M::V::from_vec(vec![2.0.into(), 2.0.into()]));
let jac2 = jac1.clone();
let p = Rc::new(M::V::zeros(0));
let op = Closure::new(
move |x: &<M as MatrixCommon>::V, _p: &<M as MatrixCommon>::V, _t, y| {
jac1.gemv(M::T::one(), x, M::T::zero(), y); y.component_mul_assign(x);
y.add_scalar_mut(M::T::from(-8.0));
},
move |x: &<M as MatrixCommon>::V, _p: &<M as MatrixCommon>::V, _t, v, y| {
jac2.gemv(M::T::from(2.0), x, M::T::zero(), y); y.component_mul_assign(v);
},
2,
2,
p,
);
let rtol = M::T::from(1e-6);
let atol = M::V::from_vec(vec![1e-6.into(), 1e-6.into()]);
let problem = SolverProblem::new(Rc::new(op), Rc::new(atol), rtol);
let solns = vec![NonLinearSolveSolution::new(
M::V::from_vec(vec![2.1.into(), 2.1.into()]),
M::V::from_vec(vec![2.0.into(), 2.0.into()]),
)];
(problem, solns)
}
pub fn test_nonlinear_solver<C>(
mut solver: impl NonLinearSolver<C>,
problem: SolverProblem<C>,
solns: Vec<NonLinearSolveSolution<C::V>>,
) where
C: NonLinearOp,
{
solver.set_problem(&problem);
let t = C::T::zero();
for soln in solns {
let x = solver.solve(&soln.x0, t).unwrap();
let tol = x.clone() * scale(problem.rtol) + problem.atol.as_ref();
x.assert_eq(&soln.x, &tol);
}
}
type MCpu = nalgebra::DMatrix<f64>;
#[test]
fn test_newton_cpu_square() {
let lu = LU::default();
let (prob, soln) = get_square_problem::<MCpu>();
let s = NewtonNonlinearSolver::new(lu);
test_nonlinear_solver(s, prob, soln);
}
}