Skip to content
Open
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
13 changes: 8 additions & 5 deletions src/gradient.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,24 @@ gradient(f, x::Real) = throw(DimensionMismatch("gradient(f, x) expects that x is
# result extraction #
#####################

# Without the perturbation the gradient is zero everywhere.
function extract_gradient!(::Type{T}, result::DiffResult, y::Real) where {T}
result = DiffResults.value!(result, y)
v = value(T, y)
result = DiffResults.value!(result, v)
grad = DiffResults.gradient(result)
fill!(grad, zero(y))
fill!(grad, zero(v))
return result
end

function extract_gradient!(::Type{T}, result::DiffResult, dual::Dual) where {T}
function extract_gradient!(::Type{T}, result::DiffResult, dual::Dual{T}) where {T}
result = DiffResults.value!(result, value(T, dual))
result = DiffResults.gradient!(result, partials(T, dual))
return result
end

extract_gradient!(::Type{T}, result::AbstractArray, y::Real) where {T} = fill!(result, zero(y))
function extract_gradient!(::Type{T}, result::AbstractArray, dual::Dual) where {T}
extract_gradient!(::Type{T}, result::AbstractArray, y::Real) where {T} =
fill!(result, zero(value(T, y)))
function extract_gradient!(::Type{T}, result::AbstractArray, dual::Dual{T}) where {T}
idxs = structural_eachindex(result)
for (i, idx) in zip(1:npartials(dual), idxs)
result[idx] = partials(T, dual, i)
Expand Down
56 changes: 56 additions & 0 deletions test/GradientTest.jl
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,62 @@ end
@test grad == SVector{3}(der, der, der)
end

# https://github.com/JuliaDiff/ForwardDiff.jl/issues/847
@testset "a result that does not carry the perturbation" begin
xs = ([1.0, 2.0, 3.0], SVector(1.0, 2.0, 3.0), MVector(1.0, 2.0, 3.0))

@testset "no perturbation: $(nameof(typeof(x)))" for x in xs
f = Returns(2.0)
@test all(iszero, ForwardDiff.gradient(f, x))
@test all(iszero, ForwardDiff.gradient!(fill(NaN, 3), f, x))
result = ForwardDiff.gradient!(DiffResults.GradientResult(collect(x)), f, x)
@test DiffResults.value(result) == 2.0
@test all(iszero, DiffResults.gradient(result))
end

@testset "an enclosing tag only: $(nameof(typeof(x)))" for x in xs
# `f` does not depend on `z`, so its result carries the `derivative` tag alone
ForwardDiff.derivative(1.0) do a
f = z -> a * 2.0
@test all(iszero, ForwardDiff.gradient(f, x))

# a buffer that can hold the enclosing tag is written in full, one that cannot errors
g = fill(a * 111.0, 3)
ForwardDiff.gradient!(g, f, x)
@test all(iszero, g)
@test_throws MethodError ForwardDiff.gradient!(fill(111.0, 3), f, x)

result = ForwardDiff.gradient!(DiffResults.GradientResult(fill(a * 111.0, 3)), f, x)
@test DiffResults.value(result) === a * 2.0
@test all(iszero, DiffResults.gradient(result))
return zero(a)
end
end

@testset "an enclosing tag only, chunk size = $c" for c in (1, 2, 3)
x = [1.0, 2.0, 3.0]
ForwardDiff.derivative(1.0) do a
f = z -> a * 2.0
cfg = ForwardDiff.GradientConfig(f, x, ForwardDiff.Chunk{c}())
@test all(iszero, ForwardDiff.gradient(f, x, cfg))
g = fill(a * 111.0, 3)
ForwardDiff.gradient!(g, f, x, cfg)
@test all(iszero, g)
return zero(a)
end
end

# a tag with no relation to the config's is a mismatch, not a derivative that vanishes
@testset "an unrelated tag, chunk size = $c" for c in (1, 2, 3)
x = [1.0, 2.0, 3.0]
f = z -> Dual{OuterTestTag}(sum(z), 1.0)
cfg = ForwardDiff.GradientConfig(f, x, ForwardDiff.Chunk{c}(), TestTag())
@test_throws ForwardDiff.DualMismatchError ForwardDiff.gradient(f, x, cfg)
@test_throws ForwardDiff.DualMismatchError ForwardDiff.gradient!(fill(NaN, 3), f, x, cfg)
@test_throws ForwardDiff.DualMismatchError ForwardDiff.gradient!(DiffResults.GradientResult(x), f, x, cfg)
end
end

@testset "NaN-safe mode" begin
# issue #774
f = x -> log(zero(x[1]) + x[2])
Expand Down