use std::{
any::Any,
sync::{Arc, Mutex, MutexGuard},
};
use diffsol::{DefaultSolver, DenseVjpTape, DiffsolError, OdeEquationsImplicit};
use crate::{
error::DiffsolRtError, linear_solver_type::LinearSolverType,
ode_solver_tag::OdeSolverMethodTag, ode_solver_type::OdeSolverType, scalar_type::Scalar,
};
pub(crate) trait VjpCheckpoint: Any + Send {
fn as_any(&self) -> &dyn Any;
fn method(&self) -> OdeSolverType;
fn linear_solver(&self) -> LinearSolverType;
fn nt(&self) -> usize;
}
impl dyn VjpCheckpoint + '_ {
pub(crate) fn data<M, Eqn, Tag>(&self) -> Result<&VjpCheckpointData<M, Eqn, Tag>, DiffsolError>
where
M: diffsol::Matrix<T: Scalar> + DefaultSolver + 'static,
Eqn: OdeEquationsImplicit<M = M, T = M::T, V = M::V, C = M::C> + 'static,
Tag: OdeSolverMethodTag<M, Eqn> + 'static,
{
self.as_any()
.downcast_ref::<VjpCheckpointData<M, Eqn, Tag>>()
.ok_or_else(|| {
DiffsolError::Other(
"VJP checkpoint is incompatible with this ODE solver".to_string(),
)
})
}
}
pub(crate) struct VjpCheckpointData<M, Eqn, Tag>
where
M: diffsol::Matrix<T: Scalar> + DefaultSolver,
Eqn: OdeEquationsImplicit<M = M, T = M::T, V = M::V, C = M::C>,
Tag: OdeSolverMethodTag<M, Eqn>,
{
pub(crate) tape: DenseVjpTape<M::V, Tag::State>,
pub(crate) params: M::V,
method: OdeSolverType,
linear_solver: LinearSolverType,
_eqn: std::marker::PhantomData<fn() -> Eqn>,
}
impl<M, Eqn, Tag> VjpCheckpointData<M, Eqn, Tag>
where
M: diffsol::Matrix<T: Scalar> + DefaultSolver,
Eqn: OdeEquationsImplicit<M = M, T = M::T, V = M::V, C = M::C>,
Tag: OdeSolverMethodTag<M, Eqn>,
{
pub(crate) fn new(
tape: DenseVjpTape<M::V, Tag::State>,
params: M::V,
method: OdeSolverType,
linear_solver: LinearSolverType,
) -> Self {
Self {
tape,
params,
method,
linear_solver,
_eqn: std::marker::PhantomData,
}
}
}
impl<M, Eqn, Tag> VjpCheckpoint for VjpCheckpointData<M, Eqn, Tag>
where
M: diffsol::Matrix<T: Scalar> + DefaultSolver + 'static,
M::V: Send,
Tag::State: Send,
Eqn: OdeEquationsImplicit<M = M, T = M::T, V = M::V, C = M::C> + 'static,
Tag: OdeSolverMethodTag<M, Eqn> + 'static,
{
fn as_any(&self) -> &dyn Any {
self
}
fn method(&self) -> OdeSolverType {
self.method
}
fn linear_solver(&self) -> LinearSolverType {
self.linear_solver
}
fn nt(&self) -> usize {
self.tape.t_eval().len()
}
}
#[derive(Clone)]
pub struct VjpCheckpointWrapper(Arc<Mutex<Box<dyn VjpCheckpoint>>>);
impl VjpCheckpointWrapper {
pub(crate) fn new(checkpoint: Box<dyn VjpCheckpoint>) -> Self {
Self(Arc::new(Mutex::new(checkpoint)))
}
pub(crate) fn guard(&self) -> Result<MutexGuard<'_, Box<dyn VjpCheckpoint>>, DiffsolRtError> {
self.0
.lock()
.map_err(|_| DiffsolError::Other("VJP checkpoint mutex poisoned".to_string()).into())
}
}