JuliaDiff / JuliaDiff/FiniteDifferences.jl

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

Open
#242 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Julia
Stars
318
Forks
32
PR merge metrics
No merged PRs in 30d

Description

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?

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the supplied MWE with ReverseDiff and inspect the jvp and _jvp paths, including central_fdm with adapt=0. Search for tests covering these APIs and determine the expected behavior for tracked directions and adaptive step sizes. Done should include a tested resolution or a clear documentation decision about the supported usage.

Written by the indexing model from the issue text.

Assessment

Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.