Skip to content

Commit 3e3acc0

Browse files
committed
Resolve SubArray issue with buffer
1 parent ae34199 commit 3e3acc0

3 files changed

Lines changed: 67 additions & 35 deletions

File tree

src/enzyme.jl

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,11 @@ function EnzymeReverseADHvprod(
6262
return EnzymeReverseADHvprod(grad,f)
6363
end
6464

65-
struct EnzymeReverseADJprod{T} <: InPlaceADbackend
66-
cx::Vector{T}
65+
struct EnzymeReverseADJprod{V} <: InPlaceADbackend
66+
cx::V # length ncon, primal output buffer
67+
xbuf::V # length nvar, input x buffer
68+
vbuf::V # length nvar, input v buffer (tangent direction)
69+
jvbuf::V # length ncon, output Jv buffer
6770
end
6871

6972
function EnzymeReverseADJprod(
@@ -74,12 +77,18 @@ function EnzymeReverseADJprod(
7477
x0::AbstractVector{T} = rand(nvar),
7578
kwargs...,
7679
) where {T}
77-
cx = zeros(T, ncon)
78-
return EnzymeReverseADJprod(cx)
80+
cx = fill!(similar(x0, ncon), zero(T))
81+
xbuf = similar(x0)
82+
vbuf = similar(x0)
83+
jvbuf = fill!(similar(x0, ncon), zero(T))
84+
return EnzymeReverseADJprod(cx, xbuf, vbuf, jvbuf)
7985
end
8086

81-
struct EnzymeReverseADJtprod{T} <: InPlaceADbackend
82-
cx::Vector{T}
87+
struct EnzymeReverseADJtprod{V} <: InPlaceADbackend
88+
cx::V # length ncon, primal output buffer
89+
xbuf::V # length nvar, input x buffer
90+
vbuf::V # length ncon, cotangent seed buffer
91+
jtvbuf::V # length nvar, output Jtv buffer
8392
end
8493

8594
function EnzymeReverseADJtprod(
@@ -90,20 +99,24 @@ function EnzymeReverseADJtprod(
9099
x0::AbstractVector{T} = rand(nvar),
91100
kwargs...,
92101
) where {T}
93-
cx = zeros(T, ncon)
94-
return EnzymeReverseADJtprod(cx)
102+
cx = fill!(similar(x0, ncon), zero(T))
103+
xbuf = similar(x0)
104+
vbuf = fill!(similar(x0, ncon), zero(T))
105+
jtvbuf = similar(x0)
106+
return EnzymeReverseADJtprod(cx, xbuf, vbuf, jtvbuf)
95107
end
96108

97-
struct SparseEnzymeADJacobian{R, C, S} <: ADBackend
109+
struct SparseEnzymeADJacobian{R, C, S, V} <: ADBackend
98110
nvar::Int
99111
ncon::Int
100112
rowval::Vector{Int}
101113
colptr::Vector{Int}
102114
nzval::Vector{R}
103115
result_coloring::C
104116
compressed_jacobian::S
105-
v::Vector{R}
106-
cx::Vector{R}
117+
v::V
118+
cx::V
119+
xbuf::V
107120
end
108121

109122
function SparseEnzymeADJacobian(
@@ -154,7 +167,8 @@ function SparseEnzymeADJacobian(
154167

155168
timer = @elapsed begin
156169
v = similar(x0)
157-
cx = zeros(T, ncon)
170+
cx = fill!(similar(x0, ncon), zero(T))
171+
xbuf = similar(x0)
158172
end
159173
show_time && println(" • Allocation of the AD buffers for the sparse Jacobian: $timer seconds.")
160174

@@ -168,24 +182,26 @@ function SparseEnzymeADJacobian(
168182
compressed_jacobian,
169183
v,
170184
cx,
185+
xbuf,
171186
)
172187
end
173188

174-
struct SparseEnzymeADHessian{R, C, S, L, F} <: ADBackend
189+
struct SparseEnzymeADHessian{R, C, S, L, F, V} <: ADBackend
175190
nvar::Int
176191
rowval::Vector{Int}
177192
colptr::Vector{Int}
178193
nzval::Vector{R}
179194
result_coloring::C
180195
coloring_mode::Symbol
181-
compressed_hessian_icol::Vector{R}
196+
compressed_hessian_icol::V
182197
compressed_hessian::S
183-
v::Vector{R}
184-
y::Vector{R}
185-
grad::Vector{R}
186-
cx::Vector{R}
198+
v::V
199+
y::V
200+
grad::V
201+
cx::V
187202
f::F
188203
::L
204+
xbuf::V
189205
end
190206

191207
function SparseEnzymeADHessian(
@@ -248,6 +264,7 @@ function SparseEnzymeADHessian(
248264
y = similar(x0, ncon)
249265
cx = similar(x0, ncon)
250266
grad = similar(x0)
267+
xbuf = similar(x0)
251268

252269
function (x, y, obj_weight, cx)
253270
if ncon != 0
@@ -279,6 +296,7 @@ function SparseEnzymeADHessian(
279296
cx,
280297
f,
281298
ℓ,
299+
xbuf,
282300
)
283301
end
284302

@@ -366,23 +384,29 @@ end
366384
end
367385

368386
function Jprod!(b::EnzymeReverseADJprod, Jv, c!, x, v, ::Val)
387+
copyto!(b.xbuf, x)
388+
copyto!(b.vbuf, v)
369389
Enzyme.autodiff(
370390
Enzyme.Forward,
371391
Enzyme.Const(c!),
372-
Enzyme.Duplicated(b.cx, Jv),
373-
Enzyme.Duplicated(x, v),
392+
Enzyme.Duplicated(b.cx, b.jvbuf),
393+
Enzyme.Duplicated(b.xbuf, b.vbuf),
374394
)
395+
copyto!(Jv, b.jvbuf)
375396
return Jv
376397
end
377398

378399
function Jtprod!(b::EnzymeReverseADJtprod, Jtv, c!, x, v, ::Val)
379-
Enzyme.make_zero!(Jtv)
400+
copyto!(b.xbuf, x)
401+
copyto!(b.vbuf, v)
402+
Enzyme.make_zero!(b.jtvbuf)
380403
Enzyme.autodiff(
381404
Enzyme.Reverse,
382405
Enzyme.Const(c!),
383-
Enzyme.Duplicated(b.cx, v),
384-
Enzyme.Duplicated(x, Jtv),
406+
Enzyme.Duplicated(b.cx, b.vbuf),
407+
Enzyme.Duplicated(b.xbuf, b.jtvbuf),
385408
)
409+
copyto!(Jtv, b.jtvbuf)
386410
return Jtv
387411
end
388412

@@ -442,6 +466,10 @@ end
442466
# SparseMatrixColorings.jl requires a SparseMatrixCSC for the decompression
443467
A = SparseMatrixCSC(b.ncon, b.nvar, b.colptr, b.rowval, b.nzval)
444468

469+
# Enzyme.Duplicated requires primal and shadow to have the same type.
470+
# Copy x into a pre-allocated buffer to ensure type match with b.v.
471+
copyto!(b.xbuf, x)
472+
445473
groups = column_groups(b.result_coloring)
446474
for (icol, cols) in enumerate(groups)
447475
# Update the seed
@@ -456,7 +484,7 @@ end
456484
Enzyme.Forward,
457485
Enzyme.Const(c!),
458486
Enzyme.Duplicated(b.cx, b.compressed_jacobian),
459-
Enzyme.Duplicated(x, b.v),
487+
Enzyme.Duplicated(b.xbuf, b.v),
460488
)
461489

462490
# Update the columns of the Jacobian that have the color `icol`
@@ -531,6 +559,10 @@ end
531559
# SparseMatrixColorings.jl requires a SparseMatrixCSC for the decompression
532560
A = SparseMatrixCSC(b.nvar, b.nvar, b.colptr, b.rowval, b.nzval)
533561

562+
# Enzyme.Duplicated requires primal and shadow to have the same type.
563+
# Copy x into a pre-allocated buffer to ensure type match with b.v.
564+
copyto!(b.xbuf, x)
565+
534566
groups = column_groups(b.result_coloring)
535567
for (icol, cols) in enumerate(groups)
536568
# Update the seed
@@ -542,7 +574,7 @@ end
542574
_hvp!(
543575
Enzyme.DuplicatedNoNeed(b.grad, b.compressed_hessian_icol),
544576
b.ℓ,
545-
x,
577+
b.xbuf,
546578
b.v,
547579
y,
548580
obj_weight,

src/predefined_backend.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ enzyme_backend = Dict(
4747
:gradient_backend => EnzymeReverseADGradient,
4848
:jprod_backend => EnzymeReverseADJprod,
4949
:jtprod_backend => EnzymeReverseADJtprod,
50-
:hprod_backend => EnzymeReverseADHvprod,
50+
:hprod_backend => ForwardDiffADHvprod,
5151
:jacobian_backend => SparseEnzymeADJacobian,
5252
:hessian_backend => SparseEnzymeADHessian,
5353
:ghjvprod_backend => ForwardDiffADGHjvprod,
5454
:jprod_residual_backend => EnzymeReverseADJprod,
5555
:jtprod_residual_backend => EnzymeReverseADJtprod,
56-
:hprod_residual_backend => EnzymeReverseADHvprod,
56+
:hprod_residual_backend => ForwardDiffADHvprod,
5757
:jacobian_residual_backend => SparseEnzymeADJacobian,
5858
:hessian_residual_backend => SparseEnzymeADHessian,
5959
)

test/enzyme.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import Enzyme
1010
EnzymeReverseAD() = ADNLPModels.ADModelBackend(
1111
ADNLPModels.EnzymeReverseADGradient(),
1212
ADNLPModels.EnzymeReverseADHvprod(zeros(1), identity),
13-
ADNLPModels.EnzymeReverseADJprod(zeros(1)),
14-
ADNLPModels.EnzymeReverseADJtprod(zeros(1)),
13+
ADNLPModels.EnzymeReverseADJprod(zeros(1), zeros(1), zeros(1), zeros(1)),
14+
ADNLPModels.EnzymeReverseADJtprod(zeros(1), zeros(1), zeros(1), zeros(1)),
1515
ADNLPModels.EnzymeReverseADJacobian(),
1616
ADNLPModels.EnzymeReverseADHessian(zeros(1), zeros(1), identity),
1717
ADNLPModels.EnzymeReverseADHvprod(zeros(1), identity),
@@ -47,15 +47,15 @@ push!(
4747
:gradient_backend => ADNLPModels.EnzymeReverseADGradient,
4848
:jprod_backend => ADNLPModels.EnzymeReverseADJprod,
4949
:jtprod_backend => ADNLPModels.EnzymeReverseADJtprod,
50-
:hprod_backend => ADNLPModels.EnzymeReverseADHvprod,
51-
:jacobian_backend => ADNLPModels.EnzymeReverseADJacobian,
52-
:hessian_backend => ADNLPModels.EnzymeReverseADHessian,
50+
:hprod_backend => ADNLPModels.ForwardDiffADHvprod,
51+
:jacobian_backend => ADNLPModels.SparseEnzymeADJacobian,
52+
:hessian_backend => ADNLPModels.SparseEnzymeADHessian,
5353
:ghjvprod_backend => ADNLPModels.ForwardDiffADGHjvprod,
5454
:jprod_residual_backend => ADNLPModels.EnzymeReverseADJprod,
5555
:jtprod_residual_backend => ADNLPModels.EnzymeReverseADJtprod,
56-
:hprod_residual_backend => ADNLPModels.EnzymeReverseADHvprod,
57-
:jacobian_residual_backend => ADNLPModels.EnzymeReverseADJacobian,
58-
:hessian_residual_backend => ADNLPModels.EnzymeReverseADHessian,
56+
:hprod_residual_backend => ADNLPModels.ForwardDiffADHvprod,
57+
:jacobian_residual_backend => ADNLPModels.SparseEnzymeADJacobian,
58+
:hessian_residual_backend => ADNLPModels.SparseEnzymeADHessian,
5959
),
6060
)
6161

0 commit comments

Comments
 (0)