diffsol/op/
nonlinear_op.rs

1use super::Op;
2use crate::{Matrix, Vector};
3use num_traits::{One, Zero};
4
5// NonLinearOp is a trait that defines a nonlinear operator or function `F` that maps an input vector `x` to an output vector `y`, (i.e. `y = F(x, t)`).
6// It extends the [Op] trait with methods for computing the operator and its Jacobian.
7//
8// The operator is defined by the [Self::call_inplace] method, which computes the function `F(x, t)` at a given state and time.
9// The Jacobian is defined by the [Self::jac_mul_inplace] method, which computes the product of the Jacobian with a given vector `J(x, t) * v`.
10pub trait NonLinearOp: Op {
11    /// Compute the operator `F(x, t)` at a given state and time.
12    fn call_inplace(&self, x: &Self::V, t: Self::T, y: &mut Self::V);
13
14    /// Compute the operator `F(x, t)` at a given state and time, and return the result.
15    /// Use `[Self::call_inplace]` to for a non-allocating version.
16    fn call(&self, x: &Self::V, t: Self::T) -> Self::V {
17        let mut y = Self::V::zeros(self.nout(), self.context().clone());
18        self.call_inplace(x, t, &mut y);
19        y
20    }
21}
22
23pub trait NonLinearOpSens: NonLinearOp {
24    /// Compute the product of the gradient of F wrt a parameter vector p with a given vector `J_p(x, t) * v`.
25    /// Note that the vector v is of size nparams() and the result is of size nstates().
26    fn sens_mul_inplace(&self, _x: &Self::V, _t: Self::T, _v: &Self::V, _y: &mut Self::V);
27
28    /// Compute the product of the partial gradient of F wrt a parameter vector p with a given vector `\parial F/\partial p(x, t) * v`, and return the result.
29    /// Use `[Self::sens_mul_inplace]` to for a non-allocating version.
30    fn sens_mul(&self, x: &Self::V, t: Self::T, v: &Self::V) -> Self::V {
31        let mut y = Self::V::zeros(self.nstates(), self.context().clone());
32        self.sens_mul_inplace(x, t, v, &mut y);
33        y
34    }
35
36    /// Compute the gradient of the operator wrt a parameter vector p and store it in the matrix `y`.
37    /// `y` should have been previously initialised using the output of [Self::sens_sparsity].
38    /// The default implementation of this method computes the gradient using [Self::sens_mul_inplace],
39    /// but it can be overriden for more efficient implementations.
40    fn sens_inplace(&self, x: &Self::V, t: Self::T, y: &mut Self::M) {
41        self._default_sens_inplace(x, t, y);
42    }
43
44    /// Default implementation of the gradient computation (this is the default for [Self::sens_inplace]).
45    fn _default_sens_inplace(&self, x: &Self::V, t: Self::T, y: &mut Self::M) {
46        let mut v = Self::V::zeros(self.nparams(), self.context().clone());
47        let mut col = Self::V::zeros(self.nout(), self.context().clone());
48        for j in 0..self.nparams() {
49            v.set_index(j, Self::T::one());
50            self.sens_mul_inplace(x, t, &v, &mut col);
51            y.set_column(j, &col);
52            v.set_index(j, Self::T::zero());
53        }
54    }
55
56    /// Compute the gradient of the operator wrt a parameter vector p and return it.
57    /// See [Self::sens_inplace] for a non-allocating version.
58    fn sens(&self, x: &Self::V, t: Self::T) -> Self::M {
59        let n = self.nstates();
60        let m = self.nparams();
61        let mut y = Self::M::new_from_sparsity(n, m, self.sens_sparsity(), self.context().clone());
62        self.sens_inplace(x, t, &mut y);
63        y
64    }
65    fn sens_sparsity(&self) -> Option<<Self::M as Matrix>::Sparsity> {
66        None
67    }
68}
69pub trait NonLinearOpSensAdjoint: NonLinearOp {
70    /// Compute the product of the negative tramspose of the gradient of F wrt a parameter vector p with a given vector `-J_p(x, t)^T * v`.
71    fn sens_transpose_mul_inplace(&self, _x: &Self::V, _t: Self::T, _v: &Self::V, _y: &mut Self::V);
72
73    /// Compute the negative transpose of the gradient of the operator wrt a parameter vector p and return it.
74    /// See [Self::sens_adjoint_inplace] for a non-allocating version.
75    fn sens_adjoint(&self, x: &Self::V, t: Self::T) -> Self::M {
76        let n = self.nstates();
77        let mut y =
78            Self::M::new_from_sparsity(n, n, self.sens_adjoint_sparsity(), self.context().clone());
79        self.sens_adjoint_inplace(x, t, &mut y);
80        y
81    }
82
83    /// Compute the negative transpose of the gradient of the operator wrt a parameter vector p and store it in the matrix `y`.
84    /// `y` should have been previously initialised using the output of [Self::sens_adjoint_sparsity].
85    /// The default implementation of this method computes the gradient using [Self::sens_transpose_mul_inplace],
86    /// but it can be overriden for more efficient implementations.
87    fn sens_adjoint_inplace(&self, x: &Self::V, t: Self::T, y: &mut Self::M) {
88        self._default_sens_adjoint_inplace(x, t, y);
89    }
90
91    /// Default implementation of the gradient computation (this is the default for [Self::sens_adjoint_inplace]).
92    fn _default_sens_adjoint_inplace(&self, x: &Self::V, t: Self::T, y: &mut Self::M) {
93        let mut v = Self::V::zeros(self.nstates(), self.context().clone());
94        let mut col = Self::V::zeros(self.nout(), self.context().clone());
95        for j in 0..self.nstates() {
96            v.set_index(j, Self::T::one());
97            self.sens_transpose_mul_inplace(x, t, &v, &mut col);
98            y.set_column(j, &col);
99            v.set_index(j, Self::T::zero());
100        }
101    }
102
103    fn sens_adjoint_sparsity(&self) -> Option<<Self::M as Matrix>::Sparsity> {
104        None
105    }
106}
107pub trait NonLinearOpAdjoint: NonLinearOp {
108    /// Compute the product of the transpose of the Jacobian with a given vector `-J(x, t)^T * v`.
109    /// The default implementation fails with a panic, as this method is not implemented by default
110    /// and should be implemented by the user if needed.
111    fn jac_transpose_mul_inplace(&self, _x: &Self::V, _t: Self::T, _v: &Self::V, _y: &mut Self::V);
112
113    /// Compute the Adjoint matrix `-J^T(x, t)` of the operator and store it in the matrix `y`.
114    /// `y` should have been previously initialised using the output of [`Self::adjoint_sparsity`].
115    /// The default implementation of this method computes the Jacobian using [Self::jac_transpose_mul_inplace],
116    /// but it can be overriden for more efficient implementations.
117    fn adjoint_inplace(&self, x: &Self::V, t: Self::T, y: &mut Self::M) {
118        self._default_adjoint_inplace(x, t, y);
119    }
120
121    /// Default implementation of the Adjoint computation (this is the default for [Self::adjoint_inplace]).
122    fn _default_adjoint_inplace(&self, x: &Self::V, t: Self::T, y: &mut Self::M) {
123        let mut v = Self::V::zeros(self.nstates(), self.context().clone());
124        let mut col = Self::V::zeros(self.nout(), self.context().clone());
125        for j in 0..self.nstates() {
126            v.set_index(j, Self::T::one());
127            self.jac_transpose_mul_inplace(x, t, &v, &mut col);
128            y.set_column(j, &col);
129            v.set_index(j, Self::T::zero());
130        }
131    }
132
133    /// Compute the Adjoint matrix `-J^T(x, t)` of the operator and return it.
134    /// See [Self::adjoint_inplace] for a non-allocating version.
135    fn adjoint(&self, x: &Self::V, t: Self::T) -> Self::M {
136        let n = self.nstates();
137        let mut y =
138            Self::M::new_from_sparsity(n, n, self.adjoint_sparsity(), self.context().clone());
139        self.adjoint_inplace(x, t, &mut y);
140        y
141    }
142    /// Return sparsity information (if available)
143    fn adjoint_sparsity(&self) -> Option<<Self::M as Matrix>::Sparsity> {
144        None
145    }
146}
147pub trait NonLinearOpJacobian: NonLinearOp {
148    /// Compute the product of the Jacobian with a given vector `J(x, t) * v`.
149    fn jac_mul_inplace(&self, x: &Self::V, t: Self::T, v: &Self::V, y: &mut Self::V);
150
151    /// Compute the product of the Jacobian with a given vector `J(x, t) * v`, and return the result.
152    /// Use `[Self::jac_mul_inplace]` to for a non-allocating version.
153    fn jac_mul(&self, x: &Self::V, t: Self::T, v: &Self::V) -> Self::V {
154        let mut y = Self::V::zeros(self.nstates(), self.context().clone());
155        self.jac_mul_inplace(x, t, v, &mut y);
156        y
157    }
158
159    /// Compute the Jacobian matrix `J(x, t)` of the operator and return it.
160    /// See [Self::jacobian_inplace] for a non-allocating version.
161    fn jacobian(&self, x: &Self::V, t: Self::T) -> Self::M {
162        let n = self.nstates();
163        let mut y =
164            Self::M::new_from_sparsity(n, n, self.jacobian_sparsity(), self.context().clone());
165        self.jacobian_inplace(x, t, &mut y);
166        y
167    }
168
169    /// Return sparsity information (if available)
170    fn jacobian_sparsity(&self) -> Option<<Self::M as Matrix>::Sparsity> {
171        None
172    }
173
174    /// Compute the Jacobian matrix `J(x, t)` of the operator and store it in the matrix `y`.
175    /// `y` should have been previously initialised using the output of [Self::jacobian_sparsity].
176    /// The default implementation of this method computes the Jacobian using [Self::jac_mul_inplace],
177    /// but it can be overriden for more efficient implementations.
178    fn jacobian_inplace(&self, x: &Self::V, t: Self::T, y: &mut Self::M) {
179        self._default_jacobian_inplace(x, t, y);
180    }
181
182    /// Default implementation of the Jacobian computation (this is the default for [Self::jacobian_inplace]).
183    fn _default_jacobian_inplace(&self, x: &Self::V, t: Self::T, y: &mut Self::M) {
184        let mut v = Self::V::zeros(self.nstates(), self.context().clone());
185        let mut col = Self::V::zeros(self.nout(), self.context().clone());
186        for j in 0..self.nstates() {
187            v.set_index(j, Self::T::one());
188            self.jac_mul_inplace(x, t, &v, &mut col);
189            y.set_column(j, &col);
190            v.set_index(j, Self::T::zero());
191        }
192    }
193}