Skip to content

DE Type Comparison

Numra supports seven types of differential equations, each designed for a different class of dynamical system. This chapter provides a unified comparison to help you choose the right equation type and solver for your problem.

TypeCrateEquation FormMemoryRandomness
ODEnumra-odey(t)=f(t,y)y'(t) = f(t, y)NoneNone
SDEnumra-sdedX=fdt+gdWdX = f\,dt + g\,dWNoneWiener process
DDEnumra-ddey(t)=f(t,y(t),y(tτ))y'(t) = f(t, y(t), y(t-\tau))Finite (discrete delays)None
FDEnumra-fdeCDαy=f(t,y){}^C D^\alpha y = f(t, y)Infinite (power-law kernel)None
IDEnumra-idey=f(t,y)+0tK(t,s,y)dsy' = f(t,y) + \int_0^t K(t,s,y)\,dsInfinite (general kernel)None
PDEnumra-pdeu/t=L[u]\partial u/\partial t = \mathcal{L}[u]NoneNone
SPDEnumra-spdeu/t=L[u]+σξ\partial u/\partial t = \mathcal{L}[u] + \sigma\,\xiNoneSpace-time noise
TypeTimeSpaceStochastic
ODEtt
SDEttW(t)W(t)
DDEtt
FDEtt
IDEtt
PDEttxx (1D, 2D, 3D)
SPDEttxx (1D)ξ(x,t)\xi(x,t)
TypeWhat You Must Provide
ODEPoint value y(t0)y(t_0)
SDEPoint value X(t0)X(t_0) + random seed
DDEHistory function φ(t)\varphi(t) for tt0t \leq t_0
FDEPoint value y(t0)y(t_0)
IDEPoint value y(t0)y(t_0)
PDEInitial field u(x,t0)u(x, t_0) + boundary conditions
SPDEInitial field u(x,t0)u(x, t_0) + boundary conditions + random seed
TypeState SpaceDimension
ODERn\mathbb{R}^nFinite
SDERn\mathbb{R}^nFinite
DDEFunction space C([τ,0],Rn)C([-\tau, 0], \mathbb{R}^n)Infinite-dimensional
FDERn\mathbb{R}^n (with full history stored internally)Finite (but O(N) storage)
IDERn\mathbb{R}^n (with full history stored internally)Finite (but O(N) storage)
PDEFunction space (discretized to grid)NgridN_{\text{grid}}
SPDEFunction space (discretized to grid)NgridN_{\text{grid}}
TypeCost Per StepTotal Cost (NN steps)Memory
ODEO(n)O(n)O(Nn)O(Nn)O(n)O(n)
SDEO(n)O(n)O(Nn)O(Nn)O(n)O(n)
DDEO(n)O(n) per stage + interpolationO(Nn)O(Nn)O(Nn)O(Nn) for history
FDEO(nN)O(n \cdot N) (history sum)O(N2n)O(N^2 n)O(Nn)O(Nn)
IDE (general)O(nN)O(n \cdot N) (quadrature)O(N2n)O(N^2 n)O(Nn)O(Nn)
IDE (Prony)O(nm)O(nm) (mm exponential terms)O(Nnm)O(Nnm)O(nm)O(nm)
PDEO(Ngrid)O(N_{\text{grid}}) per ODE stepO(NNgrid)O(N \cdot N_{\text{grid}})O(Ngrid)O(N_{\text{grid}})
SPDEO(Ngrid)O(N_{\text{grid}}) (white) to O(Ngrid2)O(N_{\text{grid}}^2) (colored)O(NNgrid)O(N \cdot N_{\text{grid}})O(Ngrid)O(N_{\text{grid}})

The O(N2)O(N^2) total cost of FDE and IDE solvers is the main computational bottleneck for long integrations of memory-dependent equations. For IDEs with sum-of-exponential kernels, the Prony solver reduces this to O(N)O(N).

