Skip to content

Commit 17c255d

Browse files
committed
SparseReverseADJacobian
1 parent b519fbe commit 17c255d

File tree

4 files changed

+119
-10
lines changed

4 files changed

+119
-10
lines changed

docs/src/backend.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ The functions used internally to define the NLPModel API and the possible backen
1010
| Functions | FowardDiff backends | ReverseDiff backends | Zygote backends | Enzyme backend | Sparse backend |
1111
| --------- | ------------------- | -------------------- | --------------- | -------------- | -------------- |
1212
| `gradient` and `gradient!` | `ForwardDiffADGradient`/`GenericForwardDiffADGradient` | `ReverseDiffADGradient`/`GenericReverseDiffADGradient` | `ZygoteADGradient` | `EnzymeReverseADGradient` | -- |
13-
| `jacobian` | `ForwardDiffADJacobian` | `ReverseDiffADJacobian` | `ZygoteADJacobian` | `SparseEnzymeADJacobian` | `SparseADJacobian` |
13+
| `jacobian` | `ForwardDiffADJacobian` | `ReverseDiffADJacobian` | `ZygoteADJacobian` | `SparseEnzymeADJacobian` | `SparseADJacobian`/`SparseReverseADJacobian` |
1414
| `hessian` | `ForwardDiffADHessian` | `ReverseDiffADHessian` | `ZygoteADHessian` | `SparseEnzymeADHessian` | `SparseADHessian`/`SparseReverseADHessian` |
1515
| `Jprod` | `ForwardDiffADJprod`/`GenericForwardDiffADJprod` | `ReverseDiffADJprod`/`GenericReverseDiffADJprod` | `ZygoteADJprod` | `EnzymeReverseADJprod` | -- |
1616
| `Jtprod` | `ForwardDiffADJtprod`/`GenericForwardDiffADJtprod` | `ReverseDiffADJtprod`/`GenericReverseDiffADJtprod` | `ZygoteADJtprod` | `EnzymeReverseADJtprod` | -- |

docs/src/sparse.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ H = hess(nlp, x)
3434

3535
## Options for sparsity pattern detection and coloring
3636

37-
The backends available for sparse derivatives (`SparseADJacobian`, `SparseEnzymeADJacobian`, `SparseADHessian`, `SparseReverseADHessian`, and `SparseEnzymeADHessian`) allow for customization through keyword arguments such as `detector` and `coloring_algorithm`.
37+
The backends available for sparse derivatives (`SparseADJacobian`, `SparseReverseADJacobian`, `SparseEnzymeADJacobian`, `SparseADHessian`, `SparseReverseADHessian`, and `SparseEnzymeADHessian`) allow for customization through keyword arguments such as `detector` and `coloring_algorithm`.
3838
These arguments specify the sparsity pattern detector and the coloring algorithm, respectively.
3939

