Skip to content

Make the v7 verbose migration path reachable from using OrdinaryDiffEq - #4189

Merged
ChrisRackauckas merged 1 commit into
SciML:masterfrom
ChrisRackauckas-Claude:fix-4163-verbose-escape-hatch
Aug 9, 2026
Merged

ChrisRackauckas merged 1 commit into
SciML:masterfrom
ChrisRackauckas-Claude:fix-4163-verbose-escape-hatch

Conversation

@ChrisRackauckas-Claude

Copy link
Copy Markdown
Member

Closes #4163

Please ignore until reviewed by @ChrisRackauckas.

What changed and why

_process_verbose_param(::Bool) threw an ArgumentError instructing users to call DEVerbosity(), but DEVerbosity was not reachable from a plain using OrdinaryDiffEq — it lives in DiffEqBase and the umbrella package neither imported nor exported it. The instruction could not be followed without first discovering two extra packages.

  • OrdinaryDiffEq now imports DEVerbosity from DiffEqBase (its owner, which exports it) and re-exports it, matching the existing AutoDePSpecialize pattern in src/OrdinaryDiffEq.jl. SciMLLogging was already exported, so verbose = DEVerbosity(SciMLLogging.None()) now works from a bare using OrdinaryDiffEq.
  • The error message is now a copy-pasteable solve(...) line for both the false and true cases, and states where the names come from. The same text is applied to the two other copies of it (DelayDiffEq, StochasticDiffEqCore) so the three do not diverge.
  • docs/src/verbosity.md gains a "Migrating from verbose::Bool" section; NEWS.md records where DEVerbosity is reachable from.

This adds public API, so OrdinaryDiffEq gets a minor bump (7.3.1 → 7.4.0), not a patch. DiffEqBase, DelayDiffEq and StochasticDiffEqCore get patch bumps for the message text.

DEVerbosity vs ODEVerbosity (issue part 1, second half)

Already resolved on master — no code change needed. ODEVerbosity has never existed in any shipped source. NEWS.md said ODEVerbosity and was corrected to DEVerbosity in #3761 (commit 0bda3109a1b9581450776b0e5f9607c2dd5e5281, "Change typo in NEWS"). The issue was filed against the released v7.3.0, which predates that fix. git log -S"ODEVerbosity" -- NEWS.md shows no later reintroduction. The new test asserts the error message never names ODEVerbosity.

PolyesterThreads (issue part 2)

Not exported, and that looks deliberate rather than accidental:

  • Sequential, BaseThreads, PolyesterThreads, AbstractThreadingOption and isthreaded are all in OrdinaryDiffEqCore's public declaration (lib/OrdinaryDiffEqCore/src/OrdinaryDiffEqCore.jl:427-447).
  • They are documented as OrdinaryDiffEqCore.X on a page whose first line reads "This page lists user-facing OrdinaryDiffEqCore API" (docs/src/api/ordinarydiffeqcore.md).
  • The v7 umbrella deliberately dropped the blanket @reexport in favour of a curated export list.

