Skip to content

Commit

Permalink
fULL support of sparse AD for NLS models
Browse files Browse the repository at this point in the history
  • Loading branch information
amontoison committed Jun 7, 2024
1 parent 29b1d2d commit 1e002d5
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 2 deletions.
4 changes: 4 additions & 0 deletions src/ADNLPModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ module ADNLPModels
using LinearAlgebra, SparseArrays

# external
<<<<<<< HEAD
using ADTypes: ADTypes, AbstractSparsityDetector
=======
using ADTypes: ADTypes
>>>>>>> 5cacfb0 (fULL support of sparse AD for NLS models)
using SparseConnectivityTracer, ColPack, ForwardDiff, ReverseDiff

# JSO
Expand Down
19 changes: 19 additions & 0 deletions src/ad_api.jl
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,25 @@ function NLPModels.hess_coord!(
return vals
end

function NLPModels.hess_structure_residuals!(
b::ADBackend,
nls::AbstractADNLSModel,
rows::AbstractVector{<:Integer},
cols::AbstractVector{<:Integer},
)
nothing
end

function NLPModels.hess_coord_residuals!(
b::ADBackend,
nls::AbstractADNLSModel,
x::AbstractVector,
v::AbstractVector,
vals::AbstractVector,
)
nothing
end

function NLPModels.hprod!(
b::ADBackend,
nlp::ADModel,
Expand Down
41 changes: 41 additions & 0 deletions src/sparse_hessian.jl
Original file line number Diff line number Diff line change
Expand Up @@ -294,3 +294,44 @@ function NLPModels.hess_coord!(
sparse_hess_coord!(ℓ, b, x, obj_weight, b.y, vals)
return vals
end

function NLPModels.hess_structure_residuals!(
b::Union{SparseADHessian, SparseReverseADHessian},
nls::AbstractADNLSModel,
rows::AbstractVector{<:Integer},
cols::AbstractVector{<:Integer},
)
function objective(x)
F = get_F(nls, b)
Fx = F(x)
return dot(Fx, Fx) / 2
end

H = compute_hessian_sparsity(objective, nls.meta.nvar, nothing, 0)
trilH = tril(H)
rowval = trilH.rowval
colptr = trilH.colptr
rows .= rowval
for i = 1:(nls.meta.nvar)
for j = colptr[i]:(colptr[i + 1] - 1)
cols[j] = i
end
end
return rows, cols
end

function NLPModels.hess_coord_residuals!(
b::Union{SparseADHessian, SparseReverseADHessian},
nls::AbstractADNLSModel,
x::AbstractVector,
v::AbstractVector,
vals::AbstractVector,
)
function objective(x)
F = get_F(nls, b)
Fx = F(x)
return dot(Fx, Fx) / 2
end

sparse_hess_coord!(objective, b, x, 1.0, v, vals)
end
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ end

@testset "Basic Hessian derivative test" begin
include("sparse_hessian.jl")
include("sparse_hessian_nls.jl")
end

for problem in NLPModelsTest.nlp_problems ["GENROSE"]
Expand Down
45 changes: 45 additions & 0 deletions test/sparse_hessian_nls.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
list_sparse_hess_backend = (
(ADNLPModels.SparseADHessian, Dict()),
(ADNLPModels.ForwardDiffADHessian, Dict()),
)

dt = (Float32, Float64)

@testset "Basic Hessian of residual derivative with backend=$(backend) and T=$(T)" for T in dt,
(backend, kw) in list_sparse_hess_backend

F!(Fx, x) = begin
Fx[1] = x[1] - 1
Fx[2] = 10 * (x[2] - x[1]^2)
Fx[3] = x[2] + 1
Fx
end
x0 = T[-1.2; 1.0]
nvar = 2
nequ = 3
nls = ADNLPModels.ADNLSModel!(F!, x0, 3, hessian_residual_backend = backend; kw...)

x = rand(T, nvar)
v = rand(T, nequ)
rows, cols = zeros(Int, nls.nls_meta.nnzh), zeros(Int, nls.nls_meta.nnzh)
vals = zeros(T, nls.nls_meta.nnzh)
hess_structure_residual!(nls, rows, cols)
hess_coord_residual!(nls, x, v, vals)
@test eltype(vals) == T
H = sparse(rows, cols, vals, nvar, nvar)
# @test H == []

# Test also the implementation of the backends
b = nls.adbackend.hessian_residual_backend
obj_weight = 0.5
@test nls.nls_meta.nnzh == ADNLPModels.get_nln_nnzh(b, nvar)
ADNLPModels.hess_structure_residual!(b, nls, rows, cols)
ADNLPModels.hess_coord_residual!(b, nls, x, y, obj_weight, vals)
@test eltype(vals) == T
H = sparse(rows, cols, vals, nvar, nvar)
# @test H == []

nls = ADNLPModels.ADNLSModel!(F!, x0, 3, matrix_free = true; kw...)
@test nls.adbackend.hessian_backend isa ADNLPModels.EmptyADbackend
@test nls.adbackend.hessian_residual_backend isa ADNLPModels.EmptyADbackend
end
4 changes: 3 additions & 1 deletion test/sparse_jacobian.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
list_sparse_jac_backend = (
(ADNLPModels.SparseADJacobian, Dict()), # default
(ADNLPModels.SparseADJacobian, Dict()),
(ADNLPModels.ForwardDiffADJacobian, Dict()),
)

dt = (Float32, Float64)

@testset "Basic Jacobian derivative with backend=$(backend) and T=$(T)" for T in dt,
(backend, kw) in list_sparse_jac_backend

Expand Down
4 changes: 3 additions & 1 deletion test/sparse_jacobian_nls.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
list_sparse_jac_backend = (
(ADNLPModels.SparseADJacobian, Dict()), # default
(ADNLPModels.SparseADJacobian, Dict()),
(ADNLPModels.ForwardDiffADJacobian, Dict()),
)

dt = (Float32, Float64)

@testset "Basic Jacobian of residual derivative with backend=$(backend) and T=$(T)" for T in dt,
(backend, kw) in list_sparse_jac_backend

Expand Down

0 comments on commit 1e002d5

Please sign in to comment.