4040
- A **`detector`** must be of type `ADTypes.AbstractSparsityDetector`.
@@ -53,7 +53,7 @@ set_adbackend!(
5353
```
5454

5555
- A **`coloring_algorithm`** must be of type `SparseMatrixColorings.GreedyColoringAlgorithm`.
56-
The default algorithm is `GreedyColoringAlgorithm{:direct}()` for `SparseADJacobian`, `SparseEnzymeADJacobian` and `SparseADHessian`, while it is `GreedyColoringAlgorithm{:substitution}()` for `SparseReverseADHessian` and `SparseEnzymeADHessian`.
56+
The default algorithm is `GreedyColoringAlgorithm{:direct}()` for `SparseADJacobian`, `SparseADReverseJacobian`, `SparseEnzymeADJacobian` and `SparseADHessian`, while it is `GreedyColoringAlgorithm{:substitution}()` for `SparseReverseADHessian` and `SparseEnzymeADHessian`.
5757
These algorithms are provided by the package `SparseMatrixColorings.jl`.
5858

5959
```@example ex1

src/sparse_jacobian.jl

Lines changed: 113 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ struct SparseADJacobian{Tag, R, T, C, S} <: ADBackend
1111
cz::Vector{ForwardDiff.Dual{Tag, T, 1}}
1212
end
1313

14+
struct SparseReverseADJacobian{T, C, S, Tape} <: ADBackend
15+
nvar::Int
16+
ncon::Int
17+
rowval::Vector{Int}
18+
colptr::Vector{Int}
19+
nzval::Vector{T}
20+
result_coloring::C
21+
compressed_jacobian::S
22+
seed::BitVector
23+
tape::Tape
24+
end
25+
1426
function SparseADJacobian(
1527
nvar,
1628
f,
@@ -30,6 +42,25 @@ function SparseADJacobian(
3042
SparseADJacobian(nvar, f, ncon, c!, J; x0, coloring_algorithm, show_time, kwargs...)
3143
end
3244

45+
function SparseReverseADJacobian(
46+
nvar,
47+
f,
48+
ncon,
49+
c!;
50+
x0::AbstractVector{T} = rand(nvar),
51+
coloring_algorithm::AbstractColoringAlgorithm = GreedyColoringAlgorithm{:direct}(),
52+
detector::AbstractSparsityDetector = TracerSparsityDetector(),
53+
show_time::Bool = false,
54+
kwargs...,
55+
) where {T}
56+
timer = @elapsed begin
57+
output = similar(x0, ncon)
58+
J = compute_jacobian_sparsity(c!, output, x0, detector = detector)
59+
end
60+
show_time && println(" • Sparsity pattern detection of the Jacobian: $timer seconds.")
61+
SparseReverseADJacobian(nvar, f, ncon, c!, J; x0, coloring_algorithm, show_time, kwargs...)
62+
end
63+
3364
function SparseADJacobian(
3465
nvar,
3566
f,
@@ -42,7 +73,7 @@ function SparseADJacobian(
4273
kwargs...,
4374
) where {T}
4475
timer = @elapsed begin
45-
# We should support :row and :bidirectional in the future
76+
# We should support :bidirectional in the future with DI.jl
4677
problem = ColoringProblem{:nonsymmetric, :column}()
4778
result_coloring = coloring(J, problem, coloring_algorithm, decompression_eltype = T)
4879

@@ -75,12 +106,56 @@ function SparseADJacobian(
75106
)
76107
end
77108

78-
function get_nln_nnzj(b::SparseADJacobian, nvar, ncon)
109+
function SparseReverseADJacobian(
110+
nvar,
111+
f,
112+
ncon,
113+
c!,
114+
J::SparseMatrixCSC{Bool, Int};
115+
x0::AbstractVector{T} = rand(nvar),
116+
coloring_algorithm::AbstractColoringAlgorithm = GreedyColoringAlgorithm{:direct}(),
117+
show_time::Bool = false,
118+
kwargs...,
119+
) where {T}
120+
timer = @elapsed begin
121+
# We should support :bidirectional in the future with DI.jl
122+
problem = ColoringProblem{:nonsymmetric, :row}()
123+
result_coloring = coloring(J, problem, coloring_algorithm, decompression_eltype = T)
124+
125+
rowval = J.rowval
126+
colptr = J.colptr
127+
nzval = T.(J.nzval)
128+
compressed_jacobian = zeros(T, nvar)
129+
seed = BitVector(undef, ncon)
130+
end
131+
show_time && println(" • Coloring of the sparse Jacobian: $timer seconds.")
132+
133+
timer = @elapsed begin
134+
trace_input = copy(x0)
135+
tape = ReverseDiff.GradientTape(x -> c!(similar(x0, ncon), x), trace_input)
136+
ReverseDiff.compile!(tape)
137+
end
138+
show_time && println(" • Compilation of the ReverseDiff tape: $timer seconds.")
139+
140+
return SparseReverseADJacobian(
141+
nvar,
142+
ncon,
143+
rowval,
144+
colptr,
145+
nzval,
146+
result_coloring,
147+
compressed_jacobian,
148+
seed,
149+
tape,
150+
)
151+
end
152+
153+
function get_nln_nnzj(b::Union{SparseADJacobian, SparseReverseADJacobian}, nvar, ncon)
79154
length(b.rowval)
80155
end
81156

82157
function NLPModels.jac_structure!(
83-
b::SparseADJacobian,
158+
b::Union{SparseADJacobian, SparseReverseADJacobian},
84159
nlp::ADModel,
85160
rows::AbstractVector{<:Integer},
86161
cols::AbstractVector{<:Integer},
@@ -122,8 +197,40 @@ function sparse_jac_coord!(
122197
return vals
123198
end
124199

200+
function sparse_jac_coord!(
201+
ℓ!::Function,
202+
b::SparseReverseADJacobian{T},
203+
x::AbstractVector,
204+
vals::AbstractVector,
205+
) where {T}
206+
# SparseMatrixColorings.jl requires a SparseMatrixCSC for the decompression
207+
A = SparseMatrixCSC(b.ncon, b.nvar, b.colptr, b.rowval, b.nzval)
208+
209+
groups = row_groups(b.result_coloring)
210+
for (irow, rows) in enumerate(groups)
211+
# Update the seed
212+
b.seed .= false
213+
for row in rows
214+
b.seed[row] = true
215+
end
216+
217+
ReverseDiff.gradient!(
218+
b.compressed_jacobian,
219+
b.tape,
220+
x;
221+
output_seed = b.seed,
222+
)
223+
224+
# Update the rows of the Jacobian that have the color `irow`
225+
decompress_single_color!(A, b.compressed_jacobian, irow, b.result_coloring)
226+
end
227+
228+
vals .= b.nzval
229+
return vals
230+
end
231+
125232
function NLPModels.jac_coord!(
126-
b::SparseADJacobian,
233+
b::Union{SparseADJacobian, SparseReverseADJacobian},
127234
nlp::ADModel,
128235
x::AbstractVector,
129236
vals::AbstractVector,
@@ -133,7 +240,7 @@ function NLPModels.jac_coord!(
133240
end
134241

135242
function NLPModels.jac_structure_residual!(
136-
b::SparseADJacobian,
243+
b::Union{SparseADJacobian, SparseReverseADJacobian},
137244
nls::AbstractADNLSModel,
138245
rows::AbstractVector{<:Integer},
139246
cols::AbstractVector{<:Integer},
@@ -148,7 +255,7 @@ function NLPModels.jac_structure_residual!(
148255
end
149256

150257
function NLPModels.jac_coord_residual!(
151-
b::SparseADJacobian,
258+
b::Union{SparseADJacobian, SparseReverseADJacobian},
152259
nls::AbstractADNLSModel,
153260
x::AbstractVector,
154261
vals::AbstractVector,

test/runtests.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ include("sparse_hessian.jl")
2929
include("sparse_hessian_nls.jl")
3030

3131
list_sparse_jac_backend =
32-
((ADNLPModels.SparseADJacobian, Dict()), (ADNLPModels.ForwardDiffADJacobian, Dict()))
32+
((ADNLPModels.SparseADJacobian, Dict()),
33+
(ADNLPModels.SparseReverseADJacobian, Dict()),
34+
(ADNLPModels.ForwardDiffADJacobian, Dict()))
3335

3436
@testset "Sparse Jacobian" begin
3537
for (backend, kw) in list_sparse_jac_backend

0 commit comments

Comments
 (0)