Skip to content

Type issues with ReverseDiff over FiniteDifferences jvp and undocumented requirement to set adapt=0 #242

@unit-of-inductance

Description

@unit-of-inductance

Consider the following MWE

using ReverseDiff
using FiniteDifferences
using LinearAlgebra

p = [1.0,2.0,3.0]
q = [4.0,5.0,6.0]
function f(x)
  return FiniteDifferences.jvp(FiniteDifferences.central_fdm(3,1), norm, (p,x))
end

ReverseDiff.gradient(f, q)

This fails because of a variety of issues. First of, to_vec fails to produce an AbstractVector{<:Real} for _jvp to consume:

ERROR: MethodError: no method matching jvp(::FiniteDifferences.AdaptedFiniteDifferenceMethod{…}, ::typeof(norm), ::Vector{…}, ::ReverseDiff.TrackedArray{…})
The function `jvp` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  jvp(::Any, ::Any, ::Tuple{Any, Any})
   @ FiniteDifferences ~/.julia/packages/FiniteDifferences/IPGFN/src/grad.jl:57
  jvp(::Any, ::Any, ::Tuple{Any, Any}...)
   @ FiniteDifferences ~/.julia/packages/FiniteDifferences/IPGFN/src/grad.jl:62

This can be remedied in this simple example by just directly using _jvp (evaluating f on q will then still work entirely without errors).
But the more important issue is that the closure that FiniteDifferences._jvp then generates is not type stable and that seems to cause the estimated step size to become tracked by ReverseDiff, and then the conversion inside _eval_function (to the eltype of the point at which the derivative is taken) fails:

ERROR: MethodError: no method matching Float64(::ReverseDiff.TrackedReal{Float64, Float64, Nothing})
The type `Float64` exists, but no method is defined for this combination of argument types when trying to construct it.

Closest candidates are:
  (::Type{T})(::Real, ::RoundingMode) where T<:AbstractFloat
   @ Base rounding.jl:265
  (::Type{T})(::T) where T<:Number
   @ Core boot.jl:900
  Float64(::Irrational{:SQRT_HALF})
   @ Random irrationals.jl:251
  ...

Stacktrace:
 [1] _eval_function(m::FiniteDifferences.AdaptedFiniteDifferenceMethod{…}, f::FiniteDifferences.var"#86#87"{…}, x::Float64, step::ReverseDiff.TrackedReal{…})
   @ FiniteDifferences ~/.julia/packages/FiniteDifferences/IPGFN/src/methods.jl:249
 [2] (::FiniteDifferences.AdaptedFiniteDifferenceMethod{…})(f::FiniteDifferences.var"#86#87"{…}, x::Float64, step::ReverseDiff.TrackedReal{…})
   @ FiniteDifferences ~/.julia/packages/FiniteDifferences/IPGFN/src/methods.jl:240
 [3] (::FiniteDifferences.AdaptedFiniteDifferenceMethod{…})(f::FiniteDifferences.var"#86#87"{…}, x::Float64)
   @ FiniteDifferences ~/.julia/packages/FiniteDifferences/IPGFN/src/methods.jl:194
 [4] _jvp(fdm::FiniteDifferences.AdaptedFiniteDifferenceMethod{…}, f::typeof(norm), x::Vector{…}, ẋ::ReverseDiff.TrackedArray{…})
   @ FiniteDifferences ~/.julia/packages/FiniteDifferences/IPGFN/src/grad.jl:48
 [5] f(x::ReverseDiff.TrackedArray{Float64, Float64, 1, Vector{Float64}, Vector{Float64}})
   @ Main ./REPL[26]:2
 [6] ReverseDiff.GradientTape(f::typeof(f), input::Vector{…}, cfg::ReverseDiff.GradientConfig{…})
   @ ReverseDiff ~/.julia/packages/ReverseDiff/rKZaG/src/api/tape.jl:199
 [7] gradient(f::Function, input::Vector{Float64}, cfg::ReverseDiff.GradientConfig{ReverseDiff.TrackedArray{…}})
   @ ReverseDiff ~/.julia/packages/ReverseDiff/rKZaG/src/api/gradients.jl:22
 [8] top-level scope
   @ REPL[27]:1

Changing to adapt=0 in central_fdm solves this issue entirely (though this still requires using _jvp). Is this use case something that should work? Should jvp be changed to accept more generic inputs for the direction? Maybe one could add the adapt=0 restriction/solution as a remark somewhere to the docs?

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