Skip to content

Commit 24709d4

Browse files
committed
Support quadratic constraints in MathOptNLSModel
1 parent a41e7d3 commit 24709d4

File tree

1 file changed

+122
-25
lines changed

1 file changed

+122
-25
lines changed

src/moi_nls_model.jl

Lines changed: 122 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mutable struct MathOptNLSModel <: AbstractNLSModel{Float64, Vector{Float64}}
99
linequ::LinearEquations
1010
nlequ::NonLinearStructure
1111
lincon::LinearConstraints
12+
quadcon::QuadraticConstraints
1213
nlcon::NonLinearStructure
1314
counters::NLSCounters
1415
end
@@ -39,11 +40,11 @@ function MathOptNLSModel(cmodel::JuMP.Model, F; hessian::Bool = true, name::Stri
3940
Fnnzj = linequ.nnzj + nlequ.nnzj
4041
Fnnzh = nlequ.nnzh
4142

42-
ncon = nlin + nnln
43-
lcon = vcat(lin_lcon, nl_lcon)
44-
ucon = vcat(lin_ucon, nl_ucon)
45-
cnnzj = lincon.nnzj + nlcon.nnzj
46-
cnnzh = lls.nnzh + nlcon.nnzh
43+
ncon = nlin + quadcon.nquad + nnln
44+
lcon = vcat(lin_lcon, quad_lcon, nl_lcon)
45+
ucon = vcat(lin_ucon, quad_ucon, nl_ucon)
46+
cnnzj = lincon.nnzj + quadcon.nnzj + nlcon.nnzj
47+
cnnzh = lls.nnzh + quadcon.nnzh + nlcon.nnzh
4748

4849
meta = NLPModelMeta(
4950
nvar,
@@ -58,7 +59,7 @@ function MathOptNLSModel(cmodel::JuMP.Model, F; hessian::Bool = true, name::Stri
5859
nnzh = cnnzh,
5960
lin = collect(1:nlin),
6061
lin_nnzj = lincon.nnzj,
61-
nln_nnzj = nlcon.nnzj,
62+
nln_nnzj = quadcon.nnzj + nlcon.nnzj,
6263
minimize = objective_sense(cmodel) == MOI.MIN_SENSE,
6364
islp = false,
6465
name = name,
@@ -73,6 +74,7 @@ function MathOptNLSModel(cmodel::JuMP.Model, F; hessian::Bool = true, name::Stri
7374
linequ,
7475
nlequ,
7576
lincon,
77+
quadcon,
7678
nlcon,
7779
NLSCounters(),
7880
)
@@ -253,7 +255,13 @@ end
253255

254256
function NLPModels.cons_nln!(nls::MathOptNLSModel, x::AbstractVector, c::AbstractVector)
255257
increment!(nls, :neval_cons_nln)
256-
MOI.eval_constraint(nls.ceval, c, x)
258+
for i = 1:(nls.quadcon.nquad)
259+
qcon = nls.quadcon.constraints[i]
260+
c[i] = 0.5 * coo_sym_dot(qcon.A.rows, qcon.A.cols, qcon.A.vals, x, x) + dot(qcon.b, x)
261+
end
262+
if nls.meta.nnln > nls.quadcon.nquad
263+
MOI.eval_constraint(nls.ceval, view(c, (nls.quadcon.nquad + 1):(nls.meta.nnln)), x)
264+
end
257265
return c
258266
end
259267

@@ -272,8 +280,20 @@ function NLPModels.jac_nln_structure!(
272280
rows::AbstractVector{<:Integer},
273281
cols::AbstractVector{<:Integer},
274282
)
275-
view(rows, 1:(nls.nlcon.nnzj)) .= nls.nlcon.jac_rows
276-
view(cols, 1:(nls.nlcon.nnzj)) .= nls.nlcon.jac_cols
283+
if nls.quadcon.nquad > 0
284+
index = 0
285+
for i = 1:(nls.quadcon.nquad)
286+
qcon = nls.quadcon.constraints[i]
287+
view(rows, index+1:index+qcon.nnzg) .= i
288+
view(cols, index+1:index+qcon.nnzg) .= qcon.g
289+
index += qcon.nnzg
290+
end
291+
end
292+
if nls.meta.nnln > nls.quadcon.nquad
293+
ind_nnln = (nls.quadcon.nnzj + 1):(nls.quadcon.nnzj + nls.nlcon.nnzj)
294+
view(rows, ind_nnln) .= nls.nlcon.jac_rows
295+
view(cols, ind_nnln) .= nls.nlcon.jac_cols
296+
end
277297
return rows, cols
278298
end
279299

@@ -285,7 +305,33 @@ end
285305

286306
function NLPModels.jac_nln_coord!(nls::MathOptNLSModel, x::AbstractVector, vals::AbstractVector)
287307
increment!(nls, :neval_jac_nln)
288-
MOI.eval_constraint_jacobian(nls.ceval, view(vals, 1:(nls.nlcon.nnzj)), x)
308+
if nls.quadcon.nquad > 0
309+
index = 0
310+
view(vals, 1:nls.quadcon.nnzj) .= 0.0
311+
for i = 1:(nls.quadcon.nquad)
312+
qcon = nls.quadcon.constraints[i]
313+
for (j, ind) in enumerate(qcon.b.nzind)
314+
k = qcon.dg[ind]
315+
vals[index+k] += qcon.b.nzval[j]
316+
end
317+
for j = 1:qcon.nnzh
318+
row = qcon.A.rows[j]
319+
col = qcon.A.cols[j]
320+
val = qcon.A.vals[j]
321+
k1 = qcon.dg[row]
322+
vals[index+k1] += val * x[col]
323+
if row != col
324+
k2 = qcon.dg[col]
325+
vals[index+k2] += val * x[row]
326+
end
327+
end
328+
index += qcon.nnzg
329+
end
330+
end
331+
if nls.meta.nnln > nls.quadcon.nquad
332+
ind_nnln = (nls.quadcon.nnzj + 1):(nls.quadcon.nnzj + nls.nlcon.nnzj)
333+
MOI.eval_constraint_jacobian(nls.ceval, view(vals, ind_nnln), x)
334+
end
289335
return vals
290336
end
291337

@@ -313,7 +359,18 @@ function NLPModels.jprod_nln!(
313359
Jv::AbstractVector,
314360
)
315361
increment!(nls, :neval_jprod_nln)
316-
MOI.eval_constraint_jacobian_product(nls.ceval, Jv, x, v)
362+
if nls.meta.nnln > nls.quadcon.nquad
363+
ind_nnln = (nls.quadcon.nquad + 1):(nls.meta.nnln)
364+
MOI.eval_constraint_jacobian_product(nls.ceval, view(Jv, ind_nnln), x, v)
365+
end
366+
(nls.meta.nnln == nls.quadcon.nquad) && (Jv .= 0.0)
367+
if nls.quadcon.nquad > 0
368+
for i = 1:(nls.quadcon.nquad)
369+
# Jv[i] = (Aᵢ * x + bᵢ)ᵀ * v
370+
qcon = nls.quadcon.constraints[i]
371+
v[i] += coo_sym_dot(qcon.A.rows, qcon.A.cols, qcon.A.vals, x, v) + dot(qcon.b, v)
372+
end
373+
end
317374
return Jv
318375
end
319376

@@ -341,7 +398,19 @@ function NLPModels.jtprod_nln!(
341398
Jtv::AbstractVector,
342399
)
343400
increment!(nls, :neval_jtprod_nln)
344-
MOI.eval_constraint_jacobian_transpose_product(nls.ceval, Jtv, x, v)
401+
if nls.meta.nnln > nls.quadcon.nquad
402+
ind_nnln = (nls.quadcon.nquad + 1):(nls.meta.nnln)
403+
MOI.eval_constraint_jacobian_transpose_product(nls.ceval, Jtv, x, view(v, ind_nnln))
404+
end
405+
(nls.meta.nnln == nls.quadcon.nquad) && (Jtv .= 0.0)
406+
if nls.quadcon.nquad > 0
407+
for i = 1:(nls.quadcon.nquad)
408+
# Jtv += v[i] * (Aᵢ * x + bᵢ)
409+
qcon = nls.quadcon.constraints[i]
410+
coo_sym_add_mul!(rows, cols, vals, x, Jtv, v[i])
411+
Jtv .+= v[i] .* qcon.b
412+
end
413+
end
345414
return Jtv
346415
end
347416

@@ -354,9 +423,18 @@ function NLPModels.hess_structure!(
354423
view(rows, 1:(nls.lls.nnzh)) .= nls.lls.hessian.rows
355424
view(cols, 1:(nls.lls.nnzh)) .= nls.lls.hessian.cols
356425
end
357-
if nls.nls_meta.nnln > 0
358-
view(rows, (nls.lls.nnzh + 1):(nls.meta.nnzh)) .= nls.nlcon.hess_rows
359-
view(cols, (nls.lls.nnzh + 1):(nls.meta.nnzh)) .= nls.nlcon.hess_cols
426+
if (nls.nls_meta.nnln > 0) || (nlp.meta.nnln > nlp.quadcon.nquad)
427+
view(rows, (nls.lls.nnzh + nlp.quadcon.nnzh + 1):(nls.meta.nnzh)) .= nls.nlcon.hess_rows
428+
view(cols, (nls.lls.nnzh + nlp.quadcon.nnzh + 1):(nls.meta.nnzh)) .= nls.nlcon.hess_cols
429+
end
430+
if nls.quadcon.nquad > 0
431+
index = nls.lls.nnzh
432+
for i = 1:(nls.quadcon.nquad)
433+
qcon = nls.quadcon.constraints[i]
434+
view(rows, (index + 1):(index + qcon.nnzh)) .= qcon.A.rows
435+
view(cols, (index + 1):(index + qcon.nnzh)) .= qcon.A.cols
436+
index += qcon.nnzh
437+
end
360438
end
361439
return rows, cols
362440
end
@@ -372,15 +450,24 @@ function NLPModels.hess_coord!(
372450
if nls.nls_meta.nlin > 0
373451
view(vals, 1:(nls.lls.nnzh)) .= obj_weight .* nls.lls.hessian.vals
374452
end
375-
if (nls.nls_meta.nnln > 0) || (nls.meta.nnln > 0)
453+
if (nls.nls_meta.nnln > 0) || (nls.meta.nnln > nls.quadcon.nquad)
454+
λ = view(y, (nls.meta.nlin + nls.quadcon.nquad + 1):(nls.meta.ncon))
376455
MOI.eval_hessian_lagrangian(
377456
nls.ceval,
378-
view(vals, (nls.lls.nnzh + 1):(nls.meta.nnzh)),
457+
view(vals, (nls.lls.nnzh + nls.quadcon.nnzh + 1):(nls.meta.nnzh)),
379458
x,
380459
obj_weight,
381-
view(y, nls.meta.nln),
460+
λ
382461
)
383462
end
463+
if nls.quadcon.nquad > 0
464+
index = nls.lls.nnzh
465+
for i = 1:(nls.quadcon.nquad)
466+
qcon = nls.quadcon.constraints[i]
467+
view(vals, (index + 1):(index + qcon.nnzh)) .= y[i] .* qcon.A.vals
468+
index += qcon.nnzh
469+
end
470+
end
384471
return vals
385472
end
386473

@@ -394,16 +481,18 @@ function NLPModels.hess_coord!(
394481
if nls.nls_meta.nlin > 0
395482
view(vals, 1:(nls.lls.nnzh)) .= obj_weight .* nls.lls.hessian.vals
396483
end
484+
view(vals, (nls.lls.nnzh + 1):(nls.lls.nnzh + nls.quadcon.nnzh)) .= 0.0
397485
if nls.nls_meta.nnln > 0
486+
λ = zeros(nlp.meta.nnln - nlp.quadcon.nquad) # Should be stored in the structure MathOptNLSModel
398487
MOI.eval_hessian_lagrangian(
399488
nls.ceval,
400-
view(vals, (nls.lls.nnzh + 1):(nls.meta.nnzh)),
489+
view(vals, (nls.lls.nnzh + nls.quadcon.nnzh + 1):(nls.meta.nnzh)),
401490
x,
402491
obj_weight,
403-
zeros(nls.meta.nnln),
492+
λ,
404493
)
405494
else
406-
view(vals, (nls.lls.nnzh + 1):(nls.meta.nnzh)) .= 0.0
495+
view(vals, (nls.lls.nnzh + nls.quadcon.nnzh + 1):(nls.meta.nnzh)) .= 0.0
407496
end
408497
return vals
409498
end
@@ -417,11 +506,12 @@ function NLPModels.hprod!(
417506
obj_weight::Float64 = 1.0,
418507
)
419508
increment!(nls, :neval_hprod)
420-
if (nls.nls_meta.nnln > 0) || (nls.meta.nnln > 0)
421-
MOI.eval_hessian_lagrangian_product(nls.ceval, hv, x, v, obj_weight, view(y, nls.meta.nln))
509+
if (nls.nls_meta.nnln > 0) || (nls.meta.nnln > nls.quadcon.nquad)
510+
λ = view(y, (nls.meta.nlin + nls.quadcon.nquad + 1):(nls.meta.ncon))
511+
MOI.eval_hessian_lagrangian_product(nls.ceval, hv, x, v, obj_weight, λ)
422512
end
513+
(nls.nls_meta.nnln == 0) && (nls.meta.nnln == nls.quadcon.nquad) && (hv .= 0.0)
423514
if nls.nls_meta.nlin > 0
424-
(nls.nls_meta.nnln == 0) && (nls.meta.nnln == 0) && (hv .= 0.0)
425515
coo_sym_add_mul!(
426516
nls.lls.hessian.rows,
427517
nls.lls.hessian.cols,
@@ -431,6 +521,12 @@ function NLPModels.hprod!(
431521
obj_weight,
432522
)
433523
end
524+
if nls.quadcon.nquad > 0
525+
for i = 1:(nls.quadcon.nquad)
526+
qcon = nls.quadcon.constraints[i]
527+
coo_sym_add_mul!(qcon.A.rows, qcon.A.cols, qcon.A.vals, v, hv, y[nls.meta.nlin + i])
528+
end
529+
end
434530
return hv
435531
end
436532

@@ -443,7 +539,8 @@ function NLPModels.hprod!(
443539
)
444540
increment!(nls, :neval_hprod)
445541
if nls.nls_meta.nnln > 0
446-
MOI.eval_hessian_lagrangian_product(nls.ceval, hv, x, v, obj_weight, zeros(nls.meta.nnln))
542+
λ = zeros(nls.meta.nnln - nls.quadcon.nquad) # Should be stored in the structure MathOptNLSModel
543+
MOI.eval_hessian_lagrangian_product(nls.ceval, hv, x, v, obj_weight, λ)
447544
end
448545
if nls.nls_meta.nlin > 0
449546
(nls.nls_meta.nnln == 0) && (hv .= 0.0)

0 commit comments

Comments
 (0)