Description
Nested differentiation should not be trusted until this issue is resolved. Not all nested calculations will be affected, but I think it's better to play it safe. Non-nested differentiation should be unaffected.
EDIT:
This bug is restricted to cases where the inner function closes over the argument at which an outer function is being differentiated.
Here are some examples:
const D = ForwardDiff.derivative
# inner function is a closure over x -> WILL GIVE WRONG RESULT
D(x -> x * D(y -> x + y, 1), 1)
# inner function is a closure over v -> WILL GIVE WRONG RESULT
ForwardDiff.gradient(v -> sum(v) * D(y -> y * norm(v), 1), [1])
# inner function doesn't use x at all -> WILL GIVE CORRECT RESULT
D(x -> x * D(y -> y^2, 1), 1)
# inner function is a closure, but over z, not x -> WILL GIVE CORRECT RESULT
z = 3
D(x -> x / D(y -> y^z, 1), 1)
I discovered today that ForwardDiff suffers from perturbation confusion.
I noticed this when I was looking at the test suite earlier today and realized we had a test missing. The missing test should've been verifying that the following bug didn't occur:
julia> const D = ForwardDiff.derivative
derivative (generic function with 7 methods)
julia> D(x -> x * D(y -> x + y, 1), 1)
2 # this should obviously be 1
I thought I had committed this test a while ago (at the same time that I wrote the other nested differentiation tests), but apparently I screwed up somewhere, because I can't find it in the commit history.
I won't have time to really dig into this for a week or two, and a fix might require significant changes to the package (likely implementing a tagging system). I won't be sure of the best path forward until I do some thorough testing/code review. I'll go ahead and update the docs/README to make it clear that this issue exists.
@mlubin I bet this is also an issue for a lot of nested differentiation that people do using DualNumbers.jl, unless they were wise enough to code around it. I don't think DualNumbers.jl has a responsibility to provide a tagging system or anything, but it might be worth mentioning in that package's README.