So the intent is public-but-qualified, and exporting it would work against that design. The gap is that the docs presenting the option still used the bare v6 spelling. Fixed on the documentation side only: the KuttaPRK2p5 and QPRK98 docstrings and the docs page now write OrdinaryDiffEqCore.PolyesterThreads() and say the type is public but not exported, and NEWS.md records that bare v6 usage now raises UndefVarError. No source behavior change. lib/OrdinaryDiffEqExtrapolation/src/algorithms.jl was deliberately left untouched (#4154 is in flight there).

If the preference is instead to export the threading options from the solver sublibraries that take a threading/thread keyword, say so and I will do that in a follow-up — it is a judgment call about export surface, not something I wanted to force into this PR.

Verification

Base commit: 9d23061ef8a2b155ca2b6ff78670f6e320967940.

New test: test/InterfaceI/verbose_migration_tests.jl, registered in the InterfaceI group. It imports nothing but OrdinaryDiffEq and Test, so it fails if the escape hatch is not reachable from a bare using OrdinaryDiffEq. Its last assertion extracts the solve(...) lines out of the thrown message and evals them verbatim, so the message itself is what is under test, not a paraphrase of it.

Failing before the fix

git stash push -- src/OrdinaryDiffEq.jl lib/DiffEqBase/src/verbosity.jl, then run the new test file:

ERROR: LoadError: Some tests did not pass: 3 passed, 2 failed, 2 errored, 0 broken.

verbose migration path is reachable from `using OrdinaryDiffEq`: Test Failed at test/InterfaceI/verbose_migration_tests.jl:8
  Expression: :DEVerbosity in names(OrdinaryDiffEq)
   Evaluated: DEVerbosity in [:AutoDePSpecialize, :AutoFiniteDiff, :AutoForwardDiff, :AutoSparse, :AutoTsit5, :AutoVern6, :AutoVern7, :AutoVern8, :AutoVern9, :CallbackSet  …  :derivative_discontinuity!, :init, :reinit!, :remake, :set_proposed_dt!, :solve, :solve!, :step!, :successful_retcode, :terminate!]

verbose migration path is reachable from `using OrdinaryDiffEq`: Error During Test at test/InterfaceI/verbose_migration_tests.jl:11
  Test threw exception
  Expression: successful_retcode(solve(prob, alg; verbose = DEVerbosity(SciMLLogging.None())))
  UndefVarError: `DEVerbosity` not defined in `Main`
  Suggestion: check for spelling errors or missing imports.
  Hint: a global variable of this name also exists in DiffEqBase.

verbose migration path is reachable from `using OrdinaryDiffEq`: Error During Test at test/InterfaceI/verbose_migration_tests.jl:12
  Test threw exception
  Expression: successful_retcode(solve(prob, alg; verbose = DEVerbosity()))
  UndefVarError: `DEVerbosity` not defined in `Main`

verbose migration path is reachable from `using OrdinaryDiffEq`: Test Failed at test/InterfaceI/verbose_migration_tests.jl:30
  Expression: length(suggestions) == 2
   Evaluated: 0 == 2

Test Summary:                                                   | Pass  Fail  Error  Total  Time
verbose migration path is reachable from `using OrdinaryDiffEq` |    3     2      2      7  6.4s

Passing after the fix

git stash pop, same command:

Test Summary:                                                   | Pass  Total  Time
verbose migration path is reachable from `using OrdinaryDiffEq` |    9      9  3.6s

The message itself

Before, on master, from solve(prob, Tsit5(), verbose = false):

ArgumentError: Passing a `Bool` for `verbose` is no longer supported in OrdinaryDiffEq v7. Use `DEVerbosity()` or a preset like `Standard()`, `None()`, etc. from SciMLLogging.

After:

ArgumentError: Passing a `Bool` for `verbose` is no longer supported in OrdinaryDiffEq v7: `verbose` now takes a verbosity object.

    solve(prob, alg; verbose = DEVerbosity(SciMLLogging.None()))  # was verbose = false
    solve(prob, alg; verbose = DEVerbosity())                     # was verbose = true

`DEVerbosity` and `SciMLLogging` are both exported by OrdinaryDiffEq, so no extra `using` is needed; from another solver package add `using DiffEqBase, SciMLLogging`. Per-message control is documented at https://docs.sciml.ai/OrdinaryDiffEq/stable/verbosity/

Reachability check in the same session:

DEVerbosity    exported=true
SciMLLogging   exported=true

Test groups

$ GROUP=InterfaceI julia --project -e 'using Pkg; Pkg.test()'
...
Test Summary:           | Pass  Total  Time
Verbose Migration Tests |    9      9  3.0s
...
2244.859976 seconds (1.76 G allocations: 87.427 GiB, 3.59% gc time, 99.00% compilation time)
     Testing OrdinaryDiffEq tests passed

GROUP=QA fails on this branch — but it fails identically on unmodified master, so it is not introduced here. Both runs are 87 passed, 1 failed with the same assertion:

# this branch                                       # master @ 9d23061
public API has docstrings: Test Failed              public API has docstrings: Test Failed
  Expression: isempty(undocumented)                   Expression: isempty(undocumented)
   Evaluated: isempty([:SciMLBase, :SciMLLogging])     Evaluated: isempty([:SciMLBase, :SciMLLogging])

Quality Assurance Tests | 87  1  88  18.7s          Quality Assurance Tests | 87  1  88  18.1s

DEVerbosity is not in the undocumented list, and every QA subtest this PR touches passes on the branch:

    ExplicitImports                     |    6            6   6.4s
    Public API documentation            |    1     1      2   3.0s
      public API has docstrings         |          1      1   3.0s   <- pre-existing, [:SciMLBase, :SciMLLogging]
      public API is rendered in docs    |    1            1   0.0s
    No unapproved public reexports      |    1            1   0.0s

That pre-existing master failure is being investigated separately; it is unrelated to this change.

Docs build

$ julia --project=docs -e 'using Pkg; Pkg.instantiate(); include("docs/make.jl")'
[ Info: Automatic `version="7.4.0"` for inventory from ../Project.toml
DOCS_EXIT=0

Exit code 0. No new warnings for docs/src/verbosity.md or docs/src/api/ordinarydiffeqcore.md; the remaining warnings are the pre-existing tableaus.md / misc.md ones.

Formatting / spelling

  • Runic.jl --check clean on every changed .jl file.
  • typos clean over the changed files.

What I did NOT verify

  • Only the QA and InterfaceI groups were run. The other ODE groups, and the DelayDiffEq / StochasticDiffEqCore suites, were not — those two packages received only a string-literal change and a patch version bump.
  • GPU, Downstream, julia pre, and ODEInterfaceRegression jobs were not run locally.
  • The message's claim that SciMLLogging is reachable was exercised for OrdinaryDiffEq only. StochasticDiffEq reaches DEVerbosity through its @reexport using DiffEqBase, which I did not run.

Things to push back on

  • Adding DiffEqBase to OrdinaryDiffEq's [deps] (it was already a transitive dep via OrdinaryDiffEqCore, and was previously in [extras]). The alternative was to re-export it through OrdinaryDiffEqCore, which would mean depending on a non-public re-export rather than on the owning package.
  • test/qa/qa_tests.jl now adds public_api_names(DiffEqBase) to ORDINARYDIFFEQ_REEXPORTS, widening the reexport allow-list by all of DiffEqBase's public API rather than just DEVerbosity. That matches how the other packages are listed there, but it is wider than strictly needed.
  • The error message text is duplicated across three packages rather than shared. Sharing it would mean adding a new public constant to DiffEqBase; I kept the existing shape.

🤖 Generated with Claude Code

https://claude.ai/code/session_01F75XsVeZ94QH3PmCuq6QUF

…ffEq`

`_process_verbose_param(::Bool)` told users to call `DEVerbosity()`, which was
not reachable from a plain `using OrdinaryDiffEq`. Export `DEVerbosity` from the
umbrella package (importing it from DiffEqBase, which owns and exports it) and
replace the message with a copy-pasteable one. Also qualify the OrdinaryDiffEqCore
threading options in the docstrings/docs that presented them as bare names.

Closes SciML#4163

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
@ChrisRackauckas-Claude
ChrisRackauckas-Claude force-pushed the fix-4163-verbose-escape-hatch branch from c2866a7 to 30fdfe4 Compare August 9, 2026 05:14
@ChrisRackauckas-Claude

Copy link
Copy Markdown
Member Author

Rebased onto origin/master after the local verification runs above; the four commits picked up (942c73e, 462c247, 4c6c423, 4cb8020) do not touch the verbosity path or any file in this diff, and the rebase was clean with no conflicts. The pasted test output is from the pre-rebase base commit 9d23061.

@ChrisRackauckas-Claude

Copy link
Copy Markdown
Member Author

CI status so far — Runic / Runic Format Check pass, Spell Check with Typos pass, runic-suggestions pass, lib/DiffEqBase / Julia lts / Tests - Core pass, lib/OrdinaryDiffEqPRK and lib/OrdinaryDiffEqQPRK Core pass, downgrade-sublibraries / test (lib/DiffEqBase) pass.

One red check, pre-existing and allowed-to-fail: PositiveIntegrators.jl/Downstream/1.10. It is a registry resolution conflict, not a test failure:

ERROR: LoadError: Unsatisfiable requirements detected for package OrdinaryDiffEqRosenbrock [43230ef6]:
 ├─restricted to versions 2.6.3-2 by OrdinaryDiffEq [1dea7af3], leaving only versions: 2.6.3
 └─restricted by compatibility requirements with LinearSolve [7ed4a6bd] to versions: 1.5.0-2.4.2 or uninstalled — no versions left
      └─restricted to versions 3.75.0-3 by PositiveIntegrators [d1b20bf0]

The same job fails on the current master tip (942c73e4, run https://github.com/SciML/OrdinaryDiffEq.jl/actions/runs/31295972808), so it is not introduced here. Downstream is continue_on_error = true in test_groups.toml.

The remaining ~260 checks are still queued; I will not have read them all before handing this over.

@ChrisRackauckas-Claude

Copy link
Copy Markdown
Member Author

Second red check, also pre-existing: tests / InterfaceV (julia lts).

GPU AutoDiff with JLArrays: Error During Test at test/InterfaceV/gpu_autodiff_interface_tests.jl:4
  Got exception outside of a @test
  Illegal conversion of a JLArray to a Ptr
  Stacktrace:
    [2] unsafe_convert(::Type{Ptr{Float64}}, x::JLArrays.JLArray{Float64, 2}) @ JLArrays
    [3] getrf!(A::JLArrays.JLArray{Float64, 2}; check::Bool) @ LinearAlgebra.LAPACK
ERROR: LoadError: Some tests did not pass: 0 passed, 0 failed, 1 errored, 0 broken.

InterfaceV fails on master at the base commit 9d23061e on all three Julia versions (lts, 1, pre) — run https://github.com/SciML/OrdinaryDiffEq.jl/actions/runs/31277347420. Nothing in this diff touches linear algebra or GPU array handling.

Both red checks on this PR are therefore pre-existing master failures; a separate investigation is open for the QA one.

@ChrisRackauckas-Claude

Copy link
Copy Markdown
Member Author

CI triage

All red checks so far are pre-existing on master. Evidence for each:

Check Verdict Evidence
tests / QA (julia 1) pre-existing CI shows 87 passed, 1 failed, isempty([:SciMLBase, :SciMLLogging]). I reproduced the identical 87 passed, 1 failed locally on a clean checkout of the base commit 9d23061. Root-caused and fixed separately in #4190 — the SciMLTesting compat bump in 12b6996 (#3867) turned on a docstring check that a re-exported module binding cannot satisfy. Upstream: SciML/SciMLTesting.jl#47
tests / InterfaceV (julia lts) pre-existing Illegal conversion of a JLArray to a Ptr. Fails on master at 9d23061 on all three Julia versions (run https://github.com/SciML/OrdinaryDiffEq.jl/actions/runs/31277347420). Caused by the JLArrays 0.3.1 → 0.3.2 bump; already tracked in #4179
sublibrary-ci / lib/StochasticDiffEqCore [QA] pre-existing Ran GROUP=QA for that sublibrary on a clean master checkout: identical result, 16 passed, 1 failed, 3 errored, same MatrixOperator/WOperator stale imports, same is_composite_cache/is_constant_cache/CompositeCache non-public accesses, same isempty([:DiffEqBase, :SciMLBase]). None of those names appear in this diff — my only change to that package is the error-message string and a patch version bump
The other ~15 sublibrary-ci … [QA] failures pre-existing Same public API has docstrings / ExplicitImports family, in packages this diff does not touch at all. 24 of the failing checks here also fail on merged #4158; the rest are jobs that #4158's detect step did not schedule, so there is no comparison run for them
downgrade-sublibraries / …, Downgrade / …, PositiveIntegrators.jl/Downstream, ProbNumDiffEq.jl/Downstream pre-existing PositiveIntegrators is a registry resolution conflict (LinearSolve 3.x caps OrdinaryDiffEqRosenbrock ≤ 2.4.2) that fails on the master tip 942c73e4 too; Downstream is continue_on_error = true. The Downgrade lanes are red on master independently

Green and relevant: Runic Format Check, Spell Check with Typos, runic-suggestions, sublibrary-ci / lib/DiffEqBase / Julia lts / Core, lib/OrdinaryDiffEqPRK, lib/OrdinaryDiffEqQPRK, downgrade-sublibraries / test (lib/DiffEqBase).

Not verified: the remaining queued checks, and the GPU lane.

@ChrisRackauckas-Claude

Copy link
Copy Markdown
Member Author

Final CI read on the jobs that actually cover this change — all green:

  • tests / InterfaceI — pass on Julia 1, lts and pre. This is the group the new test/InterfaceI/verbose_migration_tests.jl runs in.
  • Documentation / Build and Deploy Documentation — pass.
  • sublibrary-ci / lib/DiffEqBase Core on 1, lts and pre, plus [QA], [Downstream], [ModelingToolkit], [Static], [Sundials] — all pass.
  • sublibrary-ci / lib/DelayDiffEq [Interface] — pass on Julia 1 and lts. That group contains test/interface/verbosity.jl, i.e. the DDE Bool throw with the new message text.
  • Runic Format Check, Spell Check with Typos, runic-suggestions — pass.

The two remaining reds touching packages in this diff are both pre-existing and unrelated to the change:

  • lib/DelayDiffEq [Interface] / Julia pre — Unitful fails to precompile on Julia 1.13-pre (Failed to precompile Unitful … (ProcessExited(1))), in test/interface/units.jl. Nothing to do with verbosity, and pre is an allowed-to-fail lane.
  • lib/DelayDiffEq [QA] — first_tstop/has_tstop/pop_tstop! accessed non-publicly from OrdinaryDiffEqCore, plus isempty([:SciMLBase]). Same family as the umbrella QA failure; none of those names appear in this diff.

@ChrisRackauckas-Claude

Copy link
Copy Markdown
Member Author

Ran GROUP=QA for lib/DelayDiffEq on a clean master checkout to close the last gap. It fails there too, with the same specific names:

Test Summary:                                  | Pass  Fail  Error  Total     Time
QA Tests                                       |   15     2      2     19  6m51.7s
  - `first_tstop` has owner `SciMLBase` but it was accessed from `OrdinaryDiffEqCore` at lib/DelayDiffEq/src/integrators/utils.jl:309:29
  - `first_tstop` is not public in `OrdinaryDiffEqCore` but it was imported from `OrdinaryDiffEqCore` at lib/DelayDiffEq/src/integrators/utils.jl:309:29
   Evaluated: isempty([:SciMLBase])
DDEQA_EXIT=1

(Local is 15/2/2 vs CI's 16/1/2 — a Julia-version difference in one subtest; the failing assertions themselves are identical and none of the named symbols appear in this diff.)

That covers both packages this PR modifies beyond the umbrella: lib/DiffEqBase QA is green in CI, and lib/DelayDiffEq QA and lib/StochasticDiffEqCore QA both reproduce on unmodified master.

@ChrisRackauckas
ChrisRackauckas marked this pull request as ready for review August 9, 2026 11:11
@ChrisRackauckas
ChrisRackauckas merged commit aac7e45 into SciML:master Aug 9, 2026
200 of 282 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.

v7 verbose error message points at DEVerbosity(), which is unreachable from using OrdinaryDiffEq and disagrees with NEWS.md

2 participants