Summary
Enzyme.jl cannot differentiate through Interpolator(points, values) construction in loss functions. This blocks a common use case of differentiating RBF interpolation w.r.t. input values.
Minimal Reproducible Example
using RadialBasisFunctions
using StaticArrays
import Enzyme
N = 30
points = [SVector{2}(rand(), rand()) for _ in 1:N]
values = sin.(getindex.(points, 1))
eval_points = [SVector{2}(rand(), rand()) for _ in 1:10]
function loss_interp(v)
interp = Interpolator(points, v)
result = interp(eval_points)
return sum(result .^ 2)
end
dv = zeros(N)
Enzyme.autodiff(Enzyme.Reverse, loss_interp, Enzyme.Active, Enzyme.Duplicated(values, dv))
Error Messages
Without custom rules (HEAD of enzyme branch)
ERROR: IllegalTypeAnalysisException: Enzyme compilation failed due to illegal type analysis.
This usually indicates the use of a Union type, which is not fully supported...
Failure within method: MethodInstance for LinearAlgebra.var"#_factorize#135"(...)
The issue is that Interpolator internally calls factorize which returns a Union type (BunchKaufman or Diagonal).
Setting Enzyme.API.strictAliasing!(false) causes Julia to crash/segfault.
With custom EnzymeRules (attempted fix)
Adding custom augmented_primal and reverse rules for Interpolator construction leads to:
ERROR: AugmentedRuleReturnError: Incorrect return type for primal and shadow configuration...
expected : Interpolator{..., MB} where MB<:MonomialBasis
found : Interpolator{..., MonomialBasis{2, 2, ...}}
Enzyme requires exact type matching for custom rules, but the rule returns a concrete type while Julia's type inference produces an abstract type.
Root Causes
- LinearAlgebra.factorize Union return type: The
_factorize function can return either BunchKaufman or Diagonal, which Enzyme cannot handle
- Custom rule type matching: When defining custom rules to bypass the factorize issue, Enzyme's strict type matching requirements conflict with Julia's type inference
Environment
- Julia: 1.10.10 and 1.11.8
- Enzyme.jl: 0.13.129
- EnzymeCore.jl: 0.8.18
- RadialBasisFunctions.jl: 0.3.0 (enzyme branch)
Workaround
Currently, use ChainRules/Mooncake for Interpolator construction differentiation:
import DifferentiationInterface as DI
import Mooncake
backend = DI.AutoMooncake(; config=nothing)
grad = DI.gradient(loss_interp, backend, values)
Related Issues
- Enzyme.jl #2699 (Julia 1.12+ compatibility)
Potential Fixes
- Add an Enzyme-compatible implementation of
Interpolator that avoids factorize (use explicit LU or Cholesky instead)
- File an Enzyme.jl issue about relaxing type matching requirements for custom rules
- Rely on ChainRulesCore rrules which Enzyme can use on Julia < 1.12
🤖 Generated with Claude Code
Summary
Enzyme.jl cannot differentiate through
Interpolator(points, values)construction in loss functions. This blocks a common use case of differentiating RBF interpolation w.r.t. input values.Minimal Reproducible Example
Error Messages
Without custom rules (HEAD of enzyme branch)
The issue is that
Interpolatorinternally callsfactorizewhich returns a Union type (BunchKaufmanorDiagonal).Setting
Enzyme.API.strictAliasing!(false)causes Julia to crash/segfault.With custom EnzymeRules (attempted fix)
Adding custom
augmented_primalandreverserules forInterpolatorconstruction leads to:Enzyme requires exact type matching for custom rules, but the rule returns a concrete type while Julia's type inference produces an abstract type.
Root Causes
_factorizefunction can return eitherBunchKaufmanorDiagonal, which Enzyme cannot handleEnvironment
Workaround
Currently, use ChainRules/Mooncake for Interpolator construction differentiation:
Related Issues
Potential Fixes
Interpolatorthat avoidsfactorize(use explicit LU or Cholesky instead)🤖 Generated with Claude Code