use diffsol_la::{Context, IndexType, Matrix, Scalar, Vector};
pub trait NonLinearOp {
type T: Scalar;
type V: Vector<T = Self::T, C = Self::C>;
type M: Matrix<T = Self::T, V = Self::V, C = Self::C>;
type C: Context;
fn nstates(&self) -> IndexType;
fn nout(&self) -> IndexType;
fn context(&self) -> &Self::C;
fn call_inplace(&self, x: &Self::V, y: &mut Self::V);
fn call(&self, x: &Self::V) -> Self::V {
let mut y = Self::V::zeros(self.nout(), self.context().clone());
self.call_inplace(x, &mut y);
y
}
}
pub trait NonLinearOpJacobian: NonLinearOp {
fn jac_mul_inplace(&self, x: &Self::V, v: &Self::V, y: &mut Self::V);
fn jac_mul(&self, x: &Self::V, v: &Self::V) -> Self::V {
let mut y = Self::V::zeros(self.nstates(), self.context().clone());
self.jac_mul_inplace(x, v, &mut y);
y
}
fn jacobian(&self, x: &Self::V) -> Self::M {
let n = self.nstates();
let mut y =
Self::M::new_from_sparsity(n, n, self.jacobian_sparsity(), self.context().clone());
self.jacobian_inplace(x, &mut y);
y
}
fn jacobian_inplace(&self, x: &Self::V, y: &mut Self::M) {
self._default_jacobian_inplace(x, y);
}
fn _default_jacobian_inplace(&self, x: &Self::V, y: &mut Self::M) {
use num_traits::{One, Zero};
let mut v = Self::V::zeros(self.nstates(), self.context().clone());
let mut col = Self::V::zeros(self.nout(), self.context().clone());
for j in 0..self.nstates() {
v.set_index(j, Self::T::one());
self.jac_mul_inplace(x, &v, &mut col);
y.set_column(j, &col);
v.set_index(j, Self::T::zero());
}
}
fn jacobian_sparsity(&self) -> Option<<Self::M as Matrix>::Sparsity> {
None
}
}