Enzyme support for NonlinearProblem and SCCNonlinearProblem differentiation (#1358)#1397
Merged
ChrisRackauckas merged 14 commits intoApr 10, 2026
Conversation
b007f44 to
186d502
Compare
Contributor
Author
Changes in latest commit1. Unified
|
2 tasks
…L#1358) Add detailed comments to test files and source code explaining the specific upstream blockers preventing Enzyme from differentiating through NonlinearProblem/SCCNonlinearProblem initialization: - Julia 1.12: LLVM crash due to NonlinearSolve Enzyme rules disabled - Julia 1.10: MixedReturnException, EnzymeMutabilityException, NamedTuple broadcasting errors in reverse rule - SCCNonlinearProblem: no _concrete_solve_adjoint dispatch exists References: NonlinearSolve.jl#869, Enzyme.jl#2699 Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…#1358) For EnzymeOriginator with SciMLStructure parameters, return the tunable gradient vector directly from steadystatebackpass instead of the Zygote-repacked NamedTuple. The NonlinearSolveBaseEnzymeExt reverse rule uses SciMLStructures.replace! to accumulate it into the parameter shadow, going through the proper SciMLStructures interface. This avoids the NamedTuple broadcasting error and ensures all tangent accumulation uses SciMLStructures.canonicalize/replace! rather than making assumptions about the NamedTuple field structure. Companion PR: NonlinearSolve.jl#879 Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add `diff_tunables::Val` field to SteadyStateAdjoint (default Val(true)). When Val(false), the parameter VJP via vecjacobian! is computed w.r.t. the full parameter object (including caches) instead of just tunables. This is needed for SCCNonlinearProblem where explicitfuns! write active data into non-tunable parameter components (caches). The automatic sensealg choice detects non-empty caches and sets diff_tunables=Val(false) with a structured-VJP-compatible backend. Changes: - sensitivity_algorithms.jl: Add diff_tunables field to SteadyStateAdjoint - adjoint_common.jl: Add use_full_p kwarg to adjointdiffcache - steadystate_adjoint.jl: Gate use_full_p on diff_tunables flag - concrete_solve.jl: Pass original_p to automatic_sensealg_choice for cache detection; return full gradient for EnzymeOriginator when diff_tunables=Val(false) - test/scc_enzyme.jl: Direct SCC differentiation test with Enzyme Companion PR: NonlinearSolve.jl#884 Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test SCC with direct construction (2-component problem with parameter coupling through caches): - FiniteDiff: passes (ground truth) - Mooncake: passes - ForwardDiff: @test_broken (Dual numbers into Float64 mutation buffer) - Enzyme: @test_broken (EnzymeNoTypeError in SCC dispatch chain) Added to runtests.jl Core 8 group. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mooncake's build_rrule on the MTK DAE initialization problem takes too long for CI timeouts. SCCNonlinearProblem Mooncake test is in scc_nonlinearsolve.jl instead. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…aussAdjoint/QuadratureAdjoint - Add original_p parameter to ODE automatic_sensealg_choice dispatch, matching the NonlinearProblem dispatch signature - Add diff_tunables::Val field to GaussAdjoint and QuadratureAdjoint (default Val(true)), mirroring SteadyStateAdjoint - Propagate use_full_p through GaussIntegrand, AdjointSensitivityIntegrand, and adjointdiffcache for both adjoint methods - Handle diff_tunables in ODE backpass for Enzyme full-parameter gradients - Guard all diff_tunables access with hasproperty for GaussKronrodAdjoint compatibility (which shares GaussAdjoint code paths) - Remove scc_enzyme.jl (scc_nonlinearsolve.jl already in runtests.jl) Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add SCCNonlinearSolve to [extras] and [targets] in Project.toml (was missing, causing LoadError on CI for scc_nonlinearsolve.jl) - Fix runic formatting: add explicit return statements in scc_nonlinearsolve.jl functions Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0595d4e to
ad3951c
Compare
With Mooncake v0.5.25 + ModelingToolkitBase v1.28.0, the non-SCC init path works. The SCC path remains @test_broken. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ion) Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ChrisRackauckas-Claude
pushed a commit
to ChrisRackauckas-Claude/SciMLSensitivity.jl
that referenced
this pull request
Apr 10, 2026
…pport) The full-parameter VJP path (when diff_tunables=Val(false)) was gated on EnzymeOriginator only, defeating the purpose of diff_tunables for ChainRules-based AD backends like Mooncake. This blocked SCC gradient flow for Mooncake even after SciML#1397. Fix: extend the full-p path to all originators. Convert dp_full from its native SciMLStructure form (e.g. MTKParameters) to a NamedTuple for non-Enzyme originators, since Mooncake's @from_chainrules bridge expects NamedTuple tangents while Enzyme accumulates into the struct shadow directly. Verified: Mooncake through SCCNonlinearProblem init in the De Sauty DAE test now produces gradients matching FiniteDiff for use_scc=true. non-SCC path still works. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This was referenced Apr 10, 2026
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.
Summary
Enable Enzyme differentiation through NonlinearProblem and SCCNonlinearProblem initialization solves, addressing #1358.
Changes
Full-parameter VJP in SteadyStateAdjoint (
steadystate_adjoint.jl):explicitfuns!write active data into caches, this caused zero gradientsexplicitfuns!mutationsEnzymeOriginator tangent handling (
concrete_solve.jl):Documentation (test files):
@test_brokenannotationsCompanion PRs
Verified
NonlinearProblem+ explicit alg + MTKParameters: gradient matches FiniteDiff ✓explicitfun!→ caches → sub-problem solve (SCC pattern): gradient matches FiniteDiff ✓Test plan
🤖 Generated with Claude Code