@@ -10,6 +10,7 @@ using ForwardDiff
1010using Tracker
1111using Enzyme
1212using Mooncake
13+ using NonlinearSolve: NewtonRaphson
1314
1415# DAE with nonlinear algebraic constraints forming an SCC chain.
1516# Inspired by the De Sauty bridge DAE but written as a flat system
@@ -120,28 +121,49 @@ eqs = [
120121 end
121122
122123 @testset " Enzyme through init" begin
123- # Status (verified 2026-04-10 with Enzyme 0.13, NonlinearSolve
124- # 4.17, SciMLBase 2.153, ModelingToolkit current release):
124+ # Annotations follow the documented user-side pattern:
125+ # `Const(loss)` for the closure that captures the mutable
126+ # `NonlinearProblem`/`SCCNonlinearProblem`, and
127+ # `set_runtime_activity(Reverse)` so Enzyme's activity analysis
128+ # tolerates the runtime-activity transitions through MTK's
129+ # `remake` path. The inner `solve` pins `NewtonRaphson()`
130+ # explicitly so Enzyme's type analysis does not trip on the
131+ # polyalgorithm Union NonlinearSolve would otherwise dispatch
132+ # through. The previously-reported `EnzymeMutabilityException`
133+ # on the mutable closure capture is correct upstream behavior
134+ # per EnzymeAD/Enzyme.jl#3117 — annotating with `Const` is the
135+ # fix.
125136 #
126- # * Julia 1.10 (LTS): hits an `EnzymeMutabilityException` because
127- # the closure capturing `iprob`/`irepack` cannot be proven
128- # read-only. Wrapping `init_loss` in `Const(...)` advances past
129- # the activity check but then crashes the LLVM GC invariant
130- # verifier with `Illegal inttoptr` during `MTK.remake`/
131- # `SciMLStructures.replace`. Even calling
132- # `Enzyme.set_runtime_activity(Reverse)` produces the same
133- # LLVM crash. The issue reproduces equally for `use_scc=false`
134- # and `use_scc=true` and is independent of SCCNonlinearSolve.
135- # * Julia 1.11+: same crashes plus a separate
136- # `IllegalTypeAnalysisException` on `Base._typed_vcat!` inside
137- # SCCNonlinearSolve's solution assembly.
138- #
139- # Tracking issues: NonlinearSolve.jl#869, Enzyme.jl#2699,
140- # Enzyme.jl#3021 (vcat type analysis), and the upstream MTK
141- # remake/Enzyme interaction.
142- @test_broken begin
143- igs = Enzyme. gradient (Enzyme. Reverse, init_loss, itunables)
144- ! iszero (sum (igs))
137+ # With these annotations, the plain `NonlinearProblem` case
138+ # (use_scc = false) now passes. The `SCCNonlinearProblem` case
139+ # (use_scc = true) still trips a `MixedDuplicated` /
140+ # `Core.SimpleVector` MethodError further down in Enzyme's
141+ # runtime-activity wrapping for the MTK-System /
142+ # NonlinearSolution types involved in SCC sub-problem
143+ # assembly — tracked in SciMLSensitivity.jl#1359. When that
144+ # lifts, flipping `@test_broken` → `@test` in the `use_scc`
145+ # branch is the only change needed here.
146+ enzyme_init_loss = let iprob = iprob, irepack = irepack
147+ p -> begin
148+ iprob2 = remake (iprob, p = irepack (p))
149+ sol = solve (iprob2, NewtonRaphson ())
150+ sum (sol. u)
151+ end
152+ end
153+ if use_scc
154+ @test_broken begin
155+ igs = Enzyme. gradient (
156+ Enzyme. set_runtime_activity (Enzyme. Reverse),
157+ Enzyme. Const (enzyme_init_loss), itunables,
158+ )
159+ ! iszero (sum (igs))
160+ end
161+ else
162+ igs = Enzyme. gradient (
163+ Enzyme. set_runtime_activity (Enzyme. Reverse),
164+ Enzyme. Const (enzyme_init_loss), itunables,
165+ )
166+ @test ! iszero (sum (igs))
145167 end
146168 end
147169
0 commit comments