Skip to content

Commit 76fd254

Browse files
committed
handle unconstrained and unbounded problems
1 parent 534d625 commit 76fd254

File tree

1 file changed

+52
-25
lines changed

1 file changed

+52
-25
lines changed

lib/OptimizationMadNLP/src/OptimizationMadNLP.jl

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,10 @@ function NLPModelsAdaptor(
6868
jac_buffer = zeros(T, ncon, nvar)
6969
end
7070

71+
ncon = !isnothing(cache.lcons) ? length(cache.lcons) : 0
72+
7173
# Extract Hessian structure
72-
hess_proto = something(cache.f.lag_hess_prototype, cache.f.hess_prototype, nothing)
74+
hess_proto = ncon > 0 ? cache.f.lag_hess_prototype : cache.f.hess_prototype
7375

7476
if !isnothing(hess_proto) && hess_proto isa SparseMatrixCSC
7577
I, J, _ = findnz(hess_proto)
@@ -104,7 +106,10 @@ function NLPModels.grad!(nlp::NLPModelsAdaptor, x::AbstractVector, g::AbstractVe
104106
end
105107

106108
function NLPModels.cons!(nlp::NLPModelsAdaptor, x::AbstractVector, c::AbstractVector)
107-
nlp.cache.f.cons(c, x)
109+
if !isempty(c)
110+
nlp.cache.f.cons(c, x)
111+
end
112+
return c
108113
end
109114

110115
function NLPModels.jac_structure!(
@@ -116,17 +121,19 @@ end
116121

117122
function NLPModels.jac_coord!(
118123
nlp::NLPModelsAdaptor, x::AbstractVector, vals::AbstractVector)
119-
# Evaluate Jacobian into preallocated buffer
120-
nlp.cache.f.cons_j(nlp.jac_buffer, x)
121-
122-
# Extract values in COO order
123-
if nlp.jac_buffer isa SparseMatrixCSC
124-
_, _, v = findnz(nlp.jac_buffer)
125-
copyto!(vals, v)
126-
else
127-
# Dense case: extract in column-major order matching structure
128-
for (idx, (i, j)) in enumerate(zip(nlp.jac_rows, nlp.jac_cols))
129-
vals[idx] = nlp.jac_buffer[i, j]
124+
if !isempty(vals)
125+
# Evaluate Jacobian into preallocated buffer
126+
nlp.cache.f.cons_j(nlp.jac_buffer, x)
127+
128+
# Extract values in COO order
129+
if nlp.jac_buffer isa SparseMatrixCSC
130+
_, _, v = findnz(nlp.jac_buffer)
131+
copyto!(vals, v)
132+
else
133+
# Dense case: extract in column-major order matching structure
134+
for (idx, (i, j)) in enumerate(zip(nlp.jac_rows, nlp.jac_cols))
135+
vals[idx] = nlp.jac_buffer[i, j]
136+
end
130137
end
131138
end
132139

@@ -150,7 +157,7 @@ function NLPModels.hess_coord!(
150157
nlp.cache.f.hess(nlp.hess_buffer, x)
151158
nlp.hess_buffer .*= obj_weight
152159

153-
if !isnothing(nlp.cache.f.cons_h)
160+
if !isnothing(nlp.cache.f.cons_h) && !isempty(y)
154161
# Add weighted constraint Hessians
155162
cons_hessians = [similar(nlp.hess_buffer) for _ in 1:length(y)]
156163
nlp.cache.f.cons_h(cons_hessians, x)
@@ -160,9 +167,11 @@ function NLPModels.hess_coord!(
160167
end
161168
end
162169

163-
# Extract lower triangle values
164-
for (idx, (i, j)) in enumerate(zip(nlp.hess_rows, nlp.hess_cols))
165-
H[idx] = nlp.hess_buffer[i, j]
170+
if !isempty(H)
171+
# Extract lower triangle values
172+
for (idx, (i, j)) in enumerate(zip(nlp.hess_rows, nlp.hess_cols))
173+
H[idx] = nlp.hess_buffer[i, j]
174+
end
166175
end
167176

168177
return H
@@ -194,7 +203,7 @@ end
194203
additional_options::Dict{Symbol, Any} = Dict{Symbol, Any}()
195204
end
196205

197-
OptimizationBase.supports_opt_cache_interface(opt::MadNLPOptimizer) = true
206+
SciMLBase.supports_opt_cache_interface(opt::MadNLPOptimizer) = true
198207

199208
function SciMLBase.requiresgradient(opt::MadNLPOptimizer)
200209
true
@@ -245,8 +254,20 @@ function map_madnlp_status(status::MadNLP.Status)
245254
end
246255
end
247256

257+
function _get_nnzj(f)
258+
jac_prototype = f.cons_jac_prototype
259+
260+
if isnothing(jac_prototype)
261+
return 0
262+
elseif jac_prototype isa SparseMatrixCSC
263+
nnz(jac_prototype)
264+
else
265+
prod(size(jac_prototype))
266+
end
267+
end
268+
248269
function _get_nnzh(f)
249-
hess_proto = something(f.lag_hess_prototype, f.hess_prototype, nothing)
270+
hess_proto = f.lag_hess_prototype
250271

251272
if isnothing(hess_proto)
252273
return 0 # Unknown, let NLPModels compute it
@@ -271,24 +292,30 @@ function __map_optimizer_args(cache,
271292
progress::Bool = false,
272293
callback = nothing)
273294
nvar = length(cache.u0)
274-
ncon = length(cache.lcons)
295+
ncon = !isnothing(cache.lcons) ? length(cache.lcons) : 0
275296

276297
if !isnothing(progress) || !isnothing(callback)
277298
@warn("MadNLP doesn't currently support user defined callbacks.")
278299
end
279300
# TODO: add support for user callbacks in MadNLP
280301

302+
T = eltype(cache.u0)
303+
lvar = something(cache.lb, fill(-Inf, nvar))
304+
uvar = something(cache.ub, fill(Inf, nvar))
305+
lcon = something(cache.lcons, T[])
306+
ucon = something(cache.ucons, T[])
307+
281308
meta = NLPModels.NLPModelMeta(
282309
nvar;
283310
ncon,
284-
nnzj = nnz(cache.f.cons_jac_prototype),
311+
nnzj = _get_nnzj(cache.f),
285312
nnzh = _get_nnzh(cache.f),
286313
x0 = cache.u0,
287314
y0 = zeros(eltype(cache.u0), ncon),
288-
lvar = cache.lb,
289-
uvar = cache.ub,
290-
lcon = cache.lcons,
291-
ucon = cache.ucons,
315+
lvar,
316+
uvar,
317+
lcon,
318+
ucon,
292319
minimize = cache.sense == MinSense
293320
)
294321

0 commit comments

Comments
 (0)