1use diffsol_la::Matrix;
2
3use crate::{
4 convergence::Convergence,
5 error::NlError,
6 nonlinear_op::{NonLinearOp, NonLinearOpJacobian},
7};
8
9pub struct NonLinearSolveSolution<V> {
10 pub x0: V,
11 pub x: V,
12}
13
14impl<V> NonLinearSolveSolution<V> {
15 pub fn new(x0: V, x: V) -> Self {
16 Self { x0, x }
17 }
18}
19
20pub trait NonLinearSolver<M: Matrix>: Default {
22 fn set_problem<C: NonLinearOpJacobian<V = M::V, T = M::T, M = M, C = M::C>>(&mut self, op: &C);
24
25 fn is_jacobian_set(&self) -> bool;
26
27 fn reset_jacobian<C: NonLinearOpJacobian<V = M::V, T = M::T, M = M, C = M::C>>(
29 &mut self,
30 op: &C,
31 x: &M::V,
32 );
33
34 fn clear_jacobian(&mut self);
36
37 fn solve<C: NonLinearOp<V = M::V, T = M::T, M = M>>(
39 &mut self,
40 op: &C,
41 x: &M::V,
42 error_y: &M::V,
43 convergence: &mut Convergence<'_, M::V>,
44 ) -> Result<M::V, NlError> {
45 let mut x = x.clone();
46 self.solve_in_place(op, &mut x, error_y, convergence)?;
47 Ok(x)
48 }
49
50 fn solve_in_place<C: NonLinearOp<V = M::V, T = M::T, M = M>>(
52 &mut self,
53 op: &C,
54 x: &mut C::V,
55 error_y: &C::V,
56 convergence: &mut Convergence<'_, M::V>,
57 ) -> Result<(), NlError>;
58
59 fn solve_linearised_in_place(&self, x: &mut M::V) -> Result<(), NlError>;
62}
63
64#[cfg(test)]
65pub mod tests {
66 use super::*;
67 use crate::{
68 line_search::NoLineSearch, newton::NewtonNonlinearSolver, nonlinear_op::NonLinearOp,
69 };
70 use diffsol_la::{
71 scale, DenseMatrix, IndexType, MatrixCommon, NalgebraLU, NalgebraMat, Vector,
72 };
73 use num_traits::{FromPrimitive, One, Zero};
74
75 struct SquareOp<M: DenseMatrix> {
77 jac: M,
78 eights: M::V,
79 ctx: M::C,
80 }
81
82 impl<M: DenseMatrix> SquareOp<M> {
83 fn new() -> Self {
84 let jac = M::from_diagonal(&M::V::from_vec(
85 vec![M::T::from_f64(2.0).unwrap(), M::T::from_f64(2.0).unwrap()],
86 Default::default(),
87 ));
88 let ctx = jac.context().clone();
89 let eights = M::V::from_vec(
90 vec![M::T::from_f64(8.0).unwrap(), M::T::from_f64(8.0).unwrap()],
91 ctx.clone(),
92 );
93 Self { jac, eights, ctx }
94 }
95 }
96
97 impl<M: DenseMatrix> NonLinearOp for SquareOp<M> {
98 type T = M::T;
99 type V = M::V;
100 type M = M;
101 type C = M::C;
102
103 fn nstates(&self) -> IndexType {
104 2
105 }
106 fn nout(&self) -> IndexType {
107 2
108 }
109 fn context(&self) -> &Self::C {
110 &self.ctx
111 }
112 fn call_inplace(&self, x: &Self::V, y: &mut Self::V) {
113 self.jac.gemv(M::T::one(), x, M::T::zero(), y);
115 y.component_mul_assign(x);
116 y.axpy(-M::T::one(), &self.eights, M::T::one());
117 }
118 }
119
120 impl<M: DenseMatrix> NonLinearOpJacobian for SquareOp<M> {
121 fn jac_mul_inplace(&self, x: &Self::V, v: &Self::V, y: &mut Self::V) {
122 self.jac
124 .gemv(M::T::from_f64(2.0).unwrap(), x, M::T::zero(), y);
125 y.component_mul_assign(v);
126 }
127 }
128
129 #[test]
130 fn test_newton_cpu_square() {
131 type M = NalgebraMat<f64>;
132 let op = SquareOp::<M>::new();
133 let ctx = *op.context();
134 let rtol = 1e-6;
135 let atol = <M as MatrixCommon>::V::from_vec(vec![1e-6, 1e-6], ctx);
136 let x0 = <M as MatrixCommon>::V::from_vec(vec![2.1, 2.1], ctx);
137 let expected = <M as MatrixCommon>::V::from_vec(vec![2.0, 2.0], ctx);
138
139 let mut s = NewtonNonlinearSolver::new(NalgebraLU::default(), NoLineSearch);
140 s.set_problem(&op);
141 let mut convergence = Convergence::new(rtol, &atol);
142 s.reset_jacobian(&op, &x0);
143 let x = s.solve(&op, &x0, &x0, &mut convergence).unwrap();
144 let tol = x.clone() * scale(rtol) + &atol;
145 x.assert_eq(&expected, &tol);
146 }
147}