Repository navigation
NonlinearSolveBase: unwrap FunctionWrappersWrapper on the EnzymeOriginator adjoint path (MTK DAE init) - #946
Merged
ChrisRackauckas merged 1 commit intoMay 31, 2026
Conversation
…nator adjoint path Follow-up to SciML#944. When Enzyme reverse-mode differentiates through an MTK DAE solve, the initialization solves a `NonlinearProblem` via `solve_up`. The custom `solve_up` Enzyme rule re-runs the solve through `_solve_adjoint` -> `get_concrete_problem`, which `maybe_wrap_nonlinear_f`-wraps the IIP function in a `FunctionWrappersWrapper` (`AutoSpecializeCallable`). That wrapping is type-unstable: Enzyme's traced forward solve unwraps it (`maybe_unwrap_prob_for_enzyme`, SciML#940) so its inferred return type carries the bare function, but the adjoint re-wraps it, so the rule's returned primal type no longer matches and Enzyme aborts with `EnzymeRuntimeException: Expected return type of primal to be NonlinearSolution{...}`. `maybe_unwrap_prob_for_enzyme` keys off the solver's own autodiff, which for an MTK DAE init is ForwardDiff even when the outer differentiation is Enzyme, so it does not fire on this path. Key off the originator instead: unwrap `_prob.f.f` via `get_raw_f` when `originator isa EnzymeOriginator`, matching the (unwrapped) type Enzyme's traced forward produces. Companion to SciML/SciMLSensitivity#1463. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ChrisRackauckas
marked this pull request as ready for review
May 31, 2026 02:55
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.
Summary
Follow-up to #944 (which fixed the
solve_upEnzyme rule plumbing). With that landed, Enzyme reverse-mode through an MTK DAE solve gets into the initializationNonlinearProblemsolve and then fails with:Root cause — a
FunctionWrappersWrappertype-instability:solve_uprule re-runs the solve via_solve_adjoint→get_concrete_problem, whichmaybe_wrap_nonlinear_f-wraps the IIP function inAutoSpecializeCallable{FunctionWrappersWrapper}(confirmed by instrumentingtypeof(res[1])).maybe_unwrap_prob_for_enzyme(Skip FunctionWrappersWrapper in maybe_wrap_nonlinear_f under Enzyme AD #940), so the return type Enzyme infers carries the bareGeneratedFunctionWrapper; the adjoint re-wraps it, so the rule's primal type no longer matches → Enzyme aborts.maybe_unwrap_prob_for_enzymekeys off the solver's own autodiff, which for an MTK DAE init is ForwardDiff even when the outer differentiation is Enzyme — so it doesn't fire here.Fix: key off the originator instead — unwrap
_prob.f.fviaget_raw_fwhenoriginator isa EnzymeOriginator, so the adjoint primal matches the (unwrapped) type Enzyme's traced forward produces. No-op for non-Enzyme originators and for already-unwrapped functions.Verification status (please read)
retcode = Success).EnzymeRuntimeException~21 min into compilation; with the fix, the run reached ~26 min in the full reverse compile with no such error before the process was OOM-killed.oom_kill 77on the session cgroup). I'll post the gradient comparison once it completes in a less-contended window / via CI.Companion to SciML/SciMLSensitivity#1463; built on #944 (merged) and DiffEqBase 7.5.4 / OrdinaryDiffEq#3700 (merged).
🤖 Generated with Claude Code