Skip to content

Raise OrdinaryDiffEqDefault DiffEqBase floor to 7.15 - #4274

Merged
ChrisRackauckas merged 1 commit into
SciML:masterfrom
ChrisRackauckas-Claude:fix-default-diffeqbase-floor
Aug 20, 2026
Merged

ChrisRackauckas merged 1 commit into
SciML:masterfrom
ChrisRackauckas-Claude:fix-default-diffeqbase-floor

Conversation

@ChrisRackauckas-Claude

Copy link
Copy Markdown
Member

Ignore until reviewed by @ChrisRackauckas.

What changed and why

Raise the DiffEqBase compat floor of lib/OrdinaryDiffEqDefault from 7.14 to 7.15.

Since #4241 (27faa3ad3), OrdinaryDiffEqDefault defines DiffEqBase.prepare_alg(::Nothing, u0, p, ::ODEProblem) = DefaultODEAlgorithm(autodiff = AutoFiniteDiff()) and relies on DiffEqBase's solve_up/init_up calling prepare_alg(nothing, ...) first and then running the normal prepare_alg pass on the returned algorithm. That hook shipped in DiffEqBase 7.15.0 (tagged at 50d228361), but the OrdinaryDiffEqDefault floor stayed at 7.14. With DiffEqBase 7.14.0 the solve(prob) path goes through the SciMLBase.__solve(prob, ::Nothing) fallback, which builds DefaultODEAlgorithm(autodiff = AutoFiniteDiff()) and never calls prepare_alg, so the default's FBDF keeps a plain AutoFiniteDiff (dense finite-difference Jacobian) while an explicitly passed FBDF(autodiff = AutoFiniteDiff()) gets prepare_user_sparsity applied and becomes AutoSparse{AutoFiniteDiff, KnownJacobianSparsityDetector, GreedyColoringAlgorithm} from the problem's jac_prototype. The two solves then take different step/f-eval counts and the "extended Robertson" n = 100 assertions in default_solver_tests.jl:106-107 fail.

