Skip to content

Commit d47b385

Browse files
authored
Upgrade ADNLPModels.jl for SparseMatrixColoring.jl v0.4.0 (#289)
* Upgrade ADNLPModels.jl for SparseMatrixColoring.jl v0.4.0 * Update the documentation about SMC.jl * Fix a typo
1 parent eaf2c37 commit d47b385

File tree

5 files changed

+136
-146
lines changed

5 files changed

+136
-146
lines changed

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ ForwardDiff = "0.9.0, 0.10.0"
1919
NLPModels = "0.18, 0.19, 0.20, 0.21"
2020
Requires = "1"
2121
ReverseDiff = "1"
22-
SparseConnectivityTracer = "0.6"
23-
SparseMatrixColorings = "0.3.5"
22+
SparseConnectivityTracer = "0.6.1"
23+
SparseMatrixColorings = "0.4.0"
2424
julia = "^1.6"

docs/src/sparse.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ x = rand(T, 2)
3131
H = hess(nlp, x)
3232
```
3333

34-
The available backends for sparse derivatives (`SparseADJacobian`, `SparseADHessian` and `SparseReverseADHessian`) have keyword arguments `detector` and `coloring` to specify the sparsity pattern detector and the coloring algorithm, respectively.
34+
The available backends for sparse derivatives (`SparseADJacobian`, `SparseADHessian` and `SparseReverseADHessian`) have keyword arguments `detector` and `coloring_algorithm` to specify the sparsity pattern detector and the coloring algorithm, respectively.
3535

36-
- **Detector**: A `detector` must be of type `ADTypes.AbstractSparsityDetector`.
36+
- A **`detector`** must be of type `ADTypes.AbstractSparsityDetector`.
3737
The default detector is `TracerSparsityDetector()` from the package `SparseConnectivityTracer.jl`.
3838
Prior to version 0.8.0, the default detector was `SymbolicSparsityDetector()` from `Symbolics.jl`.
3939

40-
- **Coloring**: A `coloring` must be of type `ADTypes.AbstractColoringAlgorithm`.
41-
The default algorithm is `GreedyColoringAlgorithm()` from the package `SparseMatrixColorings.jl`.
40+
- A **`coloring_algorithm`** must be of type `SparseMatrixColorings.GreedyColoringAlgorithm`.
41+
The default algorithm is `GreedyColoringAlgorithm{:direct}()` from the package `SparseMatrixColorings.jl`.
4242

4343
If the sparsity pattern of the Jacobian of the constraint or the Hessian of the Lagrangian is available, you can directly provide them.
4444
```@example ex2
@@ -95,4 +95,4 @@ nlp = ADNLPModel!(f, x0, lvar, uvar, c!, lcon, ucon, jacobian_backend=J_backend,
9595
The package [`SparseConnectivityTracer.jl`](https://github.com/adrhill/SparseConnectivityTracer.jl) is used to compute the sparsity pattern of Jacobians and Hessians.
9696
The evaluation of the number of directional derivatives and the seeds required to compute compressed Jacobians and Hessians is performed using [`SparseMatrixColorings.jl`](https://github.com/gdalle/SparseMatrixColorings.jl).
9797
As of release v0.8.1, it has replaced [`ColPack.jl`](https://github.com/exanauts/ColPack.jl).
98-
We acknowledge Guillaume Dalle (@gdalle), Adrian Hill (@adrhill), and Michel Schanen (@michel2323) for the development of these packages.
98+
We acknowledge Guillaume Dalle (@gdalle), Adrian Hill (@adrhill), Alexis Montoison (@amontoison), and Michel Schanen (@michel2323) for the development of these packages.

src/ADNLPModels.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ using LinearAlgebra, SparseArrays
66
# external
77
using ADTypes: ADTypes, AbstractColoringAlgorithm, AbstractSparsityDetector
88
using SparseConnectivityTracer: TracerSparsityDetector
9-
using SparseMatrixColorings:
10-
GreedyColoringAlgorithm, symmetric_coloring_detailed, symmetric_coefficient
9+
using SparseMatrixColorings
1110
using ForwardDiff, ReverseDiff
1211

1312
# JSO

src/sparse_hessian.jl

Lines changed: 92 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
struct SparseADHessian{Tag, GT, S, T} <: ADNLPModels.ADBackend
2-
d::BitVector
1+
struct SparseADHessian{Tag, R, T, C, S, GT} <: ADNLPModels.ADBackend
2+
nvar::Int
33
rowval::Vector{Int}
44
colptr::Vector{Int}
5-
colors::Vector{Int}
6-
ncolors::Int
7-
dcolors::Dict{Int, Vector{Tuple{Int, Int}}}
8-
res::S
5+
nzval::Vector{R}
6+
result_coloring::C
7+
compressed_hessian::S
8+
seed::BitVector
99
lz::Vector{ForwardDiff.Dual{Tag, T, 1}}
1010
glz::Vector{ForwardDiff.Dual{Tag, T, 1}}
1111
sol::S
@@ -21,12 +21,12 @@ function SparseADHessian(
2121
ncon,
2222
c!;
2323
x0::AbstractVector = rand(nvar),
24-
coloring::AbstractColoringAlgorithm = GreedyColoringAlgorithm(),
24+
coloring_algorithm::AbstractColoringAlgorithm = GreedyColoringAlgorithm{:direct}(),
2525
detector::AbstractSparsityDetector = TracerSparsityDetector(),
2626
kwargs...,
2727
)
2828
H = compute_hessian_sparsity(f, nvar, c!, ncon, detector = detector)
29-
SparseADHessian(nvar, f, ncon, c!, H; x0, coloring, kwargs...)
29+
SparseADHessian(nvar, f, ncon, c!, H; x0, coloring_algorithm, kwargs...)
3030
end
3131

3232
function SparseADHessian(
@@ -36,25 +36,20 @@ function SparseADHessian(
3636
c!,
3737
H::SparseMatrixCSC{Bool, Int64};
3838
x0::S = rand(nvar),
39-
coloring::AbstractColoringAlgorithm = GreedyColoringAlgorithm(),
39+
coloring_algorithm::AbstractColoringAlgorithm = GreedyColoringAlgorithm{:direct}(),
4040
kwargs...,
4141
) where {S}
4242
T = eltype(S)
4343

44-
colors, star_set = symmetric_coloring_detailed(H, coloring)
45-
ncolors = maximum(colors)
46-
47-
d = BitVector(undef, nvar)
44+
problem = ColoringProblem{:symmetric, :column}()
45+
result_coloring = coloring(H, problem, coloring_algorithm, decompression_eltype=T)
4846

4947
trilH = tril(H)
5048
rowval = trilH.rowval
5149
colptr = trilH.colptr
52-
53-
# The indices of the nonzero elements in `vals` that will be processed by color `c` are stored in `dcolors[c]`.
54-
dcolors = nnz_colors(trilH, star_set, colors, ncolors)
55-
56-
# prepare directional derivatives
57-
res = similar(x0)
50+
nzval = T.(trilH.nzval)
51+
compressed_hessian = similar(x0)
52+
seed = BitVector(undef, nvar)
5853

5954
function lag(z; nvar = nvar, ncon = ncon, f = f, c! = c!)
6055
cx, x, y, ob = view(z, 1:ncon),
@@ -82,13 +77,13 @@ function SparseADHessian(
8277
y = fill!(S(undef, ncon), 0)
8378

8479
return SparseADHessian(
85-
d,
80+
nvar,
8681
rowval,
8782
colptr,
88-
colors,
89-
ncolors,
90-
dcolors,
91-
res,
83+
nzval,
84+
result_coloring,
85+
compressed_hessian,
86+
seed,
9287
lz,
9388
glz,
9489
sol,
@@ -99,14 +94,14 @@ function SparseADHessian(
9994
)
10095
end
10196

102-
struct SparseReverseADHessian{T, S, Tagf, F, Tagψ, P} <: ADNLPModels.ADBackend
103-
d::BitVector
97+
struct SparseReverseADHessian{Tagf, Tagψ, R, T, C, S, F, P} <: ADNLPModels.ADBackend
98+
nvar::Int
10499
rowval::Vector{Int}
105100
colptr::Vector{Int}
106-
colors::Vector{Int}
107-
ncolors::Int
108-
dcolors::Dict{Int, Vector{Tuple{Int, Int}}}
109-
res::S
101+
nzval::Vector{R}
102+
result_coloring::C
103+
compressed_hessian::S
104+
seed::BitVector
110105
z::Vector{ForwardDiff.Dual{Tagf, T, 1}}
111106
gz::Vector{ForwardDiff.Dual{Tagf, T, 1}}
112107
∇f!::F
@@ -125,38 +120,33 @@ function SparseReverseADHessian(
125120
ncon,
126121
c!;
127122
x0::AbstractVector = rand(nvar),
128-
coloring::AbstractColoringAlgorithm = GreedyColoringAlgorithm(),
123+
coloring_algorithm::AbstractColoringAlgorithm = GreedyColoringAlgorithm{:direct}(),
129124
detector::AbstractSparsityDetector = TracerSparsityDetector(),
130125
kwargs...,
131126
)
132127
H = compute_hessian_sparsity(f, nvar, c!, ncon, detector = detector)
133-
SparseReverseADHessian(nvar, f, ncon, c!, H; x0, coloring, kwargs...)
128+
SparseReverseADHessian(nvar, f, ncon, c!, H; x0, coloring_algorithm, kwargs...)
134129
end
135130

136131
function SparseReverseADHessian(
137132
nvar,
138133
f,
139134
ncon,
140135
c!,
141-
H::SparseMatrixCSC{Bool, Int64};
136+
H::SparseMatrixCSC{Bool, Int};
142137
x0::AbstractVector{T} = rand(nvar),
143-
coloring::AbstractColoringAlgorithm = GreedyColoringAlgorithm(),
138+
coloring_algorithm::AbstractColoringAlgorithm = GreedyColoringAlgorithm{:direct}(),
144139
kwargs...,
145140
) where {T}
146-
colors, star_set = symmetric_coloring_detailed(H, coloring)
147-
ncolors = maximum(colors)
148-
149-
d = BitVector(undef, nvar)
141+
problem = ColoringProblem{:symmetric, :column}()
142+
result_coloring = coloring(H, problem, coloring_algorithm, decompression_eltype=T)
150143

151144
trilH = tril(H)
152145
rowval = trilH.rowval
153146
colptr = trilH.colptr
154-
155-
# The indices of the nonzero elements in `vals` that will be processed by color `c` are stored in `dcolors[c]`.
156-
dcolors = nnz_colors(trilH, star_set, colors, ncolors)
157-
158-
# prepare directional derivatives
159-
res = similar(x0)
147+
nzval = T.(trilH.nzval)
148+
compressed_hessian = similar(x0)
149+
seed = BitVector(undef, nvar)
160150

161151
# unconstrained Hessian
162152
tagf = ForwardDiff.Tag{typeof(f), T}
@@ -188,13 +178,13 @@ function SparseReverseADHessian(
188178

189179
y = similar(x0, ncon)
190180
return SparseReverseADHessian(
191-
d,
181+
nvar,
192182
rowval,
193183
colptr,
194-
colors,
195-
ncolors,
196-
dcolors,
197-
res,
184+
nzval,
185+
result_coloring,
186+
compressed_hessian,
187+
seed,
198188
z,
199189
gz,
200190
∇f!,
@@ -237,76 +227,80 @@ function NLPModels.hess_structure_residual!(
237227
end
238228

239229
function sparse_hess_coord!(
240-
b::SparseADHessian{Tag, GT, S, T},
230+
b::SparseADHessian{Tag},
241231
x::AbstractVector,
242232
obj_weight,
243233
y::AbstractVector,
244234
vals::AbstractVector,
245-
) where {Tag, GT, S, T}
246-
nvar = length(x)
235+
) where {Tag}
247236
ncon = length(y)
248-
249-
b.sol[1:ncon] .= zero(T) # cx
250-
b.sol[(ncon + 1):(ncon + nvar)] .= x
251-
b.sol[(ncon + nvar + 1):(2 * ncon + nvar)] .= y
237+
T = eltype(x)
238+
b.sol[1:ncon] .= zero(T) # cx
239+
b.sol[(ncon + 1):(ncon + b.nvar)] .= x
240+
b.sol[(ncon + b.nvar + 1):(2 * ncon + b.nvar)] .= y
252241
b.sol[end] = obj_weight
253242

254243
b.longv .= 0
255244

256-
for icol = 1:(b.ncolors)
257-
dcolor_icol = b.dcolors[icol]
258-
if !isempty(dcolor_icol)
259-
b.d .= (b.colors .== icol)
260-
b.longv[(ncon + 1):(ncon + nvar)] .= b.d
261-
map!(ForwardDiff.Dual{Tag}, b.lz, b.sol, b.longv)
262-
b.∇φ!(b.glz, b.lz)
263-
ForwardDiff.extract_derivative!(Tag, b.Hvp, b.glz)
264-
b.res .= view(b.Hvp, (ncon + 1):(ncon + nvar))
265-
266-
# Store in `vals` the nonzeros of each column of the Hessian computed with color `icol`
267-
for (row, k) in dcolor_icol
268-
vals[k] = b.res[row]
269-
end
245+
# SparseMatrixColorings.jl requires a SparseMatrixCSC for the decompression
246+
A = SparseMatrixCSC(b.nvar, b.nvar, b.colptr, b.rowval, b.nzval)
247+
248+
groups = column_groups(b.result_coloring)
249+
for (icol, cols) in enumerate(groups)
250+
# Update the seed
251+
b.seed .= false
252+
for col in cols
253+
b.seed[col] = true
270254
end
271-
end
272255

256+
b.longv[(ncon + 1):(ncon + b.nvar)] .= b.seed
257+
map!(ForwardDiff.Dual{Tag}, b.lz, b.sol, b.longv)
258+
b.∇φ!(b.glz, b.lz)
259+
ForwardDiff.extract_derivative!(Tag, b.Hvp, b.glz)
260+
b.compressed_hessian .= view(b.Hvp, (ncon + 1):(ncon + b.nvar))
261+
262+
# Update the coefficients of the lower triangular part of the Hessian that are related to the color `icol`
263+
decompress_single_color!(A, b.compressed_hessian, icol, b.result_coloring, :L)
264+
end
265+
vals .= b.nzval
273266
return vals
274267
end
275268

276269
function sparse_hess_coord!(
277-
b::SparseReverseADHessian{T, S, Tagf, F, Tagψ, P},
270+
b::SparseReverseADHessian{Tagf, Tagψ},
278271
x::AbstractVector,
279272
obj_weight,
280273
y::AbstractVector,
281274
vals::AbstractVector,
282-
) where {T, S, Tagf, F, Tagψ, P}
283-
nvar = length(x)
284-
285-
for icol = 1:(b.ncolors)
286-
dcolor_icol = b.dcolors[icol]
287-
if !isempty(dcolor_icol)
288-
b.d .= (b.colors .== icol)
289-
290-
# objective
291-
map!(ForwardDiff.Dual{Tagf}, b.z, x, b.d) # x + ε * v
292-
b.∇f!(b.gz, b.z)
293-
ForwardDiff.extract_derivative!(Tagf, b.res, b.gz)
294-
b.res .*= obj_weight
295-
296-
# constraints
297-
map!(ForwardDiff.Dual{Tagψ}, b.zψ, x, b.d)
298-
b.yψ .= y
299-
b.∇l!(b.gzψ, b.gyψ, b.zψ, b.yψ)
300-
ForwardDiff.extract_derivative!(Tagψ, b.Hv_temp, b.gzψ)
301-
b.res .+= b.Hv_temp
302-
303-
# Store in `vals` the nonzeros of each column of the Hessian computed with color `icol`
304-
for (row, k) in dcolor_icol
305-
vals[k] = b.res[row]
306-
end
275+
) where {Tagf, Tagψ}
276+
# SparseMatrixColorings.jl requires a SparseMatrixCSC for the decompression
277+
A = SparseMatrixCSC(b.nvar, b.nvar, b.colptr, b.rowval, b.nzval)
278+
279+
groups = column_groups(b.result_coloring)
280+
for (icol, cols) in enumerate(groups)
281+
# Update the seed
282+
b.seed .= false
283+
for col in cols
284+
b.seed[col] = true
307285
end
308-
end
309286

287+
# objective
288+
map!(ForwardDiff.Dual{Tagf}, b.z, x, b.seed) # x + ε * v
289+
b.∇f!(b.gz, b.z)
290+
ForwardDiff.extract_derivative!(Tagf, b.compressed_hessian, b.gz)
291+
b.compressed_hessian .*= obj_weight
292+
293+
# constraints
294+
map!(ForwardDiff.Dual{Tagψ}, b.zψ, x, b.seed)
295+
b.yψ .= y
296+
b.∇l!(b.gzψ, b.gyψ, b.zψ, b.yψ)
297+
ForwardDiff.extract_derivative!(Tagψ, b.Hv_temp, b.gzψ)
298+
b.compressed_hessian .+= b.Hv_temp
299+
300+
# Update the coefficients of the lower triangular part of the Hessian that are related to the color `icol`
301+
decompress_single_color!(A, b.compressed_hessian, icol, b.result_coloring, :L)
302+
vals .= b.nzval
303+
end
310304
return vals
311305
end
312306

0 commit comments

Comments
 (0)