Problem SizeRecommended Approach
Small (n20n \leq 20, N104N \leq 10^4)Any solver works well
Medium (n100n \leq 100, N105N \leq 10^5)Consider implicit ODE solvers for stiff problems
Large (n1000n \leq 1000, N106N \leq 10^6)Avoid general IDE/FDE solvers; use Prony or ODE reduction
PDE (Ngrid200N_{\text{grid}} \leq 200)MOL with explicit or implicit ODE solver
PDE (Ngrid>200N_{\text{grid}} > 200)MOL with implicit solver (Radau5, Esdirk, Bdf)
SolverTypeOrderStep SizeBest For
DoPri5Explicit RK5(4)AdaptiveGeneral purpose
Tsit5Explicit RK5(4)AdaptiveEfficient (FSAL)
Vern6Explicit RK6(5)AdaptiveHigh accuracy
Vern7Explicit RK7(6)AdaptiveHigh accuracy
Vern8Explicit RK8(7)AdaptiveVery high accuracy
Radau5Implicit RK5AdaptiveStiff problems
Esdirk32ESDIRK2(1)AdaptiveMildly stiff
Esdirk43ESDIRK3(2)AdaptiveModerately stiff
Esdirk54ESDIRK4(3)AdaptiveStiff, high accuracy
BdfMultistep1—5AdaptiveStiff, variable order
AutoAutomaticVariesAdaptiveStiffness detection
SolverStrong OrderWeak OrderStep SizeBest For
EulerMaruyama0.51.0FixedQuick prototyping
Milstein1.01.0FixedMultiplicative noise
Sra11.0—1.5AdaptiveAdditive noise, accuracy
Sra22.0AdaptiveMonte Carlo statistics
SolverTypeOrderStep SizeFeatures
MethodOfStepsExplicit RK (DoPri5)5(4)AdaptiveHermite interpolation, discontinuity tracking, state-dependent delays
SolverTypeConvergenceStep SizeFeatures
L1SolverL1 schemeO(Δt2α)O(\Delta t^{2-\alpha})FixedCaputo derivative, fixed-point iteration
SolverTime SteppingQuadratureCostBest For
VolterraSolverEulerTrapezoidalO(N2)O(N^2)Simple problems
VolterraRK4SolverRK4TrapezoidalO(N2)O(N^2)General kernels
PronySolverAugmented ODERecursiveO(N)O(N)Sum-of-exponential kernels
ApproachSpatial DiscretizationTime IntegrationBest For
MOLSystemFinite differences (2nd order)Any ODE solverParabolic, reaction-diffusion
MOLSystem2DFinite differences (2nd order)Any ODE solver2D problems
SolverMethodNoise TypesStep SizeFeatures
MolSdeSolverMOL + EM/MilsteinWhite, Colored, TraceClassFixed or AdaptiveEnsemble support

All equation types follow a common pattern: define a system struct implementing a trait, configure options with a builder, call a solver, and inspect the result.

// ODE: y'(t) = f(t, y)
trait OdeSystem<S> {
fn dim(&self) -> usize;
fn rhs(&self, t: S, y: &[S], dydt: &mut [S]);
}
// SDE: dX = f dt + g dW
trait SdeSystem<S> {
fn dim(&self) -> usize;
fn drift(&self, t: S, x: &[S], f: &mut [S]);
fn diffusion(&self, t: S, x: &[S], g: &mut [S]);
fn noise_type(&self) -> NoiseType;
}
// DDE: y'(t) = f(t, y(t), y(t-tau))
trait DdeSystem<S> {
fn dim(&self) -> usize;
fn delays(&self) -> Vec<S>;
fn rhs(&self, t: S, y: &[S], y_delayed: &[&[S]], dydt: &mut [S]);
}
// FDE: D^alpha y = f(t, y)
trait FdeSystem<S> {
fn dim(&self) -> usize;
fn alpha(&self) -> S;
fn rhs(&self, t: S, y: &[S], f: &mut [S]);
}
// IDE: y' = f(t,y) + integral K(t,s,y(s)) ds
trait IdeSystem<S> {
fn dim(&self) -> usize;
fn rhs(&self, t: S, y: &[S], f: &mut [S]);
fn kernel(&self, t: S, s: S, y_s: &[S], k: &mut [S]);
}
// PDE: du/dt = F(t, x, u, du/dx, d2u/dx2)
trait PdeSystem<S> {
fn rhs(&self, t: S, x: S, u: S, du_dx: S, d2u_dx2: S) -> S;
}
// SPDE: du/dt = L[u] + sigma(u) * noise
trait SpdeSystem<S> {
fn drift(&self, t: S, u: &[S], du: &mut [S], grid: &Grid1D<S>);
fn diffusion(&self, t: S, u: &[S], sigma: &mut [S], grid: &Grid1D<S>);
fn noise_correlation(&self) -> NoiseCorrelation<S>;
}
TypeOptions StructKey Parameters
ODESolverOptionsrtol, atol, h0, h_max, dense
SDESdeOptionsdt, rtol, atol, seed, save_trajectory
DDEDdeOptionsrtol, atol, h0, track_discontinuities, discontinuity_order
FDEFdeOptionsdt, max_steps, tol, max_iter
IDEIdeOptionsdt, max_steps, tol, quad_points
PDESolverOptions (via ODE)Same as ODE (MOL system is an ODE system)
SPDESpdeOptionsdt, n_output, method, seed, adaptive

