Skip to content
New issue

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

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

Already on GitHub? # to your account

Enzyme 2nd order fix #322

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
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
4 changes: 4 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ version = "0.8.10"

[deps]
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"
Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9"
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
ManualNLPModels = "30dfa513-9b2f-4fb3-9796-781eabac1617"
NLPModels = "a4795742-8479-5a88-8948-cc11e1c8c1a6"
NLPModelsModifiers = "e01155f1-5c6f-4375-a9d8-616dd036575f"
NLPModelsTest = "7998695d-6960-4d3a-85c4-e1bceb8cd856"
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
Expand Down
3 changes: 3 additions & 0 deletions src/ADNLPModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,13 @@ get_F(::AbstractNLPModel, ::AbstractNLPModel) = () -> ()
Return the lagrangian function `ℓ(x) = obj_weight * f(x) + c(x)ᵀy`.
"""
function get_lag(nlp::AbstractADNLPModel, b::ADBackend, obj_weight::Real)
# println("Check")
# return x -> obj_weight * nlp.f(x)
return ℓ(x; obj_weight = obj_weight) = obj_weight * nlp.f(x)
end

function get_lag(nlp::AbstractADNLPModel, b::ADBackend, obj_weight::Real, y::AbstractVector)
println("Check2")
if nlp.meta.nnln == 0
return get_lag(nlp, b, obj_weight)
end
Expand Down
123 changes: 81 additions & 42 deletions src/enzyme.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,55 @@
function _gradient!(dx, f, x)
Enzyme.make_zero!(dx)
res = Enzyme.autodiff(
Enzyme.set_runtime_activity(Enzyme.Reverse),
f,
Enzyme.Active,
Enzyme.Duplicated(x, dx),
)
return nothing
end

function _hvp!(res, f, x, v)
Enzyme.autodiff(
Enzyme.set_runtime_activity(Enzyme.Forward),
_gradient!,
res,
Enzyme.Const(f),
Enzyme.Duplicated(x, v),
)
return nothing
end

function _gradient!(dx, ℓ, x, y, obj_weight, cx)
Enzyme.make_zero!(dx)
dcx = Enzyme.make_zero(cx)
res = Enzyme.autodiff(
Enzyme.set_runtime_activity(Enzyme.Reverse),
ℓ,
Enzyme.Active,
Enzyme.Duplicated(x, dx),
Enzyme.Const(y),
Enzyme.Const(obj_weight),
Enzyme.Duplicated(cx, dcx),
)
return nothing
end

function _hvp!(res, ℓ, x, v, y, obj_weight, cx)
dcx = Enzyme.make_zero(cx)
Enzyme.autodiff(
Enzyme.set_runtime_activity(Enzyme.Forward),
_gradient!,
res,
Enzyme.Const(ℓ),
Enzyme.Duplicated(x, v),
Enzyme.Const(y),
Enzyme.Const(obj_weight),
Enzyme.Duplicated(cx, dcx),
)
return nothing
end

struct EnzymeReverseADGradient <: InPlaceADbackend end

function EnzymeReverseADGradient(
Expand All @@ -23,9 +75,10 @@ function EnzymeReverseADJacobian(
return EnzymeReverseADJacobian()
end

struct EnzymeReverseADHessian{T} <: ADBackend
struct EnzymeReverseADHessian{T,F} <: ADBackend
seed::Vector{T}
Hv::Vector{T}
f::F
end

function EnzymeReverseADHessian(
Expand All @@ -41,11 +94,12 @@ function EnzymeReverseADHessian(

seed = zeros(T, nvar)
Hv = zeros(T, nvar)
return EnzymeReverseADHessian(seed, Hv)
return EnzymeReverseADHessian(seed, Hv, f)
end

struct EnzymeReverseADHvprod{T} <: InPlaceADbackend
struct EnzymeReverseADHvprod{T,F} <: InPlaceADbackend
grad::Vector{T}
f::F
end

function EnzymeReverseADHvprod(
Expand All @@ -57,7 +111,7 @@ function EnzymeReverseADHvprod(
kwargs...,
) where {T}
grad = zeros(T, nvar)
return EnzymeReverseADHvprod(grad)
return EnzymeReverseADHvprod(grad,f)
end

struct EnzymeReverseADJprod{T} <: InPlaceADbackend
Expand Down Expand Up @@ -153,7 +207,7 @@ function SparseEnzymeADJacobian(
)
end

struct SparseEnzymeADHessian{R, C, S, L} <: ADBackend
struct SparseEnzymeADHessian{R, C, S, L, F} <: ADBackend
nvar::Int
rowval::Vector{Int}
colptr::Vector{Int}
Expand All @@ -166,6 +220,7 @@ struct SparseEnzymeADHessian{R, C, S, L} <: ADBackend
y::Vector{R}
grad::Vector{R}
cx::Vector{R}
f::F
ℓ::L
end

Expand Down Expand Up @@ -237,19 +292,23 @@ function SparseEnzymeADHessian(
y,
grad,
cx,
f,
ℓ,
)
end

@init begin
@require Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" begin
# @init begin
# @require Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" begin
using Enzyme

function ADNLPModels.gradient(::EnzymeReverseADGradient, f, x)
g = similar(x)
Enzyme.gradient!(Enzyme.Reverse, g, Enzyme.Const(f), x)
Enzyme.autodiff(set_runtime_activity(Enzyme.Reverse), Enzyme.Const(f), Enzyme.Active, Enzyme.Duplicated(x, g))
return g
end

function ADNLPModels.gradient!(::EnzymeReverseADGradient, g, f, x)
Enzyme.make_zero!(g)
Enzyme.autodiff(Enzyme.Reverse, Enzyme.Const(f), Enzyme.Active, Enzyme.Duplicated(x, g))
return g
end
Expand All @@ -263,7 +322,17 @@ end
fill!(b.seed, zero(T))
for i = 1:n
b.seed[i] = one(T)
Enzyme.hvp!(b.Hv, Enzyme.Const(f), x, b.seed)
# Enzyme.hvp!(b.Hv, f, x, b.seed)
grad = make_zero(x)
_hvp!(DuplicatedNoNeed(grad, b.Hv), b.f, x, b.seed)
# Enzyme.autodiff(
# Enzyme.Forward,
# Enzyme.Const(Enzyme.gradient!),
# Enzyme.Const(Enzyme.Reverse),
# Enzyme.DuplicatedNoNeed(grad, b.Hv),
# Enzyme.Const(f),
# Enzyme.Duplicated(x, b.seed),
# )
view(hess, :, i) .= b.Hv
b.seed[i] = zero(T)
end
Expand Down Expand Up @@ -326,7 +395,7 @@ end
Enzyme.Const(Enzyme.gradient!),
Enzyme.Const(Enzyme.Reverse),
Enzyme.DuplicatedNoNeed(b.grad, Hv),
Enzyme.Const(f),
Enzyme.Const(b.f),
Enzyme.Duplicated(x, v),
)
return Hv
Expand Down Expand Up @@ -458,36 +527,6 @@ end
b.v[col] = 1
end

function _gradient!(dx, ℓ, x, y, obj_weight, cx)
Enzyme.make_zero!(dx)
dcx = Enzyme.make_zero(cx)
res = Enzyme.autodiff(
Enzyme.Reverse,
ℓ,
Enzyme.Active,
Enzyme.Duplicated(x, dx),
Enzyme.Const(y),
Enzyme.Const(obj_weight),
Enzyme.Duplicated(cx, dcx),
)
return nothing
end

function _hvp!(res, ℓ, x, v, y, obj_weight, cx)
dcx = Enzyme.make_zero(cx)
Enzyme.autodiff(
Enzyme.Forward,
_gradient!,
res,
Enzyme.Const(ℓ),
Enzyme.Duplicated(x, v),
Enzyme.Const(y),
Enzyme.Const(obj_weight),
Enzyme.Duplicated(cx, dcx),
)
return nothing
end

_hvp!(
Enzyme.DuplicatedNoNeed(b.grad, b.compressed_hessian_icol),
b.ℓ,
Expand Down Expand Up @@ -570,5 +609,5 @@ end
obj_weight = zero(eltype(x))
sparse_hess_coord!(b, x, obj_weight, v, vals)
end
end
end
# end
# end
62 changes: 33 additions & 29 deletions test/enzyme.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,32 @@ function test_autodiff_backend_error()
end
end

test_autodiff_backend_error()
# test_autodiff_backend_error()

push!(
ADNLPModels.predefined_backend,
:enzyme_backend => Dict(
:gradient_backend => ADNLPModels.EnzymeReverseADGradient,
:jprod_backend => ADNLPModels.EnzymeReverseADJprod,
:jtprod_backend => ADNLPModels.EnzymeReverseADJtprod,
:hprod_backend => ADNLPModels.EnzymeReverseADHvprod,
:jacobian_backend => ADNLPModels.EnzymeReverseADJacobian,
:hessian_backend => ADNLPModels.EnzymeReverseADHessian,
:ghjvprod_backend => ADNLPModels.ForwardDiffADGHjvprod,
:jprod_residual_backend => ADNLPModels.EnzymeReverseADJprod,
:jtprod_residual_backend => ADNLPModels.EnzymeReverseADJtprod,
:hprod_residual_backend => ADNLPModels.EnzymeReverseADHvprod,
:jacobian_residual_backend => ADNLPModels.EnzymeReverseADJacobian,
:hessian_residual_backend => ADNLPModels.EnzymeReverseADHessian,
),
)

const test_enzyme = true

include("sparse_jacobian.jl")
include("sparse_jacobian_nls.jl")
include("sparse_hessian.jl")
include("sparse_hessian_nls.jl")
# include("sparse_hessian_nls.jl")

list_sparse_jac_backend = ((ADNLPModels.SparseEnzymeADJacobian, Dict()),)

Expand All @@ -80,44 +100,28 @@ list_sparse_hess_backend = (
ADNLPModels.SparseEnzymeADHessian,
Dict(:coloring_algorithm => GreedyColoringAlgorithm{:direct}()),
),
(
ADNLPModels.SparseEnzymeADHessian,
Dict(:coloring_algorithm => GreedyColoringAlgorithm{:substitution}()),
),
# (
# ADNLPModels.SparseEnzymeADHessian,
# Dict(:coloring_algorithm => GreedyColoringAlgorithm{:substitution}()),
# ),
)

@testset "Sparse Hessian" begin
for (backend, kw) in list_sparse_hess_backend
sparse_hessian(backend, kw)
sparse_hessian_nls(backend, kw)
# sparse_hessian_nls(backend, kw)
end
end

for problem in NLPModelsTest.nlp_problems ∪ ["GENROSE"]
include("nlp/problems/$(lowercase(problem)).jl")
end
for problem in NLPModelsTest.nls_problems
include("nls/problems/$(lowercase(problem)).jl")
end
# for problem in NLPModelsTest.nls_problems
# include("nls/problems/$(lowercase(problem)).jl")
# end

include("utils.jl")
include("nlp/basic.jl")
include("nls/basic.jl")
# include("nlp/basic.jl")
# include("nls/basic.jl")
include("nlp/nlpmodelstest.jl")
include("nls/nlpmodelstest.jl")

@testset "Basic NLP tests using $backend " for backend in (:enzyme,)
test_autodiff_model("$backend", backend = backend)
end

@testset "Checking NLPModelsTest (NLP) tests with $backend" for backend in (:enzyme,)
nlp_nlpmodelstest(backend)
end

@testset "Basic NLS tests using $backend " for backend in (:enzyme,)
autodiff_nls_test("$backend", backend = backend)
end

@testset "Checking NLPModelsTest (NLS) tests with $backend" for backend in (:enzyme,)
nls_nlpmodelstest(backend)
end
# include("nls/nlpmodelstest.jl")
12 changes: 9 additions & 3 deletions test/nlp/nlpmodelstest.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
function nlp_nlpmodelstest(backend)
# @testset "Checking NLPModelsTest (NLP) tests with $backend" for backend in
# keys(ADNLPModels.predefined_backend)
backend = :enzyme_backend
# problem = NLPModelsTest.NLPModelsTest.nlp_problems[1]
@testset "Checking NLPModelsTest tests on problem $problem" for problem in
NLPModelsTest.nlp_problems
if problem == "BROWNDEN"
continue
end
nlp_from_T = eval(Meta.parse(lowercase(problem) * "_autodiff"))
nlp_ad = nlp_from_T(; backend = backend)
nlp_man = eval(Meta.parse(problem))()
Expand All @@ -17,7 +23,7 @@ function nlp_nlpmodelstest(backend)
@testset "Check multiple precision" begin
multiple_precision_nlp(nlp_from_T, exclude = [], linear_api = true)
end
if backend != :enzyme
if backend != :enzyme_backend
@testset "Check view subarray" begin
view_subarray_nlp(nlp_ad, exclude = [])
end
Expand All @@ -26,4 +32,4 @@ function nlp_nlpmodelstest(backend)
coord_memory_nlp(nlp_ad, exclude = [], linear_api = true)
end
end
end
# end
Loading
Loading