Skip to content

Commit ff45dbd

Browse files
Unwrap AutoSpecializeCallable for Enzyme in JacobianOperator path
The previous commit only unwrapped for the concrete Jacobian path (DI.prepare_jacobian/DI.jacobian). This extends the fix to the JacobianOperator path used by Krylov solvers (GMRES, etc.) and backslash with concrete_jac=false. When Enzyme is used for JVP/VJP autodiff, create a modified problem with the raw user function so SciMLJacobianOperators' DI.pushforward!/ DI.pullback! calls don't go through FunctionWrappers' llvmcall. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b6917fb commit ff45dbd

1 file changed

Lines changed: 9 additions & 1 deletion

File tree

lib/NonlinearSolveBase/src/jacobian.jl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,15 @@ function construct_jacobian_cache(
7171
# Standardize JVP/VJP autodiff tags to match FunctionWrapper signatures
7272
_jvp_ad = standardize_forwarddiff_tag(jvp_autodiff, prob)
7373
_vjp_ad = standardize_forwarddiff_tag(vjp_autodiff, prob)
74-
JacobianOperator(prob, fu, u; jvp_autodiff = _jvp_ad, vjp_autodiff = _vjp_ad)
74+
# Enzyme cannot differentiate through FunctionWrappers' llvmcall.
75+
# Unwrap AutoSpecializeCallable so DI sees the raw user function.
76+
_prob = if is_fw_wrapped(f.f) &&
77+
(_uses_enzyme_ad(_jvp_ad) || _uses_enzyme_ad(_vjp_ad))
78+
@set prob.f.f = get_raw_f(f.f)
79+
else
80+
prob
81+
end
82+
JacobianOperator(_prob, fu, u; jvp_autodiff = _jvp_ad, vjp_autodiff = _vjp_ad)
7583
else
7684
if f.jac_prototype === nothing
7785
# While this is technically wasteful, it gives out the type of the Jacobian

0 commit comments

Comments
 (0)