solver/numeric/finite_difference/pde.rs
1//! FD-specific contract for grid-based HJB solvers.
2//!
3//! [`crate::models::control::ControlProblem`] is the solver-agnostic
4//! stochastic-control contract. It supplies a scalar driver and is consumed by
5//! both the BSDE regression solver and the explicit-Euler finite-difference
6//! step. A scalar driver is not sufficient to assemble the implicit operator
7//!
8//! ```text
9//! (I - dt L) V_new = V_old + dt s
10//! ```
11//!
12//! because `L` carries off-diagonal transport coefficients. This module
13//! defines the contract consumed only by time-dependent grid PDE solvers: the
14//! model supplies the per-dimension transport coefficients and a local source,
15//! and the solver builds the operator from those parts.
16//!
17//! The shared spatial primitives (`DimensionKind`, `Transport`) are defined in
18//! [`super::discretization`].
19
20use super::discretization::{DimensionKind, JumpKernel, Transport};
21use crate::models::control::StateDerivatives;
22
23/// Finite-difference view of a time-dependent stochastic optimal control
24/// problem.
25///
26/// A model that must be solved accurately on a diffusive grid implements this
27/// contract in addition to [`crate::models::control::ControlProblem`]. The
28/// two contracts are kept separate so the generic contract remains
29/// solver-agnostic and usable by the BSDE solver.
30pub trait PdeProblem<const N: usize>: crate::models::control::ControlProblem<N> {
31 /// Discretization kind for `dim`.
32 ///
33 /// # Panics
34 ///
35 /// Implementations should return a valid kind for every `dim < N`.
36 fn dimension_kind(&self, dim: usize) -> DimensionKind;
37
38 /// Transport and local source for the given control and derivatives.
39 ///
40 /// The solver calls this after [`crate::models::control::ControlProblem::optimize`]
41 /// at each backward step and assembles the implicit operator from the
42 /// returned coefficients.
43 fn transport(
44 &self,
45 t: f64,
46 state: &[f64; N],
47 control: &Self::Control,
48 derivs: &StateDerivatives<N>,
49 ) -> Transport<N>;
50
51 /// Jump kernel for arbitrary-amplitude jump dimensions.
52 ///
53 /// The default folds [`Transport::plus`]/[`Transport::minus`] into the unit
54 /// `+1`/`-1` transitions of the `DiscreteJump` convention, so models that
55 /// do not use general jumps are unchanged. Override this only for a
56 /// `DimensionKind::Jump` dimension whose amplitudes differ from one.
57 fn jump_kernel(
58 &self,
59 _t: f64,
60 _state: &[f64; N],
61 _control: &Self::Control,
62 _derivs: &StateDerivatives<N>,
63 transport: &Transport<N>,
64 ) -> JumpKernel<N> {
65 JumpKernel::from_transport(transport)
66 }
67}