use crate::{
ConstantOp, ConstantOpSens, ConstantOpSensAdjoint, Context, LinearOp, LinearOpTranspose,
Matrix, NonLinearOp, NonLinearOpAdjoint, NonLinearOpSens, NonLinearOpSensAdjoint, Scalar,
Vector,
};
use nonlinear_op::NonLinearOpJacobian;
use serde::Serialize;
pub mod bdf;
pub mod closure;
pub mod closure_no_jac;
pub mod closure_with_adjoint;
pub mod closure_with_sens;
pub mod constant_closure;
pub mod constant_closure_with_adjoint;
pub mod constant_closure_with_sens;
pub mod constant_op;
pub mod init;
pub mod linear_closure;
pub mod linear_closure_with_adjoint;
pub mod linear_op;
pub mod linearise;
pub mod matrix;
pub mod nonlinear_op;
pub mod sdirk;
pub mod stoch;
pub mod unit;
pub trait Op {
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 context(&self) -> &Self::C;
fn nstates(&self) -> usize;
fn nout(&self) -> usize;
fn nparams(&self) -> usize;
fn statistics(&self) -> OpStatistics {
OpStatistics::default()
}
}
pub struct ParameterisedOp<'a, C: Op> {
pub op: &'a C,
pub p: &'a C::V,
}
impl<'a, C: Op> ParameterisedOp<'a, C> {
pub fn new(op: &'a C, p: &'a C::V) -> Self {
Self { op, p }
}
}
pub trait BuilderOp: Op {
fn set_nstates(&mut self, nstates: usize);
fn set_nparams(&mut self, nparams: usize);
fn set_nout(&mut self, nout: usize);
fn calculate_sparsity(&mut self, y0: &Self::V, t0: Self::T, p: &Self::V);
}
impl<C: Op> Op for ParameterisedOp<'_, C> {
type V = C::V;
type T = C::T;
type M = C::M;
type C = C::C;
fn nstates(&self) -> usize {
self.op.nstates()
}
fn nout(&self) -> usize {
self.op.nout()
}
fn nparams(&self) -> usize {
self.op.nparams()
}
fn statistics(&self) -> OpStatistics {
self.op.statistics()
}
fn context(&self) -> &Self::C {
self.op.context()
}
}
#[derive(Default, Clone, Serialize, Debug)]
pub struct OpStatistics {
pub number_of_calls: usize,
pub number_of_jac_muls: usize,
pub number_of_matrix_evals: usize,
pub number_of_jac_adj_muls: usize,
}
impl OpStatistics {
pub fn new() -> Self {
Self {
number_of_jac_muls: 0,
number_of_calls: 0,
number_of_matrix_evals: 0,
number_of_jac_adj_muls: 0,
}
}
pub fn increment_call(&mut self) {
self.number_of_calls += 1;
}
pub fn increment_jac_mul(&mut self) {
self.number_of_jac_muls += 1;
}
pub fn increment_jac_adj_mul(&mut self) {
self.number_of_jac_adj_muls += 1;
}
pub fn increment_matrix(&mut self) {
self.number_of_matrix_evals += 1;
}
}
impl<C: Op> Op for &C {
type T = C::T;
type V = C::V;
type M = C::M;
type C = C::C;
fn nstates(&self) -> usize {
C::nstates(*self)
}
fn nout(&self) -> usize {
C::nout(*self)
}
fn nparams(&self) -> usize {
C::nparams(*self)
}
fn statistics(&self) -> OpStatistics {
C::statistics(*self)
}
fn context(&self) -> &Self::C {
C::context(*self)
}
}
impl<C: Op> Op for &mut C {
type T = C::T;
type V = C::V;
type M = C::M;
type C = C::C;
fn nstates(&self) -> usize {
C::nstates(*self)
}
fn nout(&self) -> usize {
C::nout(*self)
}
fn nparams(&self) -> usize {
C::nparams(*self)
}
fn statistics(&self) -> OpStatistics {
C::statistics(*self)
}
fn context(&self) -> &Self::C {
C::context(*self)
}
}
impl<C: NonLinearOp> NonLinearOp for &C {
fn call_inplace(&self, x: &Self::V, t: Self::T, y: &mut Self::V) {
C::call_inplace(*self, x, t, y)
}
}
impl<C: NonLinearOpJacobian> NonLinearOpJacobian for &C {
fn jac_mul_inplace(&self, x: &Self::V, t: Self::T, v: &Self::V, y: &mut Self::V) {
C::jac_mul_inplace(*self, x, t, v, y)
}
fn jacobian_inplace(&self, x: &Self::V, t: Self::T, y: &mut Self::M) {
C::jacobian_inplace(*self, x, t, y)
}
fn jacobian_sparsity(&self) -> Option<<Self::M as Matrix>::Sparsity> {
C::jacobian_sparsity(*self)
}
}
impl<C: NonLinearOpAdjoint> NonLinearOpAdjoint for &C {
fn adjoint_inplace(&self, x: &Self::V, t: Self::T, y: &mut Self::M) {
C::adjoint_inplace(*self, x, t, y)
}
fn adjoint_sparsity(&self) -> Option<<Self::M as Matrix>::Sparsity> {
C::adjoint_sparsity(*self)
}
fn jac_transpose_mul_inplace(&self, x: &Self::V, t: Self::T, v: &Self::V, y: &mut Self::V) {
C::jac_transpose_mul_inplace(*self, x, t, v, y)
}
}
impl<C: NonLinearOpSens> NonLinearOpSens for &C {
fn sens_mul_inplace(&self, x: &Self::V, t: Self::T, v: &Self::V, y: &mut Self::V) {
C::sens_mul_inplace(*self, x, t, v, y)
}
fn sens_inplace(&self, x: &Self::V, t: Self::T, y: &mut Self::M) {
C::sens_inplace(*self, x, t, y)
}
fn sens_sparsity(&self) -> Option<<Self::M as Matrix>::Sparsity> {
C::sens_sparsity(*self)
}
}
impl<C: NonLinearOpSensAdjoint> NonLinearOpSensAdjoint for &C {
fn sens_transpose_mul_inplace(&self, x: &Self::V, t: Self::T, v: &Self::V, y: &mut Self::V) {
C::sens_transpose_mul_inplace(*self, x, t, v, y)
}
fn sens_adjoint_inplace(&self, x: &Self::V, t: Self::T, y: &mut Self::M) {
C::sens_adjoint_inplace(*self, x, t, y)
}
fn sens_adjoint_sparsity(&self) -> Option<<Self::M as Matrix>::Sparsity> {
C::sens_adjoint_sparsity(*self)
}
}
impl<C: LinearOp> LinearOp for &C {
fn gemv_inplace(&self, x: &Self::V, t: Self::T, beta: Self::T, y: &mut Self::V) {
C::gemv_inplace(*self, x, t, beta, y)
}
fn sparsity(&self) -> Option<<Self::M as Matrix>::Sparsity> {
C::sparsity(*self)
}
fn matrix_inplace(&self, t: Self::T, y: &mut Self::M) {
C::matrix_inplace(*self, t, y)
}
}
impl<C: LinearOpTranspose> LinearOpTranspose for &C {
fn gemv_transpose_inplace(&self, x: &Self::V, t: Self::T, beta: Self::T, y: &mut Self::V) {
C::gemv_transpose_inplace(*self, x, t, beta, y)
}
fn transpose_inplace(&self, t: Self::T, y: &mut Self::M) {
C::transpose_inplace(*self, t, y)
}
fn transpose_sparsity(&self) -> Option<<Self::M as Matrix>::Sparsity> {
C::transpose_sparsity(*self)
}
}
impl<C: ConstantOp> ConstantOp for &C {
fn call_inplace(&self, t: Self::T, y: &mut Self::V) {
C::call_inplace(*self, t, y)
}
}
impl<C: ConstantOpSens> ConstantOpSens for &C {
fn sens_mul_inplace(&self, t: Self::T, v: &Self::V, y: &mut Self::V) {
C::sens_mul_inplace(*self, t, v, y)
}
fn sens_inplace(&self, t: Self::T, y: &mut Self::M) {
C::sens_inplace(*self, t, y)
}
fn sens_sparsity(&self) -> Option<<Self::M as Matrix>::Sparsity> {
C::sens_sparsity(*self)
}
}
impl<C: ConstantOpSensAdjoint> ConstantOpSensAdjoint for &C {
fn sens_transpose_mul_inplace(&self, t: Self::T, v: &Self::V, y: &mut Self::V) {
C::sens_transpose_mul_inplace(*self, t, v, y)
}
fn sens_adjoint_inplace(&self, t: Self::T, y: &mut Self::M) {
C::sens_adjoint_inplace(*self, t, y)
}
fn sens_adjoint_sparsity(&self) -> Option<<Self::M as Matrix>::Sparsity> {
C::sens_adjoint_sparsity(*self)
}
}
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use crate::{
context::nalgebra::NalgebraContext, matrix::dense_nalgebra_serial::NalgebraMat, ConstantOp,
ConstantOpSens, ConstantOpSensAdjoint, LinearOp, LinearOpTranspose, NonLinearOp,
NonLinearOpAdjoint, NonLinearOpJacobian, NonLinearOpSens, NonLinearOpSensAdjoint, Vector,
};
use super::{Op, OpStatistics, ParameterisedOp};
type M = NalgebraMat<f64>;
struct ForwardingOp {
ctx: NalgebraContext,
stats: RefCell<OpStatistics>,
}
impl ForwardingOp {
fn new() -> Self {
Self {
ctx: NalgebraContext,
stats: RefCell::new(OpStatistics::new()),
}
}
}
impl Op for ForwardingOp {
type T = f64;
type V = crate::NalgebraVec<f64>;
type M = M;
type C = NalgebraContext;
fn context(&self) -> &Self::C {
&self.ctx
}
fn nstates(&self) -> usize {
2
}
fn nout(&self) -> usize {
2
}
fn nparams(&self) -> usize {
2
}
fn statistics(&self) -> OpStatistics {
self.stats.borrow().clone()
}
}
impl NonLinearOp for ForwardingOp {
fn call_inplace(&self, x: &Self::V, _t: Self::T, y: &mut Self::V) {
self.stats.borrow_mut().increment_call();
y.copy_from(x);
}
}
impl NonLinearOpJacobian for ForwardingOp {
fn jac_mul_inplace(&self, _x: &Self::V, _t: Self::T, v: &Self::V, y: &mut Self::V) {
self.stats.borrow_mut().increment_jac_mul();
y.copy_from(v);
}
}
impl NonLinearOpAdjoint for ForwardingOp {
fn jac_transpose_mul_inplace(
&self,
_x: &Self::V,
_t: Self::T,
v: &Self::V,
y: &mut Self::V,
) {
self.stats.borrow_mut().increment_jac_adj_mul();
y.copy_from(v);
}
}
impl NonLinearOpSens for ForwardingOp {
fn sens_mul_inplace(&self, _x: &Self::V, _t: Self::T, _v: &Self::V, y: &mut Self::V) {
y.fill(0.0);
}
}
impl NonLinearOpSensAdjoint for ForwardingOp {
fn sens_transpose_mul_inplace(
&self,
_x: &Self::V,
_t: Self::T,
_v: &Self::V,
y: &mut Self::V,
) {
y.fill(0.0);
}
}
impl LinearOp for ForwardingOp {
fn gemv_inplace(&self, x: &Self::V, _t: Self::T, beta: Self::T, y: &mut Self::V) {
self.stats.borrow_mut().increment_call();
y.axpy(1.0, x, beta);
}
}
impl LinearOpTranspose for ForwardingOp {
fn gemv_transpose_inplace(&self, x: &Self::V, _t: Self::T, beta: Self::T, y: &mut Self::V) {
self.stats.borrow_mut().increment_jac_adj_mul();
y.axpy(1.0, x, beta);
}
}
impl ConstantOp for ForwardingOp {
fn call_inplace(&self, _t: Self::T, y: &mut Self::V) {
self.stats.borrow_mut().increment_call();
y.copy_from(&Self::V::from_vec(vec![1.0, 2.0], self.ctx));
}
}
impl ConstantOpSens for ForwardingOp {
fn sens_mul_inplace(&self, _t: Self::T, _v: &Self::V, y: &mut Self::V) {
y.fill(0.0);
}
}
impl ConstantOpSensAdjoint for ForwardingOp {
fn sens_transpose_mul_inplace(&self, _t: Self::T, _v: &Self::V, y: &mut Self::V) {
y.fill(0.0);
}
}
#[test]
fn op_statistics_increment_methods_update_counters() {
let mut stats = OpStatistics::new();
stats.increment_call();
stats.increment_jac_mul();
stats.increment_jac_adj_mul();
stats.increment_matrix();
assert_eq!(stats.number_of_calls, 1);
assert_eq!(stats.number_of_jac_muls, 1);
assert_eq!(stats.number_of_jac_adj_muls, 1);
assert_eq!(stats.number_of_matrix_evals, 1);
}
#[test]
fn parameterised_op_and_reference_forwarding_delegate_to_inner_operator() {
let op = ForwardingOp::new();
let p = crate::NalgebraVec::from_vec(vec![1.0, 2.0], NalgebraContext);
let pop = ParameterisedOp::new(&op, &p);
assert_eq!(pop.nstates(), 2);
assert_eq!(pop.nout(), 2);
assert_eq!(pop.nparams(), 2);
let x = crate::NalgebraVec::from_vec(vec![3.0, 4.0], NalgebraContext);
let mut y = crate::NalgebraVec::zeros(2, NalgebraContext);
NonLinearOp::call_inplace(&&op, &x, 0.0, &mut y);
y.assert_eq_st(&x, 1e-12);
op.jac_mul_inplace(&x, 0.0, &x, &mut y);
y.assert_eq_st(&x, 1e-12);
op.jac_transpose_mul_inplace(&x, 0.0, &x, &mut y);
y.assert_eq_st(&x, 1e-12);
NonLinearOpSens::sens_mul_inplace(&&op, &x, 0.0, &x, &mut y);
y.assert_eq_st(&crate::NalgebraVec::zeros(2, NalgebraContext), 1e-12);
NonLinearOpSensAdjoint::sens_transpose_mul_inplace(&&op, &x, 0.0, &x, &mut y);
y.assert_eq_st(&crate::NalgebraVec::zeros(2, NalgebraContext), 1e-12);
op.gemv_inplace(&x, 0.0, 0.0, &mut y);
y.assert_eq_st(&x, 1e-12);
op.gemv_transpose_inplace(&x, 0.0, 0.0, &mut y);
y.assert_eq_st(&x, 1e-12);
let mut y_const = crate::NalgebraVec::zeros(2, NalgebraContext);
<&ForwardingOp as ConstantOp>::call_inplace(&&op, 0.0, &mut y_const);
y_const.assert_eq_st(
&crate::NalgebraVec::from_vec(vec![1.0, 2.0], NalgebraContext),
1e-12,
);
let op_ref_stats = pop.statistics();
assert!(op_ref_stats.number_of_calls >= 1);
let op_mut = ForwardingOp::new();
assert_eq!(op_mut.nstates(), 2);
assert_eq!(op_mut.nout(), 2);
assert_eq!(op_mut.nparams(), 2);
}
}