Skip to content

Store FIRK stages for dense output with extrapolant = :constant - #4924

Open
singhharsh1708 wants to merge 2 commits into
SciML:masterfrom
singhharsh1708:firk-constant-extrapolant-dense
Open

singhharsh1708 wants to merge 2 commits into
SciML:masterfrom
singhharsh1708:firk-constant-extrapolant-dense

Conversation

@singhharsh1708

@singhharsh1708 singhharsh1708 commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

With extrapolant = :constant, the dense output of RadauIIA3, RadauIIA5, RadauIIA9 and AdaptiveRadau is the collocation polynomial again instead of the step's starting value, so saveat and sol(t) inside a step are right. GaussLegendre now stores its stages the same way, but its dense output is the generic Hermite interpolant, which does not read them, so its results are unchanged.

The stages in integrator.k were stored after an accepted step only when extrapolant != :constant. That guard dates from when they served only as the next step's predictor; the interpolants now read them too. They are now stored after every accepted step, and :constant still only sets the next step's Newton guess to zero, as it already zeroes z and k at the start of each step, so the steps and their values are unchanged.

On u' = cos(t) u over (0, 4) at reltol 1e-8 with saveat = 0.01, the :constant error of the four Radau methods goes from 2.0e-2 to 1.2 on master to 2.8e-8 to 1.8e-6, in place and out of place, with the same step counts; for RadauIIA3, RadauIIA5 and RadauIIA9 that is about what the default extrapolant gives. AdaptiveRadau's default dense output is off by 0.2 on this problem for a separate reason: its interpolant uses the current stage count for every past step (#4925, fixed in #4935). The new testset fails on master for the four Radau methods (GaussLegendre was not affected), and FIRK Core and QA pass on Julia 1.12.

This is the extrapolant half of #4912; smooth_est = false needs its own change. Against #4935, keep its unguarded storage at the max-stage nodes. FIRK goes to 2.8.10, the same version as #4839 and #4920.

AI Disclosure: Claude assisted with this change.

@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): MERGE, risk medium-high (MH1). Reviewed head: fe0328d3004c34016a3ac529a2e8203b6a6138e7.

Full review

VERDICT: MERGE
PR: #4924
Round: 1
Head: fe0328d
Base: 45ff46a
Slot: 1
Reviewer: Codex CLI 0.163.0 / gpt-6.1-sol
Session: 01a1975d-fc30-7f50-962f-95d66a8e660a
Reviewed: 2026-11-01T14:00:50Z
Summary: The PR retains accepted FIRK stages so Radau interpolation works with extrapolant = :constant. The analytic regression fails on base and passes on head, with unchanged step endpoints in supplementary comparisons.

Risk

medium-high (MH1: changes interpolation results, an OrdinaryDiffEq main lane, for the constant FIRK predictor.)

Risk assessment

  • Risk: medium-high (MH1: restores dense interpolation across four Radau algorithms and both cache forms.)
  • Blast radius: FIRK stage storage for RadauIIA3, RadauIIA5, RadauIIA9, AdaptiveRadau and GaussLegendre. Default predictor logic and step control remain unchanged.
  • Evidence: Changed test file passes on head; its new regression fails eight cases on base. Supplementary probes exercise scalar, in-place and out-of-place problems; recorded endpoint histories match exactly across 60 configurations. FIRK Core and QA CI pass.
  • Verdict: MERGE
  • Round: 1
  • Blocking priorities: none
  • Merge: merge

Blocking findings

None.

Non-blocking findings

None.

What I ran

Commands below abbreviate the supplied Julia runner as $JRUN, the merge-base worktree as $BASE, and the permitted probe directory as $PROBES.

Read pr.json first, inspected the diff and surrounding initialization, predictor, interpolation and saving code, then recorded MH1 before reading the description and comments. No root AGENTS.md or CLAUDE.md was present.

Read-only verification commands, all exit 0:

git rev-parse HEAD
git rev-parse origin/master
git merge-base origin/master HEAD
git diff origin/master...HEAD
git diff --check origin/master...HEAD
git log -1 --no-merges --format=%B
gh pr view 4924 --repo SciML/OrdinaryDiffEq.jl --json number,url,state,isDraft,headRefOid,baseRefName,baseRefOid
gh issue view 4912 --repo SciML/OrdinaryDiffEq.jl --json number,title,body,state,url

HEAD matched the supplied SHA; live PR state was OPEN, non-draft, at that head. The merge-base matched the supplied Base SHA. Local origin/master was d03562190d2beb2c94da8d565b222263c418ce4a. Commit trailers contained neither author harness nor model.

Copied the complete changed test file into base, without modifying source files:

TEST=lib/OrdinaryDiffEqFIRK/test/ode_firk_tests.jl
cp "$TEST" "$BASE/$TEST"
cmp "$TEST" "$BASE/$TEST"

Both commands exited 0.

Ran this identical command once from head and once from base, through the supplied runner using Julia 1.13.1, four Julia threads and one OpenBLAS thread:

