Skip to content

Unify StochasticDiffEq.jl integrator onto OrdinaryDiffEqCore infrastructure #3098

@ChrisRackauckas-Claude

Description

@ChrisRackauckas-Claude

Motivation

StochasticDiffEq.jl's time loop infrastructure (loopheader!, loopfooter!, apply_step!, step!, savevalues!, handle_callbacks!, etc.) was originally forked from OrdinaryDiffEq.jl and has drifted significantly. This means:

  1. Bug fixes don't propagate — ODE fixes for floating-point snapping, composite algorithm tracking, stat counting, tstop handling, etc. are missing in SDE
  2. New features require double implementation — controller improvements, progress logging, etc.
  3. ~500 lines of duplicated loop code in StochasticDiffEq.jl

The goal is to have StochasticDiffEq.jl reuse OrdinaryDiffEqCore's loop functions via a hook-override pattern, and eventually merge SDEIntegrator into ODEIntegrator.

Work Done So Far

Merged

Open PRs

Remaining Functions to Unify

  • step! — Nearly identical; SDE version has bugs (missing do_error_check guard, returns nothing on error). Plan: extract _step!(integrator) untyped function, both types delegate to it.
  • change_t_via_interpolation! — SDE version lacks units support
  • reinit! — SDE missing several reset fields
  • __init / solve.jl — Largest diff, eventual integrator type merge

Integrator Type Comparison

ODEIntegrator fields missing from SDEIntegrator

Field Purpose Assessment
du, duprev DAE derivative storage Needed for mass-matrix SDEs
uprev2 Two-steps-back for extrapolation Drift — needed if extrapolation methods used
k, kshortsize Dense output stages Intentional — SDE uses linear interp
fsalfirst, fsallast FSAL evaluation cache Intentional — noise prevents FSAL
reeval_fsal FSAL reevaluation flag Drift
reinitialize, isdae, differential_vars DAE support Drift
saveiter_dense Dense output counter Intentional
erracc, dtacc Controller state Drift — limits controller options
controller_cache Controller state object Drift
rng RNG instance Drift

SDEIntegrator-specific fields (must be preserved)

Field Purpose
g Diffusion function
c Jump rate function
noise Raw noise distribution
W Wiener process
P Poisson process
rate_constants Tau-leaping rates
sqdt Pre-computed sqrt(|dt|)
dtnew Step size intermediate (bounded before copying to dt)
q Error ratio (stored on integrator, not returned)

Hook-Override Pattern

The pattern used throughout this unification:

  1. ODE defines the generic function with no type constraint (or with _func! naming)
  2. ODE provides default hook implementations for ODE-specific behavior
  3. SDE imports the function and adds methods typed on SDEIntegrator to override hooks
  4. SDE's typed step!/loopfooter! wrapper calls the untyped _step!/_loopfooter!

Example hooks already implemented:

  • post_apply_step! — ODE: no-op. SDE: noise acceptance + sqdt update
  • handle_step_rejection! — ODE: direct dt adjustment. SDE: dtnew intermediate + noise rejection
  • loopfooter_reset! — ODE: reset reeval_fsal + u_modified. SDE: no-op (no reeval_fsal)
  • handle_force_stepfail! — ODE: post_newton_controller!. SDE: dtnew = dt/failfactor

SDE Bug Fixes from Unification

By reusing ODE's implementations, SDE automatically gets:

  1. do_error_check guard in step!
  2. Correct return value from step! (retcode instead of nothing)
  3. increment_accept!/increment_reject! stat tracking
  4. fixed_t_for_floatingpoint_error! time snapping
  5. Proper u_modified handling at iter 0
  6. Multiple tstop deduplication in handle_tstop!
  7. force_stepfail check before discrete callbacks
  8. save_end check in saveat

End State

Eventually, SDEIntegrator could be merged into ODEIntegrator by adding the SDE-specific fields (defaulting to nothing for ODE problems). This would eliminate the need for the _func! pattern entirely, as dispatch would work naturally on a single type. The SDE-specific fields add zero runtime overhead for ODE problems since nothing checks are compiled away.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions