Repository navigation
Raise a clear ArgumentError converting an SCCNonlinearProblem to NonlinearProblem - #1614
Draft
ChrisRackauckas-Claude wants to merge 3 commits into
Draft
ChrisRackauckas-Claude wants to merge 3 commits into
ChrisRackauckas-Claude wants to merge 3 commits into
Conversation
NonlinearProblem(prob::AbstractNonlinearProblem) generically rebuilds prob via prob.f, prob.u0, prob.p, but SCCNonlinearProblem has no u0 field (its state is the concatenation of its sub-problems' states). Calling NonlinearProblem on an SCCNonlinearProblem therefore threw a FieldError instead of returning the (already valid) problem unchanged. This was latent until ModelingToolkit.jl started storing an SCCNonlinearProblem in SteadyStateProblem's lowered_problem field: NonlinearProblem(::SteadyStateProblem) correctly returns that stored problem verbatim, but a downstream caller normalizing the result via NonlinearProblem() again hit the generic fallback. Add an identity method for SCCNonlinearProblem alongside a regression test. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Sonnet <noreply@anthropic.com> Agent-Harness: Claude Code 2.1.251 (subagent) Agent-Model: claude-sonnet Agent-Session: https://claude.ai/code/session_01NB3oFTNzGW79UtDt8AyjsM
Member
|
SciML/NonlinearSolve.jl#1319 is also related to the same error |
…inearProblem NonlinearProblem(prob::AbstractNonlinearProblem) rebuilds via prob.f/prob.u0/ prob.p, but SCCNonlinearProblem has no u0 field, so this threw an opaque FieldError. Returning prob unchanged (an earlier version of this fix) is its own footgun: a NonlinearProblem constructor returning something that isn't a NonlinearProblem breaks any caller assuming .u0/.lb/.ub, and it is exactly what let a caller that re-normalizes its result (NonlinearSolve.jl's default-algorithm converter) recurse forever instead of terminating. An SCCNonlinearProblem solves as an ordered sequence of block solves, not a single residual, so there is no faithful NonlinearProblem to construct here. Raise a clear ArgumentError instead, naming what to do (solve it directly or use its component problems). Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Sonnet <noreply@anthropic.com> Agent-Harness: Claude Code 2.1.251 (subagent) Agent-Model: claude-sonnet Agent-Session: https://claude.ai/code/session_01NB3oFTNzGW79UtDt8AyjsM
Keep the source comment to the why; the recursion-hazard and review history belong in the PR body, not the code. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Sonnet <noreply@anthropic.com> Agent-Harness: Claude Code 2.1.251 (subagent) Agent-Model: claude-sonnet Agent-Session: https://claude.ai/code/session_01NB3oFTNzGW79UtDt8AyjsM
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Please ignore until reviewed by @ChrisRackauckas.
What changed and why
GROUP=SymbolicIndexingInterfaceis (or was) red on master (CI run https://github.com/SciML/SciMLBase.jl/actions/runs/35839187919):init(ssprob, DynamicSS(Tsit5()))on an MTK-lowered SCC steady-state problem throwsFieldError: type SCCNonlinearProblem has no field u0.Root cause:
NonlinearProblem(prob::AbstractNonlinearProblem) = NonlinearProblem{isinplace(prob)}(prob.f, prob.u0, prob.p)assumes everyAbstractNonlinearProblemsubtype has au0field.SCCNonlinearProblemnever has one (its state is the concatenation of its sub-problems' states), so callingNonlinearProblem(x)on anSCCNonlinearProblemhas always thrown thisFieldError, standalone, with no other package involved:Revision history on this PR
An earlier version of this PR made
NonlinearProblem(prob::SCCNonlinearProblem)an identity (= prob), matching how a storedNonlinearProblem/LinearProblemlowered_problemis already handled verbatim elsewhere ("stored problem is used verbatim"intest/problem_building_test.jl). Review caught two problems with that:SteadyStateDiffEq.jlhaving no__initforDynamicSS/SICNM, which is fixed independently and sufficiently in Add __init for DynamicSS/SICNM so init() doesn't fall through to NonlinearSolve's default SteadyStateDiffEq.jl#173 (confirmed: that fix alone, against today's unmodified SciMLBase, resolves the reported failure).NonlinearProblemconstructor returning something that is not aNonlinearProblembreaks any caller that assumes.u0/.lb/.ub/etc., and it is exactly what letNonlinearSolve.jl's default-algorithm converter (src/default.jl, which doesnlprob = NonlinearProblem(prob); return __init(nlprob, nothing, args...)) recurse forever instead of terminating, once it receivednlprob === prob. Reproduced:What this PR does now
SCCNonlinearProblemsolves as an ordered sequence of block solves (seeSciMLBase.solve(::SCCNonlinearProblem, ::Union{DynamicSS,SICNM}, ...)andSSRootfind's handling inSteadyStateDiffEq.jl), not a single residual — there is no faithfulNonlinearProblemto construct from it. SoNonlinearProblem(prob::SCCNonlinearProblem)now raises a clearArgumentError:An error cannot recurse, so this also removes the recursion hazard above without needing a companion guard in
NonlinearSolve.jl(the earlier companion PR, SciML/NonlinearSolve.jl#1327, has been closed as unnecessary:NonlinearProblem(prob)never returnsprobitself now, so thenlprob === probcase it guarded against cannot occur).Verification
Standalone reproducer, fail-before/pass-after:
Before (unmodified master):
After (this PR):
Regression test updated to
@test_throws ArgumentError NonlinearProblem(sccprob)intest/problem_building_test.jl(still discriminates: throwsFieldErroron unmodified master, so@test_throws ArgumentErrorfails there and passes here).GROUP=Corepasses locally in full, including the updated test (Problem building tests | 182 182):Runic
--checkandtyposare clean on both changed files.What I did not verify
GROUP=SymbolicIndexingInterfacetest in isolation against this PR — see Add __init for DynamicSS/SICNM so init() doesn't fall through to NonlinearSolve's default SteadyStateDiffEq.jl#173 body for why that test doesn't need this PR at all (it is fixed independently there).GROUP=QAlocally (PythonCall/CondaPkg fails to start on this machine, unrelated to this change).NonlinearProblemon anAbstractSteadyStateProblem-typed value for reliance on the old identity behavior; greppedSteadyStateDiffEq.jl's source and test suite (including its new tests in PR Add a page describing the init and solve interfaces #173) and found none that callNonlinearProblemdirectly on an already-builtSCCNonlinearProblem— all existing callers only convert aSteadyStateProblem(which correctly returns the storedSCCNonlinearProblemverbatim; that path is untouched by this change).Links
🤖 Generated with Claude Code
https://claude.ai/code/session_01NB3oFTNzGW79UtDt8AyjsM
Risk assessment
NonlinearProblem(::SCCNonlinearProblem). That call already failed on master withFieldError: type SCCNonlinearProblem has no field u0. With this PR it fails with anArgumentErrorinstead, so nothing that works today stops working. The one downstream path it touches is the MTK steady-stateinit(ssprob, DynamicSS(...))route through NonlinearSolve'ssrc/default.jl. That path fails before and after; only the error type changes. The PR adds one 4-line method,NonlinearProblem(prob::SCCNonlinearProblem), insrc/problems/nonlinear_problems.jl, plus one@test_throws ArgumentErrorintest/problem_building_test.jl. It uses no non-public internals of dependencies. There's no SemVer concern, since the only change is which exception is thrown for an input that was never supported. No tests are weakened or skipped.tests / SymbolicIndexingInterface(julia 1, lts): pre-existing. These fail on every master "Run Tests" run from 2026-09-23 to 2026-10-03, including 37100169960 on master head 432e985, and on the merge base (35923456549). It's the same testset, "Symbol and integer based indexing of interpolated solutions". On master it throws theFieldError; on this PR it throws the newArgumentError. So the PR changes the message but doesn't fix the test.tests / QA (julia 1): pre-existing at the base. ExplicitImports flagsBase.Callableatnonlinear_problems.jl:256, a line this PR doesn't touch. QA also failed on the merge base (35923456549) and on master 36036224472. Master fixed it later in Use the local Callable alias instead of non-public Base.Callable #1608, so it should clear after a rebase.SciMLSensitivity.jl/Core1,Core6,Core7: pre-existing. The same testsets fail on master IntegrationTest 37100169941 (2026-10-03) and 36518786705: "Mooncake with MooncakeAdjoint", "Complex Matrix FiniteDiff Adjoint" and "Fully Out of Place adjoint sensitivity".SciMLSensitivity.jl/Core8: pre-existing at the base. "Enzyme/Mooncake through init" indesauty_dae_mwe.jlalso fails on master 36518786705 and 36447374379. It passes on current master since Un-break Mooncake DAE initialization observable AD test (fixes #1568) #1576, so it needs a rebase to confirm.Catalyst/All/1(ReleaseTest): pre-existing. It fails on master 37100169405 with the same infrastructure error:SystemError: opening .../Catalyst/.../test/extensions/Project.toml: Permission denied.fix/scc-nonlinearproblem-identitydescribes the earlier identity-returning approach, and the PR body lacks the standard agent-attribution banner and footer. The description is otherwise complete. It says QA wasn't run locally and SymbolicIndexingInterface wasn't run against this PR.🤖 Risk assessment posted by an AI agent (fleet master) — harness: Devin CLI (Mac) / fusion-claude-opus-5-5-high-sidekick-swe-2-medium; dispatched by Devin CLI 3000.11.3 (Mac) fleet-master caretaker, model Fusion (claude-opus-5-5 medium + swe-2 medium)
Conversation: local transcript ~/.local/share/devin/cli/transcripts/speckle-whale.json (Chris's Mac)