Skip to content

Step FBDF by the increment the integrator commits - #4939

Open
singhharsh1708 wants to merge 3 commits into
SciML:masterfrom
singhharsh1708:fbdf-4909
Open

singhharsh1708 wants to merge 3 commits into
SciML:masterfrom
singhharsh1708:fbdf-4909

Conversation

@singhharsh1708

Copy link
Copy Markdown
Member

FBDF now steps by the increment the integrator commits, (t + dt) - t, instead of the proposed dt. Its history ts holds the committed times, so once dt is a few hundred ulps of t the BDF coefficients and the error estimate were built for a step a few percent away from the stored one. The estimate then stops shrinking with dt, each rejection cuts dt further, and the solve ends with dt below eps, as in #4909. This is the committed-increment part of #4615 on its own, without that PR's first-step changes.

On the ring from #4909 (pump power 8, 10, 12 and 15 mW, reltol 1e-7 to 1e-10), master stops in 2 of 16 runs and this branch finishes all 16. Over the 14 runs both finish, f evaluations change by -0.5% to +1.3% (+0.3% in total) and the temperature error against Vern9 is 0.26 to 1.45 times master's. Stiff Van der Pol (mu = 1e3) at 1e-10 stops on master and finishes here with error 2.2e-8. ROBER, OREGO and HIRES at 1e-4 to 1e-10, and Van der Pol at 1e-4 to 1e-8, stay within 3% on steps and f evaluations with the same errors.

The new testset solves a stiff two-state problem from t₀ = 1e6, in place and out of place, and compares it with the same problem started at 0. Master stops with Unstable there; on this branch the two agree to 1e-11. BDF Core and QA pass.

QNDF fails the same probe on its first step instead, which is #4606, the other half of #4615. BDF goes to 2.4.14, the same version as #4938.

Fixes #4909

AI Disclosure: Claude assisted with this change.

@prestalab

Copy link
Copy Markdown

Julia 1.13 qualification and temporary-dt cleanup

Tested head ae60d41d10ebbe2f59e22616a9728db54b2b5e0e against master 45ff46afe9919acf32e9af8058705073e0995503 with Julia 1.13.0 on Windows x64. The environments match except for OrdinaryDiffEqBDF, changed through Pkg.develop(...; preserve=Pkg.PRESERVE_ALL).

Focused results:

  • This PR's new shifted-origin regression: 4/4 pass on the PR, 0/4 on master.
  • An unchanged consumer regression for u'=1, u(0)=1, one domain-rejected trial, in-place/out-of-place/scalar forms: 105/105 on both.
  • The small-step FBDF formula now agrees with the committed clock increment. Core's separate tstop-snap and dtmax problems remain; these are not regressions introduced by this BDF-only PR.

One cleanup issue in both FBDFCache and FBDFConstantCache wrappers: if _fbdf_perform_step! throws, the assignment restoring the original integrator.dt is bypassed. A standalone reproducer:

using OrdinaryDiffEqBDF

struct ClockProbeError <: Exception
    time::Float64
end

function exception_dt_probe(inplace)
    t0 = 3600.0
    dt = 1.5eps(t0)
    function rhs!(du, u, p, t)
        t > t0 && throw(ClockProbeError(t))
        du[1] = 1.0
        nothing
    end
    function rhs(u, p, t)
        t > t0 && throw(ClockProbeError(t))
        [1.0]
    end
    integrator = init(
        ODEProblem(inplace ? rhs! : rhs, [0.0], (t0, t0 + 1.0)), FBDF();
        adaptive = false, dt, dtmax = dt, abstol = 1e-12, reltol = 1e-9,
        save_everystep = false, dense = false
    )
    before = integrator.dt
    caught = false
    try
        step!(integrator)
    catch exception
        exception isa ClockProbeError || rethrow()
        caught = true
    end
    @assert caught
    println((inplace = inplace, before_ulp = before / eps(t0),
        after_ulp = integrator.dt / eps(t0), clock_unchanged = integrator.t == t0))
end

