Skip to content

Enzyme.jl: Unable to differentiate through Interpolator construction #75

@kylebeggs

Description

@kylebeggs

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

  1. LinearAlgebra.factorize Union return type: The _factorize function can return either BunchKaufman or Diagonal, which Enzyme cannot handle
  2. 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

  1. Add an Enzyme-compatible implementation of Interpolator that avoids factorize (use explicit LU or Cholesky instead)
  2. File an Enzyme.jl issue about relaxing type matching requirements for custom rules
  3. Rely on ChainRulesCore rrules which Enzyme can use on Julia < 1.12

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions