Skip to content

Commit c12dc0c

Browse files
authored
Support quadratic constraints (#181)
1 parent 5bb0b82 commit c12dc0c

File tree

17 files changed

+540
-100
lines changed

17 files changed

+540
-100
lines changed

src/moi_nlp_model.jl

Lines changed: 126 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ mutable struct MathOptNLPModel <: AbstractNLPModel{Float64, Vector{Float64}}
44
meta::NLPModelMeta{Float64, Vector{Float64}}
55
eval::MOI.Nonlinear.Evaluator
66
lincon::LinearConstraints
7+
quadcon::QuadraticConstraints
78
nlcon::NonLinearStructure
9+
λ::Vector{Float64}
810
obj::Objective
911
counters::Counters
1012
end
@@ -27,22 +29,23 @@ end
2729

2830
function nlp_model(moimodel::MOI.ModelLike; hessian::Bool = true, name::String = "Generic")
2931
index_map, nvar, lvar, uvar, x0 = parser_variables(moimodel)
30-
nlin, lincon, lin_lcon, lin_ucon = parser_MOI(moimodel, index_map)
32+
nlin, lincon, lin_lcon, lin_ucon, quadcon, quad_lcon, quad_ucon = parser_MOI(moimodel, index_map, nvar)
3133

3234
nlp_data = _nlp_block(moimodel)
3335
nnln, nlcon, nl_lcon, nl_ucon = parser_NL(nlp_data, hessian = hessian)
36+
λ = zeros(nnln) # Lagrange multipliers for hess_coord! and hprod! without y
3437

3538
if nlp_data.has_objective
3639
obj = Objective("NONLINEAR", 0.0, spzeros(Float64, nvar), COO(), 0)
3740
else
3841
obj = parser_objective_MOI(moimodel, nvar, index_map)
3942
end
4043

41-
ncon = nlin + nnln
42-
lcon = vcat(lin_lcon, nl_lcon)
43-
ucon = vcat(lin_ucon, nl_ucon)
44-
nnzj = lincon.nnzj + nlcon.nnzj
45-
nnzh = obj.nnzh + nlcon.nnzh
44+
ncon = nlin + quadcon.nquad + nnln
45+
lcon = vcat(lin_lcon, quad_lcon, nl_lcon)
46+
ucon = vcat(lin_ucon, quad_ucon, nl_ucon)
47+
nnzj = lincon.nnzj + quadcon.nnzj + nlcon.nnzj
48+
nnzh = obj.nnzh + quadcon.nnzh + nlcon.nnzh
4649

4750
meta = NLPModelMeta(
4851
nvar,
@@ -57,13 +60,13 @@ function nlp_model(moimodel::MOI.ModelLike; hessian::Bool = true, name::String =
5760
nnzh = nnzh,
5861
lin = collect(1:nlin),
5962
lin_nnzj = lincon.nnzj,
60-
nln_nnzj = nlcon.nnzj,
63+
nln_nnzj = quadcon.nnzj + nlcon.nnzj,
6164
minimize = MOI.get(moimodel, MOI.ObjectiveSense()) == MOI.MIN_SENSE,
62-
islp = (obj.type == "LINEAR") && (nnln == 0),
65+
islp = (obj.type == "LINEAR") && (nnln == 0) && (quadcon.nquad == 0),
6366
name = name,
6467
)
6568

66-
return MathOptNLPModel(meta, nlp_data.evaluator, lincon, nlcon, obj, Counters()), index_map
69+
return MathOptNLPModel(meta, nlp_data.evaluator, lincon, quadcon, nlcon, λ, obj, Counters()), index_map
6770
end
6871

6972
function NLPModels.obj(nlp::MathOptNLPModel, x::AbstractVector)
@@ -106,7 +109,13 @@ end
106109

107110
function NLPModels.cons_nln!(nlp::MathOptNLPModel, x::AbstractVector, c::AbstractVector)
108111
increment!(nlp, :neval_cons_nln)
109-
MOI.eval_constraint(nlp.eval, c, x)
112+
for i = 1:(nlp.quadcon.nquad)
113+
qcon = nlp.quadcon.constraints[i]
114+
c[i] = 0.5 * coo_sym_dot(qcon.A.rows, qcon.A.cols, qcon.A.vals, x, x) + dot(qcon.b, x)
115+
end
116+
if nlp.meta.nnln > nlp.quadcon.nquad
117+
MOI.eval_constraint(nlp.eval, view(c, (nlp.quadcon.nquad + 1):(nlp.meta.nnln)), x)
118+
end
110119
return c
111120
end
112121

@@ -125,8 +134,20 @@ function NLPModels.jac_nln_structure!(
125134
rows::AbstractVector{<:Integer},
126135
cols::AbstractVector{<:Integer},
127136
)
128-
view(rows, 1:(nlp.nlcon.nnzj)) .= nlp.nlcon.jac_rows
129-
view(cols, 1:(nlp.nlcon.nnzj)) .= nlp.nlcon.jac_cols
137+
if nlp.quadcon.nquad > 0
138+
index = 0
139+
for i = 1:(nlp.quadcon.nquad)
140+
qcon = nlp.quadcon.constraints[i]
141+
view(rows, index+1:index+qcon.nnzg) .= i
142+
view(cols, index+1:index+qcon.nnzg) .= qcon.g
143+
index += qcon.nnzg
144+
end
145+
end
146+
if nlp.meta.nnln > nlp.quadcon.nquad
147+
ind_nnln = (nlp.quadcon.nnzj + 1):(nlp.quadcon.nnzj + nlp.nlcon.nnzj)
148+
view(rows, ind_nnln) .= nlp.quadcon.nquad .+ nlp.nlcon.jac_rows
149+
view(cols, ind_nnln) .= nlp.nlcon.jac_cols
150+
end
130151
return rows, cols
131152
end
132153

@@ -138,7 +159,33 @@ end
138159

139160
function NLPModels.jac_nln_coord!(nlp::MathOptNLPModel, x::AbstractVector, vals::AbstractVector)
140161
increment!(nlp, :neval_jac_nln)
141-
MOI.eval_constraint_jacobian(nlp.eval, view(vals, 1:(nlp.nlcon.nnzj)), x)
162+
if nlp.quadcon.nquad > 0
163+
index = 0
164+
view(vals, 1:nlp.quadcon.nnzj) .= 0.0
165+
for i = 1:(nlp.quadcon.nquad)
166+
qcon = nlp.quadcon.constraints[i]
167+
for (j, ind) in enumerate(qcon.b.nzind)
168+
k = qcon.dg[ind]
169+
vals[index+k] += qcon.b.nzval[j]
170+
end
171+
for j = 1:qcon.nnzh
172+
row = qcon.A.rows[j]
173+
col = qcon.A.cols[j]
174+
val = qcon.A.vals[j]
175+
k1 = qcon.dg[row]
176+
vals[index+k1] += val * x[col]
177+
if row != col
178+
k2 = qcon.dg[col]
179+
vals[index+k2] += val * x[row]
180+
end
181+
end
182+
index += qcon.nnzg
183+
end
184+
end
185+
if nlp.meta.nnln > nlp.quadcon.nquad
186+
ind_nnln = (nlp.quadcon.nnzj + 1):(nlp.quadcon.nnzj + nlp.nlcon.nnzj)
187+
MOI.eval_constraint_jacobian(nlp.eval, view(vals, ind_nnln), x)
188+
end
142189
return vals
143190
end
144191

@@ -166,7 +213,18 @@ function NLPModels.jprod_nln!(
166213
Jv::AbstractVector,
167214
)
168215
increment!(nlp, :neval_jprod_nln)
169-
MOI.eval_constraint_jacobian_product(nlp.eval, Jv, x, v)
216+
if nlp.meta.nnln > nlp.quadcon.nquad
217+
ind_nnln = (nlp.quadcon.nquad + 1):(nlp.meta.nnln)
218+
MOI.eval_constraint_jacobian_product(nlp.eval, view(Jv, ind_nnln), x, v)
219+
end
220+
(nlp.meta.nnln == nlp.quadcon.nquad) && (Jv .= 0.0)
221+
if nlp.quadcon.nquad > 0
222+
for i = 1:(nlp.quadcon.nquad)
223+
# Jv[i] = (Aᵢ * x + bᵢ)ᵀ * v
224+
qcon = nlp.quadcon.constraints[i]
225+
Jv[i] += coo_sym_dot(qcon.A.rows, qcon.A.cols, qcon.A.vals, x, v) + dot(qcon.b, v)
226+
end
227+
end
170228
return Jv
171229
end
172230

@@ -194,7 +252,19 @@ function NLPModels.jtprod_nln!(
194252
Jtv::AbstractVector,
195253
)
196254
increment!(nlp, :neval_jtprod_nln)
197-
MOI.eval_constraint_jacobian_transpose_product(nlp.eval, Jtv, x, v)
255+
if nlp.meta.nnln > nlp.quadcon.nquad
256+
ind_nnln = (nlp.quadcon.nquad + 1):(nlp.meta.nnln)
257+
MOI.eval_constraint_jacobian_transpose_product(nlp.eval, Jtv, x, view(v, ind_nnln))
258+
end
259+
(nlp.meta.nnln == nlp.quadcon.nquad) && (Jtv .= 0.0)
260+
if nlp.quadcon.nquad > 0
261+
for i = 1:(nlp.quadcon.nquad)
262+
# Jtv += v[i] * (Aᵢ * x + bᵢ)
263+
qcon = nlp.quadcon.constraints[i]
264+
coo_sym_add_mul!(qcon.A.rows, qcon.A.cols, qcon.A.vals, x, Jtv, v[i])
265+
Jtv .+= v[i] .* qcon.b
266+
end
267+
end
198268
return Jtv
199269
end
200270

@@ -207,9 +277,18 @@ function NLPModels.hess_structure!(
207277
view(rows, 1:(nlp.obj.nnzh)) .= nlp.obj.hessian.rows
208278
view(cols, 1:(nlp.obj.nnzh)) .= nlp.obj.hessian.cols
209279
end
210-
if (nlp.obj.type == "NONLINEAR") || (nlp.meta.nnln > 0)
211-
view(rows, (nlp.obj.nnzh + 1):(nlp.meta.nnzh)) .= nlp.nlcon.hess_rows
212-
view(cols, (nlp.obj.nnzh + 1):(nlp.meta.nnzh)) .= nlp.nlcon.hess_cols
280+
if (nlp.obj.type == "NONLINEAR") || (nlp.meta.nnln > nlp.quadcon.nquad)
281+
view(rows, (nlp.obj.nnzh + nlp.quadcon.nnzh + 1):(nlp.meta.nnzh)) .= nlp.nlcon.hess_rows
282+
view(cols, (nlp.obj.nnzh + nlp.quadcon.nnzh + 1):(nlp.meta.nnzh)) .= nlp.nlcon.hess_cols
283+
end
284+
if nlp.quadcon.nquad > 0
285+
index = nlp.obj.nnzh
286+
for i = 1:(nlp.quadcon.nquad)
287+
qcon = nlp.quadcon.constraints[i]
288+
view(rows, (index + 1):(index + qcon.nnzh)) .= qcon.A.rows
289+
view(cols, (index + 1):(index + qcon.nnzh)) .= qcon.A.cols
290+
index += qcon.nnzh
291+
end
213292
end
214293
return rows, cols
215294
end
@@ -225,15 +304,23 @@ function NLPModels.hess_coord!(
225304
if nlp.obj.type == "QUADRATIC"
226305
view(vals, 1:(nlp.obj.nnzh)) .= obj_weight .* nlp.obj.hessian.vals
227306
end
228-
if (nlp.obj.type == "NONLINEAR") || (nlp.meta.nnln > 0)
229-
MOI.eval_hessian_lagrangian(
230-
nlp.eval,
231-
view(vals, (nlp.obj.nnzh + 1):(nlp.meta.nnzh)),
307+
if (nlp.obj.type == "NONLINEAR") || (nlp.meta.nnln > nlp.quadcon.nquad)
308+
λ = view(y, (nlp.meta.nlin + nlp.quadcon.nquad + 1):(nlp.meta.ncon))
309+
MOI.eval_hessian_lagrangian(nlp.eval,
310+
view(vals, (nlp.obj.nnzh + nlp.quadcon.nnzh + 1):(nlp.meta.nnzh)),
232311
x,
233312
obj_weight,
234-
view(y, nlp.meta.nln),
313+
λ
235314
)
236315
end
316+
if nlp.quadcon.nquad > 0
317+
index = nlp.obj.nnzh
318+
for i = 1:(nlp.quadcon.nquad)
319+
qcon = nlp.quadcon.constraints[i]
320+
view(vals, (index + 1):(index + qcon.nnzh)) .= y[nlp.meta.nlin + i] .* qcon.A.vals
321+
index += qcon.nnzh
322+
end
323+
end
237324
return vals
238325
end
239326

@@ -252,7 +339,9 @@ function NLPModels.hess_coord!(
252339
view(vals, (nlp.obj.nnzh + 1):(nlp.meta.nnzh)) .= 0.0
253340
end
254341
if nlp.obj.type == "NONLINEAR"
255-
MOI.eval_hessian_lagrangian(nlp.eval, vals, x, obj_weight, zeros(nlp.meta.nnln))
342+
view(vals, 1:(nlp.obj.nnzh + nlp.quadcon.nnzh)) .= 0.0
343+
ind_nnln = (nlp.obj.nnzh + nlp.quadcon.nnzh + 1):(nlp.meta.nnzh)
344+
MOI.eval_hessian_lagrangian(nlp.eval, view(vals, ind_nnln), x, obj_weight, nlp.λ)
256345
end
257346
return vals
258347
end
@@ -269,19 +358,20 @@ function NLPModels.hprod!(
269358
if (nlp.obj.type == "LINEAR") && (nlp.meta.nnln == 0)
270359
hv .= 0.0
271360
end
272-
if (nlp.obj.type == "NONLINEAR") || (nlp.meta.nnln > 0)
273-
MOI.eval_hessian_lagrangian_product(nlp.eval, hv, x, v, obj_weight, view(y, nlp.meta.nln))
361+
if (nlp.obj.type == "NONLINEAR") || (nlp.meta.nnln > nlp.quadcon.nquad)
362+
λ = view(y, (nlp.meta.nlin + nlp.quadcon.nquad + 1):(nlp.meta.ncon))
363+
MOI.eval_hessian_lagrangian_product(nlp.eval, hv, x, v, obj_weight, λ)
274364
end
275365
if nlp.obj.type == "QUADRATIC"
276-
nlp.meta.nnln == 0 && (hv .= 0.0)
277-
coo_sym_add_mul!(
278-
nlp.obj.hessian.rows,
279-
nlp.obj.hessian.cols,
280-
nlp.obj.hessian.vals,
281-
v,
282-
hv,
283-
obj_weight,
284-
)
366+
(nlp.meta.nnln == nlp.quadcon.nquad) && (hv .= 0.0)
367+
coo_sym_add_mul!(nlp.obj.hessian.rows, nlp.obj.hessian.cols, nlp.obj.hessian.vals, v, hv, obj_weight)
368+
end
369+
if nlp.quadcon.nquad > 0
370+
(nlp.obj.type == "LINEAR") && (hv .= 0.0)
371+
for i = 1:(nlp.quadcon.nquad)
372+
qcon = nlp.quadcon.constraints[i]
373+
coo_sym_add_mul!(qcon.A.rows, qcon.A.cols, qcon.A.vals, v, hv, y[nlp.meta.nlin + i])
374+
end
285375
end
286376
return hv
287377
end
@@ -302,7 +392,7 @@ function NLPModels.hprod!(
302392
coo_sym_add_mul!(nlp.obj.hessian.rows, nlp.obj.hessian.cols, nlp.obj.hessian.vals, v, hv, obj_weight)
303393
end
304394
if nlp.obj.type == "NONLINEAR"
305-
MOI.eval_hessian_lagrangian_product(nlp.eval, hv, x, v, obj_weight, zeros(nlp.meta.nnln))
395+
MOI.eval_hessian_lagrangian_product(nlp.eval, hv, x, v, obj_weight, nlp.λ)
306396
end
307397
return hv
308398
end

0 commit comments

Comments
 (0)