Skip to content

Solve SCCSteadyStateProblem; stop routing SteadyStateProblem through a lowering - #178

Draft
ChrisRackauckas wants to merge 12 commits into
SciML:masterfrom
ChrisRackauckas:scc-steadystate
Draft

ChrisRackauckas wants to merge 12 commits into
SciML:masterfrom
ChrisRackauckas:scc-steadystate

Conversation

@ChrisRackauckas

@ChrisRackauckas ChrisRackauckas commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

🚧 UNREVIEWED — awaiting review by @ChrisRackauckas. Filed by an AI agent running as @ChrisRackauckas; Chris has not seen this.
Harness: Devin CLI 3000.11.3 (diagnosis and fix), Claude Code 2.1.285 (PR) · Model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Conversation: local Claude session 3c311569-dda6-4687-bc9f-65febb212c33 (fleet master)

This fixes a failure on the default branch's CI. The diagnosis and the evidence below come from the investigating agent's report; "confirmed by running" and "read in logs" are kept apart there.

Part of a three-PR stack implementing Chris's direction (2026-10-14) to replace the post-solve SCC reversal with an SCC form of SteadyStateProblem stored in the compressed layout: SciMLBase (new type) → ModelingToolkit (constructs it) → SteadyStateDiffEq (solves it). Supersedes #175 and #177; fixes #171. CI on the downstream PRs cannot pass until the upstream ones are released.

SteadyStateDiffEq.jl

Branch scc-steadystate (on master)

  • a3b9bd1 Solve SCCSteadyStateProblem; stop routing SteadyStateProblem through a lowering

  • Files: src/solve.jl:

    • __solve_scc_blocks is shared by both SCC types and skips stateless blocks.
    • solve(::SCCSteadyStateProblem, …; save_idxs): DynamicSS/SICNM solve block by
      block; SSRootfind, a bare solve, or a nonlinear alg go through SCCNonlinearProblem(prob).
      save_idxs takes integers or states (resolved via variable_index).
    • Deletes __solve_scc_lowering, the solve(::SteadyStateProblem, …) interception and
      the SSRootfind lowering branch.

    Also src/SteadyStateDiffEq.jl (export), src/algorithms.jl (docstrings),
    docs/src/index.md (section "SCC-split steady states"), test/qa/qa.jl (reexport
    list), Project.toml (2.18.0 → 2.19.0, SciMLBase 3.59, MTK 11.46) and test/scc.jl.

  • test/scc.jl contains the ported cases from
    Restore original state order for SCC steady-state solutions #175 and
    Map SCC-lowered steady-state solutions back onto the problem's state (stacked on #173) #177. A shared layout_cases() provides
    chain, LV, mix and the observed-XY system. Two testsets loop over it:

    Block-built SCCSteadyStateProblems are tested for every algorithm, plus save_idxs and
    short-tspan failures. LV runs only the nonlinear algorithms (its equilibrium is a center).

  • Before (new test/scc.jl against all-master env-before, via scratch/run_scc_before.jl; ssde_scc_before_r2.log):

    SteadyStateDiffEq scc.jl (before environment)            |  388  32  58  478
      SCCSteadyStateProblem from blocks                      |   28      28 (errors)
      SteadyStateProblem solutions are in the problem's layout |  76  32  13  121
      SCCSteadyStateProblem from ModelingToolkit             |       14 (errors)
    
  • After (ssde_scc_r2.log, env-stack):

    SSRootfind forwards SCCNonlinearProblem                  |  204  204
    DynamicSS sequentially solves an SCCNonlinearProblem     |   33   33
    SICNM sequentially solves an SCCNonlinearProblem         |   33   33
    SCCSteadyStateProblem from blocks                        |  228  228
    SSRootfind on a ModelingToolkit SCCSteadyStateProblem    |    3    3
    DynamicSS on a ModelingToolkit SCCSteadyStateProblem     |    4    4
    SICNM on a ModelingToolkit SCCSteadyStateProblem         |    4    4
    SteadyStateProblem solutions are in the problem's layout |  123  123
    SCCSteadyStateProblem from ModelingToolkit               |  134  134
    SSRootfind on a ModelingToolkit SCC decomposition        |   30   30
    DynamicSS on a ModelingToolkit SCC decomposition         |   12   12
    

    ssde_core_r2.log and ssde_sicnm.log: all green. Runic --check and typos: clean.

  • IntegrationTest evidence (SciMLBase test/downstream/comprehensive_indexing.jl):

  • Not verified: GROUP=QA (JET/Aqua/ExplicitImports), the full SSDE suite outside
    scc.jl/core.jl/sicnm.jl, and the docs build.

