-
-
Notifications
You must be signed in to change notification settings - Fork 248
Description
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:
- Bug fixes don't propagate — ODE fixes for floating-point snapping, composite algorithm tracking, stat counting, tstop handling, etc. are missing in SDE
- New features require double implementation — controller improvements, progress logging, etc.
- ~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
- Move modify_dt_for_tstops\! into apply_step\! after update_fsal\! #3096 — Move
modify_dt_for_tstops!intoapply_step!afterupdate_fsal!(fixes DDE ordering constraint)
Open PRs
- Add hook functions for SDE integrator unification #3095 (ODE) — Add hook functions for SDE integrator unification. Refactors
loopheader!,_loopfooter!,apply_step!,_savevalues!,_postamble!,handle_callbacks!,handle_tstop!with override points - Use OrdinaryDiffEqCore shared loop functions via hooks StochasticDiffEq.jl#682 (SDE) — Uses OrdinaryDiffEqCore's shared loop functions via hooks. Deletes SDE's local implementations and adds thin hook overrides for SDE-specific behavior (noise acceptance, dtnew intermediate, sqdt)
Remaining Functions to Unify
step!— Nearly identical; SDE version has bugs (missingdo_error_checkguard, returnsnothingon error). Plan: extract_step!(integrator)untyped function, both types delegate to it.change_t_via_interpolation!— SDE version lacks units supportreinit!— 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:
- ODE defines the generic function with no type constraint (or with
_func!naming) - ODE provides default hook implementations for ODE-specific behavior
- SDE imports the function and adds methods typed on
SDEIntegratorto override hooks - 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 updatehandle_step_rejection!— ODE: direct dt adjustment. SDE: dtnew intermediate + noise rejectionloopfooter_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:
do_error_checkguard instep!- Correct return value from
step!(retcode instead of nothing) increment_accept!/increment_reject!stat trackingfixed_t_for_floatingpoint_error!time snapping- Proper
u_modifiedhandling at iter 0 - Multiple tstop deduplication in
handle_tstop! force_stepfailcheck before discrete callbackssave_endcheck 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.