Skip to content

Commit 13e1ac5

Browse files
SebastianM-Cclaude
andcommitted
Fix MadNLP sparse Hessian and constraint Jacobian handling
Fixes dimension mismatch errors when using sparse Lagrangian Hessians & corrects constraint Jacobian structure initialization Co-authored-by: Claude <noreply@anthropic.com>
1 parent 739b8a6 commit 13e1ac5

File tree

1 file changed

+28
-12
lines changed

1 file changed

+28
-12
lines changed

lib/OptimizationMadNLP/src/OptimizationMadNLP.jl

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
module OptimizationMadNLP
22

33
using OptimizationBase
4+
using OptimizationBase: MinSense, MaxSense, DEFAULT_CALLBACK
45
using MadNLP
56
using NLPModels
67
using SparseArrays
78

89
export MadNLPOptimizer
910

10-
struct NLPModelsAdaptor{C, T} <: NLPModels.AbstractNLPModel{T, Vector{T}}
11+
struct NLPModelsAdaptor{C, T, HB} <: NLPModels.AbstractNLPModel{T, Vector{T}}
1112
cache::C
1213
meta::NLPModels.NLPModelMeta{T, Vector{T}}
1314
counters::NLPModels.Counters
@@ -16,7 +17,7 @@ struct NLPModelsAdaptor{C, T} <: NLPModels.AbstractNLPModel{T, Vector{T}}
1617
jac_buffer::AbstractMatrix{T}
1718
hess_rows::Vector{Int}
1819
hess_cols::Vector{Int}
19-
hess_buffer::AbstractMatrix{T}
20+
hess_buffer::HB # Can be Vector{T} or Matrix{T}
2021
end
2122

2223
function _enumerate_dense_structure(ncon, nvar)
@@ -79,11 +80,13 @@ function NLPModelsAdaptor(
7980
lower_mask = I .>= J
8081
hess_rows = I[lower_mask]
8182
hess_cols = J[lower_mask]
82-
hess_buffer = similar(hess_proto)
83+
# Create a values buffer matching the number of lower triangle elements
84+
hess_buffer = zeros(T, sum(lower_mask))
8385
elseif !isnothing(hess_proto)
8486
# Dense Hessian
8587
n = size(hess_proto, 1)
8688
hess_rows, hess_cols = _enumerate_lower_triangle(n)
89+
# For dense, store the full matrix but we'll extract values later
8790
hess_buffer = similar(hess_proto)
8891
else
8992
# No prototype - create dense structure
@@ -92,7 +95,7 @@ function NLPModelsAdaptor(
9295
hess_buffer = zeros(T, n, n)
9396
end
9497

95-
return NLPModelsAdaptor{C, T}(cache, meta, counters,
98+
return NLPModelsAdaptor{C, T, typeof(hess_buffer)}(cache, meta, counters,
9699
jac_rows, jac_cols, jac_buffer,
97100
hess_rows, hess_cols, hess_buffer)
98101
end
@@ -152,7 +155,13 @@ function NLPModels.hess_coord!(
152155
nlp::NLPModelsAdaptor, x, y, H::AbstractVector; obj_weight = 1.0)
153156
if !isnothing(nlp.cache.f.lag_h)
154157
# Use Lagrangian Hessian directly
155-
nlp.cache.f.lag_h(nlp.hess_buffer, x, obj_weight, y)
158+
if nlp.hess_buffer isa AbstractVector
159+
# For sparse prototypes, hess_buffer is already a values vector
160+
nlp.cache.f.lag_h(nlp.hess_buffer, x, obj_weight, y)
161+
else
162+
# For dense matrices, we need to pass the full matrix and extract values
163+
nlp.cache.f.lag_h(nlp.hess_buffer, x, obj_weight, y)
164+
end
156165
else
157166
# Manual computation: objective + constraint Hessians
158167
nlp.cache.f.hess(nlp.hess_buffer, x)
@@ -169,9 +178,15 @@ function NLPModels.hess_coord!(
169178
end
170179

171180
if !isempty(H)
172-
# Extract lower triangle values
173-
for (idx, (i, j)) in enumerate(zip(nlp.hess_rows, nlp.hess_cols))
174-
H[idx] = nlp.hess_buffer[i, j]
181+
# Extract values depending on buffer type
182+
if nlp.hess_buffer isa AbstractVector
183+
# For sparse, hess_buffer already contains just the values
184+
copyto!(H, nlp.hess_buffer)
185+
else
186+
# For dense matrices, extract lower triangle values
187+
for (idx, (i, j)) in enumerate(zip(nlp.hess_rows, nlp.hess_cols))
188+
H[idx] = nlp.hess_buffer[i, j]
189+
end
175190
end
176191
end
177192

@@ -255,11 +270,12 @@ function map_madnlp_status(status::MadNLP.Status)
255270
end
256271
end
257272

258-
function _get_nnzj(f)
273+
function _get_nnzj(f, ncon, nvar)
259274
jac_prototype = f.cons_jac_prototype
260275

261276
if isnothing(jac_prototype)
262-
return 0
277+
# No prototype - assume dense structure if there are constraints
278+
return ncon > 0 ? ncon * nvar : 0
263279
elseif jac_prototype isa SparseMatrixCSC
264280
nnz(jac_prototype)
265281
else
@@ -311,15 +327,15 @@ function __map_optimizer_args(cache,
311327
meta = NLPModels.NLPModelMeta(
312328
nvar;
313329
ncon,
314-
nnzj = _get_nnzj(cache.f),
330+
nnzj = _get_nnzj(cache.f, ncon, nvar),
315331
nnzh = _get_nnzh(cache.f, ncon, nvar),
316332
x0 = cache.u0,
317333
y0 = zeros(eltype(cache.u0), ncon),
318334
lvar,
319335
uvar,
320336
lcon,
321337
ucon,
322-
minimize = cache.sense == MinSense
338+
minimize = cache.sense !== MaxSense # Default to minimization when sense is nothing or MinSense
323339
)
324340

325341
nlp = NLPModelsAdaptor(cache, meta, NLPModels.Counters())

0 commit comments

Comments
 (0)