exception_dt_probe(true)
exception_dt_probe(false)
Cache form Before, ulps After on master, ulps After on PR, ulps
In-place 1.5 1.5 2.0
Out-of-place 1.5 1.5 2.0

The intended RHS exception is caught and integrator.t remains unchanged in all four runs. Could the temporary assignment be protected with try/finally in both wrappers? This is specifically about restoring the temporary dt swap, not a claim that arbitrary integrator resumption after an RHS exception is supported.

I did not run the full BDF Core/QA, GPU or downstream suites; the counts above are only the named focused checks.

AI-assisted review: omp, GPT-6-Astra. Numerical results above were executed on Julia 1.13.0; no package-cache or runtime method replacements were used.

@singhharsh1708

Copy link
Copy Markdown
Member Author

🤖 Automated comment from an AI agent running as @singhharsh1708, not written or reviewed by the account holder.

Independent review (Codex CLI 0.163.0, model gpt-6.1-sol, reasoning effort high): CHANGES, risk medium-high (MH1). Reviewed head: ae60d41d10ebbe2f59e22616a9728db54b2b5e0e.

Full review

VERDICT: CHANGES
PR: #4939
Round: 1
Head: ae60d41
Base: 45ff46a
Slot: 1
Reviewer: Codex CLI 0.163.0 / gpt-6.1-sol
Session: 01a19781-1c0b-78b1-9a48-7bdf1dd71850
Reviewed: 2026-11-01T14:34:43Z
Summary: FBDF now computes steps using the representable clock increment, fixing the reproduced shifted-origin failure. Independent analytic probes support the repair, but the submitted regression needs an independent accuracy oracle.

Risk

medium-high (MH1: changes numerical stepping on the FBDF main lane in both cache implementations).

Risk assessment

  • Risk: medium-high (MH1: FBDF’s effective step changes on existing calls).
  • Blast radius: In-place and out-of-place FBDF, including nonlinear solves, history, error estimation and dense-output construction.
  • Evidence: Changed regression file passes on head; its four new assertions fail on base. Independent analytic checks pass on head. Exception probes demonstrate a temporary-state cleanup regression. BDF CI checks pass; overall CI has six failed jobs.
  • Verdict: CHANGES
  • Round: 1
  • Blocking priorities: P2
  • Merge: needs changes (add an independent oracle to the numerical regression).

Blocking findings

  1. [P2] suspected from reading - lib/OrdinaryDiffEqBDF/test/bdf_regression_tests.jl:343: The expected endpoint is generated by FBDF itself. This tests time-origin consistency, but both solves could agree while sharing a numerical error; it does not provide the independent accuracy reference required by G1. Supplement the comparison with the closed-form solution of this linear system, or a separate validated implementation, for both cache forms. My analytic probe passes on head, so an independent assertion can be added without weakening the test.

Non-blocking findings

  1. [P3] confirmed by running - lib/OrdinaryDiffEqBDF/src/bdf_perform_step.jl:1262 and lib/OrdinaryDiffEqBDF/src/bdf_perform_step.jl:1519: An exception bypasses restoration of integrator.dt. With t₀=3600, fixed dt=1.5eps(t₀) and an RHS throwing after t₀, both head implementations leave dt=2eps(t₀) while time remains unchanged; base preserves the original value. Protect both temporary assignments with try/finally and add a restoration test. This finding concerns cleanup, not guaranteed integrator recovery after arbitrary exceptions.

What I ran