All result types provide a consistent interface:

// Common across all result types:
result.y_final() // Final state vector
result.y_at(i) // State at index i
result.t // Time points (Vec)
result.stats // Solver statistics
TypeResult StructStorage FormatExtra Fields
ODESolverResultRow-major flat Vec<S>n_accept, n_reject, n_eval
SDESdeResultRow-major flat Vec<S>n_drift, n_diffusion, n_accept, n_reject
DDEDdeResultRow-major flat Vec<S>n_eval, n_accept, n_reject, n_discontinuities
FDEFdeResultRow-major flat Vec<S>n_rhs, n_steps
IDEIdeResultRow-major flat Vec<S>n_rhs, n_kernel, n_steps
PDESolverResult (via ODE)Row-major flat Vec<S>Same as ODE
SPDESpdeResultVec<Vec<S>> per time stepn_steps, n_drift, n_diffusion, n_reject

Start here: What does the derivative depend on?

  1. Only the current state y(t)y(t): ODE
  2. Current state + random noise: SDE
  3. Current state + state at past time(s) y(tτ)y(t - \tau): DDE
  4. Fractional-order derivative (power-law memory): FDE
  5. Current state + integral over history (general kernel): IDE
  6. Current state + spatial derivatives 2u/x2\partial^2 u / \partial x^2: PDE
  7. Spatial derivatives + random forcing: SPDE
TypeNon-Stiff SolverStiff Solver
ODEDoPri5, Tsit5, Vern6/7/8Radau5, Esdirk, Bdf
SDEEulerMaruyama— (no implicit SDE solver currently)
DDEMethodOfSteps— (explicit only currently)
FDEL1Solver (implicit fixed-point)L1Solver handles mild stiffness
IDEVolterraRK4Solver— (explicit only currently)
PDE (via MOL)DoPri5 (coarse grid)Radau5, Bdf (fine grid)
SPDEEulerMaruyama— (explicit only currently)

If your system has memory, the type of memory determines the equation:

Memory TypeDE TypeExample
No memoryODEy=kyy' = -ky
Discrete past statesDDEy(t)=f(y(t),y(tτ))y'(t) = f(y(t), y(t-\tau))
Power-law (Caputo) kernelFDECD0.5y=f(y){}^C D^{0.5} y = f(y)
Exponential kernelIDE (Prony)y=f(y)+eb(ts)y(s)dsy' = f(y) + \int e^{-b(t-s)} y(s)\,ds
General kernelIDE (Volterra)y=f(y)+K(t,s,y(s))dsy' = f(y) + \int K(t,s,y(s))\,ds

If your system has spatial structure:

Spatial StructureDE TypeApproach
No spatial dependenceODE, SDE, DDE, FDE, or IDEDirect
Spatial derivatives (deterministic)PDEMOL -> ODE solver
Spatial derivatives + noiseSPDEMOL -> SDE solver

