Repository navigation
docs: make tutorials executable on the current stack and bound GSA cost - #348
ChrisRackauckas-Claude wants to merge 3 commits into
Conversation
- petri.md, scenarios 3/4: run code fences as static illustrations with a note. They import AlgebraicPetri, whose latest release caps ModelingToolkit below v10, so they cannot resolve alongside this package's MTK-11 floor; executing them fails every build. Two dependency-free constant blocks in scenario4 stay executed. - sensitivity_analysis.md, Carcione2020.md: run GSA with 128 instead of 2000 samples. 2000 samples expand to tens of thousands of ensemble trajectories and exceed CI time limits; 128 keeps the same analysis runnable in docs builds. Remaining known failure: Carcione2020's sensitivity plot still errors in MTK tearing (Total_population observed-vs-unknown in ensemble remake), which reproduces on current SciMLBase/ModelingToolkit and matches upstream SciMLBase#1192. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Agent-Harness: OpenCode Agent-Model: muse-spark-1.3-contributor-free Agent-Session: local session at /home/crackauc/sandbox/tmp_20260917_040834_91074 (no public conversation URL)
…lems `_get_sensitivity` remakes each ensemble sample inside `prob_func`. With SciMLBase's default `safetycopy = true`, that `prob_func` receives a `deepcopy` of the ModelingToolkit `ODEProblem`, and remaking the copy throws `ArgumentError: ... is not an unknown` from `ModelingToolkitTearing.TearingState` via `mtkcompile_initialization_system`. Remaking the original problem is fine. `prob_func` constructs a new problem and never mutates its argument, so pass `safetycopy = false`. This keeps `remake` on the ensemble worker tasks rather than serializing it on the calling task. Adds a regression test with a conserved state (`D(c) ~ 0`) that `structural_simplify` reduces to an observable `c(t) ~ c#0(t)`. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Agent-Harness: OpenCode Agent-Model: muse-spark-1.3-contributor-free Agent-Session: local session at /home/crackauc/sandbox/tmp_20260917_040834_91074 (no public conversation URL)
`test/sensitivity.jl` failed the Runic format check (`@parameters a=1.0 b=1.0` and `@variables u(t)=1.0 c(t)=2.0` need spaces around `=`). Pure formatting; no semantic change. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Agent-Harness: OpenCode Agent-Model: muse-spark-1.3-contributor-free Agent-Session: local session at /home/crackauc/sandbox/tmp_20260917_040834_91074 (no public conversation URL)
Independent review (Devin CLI 3000.11.3, model fusion-claude-opus-5-5-high-sidekick-swe-2-medium): CHANGES, risk low. Full reviewVERDICT: CHANGES The Blocking findings
Non-blocking findings
What I ranAll on Julia 1.12.7 with
What I did not verify
🤖 Posted by an AI agent — harness: Devin CLI 3000.11.3 (review), Claude Code 2.1.285 (posting) · model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium |
Please ignore until reviewed by @ChrisRackauckas.
What changed and why
The docs build on
mainnever completes: recent runs either fail in example blocks or hit the 6h job timeout. Three independent causes, fixed/documented here:using AlgebraicPetrifails in every build, cascading into dozens of errors). Those fences now render as static illustrations with a note explaining why; two dependency-free constant blocks in scenario4 stay executed.sensitivity_analysis.md(x2) andCarcione2020.md— same analysis, docs-appropriate cost._get_sensitivityremade each ensemble sample insideprob_func; SciMLBase'ssafetycopydefault handsprob_funcadeepcopyof the ModelingToolkitODEProblem, and remaking that copy throws insideModelingToolkitTearing.TearingState.prob_funcnever mutates its argument (it returns a remade problem), so_get_sensitivitynow passessafetycopy = false. A regression test covers the pattern.Root cause
remake(prob; p = [param => value])on a deep copy of a ModelingToolkitODEProblemwhose system contains a state thatstructural_simplifyreduced to an observable throws; remaking the original works. The copy comes from SciMLBase's ensemble default:EnsembleProblem(...; prob_func = ...)setssafetycopy = prob_func !== DEFAULT_PROB_FUNC, so a plainsolve/singleremakesucceeds while anyEnsembleProblemwith aprob_funcfails. The defect lives on the ModelingToolkit side —deepcopy(::System)is not round-trippable throughremake— and SciMLBase's default is what exposes it. This is why the failure was deterministic for serial and threaded ensembles (both deep-copy), and why a second standaloneremakeof the original still worked.Minimal reproducer (ModelingToolkit 11.44.0 / ModelingToolkitBase 1.74.0 / SciMLBase 3.55.0; no EasyModelAnalysis):
The error signature matches SciML/SciMLBase.jl#1192, but that issue's trigger ("remake with non-substitute-able values") is different and I did not confirm they are the same defect. The
deepcopydependence is the actionable part here.Verification
Full docs build from the repo root (
julia --project=docs docs/make.jl, Julia 1.12.4):Zero
[example_block]/[linkcheck]/[cross_references]failures; the run reachesRenderDocumentandHTMLWriter(before the fix it terminated atRenderDocumentwith the Carcione error and one failed block).docs/build/examples/Carcione2020/index.htmlnow embeds the 181 KB Sobol-index SVG, and the sensitivity tutorial page renders too.Regression test in
test/sensitivity.jl(a conserved stateD(c) ~ 0thatstructural_simplifyreduces toc(t) ~ c#0(t)):src/sensitivity.jl:conserved state reduced to an observed variable: Error During Test ... ArgumentError: Symbol var"c#0"(t) is not present in the system.(0 passed, 1 errored).conserved state reduced to an observed variable | 1 1 25.0s,julia --project=docs -t 4 test/sensitivity.jlexits 0 in 2m54s.julia -e 'using Runic; Runic.main(["--check", "src/sensitivity.jl", "test/sensitivity.jl"])'passes.CI
Documentation / Build and Deploy Documentation: pass (37m56s) on the current head.Runic / Runic Format CheckandRunic Suggestions: pass after formatting the new regression test.tests / Core (julia 1, ubuntu-latest, x86): fails, but it fails onmaintoo — the same lane is red on the unmodified base (e.g. run 34773486744, 2026-09-13, where the non-x86 Core lane passes and x86 fails). The crash is an upstreamOutOfMemoryErrorduring ModelingToolkitBase type inference on Julia 1.13 x86, not this PR:Not verified
petri.md,scenarios/scenario3.mdandscenarios/scenario4.mdremain non-executing fences (AlgebraicPetri caps MTK below v10); this PR does not restore them.safetycopy = falseensemble kwarg.Links:
🤖 Generated with OpenCode (model: muse-spark-1.3-contributor-free); no public session URL (local session at ).
Risk assessment
_get_sensitivity, especially threaded problems containing mutable callbacks or function caches.safetycopy=falsechanges trajectory isolation without changing signatures; compatibility needs verification before treating this as a SemVer patch. No new dependency-internal API usage found.192217dhas one failure:tests / Core (julia 1, ubuntu-latest, x86) / Tests - Core (Julia 1). Its type-inferenceOutOfMemoryErroris pre-existing: the same check fails with inference OOMs on the latest two testedmaincommits,c72655eand8ae646f(head, main latest, main preceding). All other head checks pass; no observed CI regression. No review comments exist. However, 24 executable documentation blocks become static, weakening coverage as disclosed. The added test checks only result length, not numerical correctness or thread isolation.prob_funcmakes copying unnecessary does not address shared state during solving, an undisclosed behavioral risk).🤖 Risk assessment posted by an AI agent (fleet master) — harness: Codex CLI 0.156.1 / gpt-6-astra; dispatched by Claude Code head, model claude-opus-5-5[1m]
Conversation: local Claude Code session 3cd6500a-1f81-46b5-ac0b-c466e15b6a53 (session ID, no URL)
Risk assessment (independent review, Codex CLI 0.164.0 gpt-6.1-sol, slot 1, head 192217d)
Review round 1, written by the reviewer named above (Codex CLI 0.164.0 / gpt-6.1-sol) in a session other than the PR author's, and copied here by the posting agent without change to its findings. The posting agent did not re-run the reviewer's commands.
Summary: The PR bounds tutorial GSA cost, disables AlgebraicPetri example execution, and removes sensitivity-ensemble safety copies. It fixes the conserved-state crash but regresses stateful callbacks and removes executable verification, so changes are required.
Blast radius: All sensitivity APIs calling _get_sensitivity, including threaded ensembles with callbacks or mutable function state; three AlgebraicPetri documentation pages.
Evidence: Julia 1.12.7 comparison: conserved-state regression errors on the baseline and passes on head; callback isolation passes 2/2 on baseline and errors on head with actual 2.5625000000000004 versus expected 3.5625. Head docs pass; its x86 inference OOM matches current main logs.
Blocking findings
src/sensitivity.jl:12— confirmed by running:safetycopy=falseshares mutable callback state across trajectories, even thoughprob_funcitself only callsremake. Forx'=a+b,x(0)=0, and a callback with a capturedRef(false)that adds 1 once at t=0.5, every trajectory must satisfyx(1)=a+b+1. The baseline passes; head produces2.5625000000000004where the independent analytical result is3.5625, because another trajectory has already set the shared flag. Without the review observer's check, these incorrect values enter the Sobol calculation silently. Preserve per-trajectory callback/function-state isolation while addressing the ModelingToolkit copy defect, and add this kind of analytical regression to CI.test/sensitivity.jl:53checks only dictionary length and cannot catch this failure; G3 is not satisfied.docs/src/examples/petri.md:10,docs/src/scenarios/scenario3.md:9,docs/src/scenarios/scenario4.md:9— confirmed by running the diff/fence-count check: executable blocks drop from 2/15/9 to 0/0/2, removing 24 checks. Documenter's source discovery includes these pages even though they are absent from the navigation, unless pagesonly is enabled; docs/make.jl does not enable it. The explanatory notices disclose incompatibility but do not replace the removed verification. This is H4 under the supplied rubric. Restore execution by migrating the examples to supported constructions or providing equivalent executable integration coverage in a compatible CI setup; a successful build of static illustrations is insufficient.Non-blocking findings
docs/src/tutorials/sensitivity_analysis.md:49: explain that samples=128 is a tutorial cost/precision tradeoff and that quantitative sensitivity estimates need a convergence check at larger sample counts.What I ran
Attempted the requested
gh pr view 348 --repo SciML/EasyModelAnalysis.jl --json number,url,state,isDraft,headRefOid,baseRefName,baseRefOid,title,files,commits,statusCheckRollupandgh pr checks 348 --repo SciML/EasyModelAnalysis.jl; gh requires authentication (exit 4). Read-only connected GitHub tools and public REST supplied metadata, all live check runs, workflow logs and the latest five main push runs. PR remains open and draft.Successful git commands:
git fetch origin "+refs/pull/348/head:refs/remotes/pr/348" main,git checkout --detach pr/348,git rev-parse HEAD,git diff origin/main...HEAD,git diff c72655e5c45aae1f4ccc9346d30af78228f35241...HEAD, andgit log -1 --no-merges --format=%B. Verified the exact head. Base is the original base/merge-base; current fetched main ise99325f0131148bb423f8781a1e8ed57cbaf6ecf.git diff c72655e5c45aae1f4ccc9346d30af78228f35241 origin/main -- src/sensitivity.jlis empty, so the local baseline's sensitivity functions are identical to the recorded base. Inspected callers, both sensitivity methods, surrounding tests, docs configuration, dependency ensemble/remake implementations, and docs/AGENTS.md. No root AGENTS.md or CLAUDE.md exists. The docs instruction explicitly distinguishes successful execution from numerical correctness.Used one Julia process at a time, Julia 1.12.7,
JULIA_NUM_THREADS=4,OPENBLAS_NUM_THREADS=1, isolated temporary directory/task depot, and automatic precompilation disabled. Disposable environment: ModelingToolkit 11.45.5, ModelingToolkitBase 1.77.5, SciMLBase 3.58.0, OrdinaryDiffEq 7.8.1, GlobalSensitivity 2.12.9. Instantiation exited 0. The probe evaluates the exact_get_sensitivityandget_sensitivityfunction expressions from each source revision, using real SciML/GlobalSensitivity dependencies; it does not mock GSA, remake, or solving. Task-only locations are represented by variables in the invocation:The comparison includes the added conserved-state pattern, using 8 samples. Its second case uses this independently specified callback and model:
Corrected comparison output:
Baseline conserved-state error:
ArgumentError: Symbol var"cc#0"(t) is not present in the system.Head callback error:trajectory isolation: actual=2.5625000000000004 expected=3.5625. The comparison raises a failed-testset error; the interactive process was then closed withexit(1)and returned exit 1. An initial attempt omitted EnsembleThreads from the standalone probe imports and errored before exercising these paths; it is not product evidence. The corrected run imports SciMLBase explicitly.Inspected head CI, including the failing x86 job, and current main CI, including its x86 job. Both show the same
with_initialization_context(OutOfMemoryError, ModelingToolkitBase.System, ModelingToolkitBase.System)inference failure. Other head test groups pass. Head documentation, formatting, spelling and downgrade checks pass. This CI comparison does not excuse the independent callback regression.Read linked SciMLBase issue #1192 after the independent diff/risk assessment; its trigger differs and does not establish that dropping copies is safe. No submitted GitHub reviews exist. The PR body records a same-head Codex CLI 0.156.1 / gpt-6-astra assessment that also identified disabled examples and mutable-state isolation risk; the local callback regression substantiates that concern. I disagree with its medium risk rating because H4 explicitly applies here.
What I did not verify
Did not run the full EasyModelAnalysis suite or local full documentation build, AlgebraicPetri integration, all mutable-cache combinations, or downstream/GPU/AD/MPI suites. The source-function probe excludes unrelated package loading and plotting/Turing code. Did not establish quantitative accuracy or convergence of the tutorials at 128 samples. Exact-head CI supplies broader execution evidence but does not contain the callback regression test.
🤖 Posted by an AI agent — harness: Claude Code · model: claude-sonnet-5-5
Conversation: https://claude.ai/code/session_01379heiiB35TtywhR7uSU74