"$JRUN" --project=lib/OrdinaryDiffEqFIRK -e 'using TestEnv; TestEnv.activate(); using Test, SafeTestsets; println("Julia ", VERSION); include("lib/OrdinaryDiffEqFIRK/test/ode_firk_tests.jl")'

Head exit 0, output tail:

Dense output with extrapolant = :constant | 10  10  11.3s

Base exit 1, output tail:

Dense output with extrapolant = :constant | 2  8  10  12.2s
ERROR: LoadError: Some tests did not pass: 2 passed, 8 failed, 0 errored, 0 broken.

The eight Radau failures at lib/OrdinaryDiffEqFIRK/test/ode_firk_tests.jl:351 had errors between 0.0200578 and 1.15192, against < 1e-5. Both GaussLegendre cases passed. Earlier tests in the file completed successfully on both trees.

Supplementary commands:

# From head:
"$JRUN" --project=lib/OrdinaryDiffEqFIRK "$PROBES/probe.jl" "$PROBES/head-endpoints.txt"

# From base:
"$JRUN" --project=lib/OrdinaryDiffEqFIRK "$PROBES/probe-base.jl" "$PROBES/base-endpoints.txt"

cmp "$PROBES/head-endpoints.txt" "$PROBES/base-endpoints.txt"

The probes cover all five algorithms, three state/function forms, both predictors and fixed/adaptive stepping, checking retained sol(t) against exp(sin(t)). AdaptiveRadau uses fixed order five.

  • Head probe exit 1: 120 passed, 0 failed, 60 errored. All 60 accuracy and 60 end-time assertions passed; my unqualified successful_retcode call caused the 60 errors.
  • Base probe imports that helper explicitly; exit 1: 156 passed, 24 failed, 0 errored. The 24 failures are constant-predictor Radau interpolation cases.
  • Snapshot comparison exit 0, no output: step times, endpoint values and accepted/rejected counts match exactly across all 60 configurations.

Final head working tree was clean. Base contained only the copied test-file change.

Inspected supplied CI excerpts and check URLs:

What I did not verify

No package-wide or heavy downstream suites were rerun. GPU hardware and outer AD/sensitivity paths were not exercised locally; no GPU kernels or AD rules changed. Variable-order AdaptiveRadau retained history, callbacks and reinit! were not separately probed.

The supplementary head probe’s return-code assertions were not successfully executed because of my missing qualification; its accuracy and end-time assertions passed. The unmatched ModelingToolkit CI failure was not reproduced locally.

AUTHOR_GRADE: {"task":"bugfix","score":5,"difficulty":2,"rounds":1,"author_harness":"unknown","author_model":"unknown","note":"Focused stage-storage fix with an independent analytic regression: eight cases fail on base and all ten pass on head. Endpoint histories and step counts match across 60 supplementary configurations; the task is a small local solver-cache correction."}


🤖 Posted by an AI agent · harness: Claude Code · model: claude-opus-5-5 (coordinating gpt-6.1-sol reviewers)
Review: unreviewed
Session: 01a1975d-fc30-7f50-962f-95d66a8e660a

@ChrisRackauckas

Copy link
Copy Markdown
Member

🤖 Automated comment from an AI agent running as @ChrisRackauckas — not written or reviewed by Chris.

PR: #4924

Reviewed head: fe0328d
Raw SHA-256: 74bde34285b1e7f275f520508cf508a4adc100fd2e614ab49d4ef185ba39fb84
Source SHA-256: cada1649d8da72527b47332544368cbd1a39031455d578be1bba00bb23a1d008
Source: standard-review-v1 (harness-log)
Reviewer session: 01a1d256-5d73-72e0-b7ea-ac44c46825ed
Slot: 1
Confidence: unknown
Own risk fixed before author rating/discussion: yes

Risk assessment

  • Risk: medium-low (ML2)
  • Verdict: CHANGES
  • Reviewed head: fe0328d

Review report

VERDICT: CHANGES
Round: 1
Head: fe0328d
Slot: 1
CI: CI is not green (regress:SciMLSensitivity.jl/Core4/1 / Downstream Tests - Core4; ModelingToolkit.jl/InterfaceI/1 / Downstream Tests - InterfaceI)
Session: 01a1d256-5d73-72e0-b7ea-ac44c46825ed

Risk

medium-low (ML2: numerical fix confined to non-default FIRK extrapolation; independent analytic regression fails before and passes after; ten tested default-path fingerprints are unchanged)

Blocking findings

  • [P1] Downstream regression status needs verification. .github/workflows/Downstream.yml:23 and .github/workflows/Downstream.yml:25 select the two jobs reported as regressions in the review input. I did not inspect their logs or reproduce those failures. Passing the FIRK suite does not establish the policy's no-downstream-regressions gate. The owning head must resolve these failures or provide head/default-branch reproductions establishing that this diff does not cause them. This is a verification blocker, not a confirmed code defect introduced by this PR.

  • Verdict: CHANGES

  • Round: 1

  • Blocking priorities: P1

  • Deferred P2 issues: None supplied.

Non-blocking findings

