use diffsol_la::Matrix;
use crate::{
convergence::Convergence,
error::NlError,
nonlinear_op::{NonLinearOp, NonLinearOpJacobian},
};
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<M: Matrix>: Default {
fn set_problem<C: NonLinearOpJacobian<V = M::V, T = M::T, M = M, C = M::C>>(&mut self, op: &C);
fn is_jacobian_set(&self) -> bool;
fn reset_jacobian<C: NonLinearOpJacobian<V = M::V, T = M::T, M = M, C = M::C>>(
&mut self,
op: &C,
x: &M::V,
);
fn clear_jacobian(&mut self);
fn solve<C: NonLinearOp<V = M::V, T = M::T, M = M>>(
&mut self,
op: &C,
x: &M::V,
error_y: &M::V,
convergence: &mut Convergence<'_, M::V>,
) -> Result<M::V, NlError> {
let mut x = x.clone();
self.solve_in_place(op, &mut x, error_y, convergence)?;
Ok(x)
}
fn solve_in_place<C: NonLinearOp<V = M::V, T = M::T, M = M>>(
&mut self,
op: &C,
x: &mut C::V,
error_y: &C::V,
convergence: &mut Convergence<'_, M::V>,
) -> Result<(), NlError>;
fn solve_linearised_in_place(&self, x: &mut M::V) -> Result<(), NlError>;
}
#[cfg(test)]
pub mod tests {
use super::*;
use crate::{
line_search::NoLineSearch, newton::NewtonNonlinearSolver, nonlinear_op::NonLinearOp,
};
use diffsol_la::{
scale, DenseMatrix, IndexType, MatrixCommon, NalgebraLU, NalgebraMat, Vector,
};
use num_traits::{FromPrimitive, One, Zero};
struct SquareOp<M: DenseMatrix> {
jac: M,
eights: M::V,
ctx: M::C,
}
impl<M: DenseMatrix> SquareOp<M> {
fn new() -> Self {
let jac = M::from_diagonal(&M::V::from_vec(
vec![M::T::from_f64(2.0).unwrap(), M::T::from_f64(2.0).unwrap()],
Default::default(),
));
let ctx = jac.context().clone();
let eights = M::V::from_vec(
vec![M::T::from_f64(8.0).unwrap(), M::T::from_f64(8.0).unwrap()],
ctx.clone(),
);
Self { jac, eights, ctx }
}
}
impl<M: DenseMatrix> NonLinearOp for SquareOp<M> {
type T = M::T;
type V = M::V;
type M = M;
type C = M::C;
fn nstates(&self) -> IndexType {
2
}
fn nout(&self) -> IndexType {
2
}
fn context(&self) -> &Self::C {
&self.ctx
}
fn call_inplace(&self, x: &Self::V, y: &mut Self::V) {
self.jac.gemv(M::T::one(), x, M::T::zero(), y);
y.component_mul_assign(x);
y.axpy(-M::T::one(), &self.eights, M::T::one());
}
}
impl<M: DenseMatrix> NonLinearOpJacobian for SquareOp<M> {
fn jac_mul_inplace(&self, x: &Self::V, v: &Self::V, y: &mut Self::V) {
self.jac
.gemv(M::T::from_f64(2.0).unwrap(), x, M::T::zero(), y);
y.component_mul_assign(v);
}
}
#[test]
fn test_newton_cpu_square() {
type M = NalgebraMat<f64>;
let op = SquareOp::<M>::new();
let ctx = *op.context();
let rtol = 1e-6;
let atol = <M as MatrixCommon>::V::from_vec(vec![1e-6, 1e-6], ctx);
let x0 = <M as MatrixCommon>::V::from_vec(vec![2.1, 2.1], ctx);
let expected = <M as MatrixCommon>::V::from_vec(vec![2.0, 2.0], ctx);
let mut s = NewtonNonlinearSolver::new(NalgebraLU::default(), NoLineSearch);
s.set_problem(&op);
let mut convergence = Convergence::new(rtol, &atol);
s.reset_jacobian(&op, &x0);
let x = s.solve(&op, &x0, &x0, &mut convergence).unwrap();
let tol = x.clone() * scale(rtol) + &atol;
x.assert_eq(&expected, &tol);
}
}