This is what makes downgrade-sublibraries / test (lib/OrdinaryDiffEqDefault) red on master (e.g. https://github.com/SciML/OrdinaryDiffEq.jl/actions/runs/31881306107, https://github.com/SciML/OrdinaryDiffEq.jl/actions/runs/31840134545, and identically on #4272). #4271 raised the LinearSolve floor for an unrelated resolution failure and did not touch this.

History: the Default downgrade job first went red on 4356f2210 (https://github.com/SciML/OrdinaryDiffEq.jl/actions/runs/31256852719, 2026-08-08 12:16 UTC); the previous run 7b1ad0368 (https://github.com/SciML/OrdinaryDiffEq.jl/actions/runs/31253935515, 11:06 UTC) was green. The only package version that differs between those two job logs is FiniteDiff 2.32.1 -> 2.33.0 (released 2026-08-08 12:25 UTC, JuliaDiff/FiniteDiff.jl#230, JVP step rescaling). The sparse-AD Jacobian path (AutoSparse{AutoFiniteDiff} via DifferentiationInterface) uses finite_difference_jvp and changed with FiniteDiff 2.33.0, while the unprepared dense-AutoFiniteDiff path did not, so the two solves stopped agreeing. Since #4241 the default is prepared too (both sides now agree with current deps), but only when DiffEqBase >= 7.15 is loaded, hence the floor.

Verification

Reproduced the CI procedure locally on Julia 1.11.9 by running julia-actions/julia-downgrade-compat@v2's downgrade.jl on lib/OrdinaryDiffEqDefault with the same effective skip list as SciML/.github/.github/workflows/sublibrary-downgrade.yml@v1 (stdlibs + all lib/* names + Pkg,TOML,Statistics,LinearAlgebra,SparseArrays,InteractiveUtils,OrdinaryDiffEqCore,OrdinaryDiffEqNonlinearSolve,OrdinaryDiffEqDifferentiation), then GROUP=Core julia --project -e 'using Pkg; Pkg.test()'.

Resolved versions in that env (unchanged by this PR except DiffEqBase): FiniteDiff 2.33.0, LinearSolve 5.2.0, OrdinaryDiffEqBDF 2.0.0, OrdinaryDiffEqCore 4.12.0, OrdinaryDiffEqDifferentiation 3.7.0, OrdinaryDiffEqNonlinearSolve 2.8.0, OrdinaryDiffEqTsit5 2.0.0, OrdinaryDiffEqVerner 2.0.0, OrdinaryDiffEqRosenbrock 2.6.1, SciMLBase 3.46.0, ADTypes 1.22.0, DiffEqBase 7.14.0 (before) / 7.15.0 (after).

Before (master, DiffEqBase 7.14.0):

Default Solver Tests: Test Failed at .../test/default_solver_tests.jl:106
   Evaluated: 48 == 54
Default Solver Tests: Test Failed at .../test/default_solver_tests.jl:107
   Evaluated: 182 == 241
Test Summary:        | Pass  Fail  Total     Time
Default Solver Tests |   46     2     48  7m09.1s
ERROR: Package OrdinaryDiffEqDefault errored during testing

After (this branch, DiffEqBase 7.15.0):

Test Summary:        | Pass  Total     Time
Default Solver Tests |   48     48  6m46.6s
     Testing OrdinaryDiffEqDefault tests passed

Diagnostic script in the failing env (extended Robertson block, printing the algorithm actually used):

n=100 default: naccept=48 nf=182 nw=27 nsolve=104 nnonliniter=101 njacs=2 choice=[1, 5]
n=100 fbdf   : naccept=54 nf=241 nw=34 nsolve=155 nnonliniter=132 njacs=4 choice=[1, 2]
default stiff FBDF autodiff = AutoFiniteDiff{...}
fsol    FBDF autodiff       = AutoSparse{AutoFiniteDiff{...}, KnownJacobianSparsityDetector{SparseMatrixCSC{Bool, Int64}}, GreedyColoringAlgorithm{:direct, ...}}
n=600 default: naccept=45 nf=452 ...   (matches fsol; the Krylov case is not affected)

Same script with current registry versions (OrdinaryDiffEqBDF 2.4.2, OrdinaryDiffEqCore 4.14.3, DiffEqBase 7.15.0, FiniteDiff 2.33.0): both n=100 solves give naccept=54 nf=241.

typos lib/OrdinaryDiffEqDefault/Project.toml: clean. Runic not applicable (TOML-only diff).

Not verified

  • Only the Core group of lib/OrdinaryDiffEqDefault was run in the downgraded env; QA/GPU groups and other sublibraries' downgrade jobs were not run locally.
  • No version bump of OrdinaryDiffEqDefault (2.4.5) in this PR, matching Raise OrdinaryDiffEqDefault LinearSolve floor to 5.2 #4271; the floor change is a compat tightening for the next release.

Alternative a reviewer may prefer

Instead of (or in addition to) the floor, SciMLBase.__solve/__init(prob::ODEProblem, ::Nothing) in lib/OrdinaryDiffEqDefault/src/default_alg.jl could call DiffEqBase.prepare_alg on the DefaultODEAlgorithm it constructs so the default is prepared even under DiffEqBase < 7.15. I kept this PR to the compat floor since that fallback is unreachable from solve(prob) once DiffEqBase >= 7.15 is loaded.

OrdinaryDiffEqDefault relies on DiffEqBase calling `prepare_alg(nothing, ...)`
so the default algorithm gets the same `prepare_alg` pass (sparse AD from
`jac_prototype`, etc.) as an explicitly passed algorithm. That hook exists
since DiffEqBase 7.15.0; with 7.14.0 the default is solved unprepared and its
stats no longer match `AutoTsit5(FBDF(...))` in the downgrade CI.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
@ChrisRackauckas-Claude
ChrisRackauckas-Claude force-pushed the fix-default-diffeqbase-floor branch from 63f64d7 to a8de9eb Compare August 20, 2026 17:50
@ChrisRackauckas
ChrisRackauckas marked this pull request as ready for review August 20, 2026 18:15
@ChrisRackauckas
ChrisRackauckas merged commit 0f6e7d1 into SciML:master Aug 20, 2026
18 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants