Summary
Reverse-mode Enzyme.autodiff over an in-place ODE RHS that calls a Lux.Chain(Dense(2,50,tanh), Dense(50,2)) (i.e. reverse over LuxLib's fused_dense/dense layers), invoked repeatedly inside a SciMLSensitivity adjoint solve (BacksolveAdjoint/QuadratureAdjoint, EnzymeVJP), segfaults with signal 11 (GC heap corruption) on Julia 1.10 and 1.12 (1.11 is fine).
Versions
Julia 1.10 / 1.12, Enzyme v0.13.171, Lux v1.31.4, LuxLib, ComponentArrays. (From SciML/SciMLSensitivity.jl CI — Core5.)
CI crash signatures (signal 11)
- Julia 1.12,
BacksolveAdjoint(): crash in gc_mark_outrefs (GC) during an Enzyme CombinedAdjointThunk for the Lux RHS, called repeatedly from _vecjacobian! per reverse step.
- Julia 1.10,
QuadratureAdjoint(): Enzyme PrimalErrorThunk in vec_pjac!.
Reproduction status — NONDETERMINISTIC
The corruption is cumulative across many in-place reverse passes; a single reverse pass is always fine. I could not reproduce a deterministic local segfault (100 Zygote.gradient iterations × 4 sensealgs survived on both 1.10.11 and 1.12.6), but it crashes reliably on the SciMLSensitivity CI runners (heavier OptimizationProblem+Adam 200-iter training path, and repeated hybrid-callback adjoints). This is the classic "GC heap corruption that depends on allocation timing/pressure" signature.
Repro scaffold (exercises the exact path; segfault is environment-dependent)
using SciMLSensitivity, OrdinaryDiffEq, Lux, ComponentArrays, Zygote, Random
dudt2 = Chain(Dense(2,50,tanh), Dense(50,2))
ps, st = Lux.setup(Xoshiro(0), dudt2); psc = ComponentArray{Float32}(ps)
dudt(du,u,p,t) = (du .= first(dudt2(u,p,st)); nothing)
z0 = Float32[2.0,0.0]; tspan=(0.0f0,10.0f0); ts=range(tspan...,length=101)
prob = ODEProblem(dudt, z0, tspan)
loss(p) = sum(abs2, Array(solve(remake(prob;p=p,u0=z0), Tsit5();
saveat=ts, sensealg=BacksolveAdjoint(autojacvec=SciMLSensitivity.EnzymeVJP()))))
for i in 1:200; Zygote.gradient(loss, psc); end # CI: signal 11 on 1.10/1.12
The differentiated unit is Enzyme reverse over the Lux dense layers in an in-place RHS, run once per reverse-integration step. Happy to provide the exact failing SciMLSensitivity tests (test/Core5/HybridNODE.jl::test_hybridNODE2/3) and a Manifest. Filing primarily so there's an upstream tracking point; I realize the nondeterminism makes this hard — suggestions for getting a deterministic repro (e.g. forcing GC stress) welcome.
Summary
Reverse-mode
Enzyme.autodiffover an in-place ODE RHS that calls aLux.Chain(Dense(2,50,tanh), Dense(50,2))(i.e. reverse over LuxLib'sfused_dense/dense layers), invoked repeatedly inside a SciMLSensitivity adjoint solve (BacksolveAdjoint/QuadratureAdjoint,EnzymeVJP), segfaults with signal 11 (GC heap corruption) on Julia 1.10 and 1.12 (1.11 is fine).Versions
Julia 1.10 / 1.12, Enzyme v0.13.171, Lux v1.31.4, LuxLib, ComponentArrays. (From SciML/SciMLSensitivity.jl CI — Core5.)
CI crash signatures (signal 11)
BacksolveAdjoint(): crash ingc_mark_outrefs(GC) during an EnzymeCombinedAdjointThunkfor the Lux RHS, called repeatedly from_vecjacobian!per reverse step.QuadratureAdjoint(): EnzymePrimalErrorThunkinvec_pjac!.Reproduction status — NONDETERMINISTIC
The corruption is cumulative across many in-place reverse passes; a single reverse pass is always fine. I could not reproduce a deterministic local segfault (100
Zygote.gradientiterations × 4 sensealgs survived on both 1.10.11 and 1.12.6), but it crashes reliably on the SciMLSensitivity CI runners (heavierOptimizationProblem+Adam 200-iter training path, and repeated hybrid-callback adjoints). This is the classic "GC heap corruption that depends on allocation timing/pressure" signature.Repro scaffold (exercises the exact path; segfault is environment-dependent)
The differentiated unit is Enzyme reverse over the Lux dense layers in an in-place RHS, run once per reverse-integration step. Happy to provide the exact failing SciMLSensitivity tests (
test/Core5/HybridNODE.jl::test_hybridNODE2/3) and a Manifest. Filing primarily so there's an upstream tracking point; I realize the nondeterminism makes this hard — suggestions for getting a deterministic repro (e.g. forcing GC stress) welcome.