Open design questions for Chris

  1. Plain SteadyStateProblem + nonlinear alg. With NewtonRaphson() the solution's
    sol.prob is the converted NonlinearProblem, as before the lowering existed (only
    SSRootfind/DynamicSS/SICNM give sol.prob isa SteadyStateProblem). The tests
    encode this. Should SSDE rewrap it? I left it alone because it changes the dispatch
    surface that SciMLSensitivity uses.
  2. Release order for removing the field. Should lowered_problem go now as SciMLBase
    4.0 (scc-steadystate-remove-lowered), or stay deprecated-but-unused in 3.x? The 4.0
    path needs SciMLBase = "3.59, 4" compat in MTK/SSDE and in every package that caps
    SciMLBase below 4.
  3. SemVer of the MTKBase accessor removal. The SCCNonlinearProblem(::SteadyStateProblem)
    method was a public-ish extension in MTKBase 1.77. I released its removal as a minor
    (1.78.0), on the grounds that it was only meaningful with the lowering. A strict reading
    makes it a major.
  4. Construction cost. SCCSteadyStateProblem(sys, op) builds a full
    SteadyStateProblem (including its initialization problem) only to process op, then
    compiles the residual system. build_initializeprob = false breaks hierarchical
    initialization_eqs. Is a lighter op-processing path worth adding?
  5. Monolithic DynamicSS with Tsit5. On the plain mix system, DynamicSS(Tsit5()) with
    the default tspan = Inf runs out of maxiters before the termination callback fires,
    even though the ODE is globally stable. A hand-written SteadyStateProblem behaves the
    same, so this is not caused by this change. The tests use tspan = 100. Worth an SSDE
    issue?
  6. No dedicated SCC solution type. SteadyStateSolution (= NonlinearSolution) on an
    SCCSteadyStateProblem carries the compressed layout through sol.prob.f.sys, and no
    dedicated SCC solution type was added. Is that the intended end state?

🤖 Posted by an AI agent — harness: Devin CLI 3000.11.3 (fix), Claude Code 2.1.285 (PR) · model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium / claude-opus-5-5
Conversation: local Claude session 3c311569-dda6-4687-bc9f-65febb212c33 (fleet master)

🤖 Generated with Claude Code

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

Risk assessment (independent review, Codex gpt-6.1-sol, head e941945)

  • Verdict: CHANGES
  • Rating: Codex gpt-6.1-sol rated it medium (M1) at head e941945
  • Risk: medium (M1: adds methods on SciMLBase.solve and introduces a public saved-problem dispatch/layout contract). Also matches L1/L2/L5/L7 for documentation, tests, versioning and internal refactoring, ML1 for the verified parameter-override fix, ML4 for raised compat floors, ML7 for the additive export, and ML8 for shared saved-problem views; none exceeds M1.
  • Blast radius: Users of SCCSteadyStateProblem, saved solutions and their reconstructed problems; solve-time overrides also affect existing SCCNonlinearProblem solves with SSRootfind/DynamicSS/SICNM. The existing SteadyStateProblem lowering routes remain present at this head, despite the title/body describing their removal. No new dependency, copyleft addition, public-name removal or signature removal appears in this diff; the minor version bump fits the additive API. The reexported SCCSteadyStateProblem has a docstring and rendered docs entry in its owning SciMLBase dependency PR, plus a manual section here.
  • Evidence: Read the linked issue, full git diff origin/HEAD...HEAD, upstream type/remake implementation, and all failing-check logs. git diff --check origin/HEAD...HEAD passed; the checkout remained clean. Ran Julia 1.12.4 with four threads, the requested depot path and timeout 3600, using scratch source copies and SciMLBase prerequisite PR head 867dba9a21cbf2e280be419d8b5bc74aa1be70e0. julia --project="$S/env" "$S/head-run.jl" passed all 422 assertions copied unchanged from test/scc.jl's six block-focused testsets (96/24/128/32/102/40). The standalone probes in probe.jl and probe-small.jl confirmed both findings below; selection-array mutation and symbolic parameter-remake probes passed. python3 "$S/run-final-checks.py" ran the same reduced parameter-override test against base and head, then ordinary head dependency resolution: base 0 passed / 2 failed (u=[1], reported p=[1]); head 2 passed (u=[3], reported p=[3]); registry resolution failed because available SciMLBase versions end at 3.58.0 while this head requires 3.59. Here $S is this report's sibling scratch directory. G1 pass: analytic equilibria and independent linear-system references; the reduced override regression also uses the analytic root u=p. G2 not applicable: no GPU kernel, AD-rule, Reactant or MPI implementation is changed; Dual-value preservation is covered by the selected tests. G3 pass: tests exercise parameter propagation and remake isolation, and the caller-selection mutation probe preserved the saved layout.
  • Merge: needs changes (fix both saved-selection errors and add regressions; release prerequisites and rerun failing CI before merging).

🤖 Posted by an AI agent — harness: Codex CLI 0.162.1 · model: gpt-6.1-sol
Conversation: local headless review run (no public session URL); transcript kept by the fleet master.

ChrisRackauckas and others added 10 commits October 14, 2026 19:15
…a lowering

DynamicSS and SICNM solve SCCSteadyStateProblem block by block, SSRootfind
and nonlinear algorithms through SCCNonlinearProblem(prob); the solution is
built on the SCC problem in its compressed layout, and save_idxs selects in
that layout (integers or states via variable_index). SteadyStateProblem
solves keep the problem's own layout again, fixing
SciML#171. Ports the LV,
chain, mixed-SCC, resid, save_idxs and eltype cases of SciML#175/SciML#177.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Devin <noreply@cognition.ai>
Agent-Harness: Devin CLI 3000.11.3
Agent-Model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Agent-Session: local headless run, log /Users/chrisrackauckas/sandbox/master/green/scc-ssprob/log.txt
… blocks

Solving an SCCSteadyStateProblem with save_idxs now builds the solution on a
problem whose index provider (SCCSavedStates) lists the saved states in
selection order, so sol[s] resolves saved states in any order, unsaved states
and observed expressions throw, parameters index as usual, and sol.original is
the full solution. LinearProblem blocks are dispatched before the stateless
shortcut so 0x0 blocks solve directly; a stateless nonlinear block's residual
is evaluated once and must be empty.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Devin <noreply@cognition.ai>
Agent-Harness: Devin CLI 3000.11.3
Agent-Model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Agent-Session: local headless run, log /Users/chrisrackauckas/sandbox/master/green/scc-ssprob/fu1/log.txt
…ut cases

DynamicSS(Tsit5()) over the layout testset plateaued at a tolerance-sized
residual noise floor at the stability-limited step size and flipped between
Success and Failure under small perturbations; the L-stable Rodas5P contracts
the residual with margin. Documents the save_idxs contract in the SCC docs.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Devin <noreply@cognition.ai>
Agent-Harness: Devin CLI 3000.11.3
Agent-Model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Agent-Session: local headless run, log /Users/chrisrackauckas/sandbox/master/green/scc-ssprob/fu1/log.txt
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Devin <noreply@cognition.ai>
Agent-Harness: Devin CLI 3000.11.3 (change, left uncommitted), Claude Code 2.1.285 (commit)
Agent-Model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Agent-Session: local headless run (SCC stack follow-up 1); committed by local Claude session 3c311569-dda6-4687-bc9f-65febb212c33
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Devin <noreply@cognition.ai>
Agent-Harness: Devin CLI 3000.11.3
Agent-Model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Agent-Session: local headless run, log /Users/chrisrackauckas/sandbox/master/green/scc-ssprob/fu2/log.txt
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Devin <noreply@cognition.ai>
Agent-Harness: Devin CLI 3000.11.3
Agent-Model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Agent-Session: local headless run, log /Users/chrisrackauckas/sandbox/master/green/scc-ssprob/fu2/log.txt
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Devin <noreply@cognition.ai>
Agent-Harness: Devin CLI 3000.11.3
Agent-Model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Agent-Session: local headless run, log /Users/chrisrackauckas/sandbox/master/green/scc-ssprob/fu2/log.txt
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Devin <noreply@cognition.ai>
Agent-Harness: Devin CLI 3000.11.3
Agent-Model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Agent-Session: local headless run, log /Users/chrisrackauckas/sandbox/master/green/scc-ssprob/fu2/log.txt
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Devin <noreply@cognition.ai>
Agent-Harness: Devin CLI 3000.11.3
Agent-Model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Agent-Session: local headless run, log /Users/chrisrackauckas/sandbox/master/green/scc-ssprob/fu2/log.txt
…ved problems

The saved-state view of an SCCSteadyStateProblem forwarded parameter_values
but not set_parameter!, so setp on it updated only the top-level parameter
object and left unaliased block caches stale: re-solving returned y = 2 while
sol.ps[:k] == 3. set_parameter! and finalize_parameters_hook! now forward to
the wrapped problem.

Building the view indexed state_values(prob) unconditionally, so save_idxs
threw MethodError getindex(::Nothing, ...) on all-LinearProblem problems
without initial guesses. Such views have uType Nothing and state_values
nothing; remaking one with a saved-layout u0 fills the unsaved positions with
zeros.

remake copied the old state vector and assigned into it, truncating BigFloat
replacements to Float64 and throwing on ForwardDiff duals. The expanded state
now uses the promoted element type.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Devin <noreply@cognition.ai>
Agent-Harness: Devin CLI 3000.11.92
Agent-Model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Agent-Session: local headless run, log /Users/chrisrackauckas/sandbox/master/green/fu/SciML_SteadyStateDiffEq.jl-178-0909/log.txt
@ChrisRackauckas

Copy link
Copy Markdown
Member Author

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

New commit 86e034a addresses the three blocking findings from the third independent review (gpt-6.1-sol):

  • Saved problems now forward set_parameter! and finalize_parameters_hook! to the wrapped SCC problem, so parameter updates reach the blocks. Before the fix the new testset had 12 failures out of 24 (the re-solve gave y = 2, not 6). After the fix: 24/24.
  • save_idxs works on linear blocks that have no initial guesses: state_values stays nothing and there is no MethodError.
  • remake(saved.prob; u0) promotes the element type instead of converting back to the old one. Tested with BigFloat and ForwardDiff dual numbers.

The fix was written by Devin (SWE-2 Fusion, Opus 5.5). A fourth review is queued.


🤖 Posted by an AI agent — harness: Claude Code 2.1.285 · model: claude-opus-5-5
Conversation: local session 3c311569-dda6-4687-bc9f-65febb212c33

Solve-time `u0`/`p` keywords on `SCCSteadyStateProblem`,
`SCCNonlinearProblem` and the saved-state view were forwarded into the
block solves instead of remaking the problem: nonlinear algorithms
silently ignored them, and `DynamicSS`/`SICNM` block solves received
full-layout `u0`/`p`, leaving the solution's `prob` reporting stale
state and parameter values. Route the keywords through `remake` before
dispatch so `u0` is chunked into the blocks, `parameters_alias` is
enforced, and the returned solution is expressed on the updated
problem.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Devin <noreply@cognition.ai>
Agent-Harness: Devin CLI 3000.11.92
Agent-Model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Agent-Session: local headless run, log /Users/chrisrackauckas/sandbox/master/green/fu/SciML_SteadyStateDiffEq.jl-178-1100/log.txt
@ChrisRackauckas

Copy link
Copy Markdown
Member Author

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

New commit f278d45 addresses the two blocking findings from the fourth independent review (gpt-6.1-sol):

  • Solve-time u0 and p overrides are now applied before the blocks are solved, and the solution is built on the overridden problem. solve(prob, alg; p = [3.0]) now gives the same answer and the same reported parameters as solve(remake(prob; p = [3.0]), alg).
  • An initial guess passed to a saved problem is now mapped from the saved layout to the full layout, so it no longer goes to the wrong state or throws a BoundsError.

The new testset "solve-time u0 and p overrides" had 20 passes, 50 failures and 2 errors without the fix, and passes all 102 checks with it. test/core.jl and test/sicnm.jl pass. QA has one error, which also occurs without this change in the same local environment. The fix was written by Devin (SWE-2 Fusion, Opus 5.5). A fifth review is queued.


🤖 Posted by an AI agent — harness: Claude Code 2.1.285 · model: claude-opus-5-5
Conversation: local session 3c311569-dda6-4687-bc9f-65febb212c33

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.

solve(::SteadyStateProblem) returns the lowered problem's solution: sol.u reordered/empty, sol.prob.f.sys time-independent

1 participant