From 9af0a7ef7b224f36fd737273d9c335fdf5cfc5f4 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Tue, 4 Jun 2024 08:22:41 +0200 Subject: [PATCH 01/12] Use generic sparsity detector --- Project.toml | 2 ++ src/sparse_sym.jl | 16 +++++----------- test/Project.toml | 6 ++---- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/Project.toml b/Project.toml index 4364b41c..8a337614 100644 --- a/Project.toml +++ b/Project.toml @@ -3,6 +3,7 @@ uuid = "54578032-b7ea-4c30-94aa-7cbd1cce6c9a" version = "0.7.2" [deps] +ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b" ColPack = "ffa27691-3a59-46ab-a8d4-551f45b8d401" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -12,6 +13,7 @@ ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [compat] +ADTypes = "1.2.1" ColPack = "0.4" ForwardDiff = "0.9.0, 0.10.0" NLPModels = "0.18, 0.19, 0.20, 0.21" diff --git a/src/sparse_sym.jl b/src/sparse_sym.jl index e90c5514..f20cb763 100644 --- a/src/sparse_sym.jl +++ b/src/sparse_sym.jl @@ -1,19 +1,13 @@ function compute_hessian_sparsity(f, nvar, c!, ncon) - Symbolics.@variables xs[1:nvar] - xsi = Symbolics.scalarize(xs) - fun = f(xsi) - if ncon > 0 - Symbolics.@variables ys[1:ncon] - ysi = Symbolics.scalarize(ys) - cx = similar(ysi) - fun = fun + dot(c!(cx, xsi), ysi) - end - S = Symbolics.hessian_sparsity(fun, ncon == 0 ? xsi : [xsi; ysi]) # , full = false + detector = Symbolics.SymbolicsSparsityDetector() # replaceable + lagrangian(x) = f(x) + dot(rand(ncon), c!(zeros(ncon), x)) + S = ADTypes.hessian_sparsity(lagrangian, rand(nvar), detector) # , full = false return S end function compute_jacobian_sparsity(c!, cx, x0) - S = Symbolics.jacobian_sparsity(c!, cx, x0) + detector = Symbolics.SymbolicsSparsityDetector() # replaceable + S = ADTypes.jacobian_sparsity(c!, cx, x0, detector) return S end diff --git a/test/Project.toml b/test/Project.toml index 0f94a872..68587070 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -10,7 +10,6 @@ NLPModelsTest = "7998695d-6960-4d3a-85c4-e1bceb8cd856" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SparseDiffTools = "47a9eef4-7e08-11e9-0b38-333d64bd3804" -SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" @@ -24,7 +23,6 @@ NLPModels = "0.21" NLPModelsModifiers = "0.7" NLPModelsTest = "0.10" ReverseDiff = "1" -SparseDiffTools = "2.3" -Symbolics = "5.3" -SymbolicUtils = "=1.5.1" +SparseDiffTools = "2.19" +Symbolics = "5.29" Zygote = "0.6" From 873e1508da51188da12a36feb97d67906cfb5277 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Tue, 4 Jun 2024 08:37:17 +0200 Subject: [PATCH 02/12] Working --- src/ADNLPModels.jl | 1 + src/sparse_sym.jl | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ADNLPModels.jl b/src/ADNLPModels.jl index 481724bb..d3f846b4 100644 --- a/src/ADNLPModels.jl +++ b/src/ADNLPModels.jl @@ -3,6 +3,7 @@ module ADNLPModels # stdlib using LinearAlgebra, SparseArrays # external +using ADTypes: ADTypes using ColPack, ForwardDiff, ReverseDiff # JSO using NLPModels diff --git a/src/sparse_sym.jl b/src/sparse_sym.jl index f20cb763..1c04aa00 100644 --- a/src/sparse_sym.jl +++ b/src/sparse_sym.jl @@ -1,6 +1,10 @@ function compute_hessian_sparsity(f, nvar, c!, ncon) detector = Symbolics.SymbolicsSparsityDetector() # replaceable - lagrangian(x) = f(x) + dot(rand(ncon), c!(zeros(ncon), x)) + function lagrangian(x) + cx = zeros(eltype(x), ncon) + c!(cx, x) + return f(x) + dot(rand(ncon), cx) + end S = ADTypes.hessian_sparsity(lagrangian, rand(nvar), detector) # , full = false return S end From 50e4e2c51d6243ee91a0d53ea46cdec045403a15 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Tue, 4 Jun 2024 08:38:16 +0200 Subject: [PATCH 03/12] Versions --- test/Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Project.toml b/test/Project.toml index 68587070..1f344a0e 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -23,6 +23,6 @@ NLPModels = "0.21" NLPModelsModifiers = "0.7" NLPModelsTest = "0.10" ReverseDiff = "1" -SparseDiffTools = "2.19" +SparseDiffTools = "2.3" Symbolics = "5.29" Zygote = "0.6" From 1d7ead401496c66907fcb2e63c473fecdf526806 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:16:30 +0200 Subject: [PATCH 04/12] Up Symbolics in docs --- docs/Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Project.toml b/docs/Project.toml index c8657e93..51be8073 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -25,5 +25,5 @@ Percival = "0.7" Plots = "1" SolverBenchmark = "0.5" SymbolicUtils = "=1.5.1" -Symbolics = "5.3" +Symbolics = "5.29" Zygote = "0.6.62" From 4616cddc7babaf779b998d38b31baadfb4741ba0 Mon Sep 17 00:00:00 2001 From: Alexis Montoison Date: Sat, 18 May 2024 03:05:18 +0200 Subject: [PATCH 05/12] Use SparseConnectivityTracer.jl --- Project.toml | 2 + src/ADNLPModels.jl | 49 +++------- src/sparse_diff_tools.jl | 145 ------------------------------ src/sparse_hessian.jl | 6 +- src/sparse_sym.jl | 172 ------------------------------------ src/sparsity_pattern.jl | 26 ++++++ test/Project.toml | 2 +- test/runtests.jl | 8 +- test/script_OP.jl | 2 - test/sparse_hessian.jl | 3 - test/sparse_jacobian.jl | 4 - test/sparse_jacobian_nls.jl | 4 - 12 files changed, 44 insertions(+), 379 deletions(-) delete mode 100644 src/sparse_diff_tools.jl delete mode 100644 src/sparse_sym.jl create mode 100644 src/sparsity_pattern.jl diff --git a/Project.toml b/Project.toml index 8a337614..c031674f 100644 --- a/Project.toml +++ b/Project.toml @@ -11,10 +11,12 @@ NLPModels = "a4795742-8479-5a88-8948-cc11e1c8c1a6" Requires = "ae029012-a4dd-5104-9daa-d747884805df" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +SparseConnectivityTracer = "9f842d2f-2579-4b1d-911e-f412cf18a3f5" [compat] ADTypes = "1.2.1" ColPack = "0.4" +SparseConnectivityTracer = "0.4" ForwardDiff = "0.9.0, 0.10.0" NLPModels = "0.18, 0.19, 0.20, 0.21" Requires = "1" diff --git a/src/ADNLPModels.jl b/src/ADNLPModels.jl index d3f846b4..391df01f 100644 --- a/src/ADNLPModels.jl +++ b/src/ADNLPModels.jl @@ -2,9 +2,11 @@ module ADNLPModels # stdlib using LinearAlgebra, SparseArrays + # external using ADTypes: ADTypes -using ColPack, ForwardDiff, ReverseDiff +using SparseConnectivityTracer, ColPack, ForwardDiff, ReverseDiff + # JSO using NLPModels using Requires @@ -17,39 +19,13 @@ const ADModel{T, S} = Union{AbstractADNLPModel{T, S}, AbstractADNLSModel{T, S}} include("ad.jl") include("ad_api.jl") -""" - compute_jacobian_sparsity(c!, cx, x0) - -Return a sparse matrix. -""" -function compute_jacobian_sparsity(args...) - throw( - ArgumentError( - "Please load Symbolics.jl to enable sparse Jacobian or implement `compute_jacobian_sparsity`.", - ), - ) -end - -""" - compute_hessian_sparsity(f, nvar, c!, ncon) - -Return a sparse matrix. -""" -function compute_hessian_sparsity(args...) - throw( - ArgumentError( - "Please load Symbolics.jl to enable sparse Hessian or implement `compute_hessian_sparsity`.", - ), - ) -end - +include("sparsity_pattern.jl") include("sparse_jacobian.jl") include("sparse_hessian.jl") include("forward.jl") include("reverse.jl") include("enzyme.jl") -include("sparse_diff_tools.jl") include("zygote.jl") include("predefined_backend.jl") include("nlp.jl") @@ -183,17 +159,12 @@ function ADNLSModel!(model::AbstractNLSModel; kwargs...) end @init begin - @require Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" begin - include("sparse_sym.jl") - - predefined_backend[:default][:jacobian_backend] = SparseADJacobian - predefined_backend[:default][:jacobian_residual_backend] = SparseADJacobian - predefined_backend[:optimized][:jacobian_backend] = SparseADJacobian - predefined_backend[:optimized][:jacobian_residual_backend] = SparseADJacobian - - predefined_backend[:default][:hessian_backend] = SparseADHessian - predefined_backend[:optimized][:hessian_backend] = SparseReverseADHessian - end + predefined_backend[:default][:jacobian_backend] = SparseADJacobian + predefined_backend[:default][:jacobian_residual_backend] = SparseADJacobian + predefined_backend[:optimized][:jacobian_backend] = SparseADJacobian + predefined_backend[:optimized][:jacobian_residual_backend] = SparseADJacobian + predefined_backend[:default][:hessian_backend] = SparseADHessian + predefined_backend[:optimized][:hessian_backend] = SparseReverseADHessian end export get_adbackend, set_adbackend! diff --git a/src/sparse_diff_tools.jl b/src/sparse_diff_tools.jl deleted file mode 100644 index 8ec2917d..00000000 --- a/src/sparse_diff_tools.jl +++ /dev/null @@ -1,145 +0,0 @@ -@init begin - @require SparseDiffTools = "47a9eef4-7e08-11e9-0b38-333d64bd3804" begin - function sparse_matrix_colors(A, alg::SparseDiffTools.SparseDiffToolsColoringAlgorithm) - return SparseDiffTools.matrix_colors(A, alg) - end - - struct SDTSparseADJacobian{Tv, Ti, T, T2, T3, T4, T5} <: ADNLPModels.ADBackend - cfJ::SparseDiffTools.ForwardColorJacCache{T, T2, T3, T4, T5, SparseMatrixCSC{Tv, Ti}} - end - - function SDTSparseADJacobian( - nvar, - f, - ncon, - c!; - x0::S = rand(nvar), - alg::SparseDiffTools.SparseDiffToolsColoringAlgorithm = SparseDiffTools.GreedyD1Color(), - kwargs..., - ) where {S} - T = eltype(S) - output = similar(x0, ncon) - J = compute_jacobian_sparsity(c!, output, x0) - colors = sparse_matrix_colors(J, alg) - jac = SparseMatrixCSC{T, Int}(J.m, J.n, J.colptr, J.rowval, T.(J.nzval)) - - dx = fill!(S(undef, ncon), 0) - cfJ = SparseDiffTools.ForwardColorJacCache(c!, x0, colorvec = colors, dx = dx, sparsity = jac) - SDTSparseADJacobian(cfJ) - end - - function get_nln_nnzj(b::SDTSparseADJacobian, nvar, ncon) - nnz(b.cfJ.sparsity) - end - - function NLPModels.jac_structure!( - b::SDTSparseADJacobian, - nlp::ADModel, - rows::AbstractVector{<:Integer}, - cols::AbstractVector{<:Integer}, - ) - rows .= rowvals(b.cfJ.sparsity) - for i = 1:(nlp.meta.nvar) - for j = b.cfJ.sparsity.colptr[i]:(b.cfJ.sparsity.colptr[i + 1] - 1) - cols[j] = i - end - end - return rows, cols - end - - function NLPModels.jac_coord!( - b::SDTSparseADJacobian, - nlp::ADModel, - x::AbstractVector, - vals::AbstractVector, - ) - SparseDiffTools.forwarddiff_color_jacobian!(b.cfJ.sparsity, nlp.c!, x, b.cfJ) - vals .= nonzeros(b.cfJ.sparsity) - return vals - end - - function NLPModels.jac_structure_residual!( - b::SDTSparseADJacobian, - nls::AbstractADNLSModel, - rows::AbstractVector{<:Integer}, - cols::AbstractVector{<:Integer}, - ) - rows .= rowvals(b.cfJ.sparsity) - for i = 1:(nls.meta.nvar) - for j = b.cfJ.sparsity.colptr[i]:(b.cfJ.sparsity.colptr[i + 1] - 1) - cols[j] = i - end - end - return rows, cols - end - - function NLPModels.jac_coord_residual!( - b::SDTSparseADJacobian, - nls::AbstractADNLSModel, - x::AbstractVector, - vals::AbstractVector, - ) - SparseDiffTools.forwarddiff_color_jacobian!(b.cfJ.sparsity, nls.F!, x, b.cfJ) - vals .= nonzeros(b.cfJ.sparsity) - return vals - end - - struct SDTForwardDiffADJprod{T} <: InPlaceADbackend - tmp_in::Vector{SparseDiffTools.Dual{ForwardDiff.Tag{SparseDiffTools.DeivVecTag, T}, T, 1}} - tmp_out::Vector{SparseDiffTools.Dual{ForwardDiff.Tag{SparseDiffTools.DeivVecTag, T}, T, 1}} - end - - function SDTForwardDiffADJprod( - nvar::Integer, - f, - ncon::Integer = 0, - c!::Function = (args...) -> []; - x0::AbstractVector{T} = rand(nvar), - kwargs..., - ) where {T} - tmp_in = Vector{SparseDiffTools.Dual{ForwardDiff.Tag{SparseDiffTools.DeivVecTag, T}, T, 1}}( - undef, - nvar, - ) - tmp_out = Vector{SparseDiffTools.Dual{ForwardDiff.Tag{SparseDiffTools.DeivVecTag, T}, T, 1}}( - undef, - ncon, - ) - return SDTForwardDiffADJprod(tmp_in, tmp_out) - end - - function Jprod!(b::SDTForwardDiffADJprod, Jv, c!, x, v, ::Val) - SparseDiffTools.auto_jacvec!(Jv, c!, x, v, b.tmp_in, b.tmp_out) - return Jv - end - - struct SDTForwardDiffADHvprod{T} <: ADBackend - tmp_in::Vector{SparseDiffTools.Dual{ForwardDiff.Tag{SparseDiffTools.DeivVecTag, T}, T, 1}} - tmp_out::Vector{SparseDiffTools.Dual{ForwardDiff.Tag{SparseDiffTools.DeivVecTag, T}, T, 1}} - end - function SDTForwardDiffADHvprod( - nvar::Integer, - f, - ncon::Integer = 0, - c::Function = (args...) -> []; - x0::AbstractVector{T} = rand(nvar), - kwargs..., - ) where {T} - tmp_in = Vector{SparseDiffTools.Dual{ForwardDiff.Tag{SparseDiffTools.DeivVecTag, T}, T, 1}}( - undef, - nvar, - ) - tmp_out = Vector{SparseDiffTools.Dual{ForwardDiff.Tag{SparseDiffTools.DeivVecTag, T}, T, 1}}( - undef, - nvar, - ) - return SDTForwardDiffADHvprod(tmp_in, tmp_out) - end - - function Hvprod!(b::SDTForwardDiffADHvprod, Hv, x, v, f, args...) - ϕ!(dy, x; f = f) = ForwardDiff.gradient!(dy, f, x) - SparseDiffTools.auto_hesvecgrad!(Hv, (dy, x) -> ϕ!(dy, x), x, v, b.tmp_in, b.tmp_out) - return Hv - end - end -end diff --git a/src/sparse_hessian.jl b/src/sparse_hessian.jl index 383042fe..38d69218 100644 --- a/src/sparse_hessian.jl +++ b/src/sparse_hessian.jl @@ -24,8 +24,7 @@ function SparseADHessian( kwargs..., ) where {S} T = eltype(S) - Hs = compute_hessian_sparsity(f, nvar, c!, ncon) - H = ncon == 0 ? Hs : Hs[1:nvar, 1:nvar] + H = compute_hessian_sparsity(f, nvar, c!, ncon) colors = sparse_matrix_colors(H, alg) ncolors = maximum(colors) @@ -95,8 +94,7 @@ function SparseReverseADHessian( alg = ColPackColoration(), kwargs..., ) where {T} - Hs = compute_hessian_sparsity(f, nvar, c!, ncon) - H = ncon == 0 ? Hs : Hs[1:nvar, 1:nvar] + H = compute_hessian_sparsity(f, nvar, c!, ncon) colors = sparse_matrix_colors(H, alg) ncolors = maximum(colors) diff --git a/src/sparse_sym.jl b/src/sparse_sym.jl deleted file mode 100644 index 1c04aa00..00000000 --- a/src/sparse_sym.jl +++ /dev/null @@ -1,172 +0,0 @@ -function compute_hessian_sparsity(f, nvar, c!, ncon) - detector = Symbolics.SymbolicsSparsityDetector() # replaceable - function lagrangian(x) - cx = zeros(eltype(x), ncon) - c!(cx, x) - return f(x) + dot(rand(ncon), cx) - end - S = ADTypes.hessian_sparsity(lagrangian, rand(nvar), detector) # , full = false - return S -end - -function compute_jacobian_sparsity(c!, cx, x0) - detector = Symbolics.SymbolicsSparsityDetector() # replaceable - S = ADTypes.jacobian_sparsity(c!, cx, x0, detector) - return S -end - -## ----- Symbolics Jacobian ----- - -struct SparseSymbolicsADJacobian{T} <: ADBackend - nnzj::Int - rows::Vector{Int} - cols::Vector{Int} - cfJ::T -end - -function SparseSymbolicsADJacobian(nvar, f, ncon, c!; kwargs...) - Symbolics.@variables xs[1:nvar] out[1:ncon] - wi = Symbolics.scalarize(xs) - wo = Symbolics.scalarize(out) - fun = c!(wo, wi) - J = Symbolics.jacobian_sparsity(c!, wo, wi) - rows, cols, _ = findnz(J) - vals = Symbolics.sparsejacobian_vals(fun, wi, rows, cols) - nnzj = length(vals) - # cfJ is a Tuple{Expr, Expr}, cfJ[2] is the in-place function - # that we need to update a vector `vals` with the nonzeros of Jc(x). - cfJ = Symbolics.build_function(vals, wi, expression = Val{false}) - SparseSymbolicsADJacobian(nnzj, rows, cols, cfJ[2]) -end - -function get_nln_nnzj(b::SparseSymbolicsADJacobian, nvar, ncon) - b.nnzj -end - -function NLPModels.jac_structure!( - b::SparseSymbolicsADJacobian, - nlp::ADModel, - rows::AbstractVector{<:Integer}, - cols::AbstractVector{<:Integer}, -) - rows .= b.rows - cols .= b.cols - return rows, cols -end - -function NLPModels.jac_coord!( - b::SparseSymbolicsADJacobian, - nlp::ADModel, - x::AbstractVector, - vals::AbstractVector, -) - @eval $(b.cfJ)($vals, $x) - return vals -end - -function NLPModels.jac_structure_residual!( - b::SparseSymbolicsADJacobian, - nls::AbstractADNLSModel, - rows::AbstractVector{<:Integer}, - cols::AbstractVector{<:Integer}, -) - rows .= b.rows - cols .= b.cols - return rows, cols -end - -function NLPModels.jac_coord_residual!( - b::SparseSymbolicsADJacobian, - nls::AbstractADNLSModel, - x::AbstractVector, - vals::AbstractVector, -) - @eval $(b.cfJ)($vals, $x) - return vals -end - -## ----- Symbolics Hessian ----- - -struct SparseSymbolicsADHessian{T, H} <: ADBackend - nnzh::Int - rows::Vector{Int} - cols::Vector{Int} - y::AbstractVector{T} - cfH::H -end - -function SparseSymbolicsADHessian(nvar, f, ncon, c!; x0::S = rand(nvar), kwargs...) where {S} - Symbolics.@variables xs[1:nvar], μs - xsi = Symbolics.scalarize(xs) - fun = μs * f(xsi) - Symbolics.@variables ys[1:ncon] - ysi = Symbolics.scalarize(ys) - if ncon > 0 - cx = similar(ysi) - fun = fun + dot(c!(cx, xsi), ysi) - end - H = Symbolics.hessian_sparsity(fun, ncon == 0 ? xsi : [xsi; ysi], full = false) - H = ncon == 0 ? H : H[1:nvar, 1:nvar] - rows, cols, _ = findnz(H) - vals = Symbolics.sparsehessian_vals(fun, xsi, rows, cols) - nnzh = length(vals) - # cfH is a Tuple{Expr, Expr}, cfH[2] is the in-place function - # that we need to update a vector `vals` with the nonzeros of ∇²ℓ(x, y, μ). - cfH = Symbolics.build_function(vals, xsi, ysi, μs, expression = Val{false}) - y = fill!(S(undef, ncon), 0) - return SparseSymbolicsADHessian(nnzh, rows, cols, y, cfH[2]) -end - -function get_nln_nnzh(b::SparseSymbolicsADHessian, nvar) - b.nnzh -end - -function NLPModels.hess_structure!( - b::SparseSymbolicsADHessian, - nlp::ADModel, - rows::AbstractVector{<:Integer}, - cols::AbstractVector{<:Integer}, -) - rows .= b.rows - cols .= b.cols - return rows, cols -end - -function NLPModels.hess_coord!( - b::SparseSymbolicsADHessian, - nlp::ADModel, - x::AbstractVector, - y::AbstractVector, - obj_weight::Real, - vals::AbstractVector, -) - @eval $(b.cfH)($vals, $x, $y, $obj_weight) - return vals -end - -function NLPModels.hess_coord!( - b::SparseSymbolicsADHessian, - nlp::ADModel, - x::AbstractVector, - obj_weight::Real, - vals::AbstractVector, -) - b.y .= 0 - @eval $(b.cfH)($vals, $x, $(b.y), $obj_weight) - return vals -end - -function NLPModels.hess_coord!( - b::SparseSymbolicsADHessian, - nlp::ADModel, - x::AbstractVector, - j::Integer, - vals::AbstractVector{T}, -) where {T} - for (w, k) in enumerate(nlp.meta.nln) - b.y[w] = k == j ? 1 : 0 - end - obj_weight = zero(T) - @eval $(b.cfH)($vals, $x, $(b.y), $obj_weight) - return vals -end diff --git a/src/sparsity_pattern.jl b/src/sparsity_pattern.jl new file mode 100644 index 00000000..aff37c32 --- /dev/null +++ b/src/sparsity_pattern.jl @@ -0,0 +1,26 @@ +""" + compute_jacobian_sparsity(c!, cx, x0) + +Return a sparse matrix. +""" +function compute_jacobian_sparsity(c!, cx, x0) + detector = SparseConnectivityTracer.TracerSparsityDetector() # replaceable + S = ADTypes.jacobian_sparsity(c!, cx, x0, detector) + return S +end + +""" + compute_hessian_sparsity(f, nvar, c!, ncon) + +Return a sparse matrix. +""" +function compute_hessian_sparsity(f, nvar, c!, ncon) + detector = SparseConnectivityTracer.TracerSparsityDetector() # replaceable + function lagrangian(x) + cx = zeros(eltype(x), ncon) + c!(cx, x) + return f(x) + dot(rand(ncon), cx) + end + S = ADTypes.hessian_sparsity(lagrangian, rand(nvar), detector) + return S +end diff --git a/test/Project.toml b/test/Project.toml index 1f344a0e..c00d9ed8 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -15,7 +15,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [compat] -CUDA = "4, 5" +CUDA = "5" Enzyme = "0.10, 0.11, 0.12" ForwardDiff = "0.10" ManualNLPModels = "0.1" diff --git a/test/runtests.jl b/test/runtests.jl index 3eea51f6..4bb8ec4e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,7 +3,7 @@ using ADNLPModels, ManualNLPModels, NLPModels, NLPModelsModifiers, NLPModelsTest using ADNLPModels: gradient, gradient!, jacobian, hessian, Jprod!, Jtprod!, directional_second_derivative, Hvprod! -@testset "Error without loading package for sparsity pattern" begin +@testset "Test sparsity pattern of Jacobian and Hessian" begin f(x) = sum(x) c!(cx, x) = begin cx .= 1 @@ -12,12 +12,10 @@ using ADNLPModels: nvar, ncon = 2, 1 x0 = ones(nvar) cx = rand(ncon) - @test_throws ArgumentError ADNLPModels.compute_jacobian_sparsity(c!, cx, x0) - @test_throws ArgumentError ADNLPModels.compute_hessian_sparsity(f, nvar, c!, ncon) + S = ADNLPModels.compute_jacobian_sparsity(c!, cx, x0) + S = ADNLPModels.compute_hessian_sparsity(f, nvar, c!, ncon) end -using SparseDiffTools, Symbolics - @testset "Test using a NLPModel instead of AD-backend" begin include("manual.jl") end diff --git a/test/script_OP.jl b/test/script_OP.jl index 2ecad87c..3b8cd908 100644 --- a/test/script_OP.jl +++ b/test/script_OP.jl @@ -2,8 +2,6 @@ # optional deps # using Enzyme -# using SparseDiffTools -using Symbolics # AD deps using ForwardDiff, ReverseDiff diff --git a/test/sparse_hessian.jl b/test/sparse_hessian.jl index ce35c886..a7f277aa 100644 --- a/test/sparse_hessian.jl +++ b/test/sparse_hessian.jl @@ -1,9 +1,6 @@ list_sparse_hess_backend = ( (ADNLPModels.SparseADHessian, Dict()), - (ADNLPModels.SparseADHessian, Dict(:alg => SparseDiffTools.GreedyD1Color())), - (ADNLPModels.SparseADHessian, Dict(:alg => SparseDiffTools.AcyclicColoring())), (ADNLPModels.ForwardDiffADHessian, Dict()), - (ADNLPModels.SparseSymbolicsADHessian, Dict()), ) dt = (Float32, Float64) diff --git a/test/sparse_jacobian.jl b/test/sparse_jacobian.jl index 1d3da99d..20b4d05e 100644 --- a/test/sparse_jacobian.jl +++ b/test/sparse_jacobian.jl @@ -1,10 +1,6 @@ list_sparse_jac_backend = ( (ADNLPModels.SparseADJacobian, Dict()), # default - (ADNLPModels.SparseADJacobian, Dict(:alg => SparseDiffTools.GreedyD1Color())), - (ADNLPModels.SparseADJacobian, Dict(:alg => SparseDiffTools.AcyclicColoring())), (ADNLPModels.ForwardDiffADJacobian, Dict()), - (ADNLPModels.SparseSymbolicsADJacobian, Dict()), - (ADNLPModels.SDTSparseADJacobian, Dict()), ) dt = (Float32, Float64) @testset "Basic Jacobian derivative with backend=$(backend) and T=$(T)" for T in dt, diff --git a/test/sparse_jacobian_nls.jl b/test/sparse_jacobian_nls.jl index f0b94607..23df0dc3 100644 --- a/test/sparse_jacobian_nls.jl +++ b/test/sparse_jacobian_nls.jl @@ -1,10 +1,6 @@ list_sparse_jac_backend = ( (ADNLPModels.SparseADJacobian, Dict()), # default - (ADNLPModels.SparseADJacobian, Dict(:alg => SparseDiffTools.GreedyD1Color())), - (ADNLPModels.SparseADJacobian, Dict(:alg => SparseDiffTools.AcyclicColoring())), (ADNLPModels.ForwardDiffADJacobian, Dict()), - (ADNLPModels.SparseSymbolicsADJacobian, Dict()), - (ADNLPModels.SDTSparseADJacobian, Dict()), ) dt = (Float32, Float64) @testset "Basic Jacobian of residual derivative with backend=$(backend) and T=$(T)" for T in dt, From c8321436dfdeaa7667723af0433a6c3fd82913b0 Mon Sep 17 00:00:00 2001 From: Alexis Montoison Date: Sun, 19 May 2024 14:56:08 +0200 Subject: [PATCH 06/12] Modifications done during the flight --- src/sparsity_pattern.jl | 25 ++++++++++++++++++++----- test/runtests.jl | 11 ++++------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/sparsity_pattern.jl b/src/sparsity_pattern.jl index aff37c32..293be764 100644 --- a/src/sparsity_pattern.jl +++ b/src/sparsity_pattern.jl @@ -1,8 +1,17 @@ """ + compute_jacobian_sparsity(c, x0) compute_jacobian_sparsity(c!, cx, x0) Return a sparse matrix. """ +function compute_jacobian_sparsity end + +function compute_jacobian_sparsity(c, x0) + detector = SparseConnectivityTracer.TracerSparsityDetector() # replaceable + S = SparseConnectivityTracer.jacobian_pattern(c, x0, detector) + return S +end + function compute_jacobian_sparsity(c!, cx, x0) detector = SparseConnectivityTracer.TracerSparsityDetector() # replaceable S = ADTypes.jacobian_sparsity(c!, cx, x0, detector) @@ -15,12 +24,18 @@ end Return a sparse matrix. """ function compute_hessian_sparsity(f, nvar, c!, ncon) - detector = SparseConnectivityTracer.TracerSparsityDetector() # replaceable function lagrangian(x) - cx = zeros(eltype(x), ncon) - c!(cx, x) - return f(x) + dot(rand(ncon), cx) + if ncon == 0 + return f(x) + else + cx = zeros(eltype(x), ncon) + y0 = rand(ncon) + return f(x) + dot(c!(cx, x), y0) + end end - S = ADTypes.hessian_sparsity(lagrangian, rand(nvar), detector) + + detector = SparseConnectivityTracer.TracerSparsityDetector() # replaceable + x0 = rand(nvar) + S = ADTypes.hessian_sparsity(lagrangian, x0, detector) return S end diff --git a/test/runtests.jl b/test/runtests.jl index 4bb8ec4e..abf66f40 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -5,13 +5,12 @@ using ADNLPModels: @testset "Test sparsity pattern of Jacobian and Hessian" begin f(x) = sum(x) - c!(cx, x) = begin - cx .= 1 - return x - end - nvar, ncon = 2, 1 + c(x) = x + c!(cx, x) = copyto!(cx, x) + nvar, ncon = 2, 2 x0 = ones(nvar) cx = rand(ncon) + S = ADNLPModels.compute_jacobian_sparsity(c, x0) S = ADNLPModels.compute_jacobian_sparsity(c!, cx, x0) S = ADNLPModels.compute_hessian_sparsity(f, nvar, c!, ncon) end @@ -41,13 +40,11 @@ push!( ADNLPModels.predefined_backend, :zygote_backend => Dict( :gradient_backend => ADNLPModels.ZygoteADGradient, - :hprod_backend => ADNLPModels.SDTForwardDiffADHvprod, :jprod_backend => ADNLPModels.ZygoteADJprod, :jtprod_backend => ADNLPModels.ZygoteADJtprod, :jacobian_backend => ADNLPModels.ZygoteADJacobian, :hessian_backend => ADNLPModels.ZygoteADHessian, :ghjvprod_backend => ADNLPModels.ForwardDiffADGHjvprod, - :hprod_residual_backend => ADNLPModels.SDTForwardDiffADHvprod, :jprod_residual_backend => ADNLPModels.ZygoteADJprod, :jtprod_residual_backend => ADNLPModels.ZygoteADJtprod, :jacobian_residual_backend => ADNLPModels.ZygoteADJacobian, From 2425a6595fe27df5512aa41c567fd7410cac5891 Mon Sep 17 00:00:00 2001 From: Alexis Montoison Date: Tue, 28 May 2024 11:24:00 -0400 Subject: [PATCH 07/12] Update SCT.jl --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index c031674f..dec03ff0 100644 --- a/Project.toml +++ b/Project.toml @@ -16,7 +16,7 @@ SparseConnectivityTracer = "9f842d2f-2579-4b1d-911e-f412cf18a3f5" [compat] ADTypes = "1.2.1" ColPack = "0.4" -SparseConnectivityTracer = "0.4" +SparseConnectivityTracer = "0.5" ForwardDiff = "0.9.0, 0.10.0" NLPModels = "0.18, 0.19, 0.20, 0.21" Requires = "1" From 5d99aaa6cc10e33ca8bc8b7ed3904f62a593c654 Mon Sep 17 00:00:00 2001 From: Alexis Montoison Date: Tue, 4 Jun 2024 12:01:50 -0400 Subject: [PATCH 08/12] Remove Symbolics.jl --- docs/Project.toml | 4 ---- test/Project.toml | 2 -- 2 files changed, 6 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index 51be8073..0d1e6c95 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -10,8 +10,6 @@ OptimizationProblems = "5049e819-d29b-5fba-b941-0eee7e64c1c6" Percival = "01435c0c-c90d-11e9-3788-63660f8fbccc" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" SolverBenchmark = "581a75fa-a23a-52d0-a590-d6201de2218a" -SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b" -Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [compat] @@ -24,6 +22,4 @@ OptimizationProblems = "0.7" Percival = "0.7" Plots = "1" SolverBenchmark = "0.5" -SymbolicUtils = "=1.5.1" -Symbolics = "5.29" Zygote = "0.6.62" diff --git a/test/Project.toml b/test/Project.toml index c00d9ed8..75c06577 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -10,7 +10,6 @@ NLPModelsTest = "7998695d-6960-4d3a-85c4-e1bceb8cd856" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SparseDiffTools = "47a9eef4-7e08-11e9-0b38-333d64bd3804" -Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" @@ -24,5 +23,4 @@ NLPModelsModifiers = "0.7" NLPModelsTest = "0.10" ReverseDiff = "1" SparseDiffTools = "2.3" -Symbolics = "5.29" Zygote = "0.6" From 370717a031df792edfddcf75c21959ef748e2990 Mon Sep 17 00:00:00 2001 From: Alexis Montoison Date: Tue, 4 Jun 2024 12:05:27 -0400 Subject: [PATCH 09/12] Keep CUDA.jl v4 for the tests... --- test/Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Project.toml b/test/Project.toml index 75c06577..7350cde7 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -14,7 +14,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [compat] -CUDA = "5" +CUDA = "4, 5" Enzyme = "0.10, 0.11, 0.12" ForwardDiff = "0.10" ManualNLPModels = "0.1" From 9c4b9ef6f29084e84d57652e8231d73ca9301f39 Mon Sep 17 00:00:00 2001 From: Alexis Montoison Date: Tue, 4 Jun 2024 19:26:37 -0400 Subject: [PATCH 10/12] Update the documentation and fix a few typos --- README.md | 2 -- docs/src/backend.md | 20 ++++++++++---------- docs/src/performance.md | 2 +- docs/src/predefined.md | 26 +++++--------------------- docs/src/reference.md | 12 ++++++------ src/ADNLPModels.jl | 9 --------- src/predefined_backend.jl | 12 ++++++------ src/sparsity_pattern.jl | 2 +- test/Project.toml | 2 -- 9 files changed, 29 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 40459ac6..67b58f2d 100644 --- a/README.md +++ b/README.md @@ -96,8 +96,6 @@ The following AD packages are supported: and as optional dependencies (you must load the package before): - `Enzyme.jl`; -- `SparseDiffTools.jl`; -- `Symbolics.jl`; - `Zygote.jl`. ## Bug reports and discussions diff --git a/docs/src/backend.md b/docs/src/backend.md index 22b0d6a8..30e0ebc6 100644 --- a/docs/src/backend.md +++ b/docs/src/backend.md @@ -7,14 +7,14 @@ The backend information is in a structure [`ADNLPModels.ADModelBackend`](@ref) i The functions used internally to define the NLPModel API and the possible backends are defined in the following table: -| Functions | FowardDiff backends | ReverseDiff backends | Zygote backends | Enzyme backend | SparseDiffTools backend | Symbolics backend | +| Functions | FowardDiff backends | ReverseDiff backends | Zygote backends | Enzyme backend | Sparse backend | | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | -| `gradient` and `gradient!` | `ForwardDiffADGradient`/`GenericForwardDiffADGradient` | `ReverseDiffADGradient`/`GenericReverseDiffADGradient` | `ZygoteADGradient` | `EnzymeADGradient` | -- | -- | -| `jacobian` | `ForwardDiffADJacobian` | `ReverseDiffADJacobian` | `ZygoteADJacobian` | -- | `SDTSparseADJacobian` | `SparseADJacobian`/`SparseSymbolicsADJacobian` | -| `hessian` | `ForwardDiffADHessian` | `ReverseDiffADHessian` | `ZygoteADHessian` | -- | -- | `SparseADHessian`/`SparseSymbolicsADHessian` | -| `Jprod` | `ForwardDiffADJprod`/`GenericForwardDiffADJprod` | `ReverseDiffADJprod`/`GenericReverseDiffADJprod` | `ZygoteADJprod` | -- | `SDTForwardDiffADJprod` | -- | -| `Jtprod` | `ForwardDiffADJtprod`/`GenericForwardDiffADJtprod` | `ReverseDiffADJtprod`/`GenericReverseDiffADJtprod` | `ZygoteADJtprod` | -- | -- | -- | -| `Hvprod` | `ForwardDiffADHvprod`/`GenericForwardDiffADHvprod` | `ReverseDiffADHvprod`/`GenericReverseDiffADHvprod` | -- | -- | `SDTForwardDiffADHvprod` | -- | +| `gradient` and `gradient!` | `ForwardDiffADGradient`/`GenericForwardDiffADGradient` | `ReverseDiffADGradient`/`GenericReverseDiffADGradient` | `ZygoteADGradient` | `EnzymeADGradient` | -- | +| `jacobian` | `ForwardDiffADJacobian` | `ReverseDiffADJacobian` | `ZygoteADJacobian` | -- | `SparseADJacobian` | +| `hessian` | `ForwardDiffADHessian` | `ReverseDiffADHessian` | `ZygoteADHessian` | -- | `SparseADHessian` | +| `Jprod` | `ForwardDiffADJprod`/`GenericForwardDiffADJprod` | `ReverseDiffADJprod`/`GenericReverseDiffADJprod` | `ZygoteADJprod` | -- | +| `Jtprod` | `ForwardDiffADJtprod`/`GenericForwardDiffADJtprod` | `ReverseDiffADJtprod`/`GenericReverseDiffADJtprod` | `ZygoteADJtprod` | -- | +| `Hvprod` | `ForwardDiffADHvprod`/`GenericForwardDiffADHvprod` | `ReverseDiffADHvprod`/`GenericReverseDiffADHvprod` | -- | | `directional_second_derivative` | `ForwardDiffADGHjvprod` | -- | -- | -- | -- | The functions `hess_structure!`, `hess_coord!`, `jac_structure!` and `jac_coord!` defined in `ad.jl` are generic to all the backends for now. @@ -49,7 +49,7 @@ Thanks to the backends inside `ADNLPModels.jl`, it is easy to change the backend ```@example adnlp nlp = ADNLPModel(f, x0, gradient_backend = ADNLPModels.ReverseDiffADGradient) -grad(nlp, nlp.meta.x0) # returns the gradient at x0 using `ReverseDiff` +grad(nlp, nlp.meta.x0) # returns the gradient at x0 using `ReverseDiff` ``` It is also possible to try some new implementation for each function. First, we define a new `ADBackend` structure. @@ -81,7 +81,7 @@ Finally, we use the homemade backend to compute the gradient. ```@example adnlp nlp = ADNLPModel(sum, ones(3), gradient_backend = NewADGradient) -grad(nlp, nlp.meta.x0) # returns the gradient at x0 using `NewADGradient` +grad(nlp, nlp.meta.x0) # returns the gradient at x0 using `NewADGradient` ``` ### Change backend @@ -104,7 +104,7 @@ set_adbackend!(nlp, adback) get_adbackend(nlp) ``` -The alternative is to use ``set_adbackend!` and pass the new backends via `kwargs`. In the second approach, it is possible to pass either the type of the desired backend or an instance as shown below. +The alternative is to use `set_adbackend!` and pass the new backends via `kwargs`. In the second approach, it is possible to pass either the type of the desired backend or an instance as shown below. ```@example adnlp2 set_adbackend!( diff --git a/docs/src/performance.md b/docs/src/performance.md index f2625538..0110a03d 100644 --- a/docs/src/performance.md +++ b/docs/src/performance.md @@ -84,7 +84,7 @@ v = ones(2) It is tempting to define the most generic and efficient `ADNLPModel` from the start. ```@example ex2 -using ADNLPModels, NLPModels, Symbolics +using ADNLPModels, NLPModels f(x) = (x[1] - x[2])^2 x0 = ones(2) lcon = ucon = ones(1) diff --git a/docs/src/predefined.md b/docs/src/predefined.md index 5dce2e6b..214c6350 100644 --- a/docs/src/predefined.md +++ b/docs/src/predefined.md @@ -49,30 +49,12 @@ get_adbackend(nlp) ## Hessian and Jacobian computations -It is to be noted that by default the Jacobian and Hessian matrices are dense. +It is to be noted that by default the Jacobian and Hessian matrices are sparse. ```@example ex1 -(get_nnzj(nlp), get_nnzh(nlp)) # number of nonzeros elements in the Jacobian and Hessian +(get_nnzj(nlp), get_nnzh(nlp)) # number of nonzeros elements in the Jacobian and Hessian ``` -To enable sparse computations of these entries, one needs to first load the package [`Symbolics.jl`](https://github.com/JuliaSymbolics/Symbolics.jl) - -```@example ex1 -using Symbolics -``` - -and now - -```@example ex1 -ADNLPModels.predefined_backend[:optimized][:jacobian_backend] -``` - -```@example ex1 -ADNLPModels.predefined_backend[:optimized][:hessian_backend] -``` - -Choosing another optimization problem with the optimized backend will compute sparse Jacobian and Hessian matrices. - ```@example ex1 f(x) = (x[1] - 1)^2 T = Float64 @@ -92,4 +74,6 @@ x = rand(T, 2) jac(nlp, x) ``` -The package [`Symbolics.jl`](https://github.com/JuliaSymbolics/Symbolics.jl) is used to compute the sparsity pattern of the sparse matrix. The evaluation of the number of directional derivatives needed to evaluate the matrix is done by [`ColPack.jl`](https://github.com/michel2323/ColPack.jl). +The package [`SparseConnectivityTracer.jl`](https://github.com/adrhill/SparseConnectivityTracer.jl) is used to compute the sparsity pattern of Jacobians and Hessians. +The evaluation of the number of directional derivatives needed to evaluate the matrix is done by [`ColPack.jl`](https://github.com/exanauts/ColPack.jl). +We acknowledge Guillaume Dalle (@gdalle), Adrian Hill (@adrhill), and Michel Schanen (@michel2323) for the development of these packages. diff --git a/docs/src/reference.md b/docs/src/reference.md index bfe346a7..d0ac148a 100644 --- a/docs/src/reference.md +++ b/docs/src/reference.md @@ -1,17 +1,17 @@ # Reference -​ + ## Contents -​ + ```@contents Pages = ["reference.md"] ``` -​ + ## Index -​ + ```@index Pages = ["reference.md"] ``` -​ + ```@autodocs Modules = [ADNLPModels] -``` \ No newline at end of file +``` diff --git a/src/ADNLPModels.jl b/src/ADNLPModels.jl index 391df01f..b86d5947 100644 --- a/src/ADNLPModels.jl +++ b/src/ADNLPModels.jl @@ -158,15 +158,6 @@ function ADNLSModel!(model::AbstractNLSModel; kwargs...) end end -@init begin - predefined_backend[:default][:jacobian_backend] = SparseADJacobian - predefined_backend[:default][:jacobian_residual_backend] = SparseADJacobian - predefined_backend[:optimized][:jacobian_backend] = SparseADJacobian - predefined_backend[:optimized][:jacobian_residual_backend] = SparseADJacobian - predefined_backend[:default][:hessian_backend] = SparseADHessian - predefined_backend[:optimized][:hessian_backend] = SparseReverseADHessian -end - export get_adbackend, set_adbackend! """ diff --git a/src/predefined_backend.jl b/src/predefined_backend.jl index 2b207ccf..6e2d0ffd 100644 --- a/src/predefined_backend.jl +++ b/src/predefined_backend.jl @@ -3,13 +3,13 @@ default_backend = Dict( :hprod_backend => ForwardDiffADHvprod, :jprod_backend => ForwardDiffADJprod, :jtprod_backend => ForwardDiffADJtprod, - :jacobian_backend => ForwardDiffADJacobian, - :hessian_backend => ForwardDiffADHessian, + :jacobian_backend => SparseADJacobian, # ForwardDiffADJacobian + :hessian_backend => SparseADHessian, # ForwardDiffADHessian :ghjvprod_backend => ForwardDiffADGHjvprod, :hprod_residual_backend => ForwardDiffADHvprod, :jprod_residual_backend => ForwardDiffADJprod, :jtprod_residual_backend => ForwardDiffADJtprod, - :jacobian_residual_backend => ForwardDiffADJacobian, + :jacobian_residual_backend => SparseADJacobian, # ForwardDiffADJacobian, :hessian_residual_backend => ForwardDiffADHessian, ) @@ -18,13 +18,13 @@ optimized = Dict( :hprod_backend => ReverseDiffADHvprod, :jprod_backend => ForwardDiffADJprod, :jtprod_backend => ReverseDiffADJtprod, - :jacobian_backend => ForwardDiffADJacobian, - :hessian_backend => ForwardDiffADHessian, + :jacobian_backend => SparseADJacobian, # ForwardDiffADJacobian + :hessian_backend => SparseReverseADHessian, # ForwardDiffADHessian, :ghjvprod_backend => ForwardDiffADGHjvprod, :hprod_residual_backend => ReverseDiffADHvprod, :jprod_residual_backend => ForwardDiffADJprod, :jtprod_residual_backend => ReverseDiffADJtprod, - :jacobian_residual_backend => ForwardDiffADJacobian, + :jacobian_residual_backend => SparseADJacobian, # ForwardDiffADJacobian :hessian_residual_backend => ForwardDiffADHessian, ) diff --git a/src/sparsity_pattern.jl b/src/sparsity_pattern.jl index 293be764..27a4a67d 100644 --- a/src/sparsity_pattern.jl +++ b/src/sparsity_pattern.jl @@ -8,7 +8,7 @@ function compute_jacobian_sparsity end function compute_jacobian_sparsity(c, x0) detector = SparseConnectivityTracer.TracerSparsityDetector() # replaceable - S = SparseConnectivityTracer.jacobian_pattern(c, x0, detector) + S = ADTypes.jacobian_sparsity(c, x0, detector) return S end diff --git a/test/Project.toml b/test/Project.toml index 7350cde7..2843f676 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -9,7 +9,6 @@ NLPModelsModifiers = "e01155f1-5c6f-4375-a9d8-616dd036575f" NLPModelsTest = "7998695d-6960-4d3a-85c4-e1bceb8cd856" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -SparseDiffTools = "47a9eef4-7e08-11e9-0b38-333d64bd3804" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" @@ -22,5 +21,4 @@ NLPModels = "0.21" NLPModelsModifiers = "0.7" NLPModelsTest = "0.10" ReverseDiff = "1" -SparseDiffTools = "2.3" Zygote = "0.6" From d1cde67e6862042c971d3a645d1366ac4bfd50fe Mon Sep 17 00:00:00 2001 From: Alexis Montoison Date: Tue, 4 Jun 2024 19:29:41 -0400 Subject: [PATCH 11/12] Update runtests.jl --- test/runtests.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index abf66f40..fb31e839 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -42,11 +42,13 @@ push!( :gradient_backend => ADNLPModels.ZygoteADGradient, :jprod_backend => ADNLPModels.ZygoteADJprod, :jtprod_backend => ADNLPModels.ZygoteADJtprod, + :hprod_backend => ADNLPModels.ForwardDiffADHvprod, :jacobian_backend => ADNLPModels.ZygoteADJacobian, :hessian_backend => ADNLPModels.ZygoteADHessian, :ghjvprod_backend => ADNLPModels.ForwardDiffADGHjvprod, :jprod_residual_backend => ADNLPModels.ZygoteADJprod, :jtprod_residual_backend => ADNLPModels.ZygoteADJtprod, + :hprod_residual_backend => ADNLPModels.ForwardDiffADHvprod, :jacobian_residual_backend => ADNLPModels.ZygoteADJacobian, :hessian_residual_backend => ADNLPModels.ZygoteADHessian, ), From 9529aa21bd85b66eb1d62f2d221628fee63dd843 Mon Sep 17 00:00:00 2001 From: Alexis Montoison Date: Wed, 5 Jun 2024 09:46:08 -0400 Subject: [PATCH 12/12] Add an option to provide a detector --- docs/src/predefined.md | 2 +- src/ADNLPModels.jl | 2 +- src/sparse_hessian.jl | 6 ++++-- src/sparse_jacobian.jl | 3 ++- src/sparsity_pattern.jl | 19 ++++++++----------- test/runtests.jl | 5 ++++- 6 files changed, 20 insertions(+), 17 deletions(-) diff --git a/docs/src/predefined.md b/docs/src/predefined.md index 214c6350..8a64c2a7 100644 --- a/docs/src/predefined.md +++ b/docs/src/predefined.md @@ -75,5 +75,5 @@ jac(nlp, x) ``` The package [`SparseConnectivityTracer.jl`](https://github.com/adrhill/SparseConnectivityTracer.jl) is used to compute the sparsity pattern of Jacobians and Hessians. -The evaluation of the number of directional derivatives needed to evaluate the matrix is done by [`ColPack.jl`](https://github.com/exanauts/ColPack.jl). +The evaluation of the number of directional derivatives and the seeds needed to evaluate the compressed Jacobians and Hessians is done by [`ColPack.jl`](https://github.com/exanauts/ColPack.jl). We acknowledge Guillaume Dalle (@gdalle), Adrian Hill (@adrhill), and Michel Schanen (@michel2323) for the development of these packages. diff --git a/src/ADNLPModels.jl b/src/ADNLPModels.jl index b86d5947..18650372 100644 --- a/src/ADNLPModels.jl +++ b/src/ADNLPModels.jl @@ -4,7 +4,7 @@ module ADNLPModels using LinearAlgebra, SparseArrays # external -using ADTypes: ADTypes +using ADTypes: ADTypes, AbstractSparsityDetector using SparseConnectivityTracer, ColPack, ForwardDiff, ReverseDiff # JSO diff --git a/src/sparse_hessian.jl b/src/sparse_hessian.jl index 38d69218..d72d5dcd 100644 --- a/src/sparse_hessian.jl +++ b/src/sparse_hessian.jl @@ -21,10 +21,11 @@ function SparseADHessian( c!; x0::S = rand(nvar), alg = ColPackColoration(), + detector::AbstractSparsityDetector = TracerSparsityDetector(), kwargs..., ) where {S} T = eltype(S) - H = compute_hessian_sparsity(f, nvar, c!, ncon) + H = compute_hessian_sparsity(f, nvar, c!, ncon, detector=detector) colors = sparse_matrix_colors(H, alg) ncolors = maximum(colors) @@ -92,9 +93,10 @@ function SparseReverseADHessian( c!; x0::AbstractVector{T} = rand(nvar), alg = ColPackColoration(), + detector::AbstractSparsityDetector = TracerSparsityDetector(), kwargs..., ) where {T} - H = compute_hessian_sparsity(f, nvar, c!, ncon) + H = compute_hessian_sparsity(f, nvar, c!, ncon, detector=detector) colors = sparse_matrix_colors(H, alg) ncolors = maximum(colors) diff --git a/src/sparse_jacobian.jl b/src/sparse_jacobian.jl index 1b12fd2c..fccfad2d 100644 --- a/src/sparse_jacobian.jl +++ b/src/sparse_jacobian.jl @@ -16,10 +16,11 @@ function SparseADJacobian( c!; x0::AbstractVector{T} = rand(nvar), alg = ColPackColoration(), + detector::AbstractSparsityDetector = TracerSparsityDetector(), kwargs..., ) where {T} output = similar(x0, ncon) - J = compute_jacobian_sparsity(c!, output, x0) + J = compute_jacobian_sparsity(c!, output, x0, detector=detector) colors = sparse_matrix_colors(J, alg) ncolors = maximum(colors) diff --git a/src/sparsity_pattern.jl b/src/sparsity_pattern.jl index 27a4a67d..74051fe0 100644 --- a/src/sparsity_pattern.jl +++ b/src/sparsity_pattern.jl @@ -1,29 +1,27 @@ """ - compute_jacobian_sparsity(c, x0) - compute_jacobian_sparsity(c!, cx, x0) + compute_jacobian_sparsity(c, x0; detector) + compute_jacobian_sparsity(c!, cx, x0; detector) -Return a sparse matrix. +Return a sparse boolean matrix that represents the adjacency matrix of the Jacobian of c(x). """ function compute_jacobian_sparsity end -function compute_jacobian_sparsity(c, x0) - detector = SparseConnectivityTracer.TracerSparsityDetector() # replaceable +function compute_jacobian_sparsity(c, x0; detector::AbstractSparsityDetector=TracerSparsityDetector()) S = ADTypes.jacobian_sparsity(c, x0, detector) return S end -function compute_jacobian_sparsity(c!, cx, x0) - detector = SparseConnectivityTracer.TracerSparsityDetector() # replaceable +function compute_jacobian_sparsity(c!, cx, x0; detector::AbstractSparsityDetector=TracerSparsityDetector()) S = ADTypes.jacobian_sparsity(c!, cx, x0, detector) return S end """ - compute_hessian_sparsity(f, nvar, c!, ncon) + compute_hessian_sparsity(f, nvar, c!, ncon; detector) -Return a sparse matrix. +Return a sparse boolean matrix that represents the adjacency matrix of the Hessian of f(x) + λᵀc(x). """ -function compute_hessian_sparsity(f, nvar, c!, ncon) +function compute_hessian_sparsity(f, nvar, c!, ncon; detector::AbstractSparsityDetector=TracerSparsityDetector()) function lagrangian(x) if ncon == 0 return f(x) @@ -34,7 +32,6 @@ function compute_hessian_sparsity(f, nvar, c!, ncon) end end - detector = SparseConnectivityTracer.TracerSparsityDetector() # replaceable x0 = rand(nvar) S = ADTypes.hessian_sparsity(lagrangian, x0, detector) return S diff --git a/test/runtests.jl b/test/runtests.jl index fb31e839..565d85cb 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,15 +4,18 @@ using ADNLPModels: gradient, gradient!, jacobian, hessian, Jprod!, Jtprod!, directional_second_derivative, Hvprod! @testset "Test sparsity pattern of Jacobian and Hessian" begin - f(x) = sum(x) + f(x) = sum(x.^2) c(x) = x c!(cx, x) = copyto!(cx, x) nvar, ncon = 2, 2 x0 = ones(nvar) cx = rand(ncon) S = ADNLPModels.compute_jacobian_sparsity(c, x0) + @test S == I S = ADNLPModels.compute_jacobian_sparsity(c!, cx, x0) + @test S == I S = ADNLPModels.compute_hessian_sparsity(f, nvar, c!, ncon) + @test S == I end @testset "Test using a NLPModel instead of AD-backend" begin