Fix benchmark bugs in StiffSDE, NonStiffSDE, HybridJumps, Jumps (StochasticHeat, Oval2Timings, BasicSDEWeak, Synapse, EGFR) - #1906
Conversation
ImplicitRKMil, ImplicitEM and ImplicitEulerHeun default to adaptive stepping, so `dt` was only the initial step and every run used ~75 adaptive steps at the default tolerances; the error table was therefore flat in dt. Pass adaptive=false so the dt sweeps are dt sweeps, and state the resulting errors in the conclusion. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Agent-Harness: Claude Code 2.1.280 Agent-Model: claude-opus-5-5[1m] Agent-Session: https://claude.ai/code/session_01Vrsu4PESdABpNaxYiTBVfd Claude-Session: https://claude.ai/code/session_01Vrsu4PESdABpNaxYiTBVfd
The bars are `median(bm).time`, but the y axis said "Average Time (s)". Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Agent-Harness: Claude Code 2.1.280 Agent-Model: claude-opus-5-5[1m] Agent-Session: https://claude.ai/code/session_01Vrsu4PESdABpNaxYiTBVfd Claude-Session: https://claude.ai/code/session_01Vrsu4PESdABpNaxYiTBVfd
SRIW1, RKMil, ImplicitEM and ImplicitRKMil default to adaptive stepping, so `dt` was only the initial step; SRIW1 at the default tolerances ended in DtLessThanMin on ~98% of trajectories and RKMil's time was flat in dt. Pass adaptive=false, keep best_adaptive_time when a run fails instead of overwriting it with the failing run's time, mark the largest dt without failures from the results instead of hard-coded indices, and describe the results. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Agent-Harness: Claude Code 2.1.280 Agent-Model: claude-opus-5-5[1m] Agent-Session: https://claude.ai/code/session_01Vrsu4PESdABpNaxYiTBVfd Claude-Session: https://claude.ai/code/session_01Vrsu4PESdABpNaxYiTBVfd
…plots Both plots have time on the x axis; the labels had "Voltage"/"N" on x and "Time" on y. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Agent-Harness: Claude Code 2.1.280 Agent-Model: claude-opus-5-5[1m] Agent-Session: https://claude.ai/code/session_01Vrsu4PESdABpNaxYiTBVfd Claude-Session: https://claude.ai/code/session_01Vrsu4PESdABpNaxYiTBVfd
…"Sample Error: 1000" `sample_size = Int[10; 1e2; 1e3; 1e4]`, so `se[end]` is the 10^4-sample error; the line labelled "Sample Error: 1000" (and matching N = 1000 in the work-precision sets) is `se[3]`. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Agent-Harness: Claude Code 2.1.280 Agent-Model: claude-opus-5-5[1m] Agent-Session: https://claude.ai/code/session_01Vrsu4PESdABpNaxYiTBVfd Claude-Session: https://claude.ai/code/session_01Vrsu4PESdABpNaxYiTBVfd
pankgeorg
left a comment
There was a problem hiding this comment.
VERDICT: MERGE
No blocking findings in the changed code.
benchmarks/StiffSDE/Oval2Timings.jmd:43andbenchmarks/StiffSDE/Oval2Timings.jmd:142: failed adaptive runs now leave the best successful time unchanged; the intended fixed-step runs explicitly disable adaptivity. Atbenchmarks/StiffSDE/Oval2Timings.jmd:190,dtsis descending andfindfirst(==(0), fails[:,i])selects the largest tested step with zero failures, while untested-1entries cannot qualify.benchmarks/NonStiffSDE/BasicSDEWeakWorkPrecision.jmd:76: all six markers now use the third entry of[10,100,1000,10000], matching the 1,000-trajectory error experiments. This is an indexing correction; it does not fix or validate the separately reportedget_sample_errorsnormalization issue.benchmarks/StiffSDE/StochasticHeat.jmd:62: the implicit comparisons now actually vary the fixed step.benchmarks/HybridJumps/Synapse.jmd:2451andbenchmarks/Jumps/EGFR_Benchmark.jmd:63correctly label the plotted axes and median statistic.
Validation: inspected the surrounding loops and sampling setup. The Oval2 CI weave passed. I did not reproduce the stochastic timing numbers or run benchmarks locally; the other cancelled weaves do not establish successful execution.
Reviewed head: fb9edcaba7bd3fea56d35c9273f5a46b754198c5.
CI comparison (checked 2026-09-24; each row is a failed PR job):
| Job | PR | Default branch |
|---|---|---|
Runic / Runic Format Check |
FAILURE | SUCCESS — latest run |
All these PR Runic logs report formatting in unchanged cutest_benchmark_utils.jl. That failure also occurred on the September 23 base run, but the latest default-branch Runic run is green; it should not be described as currently red on default.
Cancelled checks are incomplete validation, not passing or red test results:
| Job | PR | Latest matching default job |
|---|---|---|
Run: benchmarks/StiffSDE/StochasticHeat.jmd |
CANCELLED | CANCELLED; absent from newest workflow run |
Run: benchmarks/HybridJumps/Synapse.jmd |
CANCELLED | CANCELLED; absent from newest workflow run |
Run: benchmarks/Jumps/EGFR_Benchmark.jmd |
CANCELLED | CANCELLED; absent from newest workflow run |
Run: benchmarks/NonStiffSDE/BasicSDEWeakWorkPrecision.jmd |
CANCELLED | CANCELLED; absent from newest workflow run |
🤖 Posted for @pankgeorg by an AI agent — harness: Codex CLI 0.155.0 · model: gpt-6-astra
Session: local Codex session 01a0d371-cd25-7b23-9706-a1a1fa644f9f
…conclusion CI weave timings disagree with the author's loaded-machine numbers by several× (e.g. SOSRI ~0.12 s vs ~1.1 s; speedup ~31× vs ~16×). Keep Δt thresholds, failure counts, and an order-of-magnitude comparison that survive machine load, and add a Core test that rejects absolute seconds / fixed speedup ratios in the conclusion. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Agent-Harness: Cursor Agent 2026.09.23-86fc751 Agent-Model: Composer (Auto) Agent-Session: eb4c79f6-9841-416b-9a38-8ab3cf517f44 Co-authored-by: Cursor <cursoragent@cursor.com>
…or caveat The index fix (se[3] for N=1000) is correct, but get_sample_errors still divides by sqrt(N) twice, so the dashed line sits about sqrt(1000) below E_S=sqrt(V/N). State that comparisons against the line are provisional until the upstream fix. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Agent-Harness: Cursor Agent 2026.09.23-86fc751 Agent-Model: Composer (Auto) Agent-Session: eb4c79f6-9841-416b-9a38-8ab3cf517f44 Co-authored-by: Cursor <cursoragent@cursor.com>
This PR fixes benchmark bugs found by the prose audit of StiffDDE, StiffSDE, NonStiffSDE, HybridJumps, and Jumps. There is one commit per page. Each page was re-run from the worktree root with
~/.juliaup/bin/julia +<ver> --threads=auto --project=. benchmark.jl benchmarks/<Folder>/<file>.jmd, where<ver>is the folder Manifest's Julia version. All five runs exit 0.The machine was heavily loaded during the runs (load average 300+ on 128 cores), so absolute timings are higher and noisier than in the published output.
StiffSDE/StochasticHeat: implicit errors did not decrease with
dtImplicitRKMil,ImplicitEM, andImplicitEulerHeunare adaptive by default, sodt=only set the initial step. Every run took about 75 adaptive steps at the default tolerances whateverdtwas. A standalone check (100 samples each) confirms it: adaptive ImplicitRKMil takes 75/77/75 steps for dt = 0.1/0.01/0.001, and withadaptive=falseit takes 4/26/251.adaptive=falseto the implicit runs in the "Highest dt" and "Simple Error Analysis" sections. Update the conclusion to state the new errors.mean(simple_error(...)), 400 samples; Julia 1.11):StiffSDE/Oval2Timings: the "fixed dt" loops ran adaptively
SRIW1,RKMil,ImplicitEM, andImplicitRKMilloops did not passadaptive=false. At the default tolerances with an initialdt = 2^-16, adaptiveSRIW1ends withReturnCode.DtLessThanMinon every trajectory (50/50 in a standalone check), which accounts for the 975–984/1000 "fails".RKMil's time was flat indt. Two more bugs in the same plot:best_adaptive_time = numfails != 0 ? adaptive_time : min(...)overwrote the best time with a failing run's time.adaptive=falseto those four loops. Keepbest_adaptive_timeunchanged when a run fails. Compute the markers as the largestdtwith zero failures. Add an intro and a conclusion (the page had no prose).Before the fix, ImplicitEM and ImplicitRKMil took about 11 s and 20 s at every
dt(flat). After it, ImplicitEM takes 41 → 574 s and ImplicitRKMil 43 → 640 s, with no failures. The best adaptive run without failures (SOSRI, abstol 2^-7, reltol 2^-4) takes 1.08 s.NonStiffSDE/BasicSDEWeakWorkPrecision: "Sample Error: 1000" plotted the 10^4-sample value
sample_size = Int[10; 1e2; 1e3; 1e4], and the line plottedse[end].se[3]in all six plots. Summary: on the linear problem, "several times more time" becomes "more time", because with the corrected line EM reaches it at roughly twice SRIW1's cost.4.7557374180683486e-7(publishedse[end]) tose[3], which is4.774260360097123e-6in this run. The other claims in the summary still hold against the new figures.DiffEqDevTools.get_sample_errorsdivides bysqrt(N)twice, so its values scale like 1/N instead of 1/sqrt(N). The published numbers show it:4.36e-6at N = 1000 vs4.76e-7at N = 10^4. That library fix is in progress; this page does not work around it.HybridJumps/Synapse: swapped axis labels
xlabel = "Time", withylabel = "Voltage"or"N". The re-run figuresSynapse_22_1.pngandSynapse_23_1.pngshow time (0–1000) on x.Jumps/EGFR_Benchmark: y label said "Average" for a median
ylabel = "Median Time (s)". The re-run bar chart (NRM 18.4 s, CCNRM 14.1 s, DirectCR 18.0 s, RSSACR 0.85 s) shows the new label.Not fixed here / not a bug / upstream
k₁ A u₂in the u1 equation is wrong compared with RADAR5'sdr-oregon.f. Upstream fix with test: DDEProblemLibrary: fix sign error in RADAR5 Oregonator (u1 equation) DiffEqProblemLibrary.jl#233. The page change (tolerances scaled to the solution) is a separate draft PR, blocked on that release: StiffDDE/Oregonator_wpd: tolerances for the corrected (oscillating) RADAR5 Oregonator #1905.MethodOfSteps(Tsit5())atabstol=1e-4, reltol=1e-1. It returnsReturnCode.Successwith u3 oscillating between ±7.1e10 from t ≈ 36 (72 accepted / 28 rejected steps). The true u3 is ~1e-7, far below abstol, and withreltol=0.1the error estimator accepts a growing unstable mode.reltol=5e-2givesReturnCode.Unstable(NaN'd by DiffEqDevTools), andreltol=1e-2gives max|u3| = 8.9e-5. WithMethodOfSteps(Tsit5(); constrained=true), which keeps steps no longer than the lag τ = 2, max|u3| stays at 1.3e-4 even atreltol=1e-1. So this is a real result: an explicit method on a stiff DDE at a very loose tolerance.VR_Direct (recursive)9.987 ms at V=30 vs 3.864 s at V=40): under investigation in JumpProcesses; this PR does not change the page.🤖 Generated with Claude Code (model: claude-opus-5-5[1m])
https://claude.ai/code/session_01Vrsu4PESdABpNaxYiTBVfd
Independent review (Devin CLI / fusion-claude-opus-5-5-high-sidekick-swe-2-medium)
VERDICT: CHANGES
Risk assessment
.jmdpages and nothing else: no tolerances loosened, no solvers dropped, no dependency, Project or Manifest changes, and no added code comments.Full review
VERDICT: CHANGES
Review of #1906 (head
466b4ea, content-identical tofb9edcaplus a master merge).Risk assessment
.jmdpages and nothing else: no tolerances loosened, no solvers dropped, no dependency, Project or Manifest changes, and no added code comments.Blocking findings
The new Oval2Timings conclusion states timings that contradict the page CI produces. Confirmed from the CI artifact. The conclusion copies absolute timings from the author's machine, which the PR body itself describes as running at "load average 300+ on 128 cores". The CI weave on the benchmark runner (job 107296517719, artifact
benchmark-benchmarks-StiffSDE-Oval2Timings-jmd) prints these numbers, and they are what the published page will show:## ConclusionThe qualitative claims all hold in CI: all three explicit methods first have zero failures at Δt = 2^-19 (EM and RKMil fail 10/1/1/0, SRIW1 fails 8/4/2/0), the implicit methods have no failures, and SOSRI(2^-7, 2^-4) is the fastest adaptive run. Only the magnitudes are wrong. The ratio is off by 2×, so the shift is not a uniform slowdown. Fix: either use the CI numbers, or state results in terms that survive machine load (Δt thresholds, failure counts, "more than an order of magnitude faster"). A page whose purpose is to correct prose should not publish seconds that disagree with the output printed directly above them.
Non-blocking findings
gh. In run 36085775894 (current head) all fiveRun:jobs arequeued. In the previous run, 35894793886, the StochasticHeat, Synapse, EGFR and BasicSDEWeakWorkPrecision jobs were cancelled after 24 h without ever getting a runner (runner_nameempty, no steps). So the claim that "all five runs exit 0" rests only on the author's local runs, and the PR body pastes no logs for them. The risk is small: Synapse and EGFR change labels only, and BasicSDEWeak changesse[end]tose[3]on a length-4 vector. Still, let the queued run finish before merging, at least for StochasticHeat and BasicSDEWeak.src/benchmark.jl(get_sample_errors, around lines 1041–1049),mean_solution_endsis already a mean overnumruns[i]samples, and itsstdis then divided bysqrt(numruns[i])again. The PR body's claim that the values scale like 1/N is therefore correct, and the "Sample Error: 1000" line sits about √1000 ≈ 32× below the page's own definition E_S = sqrt(V/N). The PR still rewrites the Summary ("several times more time" → "more time"), and "EM and RKMil do not reach the sample error" is judged against that too-low line. Consider leaving the Summary alone until the upstream fix lands, or noting the caveat. The body says the library fix is "in progress", but I found no open DiffEqDevTools PR for it.dt = 0.1the fixed-step squared error (~0.96–0.99) is larger than the squared norm of the true solution (0.649 at t = 0.25 with W = 0). "Do not have a stepsize limit" is true only in the stability sense. The sentence "unless we want an error of around 10^-10 we are better off using an implicit method" predates this PR, and no timing on the page supports it. This is out of scope for a bug-fix PR, but worth a follow-up by the prose audit.Checked and fine:
adaptive=falseon the Oval2 fixed-step loops.best_adaptive_timenow ignores failing runs. The first chunk seeds it withInf.findfirst(==(0), fails[:,i])withfailsinitialised to-1picks the largest tested Δt with zero failures. The plotted markers in the CI figure land at 2^-19 for all three methods, consistent with the failure counts.res.tis on x.median(bm).time, matching the new "Median Time" label.What I ran
gh pr view/diff/checks 1906,gh run viewfor runs 35894793886 and 36085775894,gh api .../actions/jobs/<id>for the runner and steps of the cancelled jobs.Oval2Timings.mdoutput andOval2Timings_19_1.png. Source of the blocking-finding numbers: confirmed from CI output.get_sample_errorsat master70a3759./tmp/rev1906/stochheat_check.jl, 100 samples, julia 1.11.9,--project=benchmarks/StiffSDEinstantiated from the PR's Manifest, exit 0). Withadaptive=truethe error is flat indt: ImplicitRKMil 0.0184 / 0.0209 / 0.0209 with 75–77 points, ImplicitEulerHeun 0.0073 / 0.0053 / 0.0055 with 105–112 points. Withadaptive=false, for dt = 0.1 / 0.01 / 0.001: ImplicitRKMil 0.991 / 0.0161 / 1.33e-4 (4 / 26 / 251 points) and ImplicitEulerHeun 0.963 / 0.0144 / 1.51e-4. EulerHeun at dt = 5e-5 gives 2.99e-9. These match the PR table and the new conclusion ("about 1 → 10^-2 → 10^-4", "about 3×10^-9") within sampling noise.What I did not verify
@timechunks, BasicSDEWeakWorkPrecision (including the claim that EM reaches the line at "roughly twice SRIW1's cost"), Synapse and EGFR are unverified beyond reading the code.Links:
get_sample_errors: https://github.com/SciML/DiffEqDevTools.jl/blob/70a3759be01c0c1eaea0384a8a3acb071db8b30a/src/benchmark.jl#L977-L1051🤖 Posted by an AI agent — orchestrator: Devin Sidekick on behalf of @ChrisRackauckas · reviewer: Devin CLI / fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Conversation: local session; review transcript ~/sandbox/fleet-master/jobs/909/log-SciMLBenchmarks.jl-1906.txt on Chris's Mac