Several equation types are closely related and can sometimes be converted:

A fractional differential equation CDαy=f(t,y){}^C D^\alpha y = f(t,y) is equivalent to a Volterra IDE with a power-law kernel:

y(t)=f(t,y)+1Γ(1α)0t(ts)αy(s)dsy'(t) = f(t,y) + \frac{1}{\Gamma(1-\alpha)} \int_0^t (t-s)^{-\alpha} y'(s)\,ds

The dedicated FDE solver (L1Solver) uses optimized Caputo weights that are more efficient than the general Volterra quadrature for this specific kernel.

Through the Method of Lines, any PDE becomes a large ODE system:

duidt=F(ui1,ui,ui+1),i=1,,N1\frac{du_i}{dt} = F(u_{i-1}, u_i, u_{i+1}), \quad i = 1, \ldots, N-1

Numra’s MOLSystem implements OdeSystem, so all ODE solvers work directly.

Through the Method of Lines, an SPDE becomes a system of SDEs (one per grid point). Numra’s MolSdeWrapper implements SdeSystem, bridging the two crates.

When the IDE kernel is a sum of exponentials K(τ)=aiebiτK(\tau) = \sum a_i e^{-b_i \tau}, the integral satisfies an auxiliary ODE, so the IDE becomes an augmented ODE system of dimension n+nmn + nm where mm is the number of exponential terms.

When the diffusion coefficient g=0g = 0, an SDE reduces to a deterministic ODE. Use numra-ode for better accuracy and efficiency in this case.

When all delays τi=0\tau_i = 0, a DDE reduces to an ODE. Use numra-ode for better performance.

Example: Same Problem, Different Formulations

Section titled “Example: Same Problem, Different Formulations”

Consider modeling a system with exponential memory decay. This can be formulated as three different equation types:

use numra_ide::{IdeSystem, VolterraSolver, IdeSolver, IdeOptions};
struct MemorySystem;
impl IdeSystem<f64> for MemorySystem {
fn dim(&self) -> usize { 1 }
fn rhs(&self, _t: f64, y: &[f64], f: &mut [f64]) {
f[0] = -y[0];
}
fn kernel(&self, t: f64, s: f64, y_s: &[f64], k: &mut [f64]) {
k[0] = (-(t - s)).exp() * y_s[0];
}
}
let opts = IdeOptions::default().dt(0.01);
let result = VolterraSolver::solve(&MemorySystem, 0.0, 5.0, &[1.0], &opts)?;
// Cost: O(N^2)
use numra_ide::{IdeSystem, PronySolver, IdeSolver, IdeOptions};
// Same system, but recognized as sum-of-exponentials
// PronySolver reduces the integral to an auxiliary ODE
let opts = IdeOptions::default().dt(0.01);
let result = PronySolver::solve(&MemorySystem, 0.0, 5.0, &[1.0], &opts)?;
// Cost: O(N)
use numra_ode::{DoPri5, Solver, SolverOptions};
// Manually augment: y' = -y + I, I' = y - I
// where I(t) = integral_0^t exp(-(t-s)) y(s) ds
struct AugmentedODE;
// ... (implement OdeSystem with dim = 2)
// Cost: O(N), and benefits from adaptive high-order ODE solvers
FeatureODESDEDDEFDEIDEPDESPDE
Cratenumra-odenumra-sdenumra-ddenumra-fdenumra-idenumra-pdenumra-spde
SpatialNoNoNoNoNoYesYes
StochasticNoYesNoNoNoNoYes
MemoryNoNoDiscretePower-lawGeneralNoNo
Adaptive dtYesYes (SRA)YesNoNoYes (via ODE)Yes (step-doubling)
ImplicitYesNoNoYes (FP)NoYes (via ODE)No
EnsembleNoYesNoNoNoNoYes
Total costO(N)O(N)O(N)O(N)O(N)O(N)O(N2)O(N^2)O(N2)O(N^2)O(N)O(N)O(N)O(N)