Skip to content

MethodError when evaluating vector-valued interpolants (SVector) during ForwardDiff automatic differentiation (v0.4.10) #144

@henry2004y

Description

@henry2004y

Hi,

I'm integrating the changes in v0.4.10 in my package. During the upgrades, I noticed that I need a type piracy currently as a workaround to handle derivatives:

# Workaround for FastInterpolations v0.4.10 type promotion limitation with SVector and ForwardDiff.Dual
@inline function FastInterpolations._output_eltype(::Type{SVector{N, T}}, types::Type...) where {N, T}
    return SVector{N, promote_type(T, types...)}
end

Probably it's better to fix it in FastInterpolations.jl directly.

Description

When attempting to run automatic differentiation via ForwardDiff.jl on a vector-valued interpolant (using StaticArrays.SVector), evaluation throws a MethodError. This occurs due to new type promotion/conversion constraints introduced in FastInterpolations.jl v0.4.10.

Minimal Working Example (MWE)

using FastInterpolations, StaticArrays, ForwardDiff

x = 1.0:10.0
y = [SA[1.0, 2.0, 3.0] for _ in 1:10]
itp = linear_interp(x, y)

# Evaluating the derivative of itp(t)[1] at t = 2.0 throws a MethodError
ForwardDiff.derivative(t -> itp(t)[1], 2.0)

Root Cause Analysis

  1. In src/core/interpolant_protocol.jl (line 58), the interpolant call protocol attempts to convert the evaluation result val to _output_eltype(itp, typeof(xq)):
val = _itp_eval_scalar(itp, xq, extrap, deriv, searcher)
return convert(_output_eltype(itp, typeof(xq)), val)
  1. The trait method _output_eltype(::Type{Tv}, types::Type...) (defined in src/core/utils.jl, line 145) calls promote_type(Tv, types...) to compute the promoted return type. In this scenario:
    Tv is SVector{3, Float64}.
    types... contains Float64 (grid element type) and ForwardDiff.Dual (query point type).

  2. Since SVector{3, Float64} is a vector and ForwardDiff.Dual is a scalar, promote_type has no standard promotion rules defined between them and falls back to returning Any.

  3. Due to the fallback logic in _output_eltype:

Tr = promote_type(Tv, types...)
Tc = isconcretetype(Tr) ? Tr : Tv # Returns SVector{3, Float64} because Any is not concrete

The expected output type is resolved to SVector{3, Float64}.

  1. The actual calculated evaluation val produces SVector{3, ForwardDiff.Dual} carrying the derivatives. FastInterpolations tries to perform:
convert(SVector{3, Float64}, val)

This fails with a MethodError: no method matching Float64(::ForwardDiff.Dual...) because dual numbers cannot be converted back to standard floats without discarding derivative information.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    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