Skip to content

Forward-over-reverse: CallingConventionMismatchError parsing idx_jl_getfield_aug (type-unstable getfield of a Duplicated ODEProblem) — typeunstablerules.jl:1261 #3280

Description

@ChrisRackauckas-Claude

Summary

Forward-over-reverse (nested Enzyme: outer Forward over inner Reverse) through an ODE solve — using direct differentiation through the solver (sensealg = SensitivityADPassThrough(), i.e. no continuous-adjoint) — fails with an internal Enzyme error while parsing the calling convention of its own type-unstable getfield rule:

CallingConventionMismatchError: Enzyme hit an internal error trying to parse the julia calling
convention definition for:
  idx_jl_getfield_aug(::Val{@NamedTuple{1}}, ::SciMLBase.ODEProblem{Vector{Float64}, ...}, ...)
    @ Enzyme.Compiler ~/.julia/packages/Enzyme/.../src/rules/typeunstablerules.jl:1261

expectLen != length(parameters(f))
{} addrspace(10)* ({} addrspace(10)*, {} addrspace(10)**, i32, {} addrspace(10)**)
expectLen=1

The inner reverse succeeds (gradient is correct); only the outer forward over it fails, when it differentiates a getfield of the Duplicated ODEProblem through the type-unstable / generic-runtime getfield rule idx_jl_getfield_aug, whose generated function signature Enzyme then can't match (expectLen=1 vs the 4-parameter LLVM signature).

MWE (SciMLSensitivity + OrdinaryDiffEq + DiffEqBase + Enzyme; manual nesting)

import SciMLSensitivity as SMS   # loads the DiffEqBase Enzyme extension + SensitivityADPassThrough
import OrdinaryDiffEq as ODE
import DiffEqBase
import Enzyme

f(u, p, t) = -p[1] .* u
u0    = [1.0]
tspan = (0.0, 1.0)
ts    = range(0.0, 1.0, length = 5)

function loss(p)
    prob = ODE.ODEProblem(f, u0, tspan, p)
    sol  = ODE.solve(prob, ODE.Tsit5(); saveat = ts,
                     sensealg = DiffEqBase.SensitivityADPassThrough())  # differentiate THROUGH the solver
    sum(abs2, Array(sol))
end

const RA_R = Enzyme.set_runtime_activity(Enzyme.Reverse)
const RA_F = Enzyme.set_runtime_activity(Enzyme.Forward)

@noinline function grad(p)                 # inner reverse — works
    dp = zero(p)
    Enzyme.autodiff(RA_R, Enzyme.Const(loss), Enzyme.Active, Enzyme.Duplicated(p, dp))
    dp
end

grad([0.5])                                                    # [-2.4402421907949057]  (fine)
Enzyme.autodiff(RA_F, Enzyme.Const(p -> sum(grad(p))),        # outer forward over the reverse gradient
                Enzyme.Duplicated([0.5], [1.0]))
# CallingConventionMismatchError ... idx_jl_getfield_aug(::Val{@NamedTuple{1}}, ::ODEProblem, ...)
# @ typeunstablerules.jl:1261

Reduction attempts (why no dependency-free MWE)

I tried to reduce this to an Enzyme-only MWE without success (~6 shapes): forward-over-reverse of a getfield on an immutable/mutable struct with an active Vector field plus an ::Any field (ODEProblem-like); getfield returning the boxed Any field; and forcing the dynamic getfield path via Base.inferencebarrier, an ::Any-returning getter closure, and invokelatest. All return the correct derivative — the type-stable and even the dynamically-dispatched getfields I could construct don't land on the idx_jl_getfield_aug type-unstable-getfield rule the way the real solve_up reverse rule (getfielding a Duplicated ODEProblem inside runtime_generic_*) does. Filing with the SciML-stack MWE per maintainer preference; happy to keep reducing if a dependency-free repro is required.

Versions

  • Enzyme v0.13.173
  • SciMLSensitivity v7.112.2, OrdinaryDiffEq v7.1.1 / OrdinaryDiffEqCore v4.5.0, DiffEqBase v7.6.0, SciMLBase v3.30.1
  • Julia 1.11.9

Context

Part of getting SecondOrder(AutoEnzyme(Forward), AutoEnzyme(Reverse)) forward-over-reverse Hessians/HVPs working through SciMLSensitivity + OrdinaryDiffEq (SciML/SciMLSensitivity.jl#1427). There are two routes to that second derivative, and each currently hits a distinct Enzyme forward-over-reverse blocker:

(I used a Float64 problem to get past the separate Float32-only _ode_init type-analysis blocker #3276.) Prior cleared blockers in this chain: #3135#3137, #3213, #3246#3251, #3253#3254, #3258#3261.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions