Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some missing primal_value methods #47

Merged
merged 7 commits into from
Feb 3, 2022
Merged
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
1 change: 1 addition & 0 deletions src/forwarddiff.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ end
end

primal_value(x::ForwardDiff.Dual) = ForwardDiff.value(x)
primal_value(x::AbstractArray{<:ForwardDiff.Dual}) = ForwardDiff.value.(x)

# these implementations are more efficient than the fallbacks

Expand Down
2 changes: 2 additions & 0 deletions src/reversediff.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using .ReverseDiff: ReverseDiff, DiffResults

primal_value(x::ReverseDiff.TrackedReal) = ReverseDiff.value(x)
primal_value(x::AbstractArray{<:ReverseDiff.TrackedReal}) = ReverseDiff.value.(x)
primal_value(x::ReverseDiff.TrackedArray) = ReverseDiff.value(x)

"""
ReverseDiffBackend
Expand Down
4 changes: 4 additions & 0 deletions src/tracker.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ function second_lowest(::TrackerBackend)
return throw(ArgumentError("Tracker backend does not support nested differentiation."))
end

primal_value(x::Tracker.TrackedReal) = Tracker.data(x)
primal_value(x::Tracker.TrackedArray) = Tracker.data(x)
primal_value(x::AbstractArray{<:Tracker.TrackedReal}) = Tracker.data.(x)
mohamed82008 marked this conversation as resolved.
Show resolved Hide resolved

@primitive function pullback_function(ba::TrackerBackend, f, xs...)
value, back = Tracker.forward(f, xs...)
function pullback(ws)
Expand Down
2 changes: 1 addition & 1 deletion test/defaults.jl
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ end
test_hessians(fdm_backend3)
end
@testset "jvp" begin
test_jvp(fdm_backend1)
test_jvp(fdm_backend1, test_types=false)
test_jvp(fdm_backend2; vaugmented=true)
test_jvp(fdm_backend3)
end
Expand Down
93 changes: 84 additions & 9 deletions test/test_utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function test_higher_order_backend(backends...)
backends[1] == AD.reduce_order(ADbackends)
end

function test_derivatives(backend; multiple_inputs=true)
function test_derivatives(backend; multiple_inputs=true, test_types=true)
# test with respect to analytical solution
der_exact = (dfderdx(xscalar,yscalar), dfderdy(xscalar,yscalar))
if multiple_inputs
Expand All @@ -68,25 +68,44 @@ function test_derivatives(backend; multiple_inputs=true)
# test if single input (no tuple works)
valscalara, dera = AD.value_and_derivative(backend, x -> fder(x, yscalar), xscalar)
valscalarb, derb = AD.value_and_derivative(backend, y -> fder(xscalar, y), yscalar)
if test_types
@test valscalara isa Float64
@test valscalarb isa Float64
@test dera[1] isa Float64
@test derb[1] isa Float64
end
@test fder(xscalar, yscalar) == valscalara
@test fder(xscalar, yscalar) == valscalarb
@test isapprox(dera[1], der_exact[1], rtol=1e-10)
@test isapprox(derb[1], der_exact[2], rtol=1e-10)
end

function test_gradients(backend; multiple_inputs=true)
function test_gradients(backend; multiple_inputs=true, test_types=true)
# test with respect to analytical solution
grad_exact = (dfgraddx(xvec,yvec), dfgraddy(xvec,yvec))
if multiple_inputs
grad1 = AD.gradient(backend, fgrad, xvec, yvec)
@test minimum(isapprox.(grad_exact, grad1, rtol=1e-10))
valscalar, grad2 = AD.value_and_gradient(backend, fgrad, xvec, yvec)
if test_types
@test valscalar isa Float64
@test grad1[1] isa AbstractVector{Float64}
@test grad1[2] isa AbstractVector{Float64}
@test grad2[1] isa AbstractVector{Float64}
@test grad2[2] isa AbstractVector{Float64}
end
@test valscalar == fgrad(xvec, yvec)
@test norm.(grad2 .- grad1) == (0, 0)
end
# test if single input (no tuple works)
valscalara, grada = AD.value_and_gradient(backend, x -> fgrad(x, yvec), xvec)
valscalarb, gradb = AD.value_and_gradient(backend, y -> fgrad(xvec, y), yvec)
if test_types
@test valscalara isa Float64
@test valscalarb isa Float64
@test grada[1] isa AbstractVector{Float64}
@test gradb[1] isa AbstractVector{Float64}
end
@test fgrad(xvec, yvec) == valscalara
@test fgrad(xvec, yvec) == valscalarb
@test isapprox(grada[1], grad_exact[1], rtol=1e-10)
Expand All @@ -95,20 +114,33 @@ function test_gradients(backend; multiple_inputs=true)
@test yvec == yvec2
end

function test_jacobians(backend; multiple_inputs=true)
function test_jacobians(backend; multiple_inputs=true, test_types=true)
# test with respect to analytical solution
jac_exact = (dfjacdx(xvec, yvec), dfjacdy(xvec, yvec))
if multiple_inputs
jac1 = AD.jacobian(backend, fjac, xvec, yvec)
@test minimum(isapprox.(jac_exact, jac1, rtol=1e-10))
valvec, jac2 = AD.value_and_jacobian(backend, fjac, xvec, yvec)
if test_types
@test valvec isa Vector{Float64}
@test jac1[1] isa Matrix{Float64}
@test jac1[2] isa Matrix{Float64}
@test jac2[1] isa Matrix{Float64}
@test jac2[2] isa Matrix{Float64}
end
@test valvec == fjac(xvec, yvec)
@test norm.(jac2 .- jac1) == (0, 0)
end

# test if single input (no tuple works)
valveca, jaca = AD.value_and_jacobian(backend, x -> fjac(x, yvec), xvec)
valvecb, jacb = AD.value_and_jacobian(backend, y -> fjac(xvec, y), yvec)
if test_types
@test valveca isa Vector{Float64}
@test valvecb isa Vector{Float64}
@test jaca[1] isa Matrix{Float64}
@test jacb[1] isa Matrix{Float64}
end
@test fjac(xvec, yvec) == valveca
@test fjac(xvec, yvec) == valvecb
@test isapprox(jaca[1], jac_exact[1], rtol=1e-10)
Expand All @@ -117,7 +149,7 @@ function test_jacobians(backend; multiple_inputs=true)
@test yvec == yvec2
end

function test_hessians(backend; multiple_inputs=false)
function test_hessians(backend; multiple_inputs=false, test_types=true)
if multiple_inputs
# ... but
error("multiple_inputs=true is not supported.")
Expand All @@ -134,25 +166,40 @@ function test_hessians(backend; multiple_inputs=false)
# test if single input (no tuple works)
fhess = x -> fgrad(x, yvec)
hess1 = AD.hessian(backend, fhess, xvec)
if test_types
@test hess1[1] isa Matrix{Float64}
end
# test with respect to analytical solution
@test dfgraddxdx(xvec,yvec) ≈ hess1[1] atol=1e-10
@test dfgraddxdx(xvec, yvec) ≈ hess1[1] atol=1e-10

valscalar, hess2 = AD.value_and_hessian(backend, fhess, xvec)
if test_types
@test valscalar isa Float64
@test hess2[1] isa Matrix{Float64}
end
@test valscalar == fgrad(xvec, yvec)
@test norm.(hess2 .- hess1) == (0,)
valscalar, grad, hess3 = AD.value_gradient_and_hessian(backend, fhess, xvec)
if test_types
@test valscalar isa Float64
@test grad[1] isa AbstractVector{Float64}
@test hess3[1] isa Matrix{Float64}
end
@test valscalar == fgrad(xvec, yvec)
@test norm.(grad .- AD.gradient(backend, fhess, xvec)) == (0,)
@test norm.(hess3 .- hess1) == (0,)

@test xvec == xvec2
@test yvec == yvec2
fhess2 = x-> dfgraddx(x, yvec)
fhess2 = x -> dfgraddx(x, yvec)
hess4 = AD.jacobian(backend, fhess2, xvec)
if test_types
@test hess4[1] isa Matrix{Float64}
end
@test minimum(isapprox.(hess4, hess1, atol=1e-10))
end

function test_jvp(backend; multiple_inputs=true, vaugmented=false, rng=Random.GLOBAL_RNG)
function test_jvp(backend; multiple_inputs=true, vaugmented=false, rng=Random.GLOBAL_RNG, test_types=true)
v = (rand(rng, length(xvec)), rand(rng, length(yvec)))

if multiple_inputs
Expand All @@ -170,6 +217,14 @@ function test_jvp(backend; multiple_inputs=true, vaugmented=false, rng=Random.GL
((valvec1, pf2x), (valvec2, pf2y)) = (valvec, pf2[1]), (valvec, pf2[2])
end

if test_types
@test valvec1 isa Vector{Float64}
@test valvec2 isa Vector{Float64}
@test pf1[1] isa Vector{Float64}
@test pf1[2] isa Vector{Float64}
@test pf2x isa Vector{Float64}
@test pf2y isa Vector{Float64}
end
@test valvec1 == fjac(xvec, yvec)
@test valvec2 == fjac(xvec, yvec)
@test norm.((pf2x,pf2y) .- pf1) == (0, 0)
Expand All @@ -182,17 +237,31 @@ function test_jvp(backend; multiple_inputs=true, vaugmented=false, rng=Random.GL
valvec1, pf1 = AD.value_and_pushforward_function(backend, x -> fjac(x, yvec), xvec)(v[1])
valvec2, pf2 = AD.value_and_pushforward_function(backend, y -> fjac(xvec, y), yvec)(v[2])

if test_types
@test valvec1 isa Vector{Float64}
@test valvec2 isa Vector{Float64}
@test pf1[1] isa Vector{Float64}
@test pf2[1] isa Vector{Float64}
end
@test valvec1 == fjac(xvec, yvec)
@test valvec2 == fjac(xvec, yvec)
@test minimum(isapprox.((pf1[1],pf2[1]), (jxvp(xvec,yvec,v[1]), jyvp(xvec,yvec,v[2])), atol=1e-10))
end

function test_j′vp(backend; multiple_inputs=true, rng=Random.GLOBAL_RNG)
function test_j′vp(backend; multiple_inputs=true, rng=Random.GLOBAL_RNG, test_types=true)
# test with respect to analytical solution
w = rand(rng, length(fjac(xvec, yvec)))
if multiple_inputs
pb1 = AD.pullback_function(backend, fjac, xvec, yvec)(w)
valvec, pb2 = AD.value_and_pullback_function(backend, fjac, xvec, yvec)(w)

if test_types
@test valvec isa Vector{Float64}
@test pb1[1] isa AbstractVector{Float64}
@test pb1[2] isa AbstractVector{Float64}
@test pb2[1] isa AbstractVector{Float64}
@test pb2[2] isa AbstractVector{Float64}
end
@test valvec == fjac(xvec, yvec)
@test norm.(pb2 .- pb1) == (0, 0)
@test minimum(isapprox.(pb1, (vJxp(xvec,yvec,w), vJyp(xvec,yvec,w)), atol=1e-10))
Expand All @@ -202,6 +271,12 @@ function test_j′vp(backend; multiple_inputs=true, rng=Random.GLOBAL_RNG)

valvec1, pb1 = AD.value_and_pullback_function(backend, x -> fjac(x, yvec), xvec)(w)
valvec2, pb2 = AD.value_and_pullback_function(backend, y -> fjac(xvec, y), yvec)(w)
if test_types
@test valvec1 isa Vector{Float64}
@test valvec2 isa Vector{Float64}
@test pb1[1] isa AbstractVector{Float64}
@test pb2[1] isa AbstractVector{Float64}
end
@test valvec1 == fjac(xvec, yvec)
@test valvec2 == fjac(xvec, yvec)
@test minimum(isapprox.((pb1[1],pb2[1]), (vJxp(xvec,yvec,w), vJyp(xvec,yvec,w)), atol=1e-10))
Expand Down
4 changes: 4 additions & 0 deletions test/tracker.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ using Tracker
@test_throws ArgumentError AD.second_lowest(backend)
@test_throws ArgumentError AD.hessian(backend, sum, randn(3))
end
@testset "primal_value for array of tracked reals" begin
@test AD.primal_value([Tracker.TrackedReal(1.0)]) isa Vector{Float64}
@test AD.primal_value(fill(Tracker.TrackedReal(1.0), 1, 1)) isa Matrix{Float64}
end
@testset "Derivative" begin
test_derivatives(backend)
end
Expand Down