Skip to content

Commit bede0d2

Browse files
sshin23claude
andcommitted
Fix GPU scalar indexing in BatchExaModel structure queries
BatchExaModel.jac_structure! and hess_structure! now pass getbackend(m) to backend-aware _jac_structure!/_obj_hess_structure!/_con_hess_structure! so the KA extension can intercept and use GPU kernels instead of scalar-indexing f.itr arrays. Also converts obj_weight to model type T in batch hess_coord! to prevent Metal InvalidIRError from Float64 values on Float32 models. FlatNLPModel structure queries now use device-native temp arrays for the per-instance query, then copy to CPU for replication. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b451da2 commit bede0d2

2 files changed

Lines changed: 31 additions & 12 deletions

File tree

src/BatchNLPModels.jl

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -226,12 +226,18 @@ function NLPModels.jac_nln_structure!(m::FlatNLPModel, rows::AbstractVector{<:In
226226
ncon = NLPModels.get_ncon(m.batch)
227227
nnzj = NLPModels.get_nnzj(m.batch)
228228

229-
# Compute structure on CPU (structure queries use scalar indexing)
229+
# Use device-native arrays for the per-instance query (avoids scalar indexing on GPU itr)
230+
r1_dev = similar(m.batch.meta.x0, Int, nnzj)
231+
c1_dev = similar(m.batch.meta.x0, Int, nnzj)
232+
NLPModels.jac_structure!(m.batch, r1_dev, c1_dev)
233+
234+
# Copy to CPU for replication
235+
r1 = Vector{Int}(r1_dev)
236+
c1 = Vector{Int}(c1_dev)
230237
r_cpu = Vector{Int}(undef, nnzj * nb)
231238
c_cpu = Vector{Int}(undef, nnzj * nb)
232-
r1 = @view r_cpu[1:nnzj]
233-
c1 = @view c_cpu[1:nnzj]
234-
NLPModels.jac_structure!(m.batch, r1, c1)
239+
copyto!(r_cpu, 1, r1, 1, nnzj)
240+
copyto!(c_cpu, 1, c1, 1, nnzj)
235241

236242
# Replicate for each instance with shifted indices
237243
@inbounds for s in 2:nb
@@ -261,12 +267,18 @@ function NLPModels.hess_structure!(m::FlatNLPModel, rows::AbstractVector{<:Integ
261267
nvar = NLPModels.get_nvar(m.batch)
262268
nnzh = NLPModels.get_nnzh(m.batch)
263269

264-
# Compute structure on CPU (structure queries use scalar indexing)
270+
# Use device-native arrays for the per-instance query (avoids scalar indexing on GPU)
271+
r1_dev = similar(m.batch.meta.x0, Int, nnzh)
272+
c1_dev = similar(m.batch.meta.x0, Int, nnzh)
273+
NLPModels.hess_structure!(m.batch, r1_dev, c1_dev)
274+
275+
# Copy to CPU for replication
276+
r1 = Vector{Int}(r1_dev)
277+
c1 = Vector{Int}(c1_dev)
265278
r_cpu = Vector{Int}(undef, nnzh * nb)
266279
c_cpu = Vector{Int}(undef, nnzh * nb)
267-
r1 = @view r_cpu[1:nnzh]
268-
c1 = @view c_cpu[1:nnzh]
269-
NLPModels.hess_structure!(m.batch, r1, c1)
280+
copyto!(r_cpu, 1, r1, 1, nnzh)
281+
copyto!(c_cpu, 1, c1, 1, nnzh)
270282

271283
# Replicate for each instance with shifted indices
272284
@inbounds for s in 2:nb

src/nlp.jl

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,8 @@ _jac_structure!(T, cons::Tuple{}, rows, cols) = nothing
13291329
_jac_structure!(T, Base.tail(cons), rows, cols)
13301330
sjacobian!(rows, cols, first(cons), NaNSource{T}(), NaNSource{T}(), T(NaN))
13311331
end
1332+
# Backend-aware fallbacks (KA extension overrides these for GPU backends)
1333+
_jac_structure!(T, ::Nothing, cons, rows, cols) = _jac_structure!(T, cons, rows, cols)
13321334

13331335
function hess_structure!(m::AbstractExaModel{T}, rows::AbstractVector, cols::AbstractVector) where T
13341336
_obj_hess_structure!(T, m.objs, rows, cols)
@@ -1341,12 +1343,16 @@ _obj_hess_structure!(T, objs::Tuple{}, rows, cols) = nothing
13411343
_obj_hess_structure!(T, Base.tail(objs), rows, cols)
13421344
shessian!(rows, cols, first(objs), NaNSource{T}(), NaNSource{T}(), T(NaN), T(NaN))
13431345
end
1346+
# Backend-aware fallback (KA extension overrides for GPU backends)
1347+
_obj_hess_structure!(T, ::Nothing, objs, rows, cols) = _obj_hess_structure!(T, objs, rows, cols)
13441348

13451349
_con_hess_structure!(T, cons::Tuple{}, rows, cols) = nothing
13461350
@inline function _con_hess_structure!(T, cons::Tuple, rows, cols)
13471351
_con_hess_structure!(T, Base.tail(cons), rows, cols)
13481352
shessian!(rows, cols, first(cons), NaNSource{T}(), NaNSource{T}(), T(NaN), T(NaN))
13491353
end
1354+
# Backend-aware fallback (KA extension overrides for GPU backends)
1355+
_con_hess_structure!(T, ::Nothing, cons, rows, cols) = _con_hess_structure!(T, cons, rows, cols)
13501356

13511357
# ============================================================================
13521358
# Batch-aware evaluation — all low-level functions loop over 1:nb,
@@ -1890,7 +1896,7 @@ function NLPModels.jac_structure!(
18901896
rows::AbstractVector{<:Integer},
18911897
cols::AbstractVector{<:Integer},
18921898
) where {T}
1893-
_jac_structure!(T, m.cons, rows, cols)
1899+
_jac_structure!(T, getbackend(m), m.cons, rows, cols)
18941900
return rows, cols
18951901
end
18961902

@@ -1909,8 +1915,9 @@ function NLPModels.hess_structure!(
19091915
rows::AbstractVector{<:Integer},
19101916
cols::AbstractVector{<:Integer},
19111917
) where {T}
1912-
_obj_hess_structure!(T, m.objs, rows, cols)
1913-
_con_hess_structure!(T, m.cons, rows, cols)
1918+
backend = getbackend(m)
1919+
_obj_hess_structure!(T, backend, m.objs, rows, cols)
1920+
_con_hess_structure!(T, backend, m.cons, rows, cols)
19141921
return rows, cols
19151922
end
19161923

@@ -1928,7 +1935,7 @@ function NLPModels.hess_coord!(
19281935
npar = Base.size(m.θ, 1)
19291936
nnzh = NLPModels.get_nnzh(m)
19301937
backend = getbackend(m)
1931-
_obj_hess_coord!(m.objs, vec(bx), vec(m.θ), vec(hvals), obj_weight, nb, nvar, npar, nnzh, backend)
1938+
_obj_hess_coord!(m.objs, vec(bx), vec(m.θ), vec(hvals), obj_weight isa Number ? T(obj_weight) : obj_weight, nb, nvar, npar, nnzh, backend)
19321939
_con_hess_coord!(m.cons, vec(bx), vec(m.θ), vec(by), vec(hvals), nb, nvar, npar, ncon, nnzh, backend)
19331940
return hvals
19341941
end

0 commit comments

Comments
 (0)