@@ -177,3 +177,147 @@ function Hvprod!(
177177 )
178178 return Hv
179179end
180+
181+ # Sparse Jacobian
182+ struct SparseEnzymeADJacobian{R, C, S} <: ADBackend
183+ nvar:: Int
184+ ncon:: Int
185+ rowval:: Vector{Int}
186+ colptr:: Vector{Int}
187+ nzval:: Vector{R}
188+ result_coloring:: C
189+ compressed_jacobian:: S
190+ v:: Vector{R}
191+ buffer:: Vector{R}
192+ end
193+
194+ function SparseEnzymeADJacobian(
195+ nvar,
196+ f,
197+ ncon,
198+ c!;
199+ x0:: AbstractVector = rand(nvar),
200+ coloring_algorithm:: AbstractColoringAlgorithm = GreedyColoringAlgorithm{:direct}(),
201+ detector:: AbstractSparsityDetector = TracerSparsityDetector(),
202+ kwargs... ,
203+ )
204+ output = similar(x0, ncon)
205+ J = compute_jacobian_sparsity(c!, output, x0, detector = detector)
206+ SparseEnzymeADJacobian(nvar, f, ncon, c!, J; x0, coloring_algorithm, kwargs... )
207+ end
208+
209+ function SparseEnzymeADJacobian(
210+ nvar,
211+ f,
212+ ncon,
213+ c!,
214+ J:: SparseMatrixCSC{Bool, Int} ;
215+ x0:: AbstractVector{T} = rand(nvar),
216+ coloring_algorithm:: AbstractColoringAlgorithm = GreedyColoringAlgorithm{:direct}(),
217+ kwargs... ,
218+ ) where {T}
219+ # We should support :row and :bidirectional in the future
220+ problem = ColoringProblem{:nonsymmetric, :column}()
221+ result_coloring = coloring(J, problem, coloring_algorithm, decompression_eltype = T)
222+
223+ rowval = J. rowval
224+ colptr = J. colptr
225+ nzval = T.(J. nzval)
226+ compressed_jacobian = similar(x0, ncon)
227+ v = similar(x0)
228+ buffer = zeros(T, ncon)
229+
230+ SparseEnzymeADJacobian(
231+ nvar,
232+ ncon,
233+ rowval,
234+ colptr,
235+ nzval,
236+ result_coloring,
237+ compressed_jacobian,
238+ v,
239+ buffer,
240+ )
241+ end
242+
243+ function sparse_jac_coord!(
244+ c!:: Function ,
245+ b:: SparseEnzymeADJacobian ,
246+ x:: AbstractVector ,
247+ vals:: AbstractVector ,
248+ )
249+ # SparseMatrixColorings.jl requires a SparseMatrixCSC for the decompression
250+ A = SparseMatrixCSC(b. ncon, b. nvar, b. colptr, b. rowval, b. nzval)
251+
252+ groups = column_groups(b. result_coloring)
253+ for (icol, cols) in enumerate(groups)
254+ # Update the seed
255+ b. v .= 0
256+ for col in cols
257+ b. v[col] = 1
258+ end
259+
260+ # b.compressed_jacobian is just a vector Jv here
261+ # We don't use the vector mode
262+ Enzyme. autodiff(Enzyme. Forward, Const(c!), Duplicated(b. buffer, b. compressed_jacobian), Duplicated(x, b. v))
263+
264+ # Update the columns of the Jacobian that have the color `icol`
265+ decompress_single_color!(A, b. compressed_jacobian, icol, b. result_coloring)
266+ end
267+ vals .= b. nzval
268+ return vals
269+ end
270+
271+ function get_nln_nnzj(b:: SparseEnzymeADJacobian , nvar, ncon)
272+ length(b. rowval)
273+ end
274+
275+ function NLPModels. jac_structure!(
276+ b:: SparseEnzymeADJacobian ,
277+ nlp:: ADModel ,
278+ rows:: AbstractVector{<:Integer} ,
279+ cols:: AbstractVector{<:Integer} ,
280+ )
281+ rows .= b. rowval
282+ for i = 1 : (nlp. meta. nvar)
283+ for j = b. colptr[i]: (b. colptr[i + 1 ] - 1 )
284+ cols[j] = i
285+ end
286+ end
287+ return rows, cols
288+ end
289+
290+ function NLPModels. jac_coord!(
291+ b:: SparseEnzymeADJacobian ,
292+ nlp:: ADModel ,
293+ x:: AbstractVector ,
294+ vals:: AbstractVector ,
295+ )
296+ sparse_jac_coord!(nlp. c!, b, x, vals)
297+ return vals
298+ end
299+
300+ function NLPModels. jac_structure_residual!(
301+ b:: SparseEnzymeADJacobian ,
302+ nls:: AbstractADNLSModel ,
303+ rows:: AbstractVector{<:Integer} ,
304+ cols:: AbstractVector{<:Integer} ,
305+ )
306+ rows .= b. rowval
307+ for i = 1 : (nls. meta. nvar)
308+ for j = b. colptr[i]: (b. colptr[i + 1 ] - 1 )
309+ cols[j] = i
310+ end
311+ end
312+ return rows, cols
313+ end
314+
315+ function NLPModels. jac_coord_residual!(
316+ b:: SparseEnzymeADJacobian ,
317+ nls:: AbstractADNLSModel ,
318+ x:: AbstractVector ,
319+ vals:: AbstractVector ,
320+ )
321+ sparse_jac_coord!(nls. F!, b, x, vals)
322+ return vals
323+ end
0 commit comments