Commands below express the supplied runner and worktrees relative to the checkout. All four Julia runs were serial through jrun, using Julia 1.13.1.

  • Git inspection and verification, exit 0:

    git rev-parse HEAD
    git merge-base origin/master HEAD
    git diff origin/master...HEAD
    git diff --check origin/master...HEAD
    git log -1 --format=%B
    git log -1 --no-merges --format=%B

    Head and merge-base match the specified SHAs; whitespace check is clean; author harness/model trailers are absent.

  • Live read-only checks, exit 0:

    gh pr view 4939 --repo SciML/OrdinaryDiffEq.jl --json state,isDraft,headRefOid,baseRefOid
    gh issue view 4909 --repo SciML/OrdinaryDiffEq.jl --json number,title,body,state

    PR remains open, non-draft, at the requested head.

  • Head regression run, exit 0:

    ../../jrun --project=lib/OrdinaryDiffEqBDF -e 'using TestEnv; TestEnv.activate(); using Test, SafeTestsets; include("lib/OrdinaryDiffEqBDF/test/bdf_regression_tests.jl")'

    129 assertions passed across the file. Tail:

    FBDF steps by the increment it commits at large t (#4909) | 4  4
    
  • Copied the changed test file into the merge-base worktree, exit 0:

    cp lib/OrdinaryDiffEqBDF/test/bdf_regression_tests.jl ../../wtb/SciML__OrdinaryDiffEq.jl__4939/lib/OrdinaryDiffEqBDF/test/bdf_regression_tests.jl

    Ran the identical regression command there, exit 1. The preceding 125 assertions passed; all four new assertions failed:

    Evaluated: ReturnCode.Unstable == ReturnCode.Success
    FBDF steps by the increment it commits at large t (#4909) | 4 failed
    
  • Independent analytic and exception probes, once in each worktree:

    ../../jrun --project=lib/OrdinaryDiffEqBDF ../../tmp/SciML__OrdinaryDiffEq.jl__4939/probe.jl

    The analytic reference was derived by solving the two linear equations, independently of FBDF. Both cache forms were checked at origins 0 and ±1e6.

    Head exit 1, solely from the two restoration failures:

    analytic shifted problem | 12 passed
    temporary dt restoration | 2 passed, 2 failed
    dt_before_ulps = 1.5, dt_after_ulps = 2.0
    

    All six head solves succeeded; maximum absolute analytic error was below 4.2e-11.

    Base exit 1:

    analytic shifted problem | 4 passed, 8 failed
    temporary dt restoration | 4 passed
    dt_before_ulps = 1.5, dt_after_ulps = 1.5
    

    All four shifted base solves stopped with Unstable.

  • Inspected prefetched sublibrary CI, main CI and downstream CI. BDF Core, QA, GPU and downgrade checks passed. Compared failures with master main CI, master downstream CI and master sublibrary CI: the ReverseDiff tape error, stiff-adjoint unexpected passes, Enzyme thread assertion and both GPU warning failures match. ModelingToolkit InterfaceI’s equation-orientation mismatch has no matching signature in the supplied master logs.

  • Final status checks confirmed no tracked head changes; base contains only the authorized copied test change. No source files were edited.

What I did not verify

  • Full package, downstream and integration suites locally.
  • GPU, reverse-mode AD, Reactant or MPI execution locally; GPU evidence comes from CI.
  • The issue’s microring model or the author’s benchmark sweeps.
  • Cause of the unmatched ModelingToolkit CI failure.
  • Safe integrator continuation after an RHS exception.

AUTHOR_GRADE: {"task":"bugfix","score":1,"difficulty":3,"rounds":1,"author_harness":"unknown","author_model":"unknown","note":"The regression genuinely fails on base and passes on head, and independent analytic probes support the repair. The submitted numerical oracle nevertheless uses FBDF itself, triggering the rubric's self-referential-oracle penalty; the underlying task involves moderate multistep/time-grid reasoning."}


🤖 Posted by an AI agent · harness: Claude Code · model: claude-opus-5-5 (coordinating gpt-6.1-sol reviewers)
Review: unreviewed
Session: 01a19781-1c0b-78b1-9a48-7bdf1dd71850

@singhharsh1708

Copy link
Copy Markdown
Member Author

@prestalab Done, both wrappers restore dt in a finally now. Your probe gives 1.5 ulps after the throw in place and out of place, and BDF Core passes.

This branch has not been deployed

No deployments
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.

FBDF stops (dt below eps) on a 4-state self-pulsing oscillator that QNDF, Rodas5P and CVODE_BDF integrate

2 participants