No introduced code defect was confirmed. The probes exposed these pre-existing problems; they should be handled separately:

  • lib/OrdinaryDiffEqFIRK/src/firk_interpolants.jl:107: AdaptiveRadau historical dense output uses the current cache's stage count/tableau. For u' = u, u0 = [1, 2], (0, 4), reltol=1e-8, abstol=1e-10, stages change from 3 to 5. Against exp(t) .* u0 on 803 points, head errors are 0.475903 OOP / 0.449380 IIP, with either extrapolant. The merge-base default extrapolant gives the same errors. Suggested fix: retain the stage count/tableau for each saved interval and test interpolation across order changes. The connection to the mutable cache is inferred from reading; the numerical counterexample is confirmed by running.

  • lib/OrdinaryDiffEqFIRK/src/firk_perform_step.jl:2433: GaussLegendre OOP with the same RHS and matrix u0=[1.0 2.0; 3.0 4.0] throws DimensionMismatch: array could not be broadcast to match destination, on both head and merge-base implementation. Suggested fix: flatten or reshape the stage residual consistently with the flat Newton vector, with a matrix-state regression test.

What I ran

AI reviewer: codex-cli 0.164.0, model gpt-6.1-sol; actual local session ID above, no session URL available.

All Julia tests preserved the supplied layered JULIA_DEPOT_PATH, used the requested job TMPDIR, two Julia threads, one precompile task, and an 8 GiB systemd scope. Execution and all temporary source changes were confined to scratch/; the checkout remains clean.

  • git diff origin/master...HEAD, relevant implementations/tests and risk criteria; git diff --check origin/master...HEAD: clean. No root AGENTS.md/CLAUDE.md files were present. Newest commit has no Agent-Harness/Agent-Model trailers.
  • julia --version: 1.13.2. No committed Julia manifest or .julia-version was present. Created one scratch environment from FIRK's declared dependencies/test extras, compat and local source dependencies; resolved SciMLBase 3.58.0 and reused that manifest for every comparison.
  • uv run --locked python --version: Python 3.15.0, with --locked has no effect when used outside of a project. No Python project/uv.lock exists here, so this does not establish a project-locked Python environment.
  • GROUP=Core julia --startup-file=no --project=../scratch/env ../scratch/run_core.jl, invoking Pkg.test("OrdinaryDiffEqFIRK"; allow_reresolve=false): 337/337 pass — FIRK 201, time reversal 16, Krylov 98, LHL 22.
  • julia --startup-file=no --project=../scratch/env ../scratch/probes.jl: adaptive dense accuracy, forced rejected steps, backward solves and repeated interpolation 80/80 pass, worst absolute error 1.80118e-6. Refinement 47 pass / 3 fail under fixed nonlinear tolerances. Constant OOP RadauIIA9 endpoint errors for dt=1/8,1/16,1/32 are 5.55991e-10, 1.92214e-9, 1.67502e-8, exactly identical before/after; the IIP fine-grid dense error reaches 3.39018e-12. These failures do not establish a regression or an asymptotic-order defect; this was not a clean probe run.
  • ../scratch/followups.jl: event/reinitialization 40/40 pass; scalar/matrix probes 18 pass / 1 error, the GaussLegendre counterexample above. That error stopped this script; remaining checks were run separately.
  • ../scratch/final_checks.jl: Float32 20/20 pass. With only the scratch production step file restored from merge base 45ff46afe9919acf32e9af8058705073e0995503, the added regression gives 2 pass / 8 fail (errors 0.0201–1.1519, threshold 1e-5). Restoring the head gives 10/10 pass. Baseline diagnostics reproduce the counterexamples above and the unchanged refinement results. Control checks 6/6 pass.
  • Compared serialized default-extrapolant (t,u,naccept,nreject,njacs) fingerprints for all five algorithms, IIP/OOP: 10/10 identical between merge-base and head step implementations. Six constant-RadauIIA9 endpoint-error records also match exactly. Scratch production source was restored byte-for-byte; git status --porcelain is empty.

What I did not verify

The supplied downstream regressions and their CI logs; QA/formatter/docs checks; other Julia versions or platforms. No PR body, comments, reviews, earlier same-head opinions or author risk rating were consulted. No GitHub writes were made.

AUTHOR_GRADE: {"task":"bugfix","score":4,"difficulty":2,"rounds":1,"author_harness":"unknown","author_model":"unknown","note":"Independent analytic regression fails 8/10 before and passes 10/10 after; all 337 FIRK Core assertions pass and ten default-path fingerprints match. Additional counterexamples predate the diff; supplied downstream regressions remain unverified."}

Head link: fe0328d


🤖 Posted by an AI agent — harness: codex-cli 0.164.0 · model: gpt-6.1-sol
Conversation: local session 01a1d256-5d73-72e0-b7ea-ac44c46825ed (no session URL)

@singhharsh1708

Copy link
Copy Markdown
Member Author

Both downstream failures are green on master and these runs predate that, so I merged master to re-run. The GaussLegendre matrix-state DimensionMismatch is on master too and gets its own issue; the AdaptiveRadau dense-output part is #4935.

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.

2 participants