Skip to content

Commit 536e263

Browse files
committed
[WIP] Quadratic constraints
1 parent c17a06b commit 536e263

File tree

3 files changed

+165
-18
lines changed

3 files changed

+165
-18
lines changed

src/moi_nlp_model.jl

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ mutable struct MathOptNLPModel <: AbstractNLPModel{Float64, Vector{Float64}}
44
meta::NLPModelMeta{Float64, Vector{Float64}}
55
eval::Union{MOI.AbstractNLPEvaluator, Nothing}
66
lincon::LinearConstraints
7+
quadcon::Vector{QuadraticConstraint}
8+
nquad::Int
9+
nnln::Int
710
obj::Objective
811
counters::Counters
912
end
@@ -33,19 +36,22 @@ function MathOptNLPModel(jmodel::JuMP.Model; hessian::Bool = true, name::String
3336
(nnln == 0 ? 0 : sum(length(nl_con.hess_I) for nl_con in eval.constraints)) : 0
3437

3538
moimodel = backend(jmodel)
36-
nlin, lincon, lin_lcon, lin_ucon = parser_MOI(moimodel)
39+
nlin, lincon, lin_lcon, lin_ucon, nquad, quadcon, quad_lcon, quad_ucon = parser_MOI(moimodel, nvar)
40+
41+
42+
quad_nnzh = nquad == 0 ? 0 : sum(length(quadcon[i].vals) for i = 1 : nquad)
3743

3844
if (eval nothing) && eval.has_nlobj
3945
obj = Objective("NONLINEAR", 0.0, spzeros(Float64, nvar), COO(), 0)
4046
else
4147
obj = parser_objective_MOI(moimodel, nvar)
4248
end
4349

44-
ncon = nlin + nnln
45-
lcon = vcat(lin_lcon, nl_lcon)
46-
ucon = vcat(lin_ucon, nl_ucon)
47-
nnzj = lincon.nnzj + nl_nnzj
48-
nnzh = obj.nnzh + nl_nnzh
50+
ncon = nlin + nquad + nnln
51+
lcon = vcat(lin_lcon, quad_lcon, nl_lcon)
52+
ucon = vcat(lin_ucon, quad_ucon, nl_ucon)
53+
nnzj = lincon.nnzj + ... + nl_nnzj
54+
nnzh = obj.nnzh + quad_nnzh + nl_nnzh
4955

5056
meta = NLPModelMeta(
5157
nvar,
@@ -60,11 +66,11 @@ function MathOptNLPModel(jmodel::JuMP.Model; hessian::Bool = true, name::String
6066
nnzh = nnzh,
6167
lin = collect(1:nlin),
6268
minimize = objective_sense(jmodel) == MOI.MIN_SENSE,
63-
islp = (obj.type == "LINEAR") && (nnln == 0),
69+
islp = (obj.type == "LINEAR") && (nnln == 0) && (nquad == 0),
6470
name = name,
6571
)
6672

67-
return MathOptNLPModel(meta, eval, lincon, obj, Counters())
73+
return MathOptNLPModel(meta, eval, lincon, quadcon, nquad, obj, Counters())
6874
end
6975

7076
function NLPModels.obj(nlp::MathOptNLPModel, x::AbstractVector)
@@ -110,6 +116,10 @@ function NLPModels.cons!(nlp::MathOptNLPModel, x::AbstractVector, c::AbstractVec
110116
view(c, nlp.meta.lin),
111117
)
112118
end
119+
for i = 1 : nlp.nquad
120+
qcon = nlp.quadcon[i]
121+
c[nlp.meta.lin + i] = 0.5 * coo_sym_dot(qcon.hessian.rows, qcon.hessian.cols, qcon.hessian.vals, x, x) + dot(qcon.b, x)
122+
end
113123
if nlp.meta.nnln > 0
114124
MOI.eval_constraint(nlp.eval, view(c, nlp.meta.nln), x)
115125
end

src/moi_nls_model.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function MathOptNLSModel(cmodel::JuMP.Model, F; hessian::Bool = true, name::Stri
4040
(nnln == 0 ? 0 : sum(length(con.hess_I) for con in ceval.constraints)) : 0
4141

4242
moimodel = backend(cmodel)
43-
nlin, lincon, lin_lcon, lin_ucon = parser_MOI(moimodel)
43+
nlin, lincon, lin_lcon, lin_ucon, nquad, quadcon, quad_lcon, quad_ucon = parser_MOI(moimodel, nvar)
4444

4545
nequ = nlinequ + nnlnequ
4646
Fnnzj = linequ.nnzj + nl_Fnnzj

src/utils.jl

Lines changed: 146 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,18 @@ import NLPModels.increment!, NLPModels.decrement!
66
using JuMP, MathOptInterface
77
const MOI = MathOptInterface
88

9+
# VariableIndex
10+
const VI = MOI.VariableIndex # VariableIndex(value)
11+
912
# ScalarAffineFunctions and VectorAffineFunctions
10-
const SAF = MOI.ScalarAffineFunction{Float64}
11-
const VAF = MOI.VectorAffineFunction{Float64}
12-
const AF = Union{SAF, VAF}
13+
const SAF = MOI.ScalarAffineFunction{Float64} # ScalarAffineFunction{T}(terms, constant)
14+
const VAF = MOI.VectorAffineFunction{Float64} # VectorAffineFunction{T}(terms, constants)
15+
const AF = Union{SAF, VAF}
16+
17+
# ScalarQuadraticFunctions and VectorQuadraticFunctions
18+
const SQF = MOI.ScalarQuadraticFunction{Float64} # ScalarQuadraticFunction{T}(affine_terms, quadratic_terms, constant)
19+
const VQF = MOI.VectorQuadraticFunction{Float64} # VectorQuadraticFunction{T}(affine_terms, quadratic_terms, constants)
20+
const QF = Union{SQF, VQF}
1321

1422
# AffLinSets and VecLinSets
1523
const ALS = Union{
@@ -21,10 +29,11 @@ const ALS = Union{
2129
const VLS = Union{MOI.Nonnegatives, MOI.Nonpositives, MOI.Zeros}
2230
const LS = Union{ALS, VLS}
2331

32+
# Objective
2433
const VI = MOI.VariableIndex
25-
const SQF = MOI.ScalarQuadraticFunction{Float64}
2634
const OBJ = Union{VI, SAF, SQF}
2735

36+
# Coordinate Matrix
2837
mutable struct COO
2938
rows::Vector{Int}
3039
cols::Vector{Int}
@@ -38,6 +47,21 @@ mutable struct LinearConstraints
3847
nnzj::Int
3948
end
4049

50+
mutable struct QuadraticConstraint
51+
hessian::COO
52+
b::SparseVector{Float64}
53+
end
54+
55+
mutable struct QuadraticConstraints
56+
qcons::Vector{QuadraticConstraint}
57+
nnzj::Int
58+
jrows::Vector{Int}
59+
jcols::Vector{Int}
60+
nnzh::Int
61+
hrows::Vector{Int}
62+
hcols::Vector{Int}
63+
end
64+
4165
mutable struct LinearEquations
4266
jacobian::COO
4367
constants::Vector{Float64}
@@ -96,6 +120,52 @@ function coo_sym_dot(
96120
return xᵀAy
97121
end
98122

123+
"""
124+
jacobian_quad(qcons)
125+
126+
`qcons` is a vector of `QuadraticConstraint` where each constraint has the form ½xᵀQᵢx + xᵀbᵢ.
127+
Compute the sparcity pattern of the jacobian [Q₁x + b₁; ...; Qₚx + bₚ]ᵀ of `qcons`.
128+
"""
129+
function jacobian_quad(qcons)
130+
jrows = Int[]
131+
jcols = Int[]
132+
nquad = length(qcons)
133+
for i = 1 : nquad
134+
# rows of Qᵢx + bᵢ with nonzeros coefficients
135+
vec = unique(con.hessian.rows con.b.nzind)
136+
for elt vec
137+
push!(elt, jcols)
138+
push!(i, jrows)
139+
end
140+
end
141+
nnzj = length(jrows)
142+
return nnzj, jrows, jcols
143+
end
144+
145+
"""
146+
hessian_quad(qcons)
147+
148+
`qcons` is a vector of `QuadraticConstraint` where each constraint has the form ½xᵀQᵢx + xᵀbᵢ.
149+
Compute the sparcity pattern of the hessian ΣᵢQᵢ of `qcons`.
150+
"""
151+
function hessian_quad(qcons)
152+
set = Set{Tuple{Int,Int}}()
153+
for con qcons
154+
for tuple zip(con.rows, con.vals)
155+
# Only disctinct tuples are stored in the set
156+
push!(tuple, set)
157+
end
158+
end
159+
nnzh = length(set)
160+
hrows = zeros(Int, nnzh)
161+
hcols = zeros(Int, nnzh)
162+
for (index,tuple) in enumerate(set)
163+
hrows[index] = tuple[1]
164+
hcols[index] = tuple[2]
165+
end
166+
return nnzh, hrows, hcols
167+
end
168+
99169
"""
100170
parser_SAF(fun, set, linrows, lincols, linvals, nlin, lin_lcon, lin_ucon)
101171
@@ -157,11 +227,64 @@ function parser_VAF(fun, set, linrows, lincols, linvals, nlin, lin_lcon, lin_uco
157227
end
158228

159229
"""
160-
parser_MOI(moimodel)
230+
parser_SQF(fun, set, qcons, quad_lcon, quad_ucon)
161231
162-
Parse linear constraints of a `MOI.ModelLike`.
232+
Parse a `ScalarQuadraticFunction` fun with its associated set.
233+
`qcons`, `quad_lcon`, `quad_ucon` are updated.
163234
"""
164-
function parser_MOI(moimodel)
235+
function parser_SQF(fun, set, nvar, qcons, quad_lcon, quad_ucon)
236+
237+
b = spzeros(Float64, nvar)
238+
rows = Int[]
239+
cols = Int[]
240+
vals = Float64[]
241+
242+
# Parse a ScalarAffineTerm{Float64}(coefficient, variable_index)
243+
for term in fun.affine_terms
244+
b[term.variable.value] = term.coefficient
245+
end
246+
247+
# Parse a ScalarQuadraticTerm{Float64}(coefficient, variable_index_1, variable_index_2)
248+
for term in fun.quadratic_terms
249+
i = term.variable_1.value
250+
j = term.variable_2.value
251+
if i j
252+
push!(rows, i)
253+
push!(cols, j)
254+
else
255+
push!(cols, j)
256+
push!(rows, i)
257+
end
258+
push!(vals, term.coefficient)
259+
end
260+
261+
if typeof(set) in (MOI.Interval{Float64}, MOI.GreaterThan{Float64})
262+
push!(quad_lcon, -fun.constant + set.lower)
263+
elseif typeof(set) == MOI.EqualTo{Float64}
264+
push!(quad_lcon, -fun.constant + set.value)
265+
else
266+
push!(quad_lcon, -Inf)
267+
end
268+
269+
if typeof(set) in (MOI.Interval{Float64}, MOI.LessThan{Float64})
270+
push!(quad_ucon, -fun.constant + set.upper)
271+
elseif typeof(set) == MOI.EqualTo{Float64}
272+
push!(quad_ucon, -fun.constant + set.value)
273+
else
274+
push!(quad_ucon, Inf)
275+
end
276+
277+
nnzh = length(vals)
278+
qcon = QuadraticConstraint(COO[rows, cols, vals], b)
279+
push!(qcons, qcon)
280+
end
281+
282+
"""
283+
parser_MOI(moimodel, nvar)
284+
285+
Parse linear and quadratic constraints of a `MOI.ModelLike`.
286+
"""
287+
function parser_MOI(moimodel, nvar)
165288

166289
# Variables associated to linear constraints
167290
nlin = 0
@@ -171,10 +294,16 @@ function parser_MOI(moimodel)
171294
lin_lcon = Float64[]
172295
lin_ucon = Float64[]
173296

297+
# Variables associated to quadratic constraints
298+
nquad = 0
299+
qcons = QuadraticConstraint[]
300+
quad_lcon = Float64[]
301+
quad_ucon = Float64[]
302+
174303
contypes = MOI.get(moimodel, MOI.ListOfConstraintTypesPresent())
175304
for (F, S) in contypes
176305
F == VI && continue
177-
F <: AF || @warn("Function $F is not supported.")
306+
F <: AF || F <: SQF || @warn("Function $F is not supported.")
178307
S <: LS || @warn("Set $S is not supported.")
179308

180309
conindices = MOI.get(moimodel, MOI.ListOfConstraintIndices{F, S}())
@@ -189,13 +318,21 @@ function parser_MOI(moimodel)
189318
parser_VAF(fun, set, linrows, lincols, linvals, nlin, lin_lcon, lin_ucon)
190319
nlin += set.dimension
191320
end
321+
if typeof(fun) <: SQF
322+
parser_SQF(fun, set, nvar, qcons, quad_lcon, quad_ucon)
323+
nquad += 1
324+
end
192325
end
193326
end
194327
coo = COO(linrows, lincols, linvals)
195328
nnzj = length(linvals)
196329
lincon = LinearConstraints(coo, nnzj)
197330

198-
return nlin, lincon, lin_lcon, lin_ucon
331+
nnzj, jrows, jcols = jacobian_quad(qcons)
332+
nnzh, hrows, hcols = hessian_quad(qcons)
333+
quadcon = QuadraticConstraints(qcons, nnzj, jrows, jcols, nnzh, hrows, hcols)
334+
335+
return nlin, lincon, lin_lcon, lin_ucon, nquad, quadcon, quad_lcon, quad_ucon
199336
end
200337

201338
"""

0 commit comments

Comments
 (0)