Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/ReverseDiff.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module ReverseDiff

using Base: RefValue
using ChainRulesCore: ChainRulesCore, AbstractThunk, AbstractZero, unthunk
using ChainRulesCore: ChainRulesCore, AbstractThunk, AbstractZero, NoTangent, unthunk
using DiffResults: DiffResults, DiffResult
using DiffRules: DiffRules
using ForwardDiff: ForwardDiff, Dual
Expand Down
2 changes: 1 addition & 1 deletion src/derivatives/arrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ end
@grad function fill(v::Real, dims...)
function back(Δ)
size(Δ) ≢ dims && error("Dimension mismatch")
return (sum(Δ), map(_ -> nothing, dims)...)
return (sum(Δ), map(_ -> NoTangent(), dims)...)
end
return fill(value(v), dims...), back
end
Expand Down
14 changes: 11 additions & 3 deletions src/macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ skipvalue(x::AbstractArray{<:TrackedReal}) = skipvalue(value(x))
return dot(xv, xv), Δ -> (Δ * 2 * xv,)
end
The `@grad` macro provides a way for the users to define custom adjoints for single-output functions wrt to their input numbers or arrays.

The pullback returns a tuple with one tangent per positional argument: a number or an array, a
thunk, `ChainRulesCore.NoTangent()` or `ChainRulesCore.ZeroTangent()`.
"""
macro grad(expr)
d = MacroTools.isdef(expr) ? MacroTools.splitdef(expr) : nothing
Expand Down Expand Up @@ -231,8 +234,9 @@ macro grad(expr)
output = instruction.output
input = instruction.input
back = instruction.cache[1]
input_derivs = back($ReverseDiff.deriv(output))
$ReverseDiff._check_tangents($f, input_derivs, length(input))
back_output = back($ReverseDiff.deriv(output))
$ReverseDiff._check_tangents($f, back_output, length(input))
input_derivs = map($ReverseDiff._totangent, back_output)
$ReverseDiff._add_to_deriv!.(input, input_derivs)
$ReverseDiff.unseed!(output)
return nothing
Expand Down Expand Up @@ -426,8 +430,12 @@ function _check_tangents(f, tangents, n)
end
end

# `@grad` pullbacks may use `nothing`, like in Tracker
_totangent(x) = x
_totangent(::Nothing) = NoTangent()

_add_to_deriv!(d1, d2) = nothing
_add_to_deriv!(d1::Union{TrackedReal, AbstractArray}, ::Union{AbstractZero, Nothing}) = nothing
_add_to_deriv!(d1::Union{TrackedReal, AbstractArray}, ::AbstractZero) = nothing
function _add_to_deriv!(d1::Union{TrackedReal, AbstractArray}, d2::AbstractThunk)
istracked(d1) && increment_deriv!(d1, unthunk(d2))
return nothing
Expand Down
15 changes: 15 additions & 0 deletions test/ChainRulesTests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,16 @@ ReverseDiff.@grad function k_zero(x, y)
return sum(4xv) + sum(4yv), pb_k_zero
end

l_zero(x, y) = sum(4x) + sum(4y)
function l_zero(x::ReverseDiff.TrackedArray, y::ReverseDiff.TrackedArray)
return ReverseDiff.track(l_zero, x, y)
end
ReverseDiff.@grad function l_zero(x, y)
xv, yv = ReverseDiff.value(x), ReverseDiff.value(y)
pb_l_zero(Δ) = (fill(3 * Δ, size(xv)), NoTangent())
return sum(4xv) + sum(4yv), pb_l_zero
end

h_zero_vec(z) = h_zero(z[1], z[2])

@testset "zero tangents for tracked arguments" begin
Expand All @@ -388,6 +398,11 @@ h_zero_vec(z) = h_zero(z[1], z[2])
results = ReverseDiff.gradient(k_zero, (x, y))
@test results[1] == fill(3, size(x))
@test results[2] == zeros(size(y))

# `NoTangent()` from a `@grad` pullback
results = ReverseDiff.gradient(l_zero, (x, y))
@test results[1] == fill(3, size(x))
@test results[2] == zeros(size(y))
end

### `where` clauses in the imported signature
Expand Down
Loading