Skip to content

Commit 736e4fd

Browse files
geoffroylecontedpo
authored andcommitted
convert qp, dispatch
1 parent 0bb5d9b commit 736e4fd

File tree

3 files changed

+147
-33
lines changed

3 files changed

+147
-33
lines changed

src/qpmodel.jl

Lines changed: 131 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,12 @@ abstract type AbstractQuadraticModel{T, S} <: AbstractNLPModel{T, S} end
2929
3030
qp = QuadraticModel(c, H; A = A, lcon = lcon, ucon = ucon, lvar = lvar, uvar = uvar, coo_matrices = true)
3131
32-
Create a Quadratic model ``min ~\\tfrac{1}{2} x^T Q x + c^T x + c_0`` with optional bounds
32+
Create a Quadratic model ``min ~\\tfrac{1}{2} x^T H x + c^T x + c_0`` with optional bounds
3333
`lvar ≦ x ≦ uvar` and optional linear constraints `lcon ≦ Ax ≦ ucon`.
34+
The user should only give the lower triangle of `H` to the `QuadraticModel` constructor.
3435
3536
With the first constructor, if `sortcols = true`, then `Hcols` and `Acols` are sorted in ascending order
3637
(`Hrows`, `Hvals` and `Arows`, `Avals` are then sorted accordingly).
37-
With the second constructor, if `coo_matrices = true`, `H` and/or `A` will be converted to SparseMatricesCOO
38-
(this will be ignored if they already are SparseMatricesCOO).
3938
4039
You can also use [`QPSReader.jl`](https://github.com/JuliaSmoothOptimizers/QPSReader.jl) to
4140
create a Quadratic model from a QPS file:
@@ -65,6 +64,11 @@ mutable struct QuadraticModel{T, S, M1, M2} <: AbstractQuadraticModel{T, S}
6564
data::QPData{T, S, M1, M2}
6665
end
6766

67+
function Base.convert(::Type{QuadraticModel{T, S, Mconv, Mconv}}, qm::QuadraticModel{T, S, M1, M2}) where {T, S, M1 <: AbstractMatrix, M2 <: AbstractMatrix, Mconv}
68+
data_conv = convert(QPData{T, S, Mconv, Mconv}, qm.data)
69+
return QuadraticModel(qm.meta, qm.counters, data_conv)
70+
end
71+
6872
function QuadraticModel(
6973
c::S,
7074
Hrows::AbstractVector{<:Integer},
@@ -140,7 +144,6 @@ function QuadraticModel(
140144
lvar::S = fill!(S(undef, length(c)), T(-Inf)),
141145
uvar::S = fill!(S(undef, length(c)), T(Inf)),
142146
c0::T = zero(T),
143-
coo_matrices = true,
144147
kwargs...,
145148
) where {T, S}
146149
ncon, nvar = size(A)
@@ -149,25 +152,9 @@ function QuadraticModel(
149152
nnzj = 0
150153
data = QPData(c0, c, H, A)
151154
else
152-
if coo_matrices
153-
if typeof(H) <: Symmetric && !(typeof(H.data) <: SparseMatrixCOO)
154-
tril!(H.data)
155-
HCOO = SparseMatrixCOO(H.data)
156-
elseif !(typeof(H) <: SparseMatrixCOO)
157-
tril!(H)
158-
HCOO = SparseMatrixCOO(H)
159-
else
160-
HCOO = H
161-
end
162-
ACOO = !(typeof(A) <: SparseMatrixCOO) ? SparseMatrixCOO(A) : ACOO = A
163-
nnzh = nnz(HCOO)
164-
nnzj = nnz(ACOO)
165-
data = QPData(c0, c, HCOO, ACOO)
166-
else
167-
nnzh = typeof(H) <: DenseMatrix ? nvar * (nvar + 1) / 2 : nnz(H)
168-
nnzj = nnz(A)
169-
data = QPData(c0, c, H, A)
170-
end
155+
nnzh = typeof(H) <: DenseMatrix ? nvar * (nvar + 1) / 2 : nnz(H)
156+
nnzj = nnz(A)
157+
data = typeof(H) <: Symmetric ? QPData(c0, c, H.data, A) : QPData(c0, c, H, A)
171158
end
172159

173160
QuadraticModel(
@@ -271,6 +258,48 @@ function NLPModels.hess_structure!(
271258
return rows, cols
272259
end
273260

261+
function fill_structure!(S::SparseMatrixCSC, rows, cols)
262+
count = 1
263+
@inbounds for col = 1 : size(S, 2), k = S.colptr[col] : (S.colptr[col+1]-1)
264+
rows[count] = S.rowval[k]
265+
cols[count] = col
266+
count += 1
267+
end
268+
end
269+
270+
function fill_coord!(S::SparseMatrixCSC, vals, obj_weight)
271+
count = 1
272+
@inbounds for col = 1 : size(S, 2), k = S.colptr[col] : (S.colptr[col+1]-1)
273+
vals[count] = obj_weight * S.nzval[k]
274+
count += 1
275+
end
276+
end
277+
278+
function NLPModels.hess_structure!(
279+
qp::QuadraticModel{T, S, M1},
280+
rows::AbstractVector{<:Integer},
281+
cols::AbstractVector{<:Integer},
282+
) where {T, S, M1 <: SparseMatrixCSC}
283+
fill_structure!(qp.data.H, rows, cols)
284+
return rows, cols
285+
end
286+
287+
function NLPModels.hess_structure!(
288+
qp::QuadraticModel{T, S, M1},
289+
rows::AbstractVector{<:Integer},
290+
cols::AbstractVector{<:Integer},
291+
) where {T, S, M1 <: Matrix}
292+
count = 1
293+
for j=1:qp.meta.nvar
294+
for i=j:qp.meta.nvar
295+
rows[count] = i
296+
cols[count] = j
297+
count += 1
298+
end
299+
end
300+
return rows, cols
301+
end
302+
274303
function NLPModels.hess_coord!(
275304
qp::QuadraticModel{T, S, M1},
276305
x::AbstractVector{T},
@@ -282,6 +311,34 @@ function NLPModels.hess_coord!(
282311
return vals
283312
end
284313

314+
function NLPModels.hess_coord!(
315+
qp::QuadraticModel{T, S, M1},
316+
x::AbstractVector{T},
317+
vals::AbstractVector{T};
318+
obj_weight::Real = one(eltype(x)),
319+
) where {T, S, M1 <: SparseMatrixCSC}
320+
NLPModels.increment!(qp, :neval_hess)
321+
fill_coord!(qp.data.H, vals, obj_weight)
322+
return vals
323+
end
324+
325+
function NLPModels.hess_coord!(
326+
qp::QuadraticModel{T, S, M1},
327+
x::AbstractVector{T},
328+
vals::AbstractVector{T};
329+
obj_weight::Real = one(eltype(x)),
330+
) where {T, S, M1 <: Matrix}
331+
NLPModels.increment!(qp, :neval_hess)
332+
count = 1
333+
for j=1:qp.meta.nvar
334+
for i=j:qp.meta.nvar
335+
vals[count] = obj_weight * qp.data.H[i,j]
336+
count += 1
337+
end
338+
end
339+
return vals
340+
end
341+
285342
NLPModels.hess_coord!(
286343
qp::QuadraticModel,
287344
x::AbstractVector,
@@ -300,6 +357,31 @@ function NLPModels.jac_structure!(
300357
return rows, cols
301358
end
302359

360+
function NLPModels.jac_structure!(
361+
qp::QuadraticModel{T, S, M1, M2},
362+
rows::AbstractVector{<:Integer},
363+
cols::AbstractVector{<:Integer},
364+
) where {T, S, M1, M2 <: SparseMatrixCSC}
365+
fill_structure!(qp.data.A, rows, cols)
366+
return rows, cols
367+
end
368+
369+
function NLPModels.jac_structure!(
370+
qp::QuadraticModel{T, S, M1, M2},
371+
rows::AbstractVector{<:Integer},
372+
cols::AbstractVector{<:Integer},
373+
) where {T, S, M1, M2 <: DenseMatrix}
374+
count = 1
375+
for j=1:qp.meta.nvar
376+
for i=1:qp.meta.ncon
377+
rows[count] = i
378+
cols[count] = j
379+
count += 1
380+
end
381+
end
382+
return rows, cols
383+
end
384+
303385
function NLPModels.jac_coord!(
304386
qp::QuadraticModel{T, S, M1, M2},
305387
x::AbstractVector,
@@ -310,6 +392,32 @@ function NLPModels.jac_coord!(
310392
return vals
311393
end
312394

395+
function NLPModels.jac_coord!(
396+
qp::QuadraticModel{T, S, M1, M2},
397+
x::AbstractVector,
398+
vals::AbstractVector
399+
) where {T, S, M1, M2 <: SparseMatrixCSC}
400+
NLPModels.increment!(qp, :neval_jac)
401+
fill_coord!(qp.data.H, vals, one(T))
402+
return vals
403+
end
404+
405+
function NLPModels.jac_coord!(
406+
qp::QuadraticModel{T, S, M1, M2},
407+
x::AbstractVector,
408+
vals::AbstractVector
409+
) where {T, S, M1, M2 <: DenseMatrix}
410+
NLPModels.increment!(qp, :neval_jac)
411+
count = 1
412+
for j=1:qp.meta.nvar
413+
for i=1:qp.meta.ncon
414+
vals[count] = qp.data.A[i, j]
415+
count += 1
416+
end
417+
end
418+
return vals
419+
end
420+
313421
function NLPModels.cons!(qp::AbstractQuadraticModel, x::AbstractVector, c::AbstractVector)
314422
NLPModels.increment!(qp, :neval_cons)
315423
mul!(c, qp.data.A, x)

test/runtests.jl

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ function testSM(sm) # test function for a specific problem
4646

4747
@test all(lvar_sm_true .== sm.meta.lvar)
4848
@test all(uvar_sm_true .== sm.meta.uvar)
49-
@test all(sparse(sm.data.A.rows, sm.data.A.cols, sm.data.A.vals, 2, 5) .== A_sm_true)
50-
@test all(sparse(sm.data.H.rows, sm.data.H.cols, sm.data.H.vals, 5, 5) .== H_sm_true)
49+
@test all(sparse(sm.data.A.rows, sm.data.A.cols, sm.data.A.vals, 2, 5) . A_sm_true)
50+
@test all(sparse(sm.data.H.rows, sm.data.H.cols, sm.data.H.vals, 5, 5) . H_sm_true)
5151
end
5252

5353
@testset "SlackModel" begin
@@ -67,8 +67,8 @@ end
6767
T = eltype(c)
6868
qp = QuadraticModel(
6969
c,
70-
sparse(H),
71-
A = A,
70+
SparseMatrixCOO(tril(H)),
71+
A = SparseMatrixCOO(A),
7272
lcon = [-3.0; -4.0],
7373
ucon = [-2.0; Inf],
7474
lvar = l,
@@ -83,7 +83,7 @@ end
8383
testSM(qp)
8484
end
8585

86-
@testset "dense QP" begin
86+
@testset "dense QP, convert" begin
8787
H = [
8888
6.0 2.0 1.0
8989
2.0 5.0 2.0
@@ -109,11 +109,17 @@ end
109109
uvar = u,
110110
c0 = 0.0,
111111
name = "QM1",
112-
coo_matrices = false,
113112
)
114113

115114
smdense = SlackModel(qpdense)
116115
testSM(smdense)
116+
117+
qpcoo = convert(
118+
QuadraticModel{T, Vector{T}, SparseMatrixCOO{T, Int}, SparseMatrixCOO{T, Int}},
119+
qpdense
120+
)
121+
@test typeof(qpcoo.data.H) <: SparseMatrixCOO
122+
@test typeof(qpcoo.data.A) <: SparseMatrixCOO
117123
end
118124

119125
@testset "sort cols COO" begin
@@ -149,7 +155,7 @@ end
149155
@testset "LinearOperators" begin
150156
nvar, ncon = 10, 7
151157
T = Float64
152-
H = Symmetric(tril!(sprand(T, nvar, nvar, 0.3)), :L)
158+
H = Symmetric(tril(sprand(T, nvar, nvar, 0.3)), :L)
153159
A = sprand(T, ncon, nvar, 0.4)
154160
c = rand(nvar)
155161
lvar = fill(-Inf, nvar)
@@ -159,7 +165,7 @@ end
159165
qp = QuadraticModel(
160166
c,
161167
SparseMatrixCOO(H.data),
162-
A = A,
168+
A = SparseMatrixCOO(A),
163169
lcon = lcon,
164170
ucon = ucon,
165171
lvar = lvar,

test/test_presolve.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
T = eltype(c)
1616
qp = QuadraticModel(
1717
c,
18-
sparse(H),
19-
A = A,
18+
SparseMatrixCOO(tril(H)),
19+
A = SparseMatrixCOO(A),
2020
lcon = [-3.0; -4.0],
2121
ucon = [-2.0; Inf],
2222
lvar = l,

0 commit comments

Comments
 (0)