Skip to content

Commit 8f85f78

Browse files
QPDataLinOP
1 parent 97f75be commit 8f85f78

File tree

2 files changed

+203
-110
lines changed

2 files changed

+203
-110
lines changed

src/qpmodel.jl

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,14 @@ mutable struct QPDataDense{T, S, M1 <: AbstractMatrix{T}, M2 <: AbstractMatrix{T
2121
A::M2
2222
end
2323

24-
function get_QPDataCOO(c0::T, c::S, H::SparseMatrixCSC{T}, A::AbstractMatrix{T}) where {T, S}
24+
mutable struct QPDataLinOp{T, S, L1 <: AbstractLinearOperator{T}, L2 <: AbstractLinearOperator{T}} <: AbstractQPData{T, S}
25+
c0::T
26+
c::S
27+
H::L1
28+
A::L2
29+
end
30+
31+
function get_QPDataCOO(c0::T, c ::S, H::SparseMatrixCSC{T}, A::AbstractMatrix{T}) where {T, S}
2532
ncon, nvar = size(A)
2633
tril!(H)
2734
nnzh, Hrows, Hcols, Hvals = nnz(H), findnz(H)...
@@ -143,8 +150,8 @@ end
143150

144151
function QuadraticModel(
145152
c::S,
146-
H::AbstractMatrix{T};
147-
A::AbstractMatrix = similar(c, 0, length(c)),
153+
H::Union{AbstractMatrix{T}, AbstractLinearOperator{T}};
154+
A::Union{AbstractMatrix, AbstractLinearOperator} = similar(c, 0, length(c)),
148155
lcon::S = S(undef, 0),
149156
ucon::S = S(undef, 0),
150157
lvar::S = fill!(S(undef, length(c)), T(-Inf)),
@@ -153,7 +160,11 @@ function QuadraticModel(
153160
kwargs...,
154161
) where {T, S}
155162
ncon, nvar = size(A)
156-
if issparse(H)
163+
if typeof(H) <: AbstractLinearOperator # convert A to a LinOp if A is a Matrix?
164+
nnzh = 0
165+
nnzj = 0
166+
data = QPDataLinOp(c0, c, H, A)
167+
elseif issparse(H)
157168
data, nnzh, nnzj = get_QPDataCOO(c0, c, H, A)
158169
else
159170
nnzh = typeof(H) <: DenseMatrix ? nvar * (nvar + 1) / 2 : nnz(H)
@@ -232,6 +243,8 @@ function NLPModels.objgrad!(qp::AbstractQuadraticModel, x::AbstractVector, g::Ab
232243
NLPModels.increment!(qp, :neval_grad)
233244
if typeof(qp.data) <: QPDataCOO
234245
coo_sym_prod!(qp.data.Hrows, qp.data.Hcols, qp.data.Hvals, x, g)
246+
elseif typeof(qp.data) <: QPDataLinOp
247+
mul!(g, qp.data.H, x)
235248
else
236249
mul!(g, Symmetric(qp.data.H, :L), x)
237250
end
@@ -245,6 +258,8 @@ function NLPModels.obj(qp::AbstractQuadraticModel{T, S}, x::AbstractVector) wher
245258
Hx = fill!(S(undef, qp.meta.nvar), zero(T))
246259
if typeof(qp.data) <: QPDataCOO
247260
coo_sym_prod!(qp.data.Hrows, qp.data.Hcols, qp.data.Hvals, x, Hx)
261+
elseif typeof(qp.data) <: QPDataLinOp
262+
mul!(Hx, qp.data.H, x)
248263
else
249264
mul!(Hx, Symmetric(qp.data.H, :L), x)
250265
end
@@ -255,6 +270,8 @@ function NLPModels.grad!(qp::AbstractQuadraticModel, x::AbstractVector, g::Abstr
255270
NLPModels.increment!(qp, :neval_grad)
256271
if typeof(qp.data) <: QPDataCOO
257272
coo_sym_prod!(qp.data.Hrows, qp.data.Hcols, qp.data.Hvals, x, g)
273+
elseif typeof(qp.data) <: QPDataLinOp
274+
mul!(g, qp.data.H, x)
258275
else
259276
mul!(g, Symmetric(qp.data.H, :L), x)
260277
end
@@ -366,6 +383,8 @@ function NLPModels.hprod!(
366383
NLPModels.increment!(qp, :neval_hprod)
367384
if typeof(qp.data) <: QPDataCOO
368385
coo_sym_prod!(qp.data.Hrows, qp.data.Hcols, qp.data.Hvals, v, Hv)
386+
elseif typeof(qp.data) <: QPDataLinOp
387+
mul!(Hv, qp.data.H, v)
369388
else
370389
mul!(Hv, Symmetric(qp.data.H, :L), v)
371390
end
@@ -443,17 +462,43 @@ function slackdata(data::QPDataCOO{T}, meta::NLPModelMeta{T}, ns::Int) where {T}
443462
)
444463
end
445464

465+
function prodPermutedMinusOnes!(res, v, α, β::T, p::Vector{Int}) where T
466+
res .= β == zero(T) ? zero(T) : β .* res
467+
res[p] .-= α .* v
468+
return res
469+
end
470+
function tprodPermutedMinusOnes!(res, v, α, β::T, p::Vector{Int}) where T
471+
res .= β == zero(T) ? zero(T) : β .* res
472+
res .-= @views α .* v[p]
473+
return res
474+
end
475+
476+
function opPermutedMinusOnes(T::DataType, ncon::Int, ns::Int, p::Vector{Int})
477+
prod! = (res, v, α, β) -> prodPermutedMinusOnes!(res, v, α, β, p)
478+
tprod! = (res, v, α, β) -> tprodPermutedMinusOnes!(res, v, α, β, p)
479+
return LinearOperator(T, ncon, ns, false, false, prod!, tprod!)
480+
end
481+
482+
function slackdata(data::QPDataLinOp{T}, meta::NLPModelMeta{T}, ns::Int) where {T}
483+
return QPDataLinOp(
484+
copy(data.c0),
485+
[data.c; fill!(similar(data.c, ns), zero(T))],
486+
BlockDiagonalOperator(data.H, opZeros(T, ns, ns)),
487+
[data.A opPermutedMinusOnes(T, meta.ncon, ns, [meta.jlow; meta.jupp; meta.jrng])],
488+
)
489+
end
490+
446491
function NLPModelsModifiers.SlackModel(qp::AbstractQuadraticModel, name = qp.meta.name * "-slack")
447492
qp.meta.ncon == length(qp.meta.jfix) && return qp
448493
nfix = length(qp.meta.jfix)
449494
ns = qp.meta.ncon - nfix
450495
T = eltype(qp.data.c)
451496

452-
if typeof(qp.data) <: QPDataCOO
453-
data = slackdata(qp.data, qp.meta, ns)
454-
elseif typeof(qp.data) <: QPDataDense # convert to QPDataCOO first
497+
if typeof(qp.data) <: QPDataDense # convert to QPDataCOO first
455498
dataCOO, nnzj, nnzh = get_QPDataCOO(qp.data.c0, qp.data.c, qp.data.H, qp.data.A)
456499
data = slackdata(dataCOO, qp.meta, ns)
500+
else
501+
data = slackdata(qp.data, qp.meta, ns)
457502
end
458503

459504
meta = NLPModelsModifiers.slack_meta(qp.meta, name = qp.meta.name)

test/runtests.jl

Lines changed: 151 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -5,124 +5,172 @@ using LinearAlgebra, Printf, SparseArrays, Test
55
using ADNLPModels,
66
LinearOperators, NLPModels, NLPModelsModifiers, NLPModelsTest, QPSReader, QuadraticModels
77

8-
@testset "test utils" begin
9-
A = rand(10, 10)
10-
@test nnz(A) == 100
11-
@test nnz(Diagonal(A)) == 10
12-
@test nnz(Symmetric(A)) == 55
13-
v1, v2 = rand(10), rand(9)
14-
@test nnz(SymTridiagonal(v1, v2)) == 19
15-
end
8+
# @testset "test utils" begin
9+
# A = rand(10, 10)
10+
# @test nnz(A) == 100
11+
# @test nnz(Diagonal(A)) == 10
12+
# @test nnz(Symmetric(A)) == 55
13+
# v1, v2 = rand(10), rand(9)
14+
# @test nnz(SymTridiagonal(v1, v2)) == 19
15+
# end
1616

17-
# Definition of quadratic problems
18-
qp_problems_Matrix = ["bndqp", "eqconqp"]
19-
qp_problems_COO = ["uncqp", "ineqconqp"]
20-
for qp in [qp_problems_Matrix; qp_problems_COO]
21-
include(joinpath("problems", "$qp.jl"))
22-
end
17+
# # Definition of quadratic problems
18+
# qp_problems_Matrix = ["bndqp", "eqconqp"]
19+
# qp_problems_COO = ["uncqp", "ineqconqp"]
20+
# for qp in [qp_problems_Matrix; qp_problems_COO]
21+
# include(joinpath("problems", "$qp.jl"))
22+
# end
2323

24-
include("test_consistency.jl")
24+
# include("test_consistency.jl")
2525

26-
function testSM(sm) # test function for a specific problem
27-
@test (sm.meta.ncon == 2 && sm.meta.nvar == 5)
28-
@test sm.meta.lcon == sm.meta.ucon
29-
lvar_sm_true = [0.0; 0.0; 0.0; -4.0; -3.0]
30-
uvar_sm_true = [Inf; Inf; Inf; Inf; -2.0]
31-
H_sm_true = sparse(
32-
[
33-
6.0 0.0 0.0 0.0 0.0
34-
2.0 5.0 0.0 0.0 0.0
35-
1.0 2.0 4.0 0.0 0.0
36-
0.0 0.0 0.0 0.0 0.0
37-
0.0 0.0 0.0 0.0 0.0
38-
],
39-
)
40-
A_sm_true = sparse([
41-
1.0 0.0 1.0 0.0 -1.0
42-
0.0 2.0 1.0 -1.0 0.0
43-
])
26+
# function testSM(sm) # test function for a specific problem
27+
# @test (sm.meta.ncon == 2 && sm.meta.nvar == 5)
28+
# @test sm.meta.lcon == sm.meta.ucon
29+
# lvar_sm_true = [0.0; 0.0; 0.0; -4.0; -3.0]
30+
# uvar_sm_true = [Inf; Inf; Inf; Inf; -2.0]
31+
# H_sm_true = sparse(
32+
# [
33+
# 6.0 0.0 0.0 0.0 0.0
34+
# 2.0 5.0 0.0 0.0 0.0
35+
# 1.0 2.0 4.0 0.0 0.0
36+
# 0.0 0.0 0.0 0.0 0.0
37+
# 0.0 0.0 0.0 0.0 0.0
38+
# ],
39+
# )
40+
# A_sm_true = sparse([
41+
# 1.0 0.0 1.0 0.0 -1.0
42+
# 0.0 2.0 1.0 -1.0 0.0
43+
# ])
4444

45-
@test all(lvar_sm_true .== sm.meta.lvar)
46-
@test all(uvar_sm_true .== sm.meta.uvar)
47-
@test all(sparse(sm.data.Arows, sm.data.Acols, sm.data.Avals, 2, 5) .== A_sm_true)
48-
@test all(sparse(sm.data.Hrows, sm.data.Hcols, sm.data.Hvals, 5, 5) .== H_sm_true)
49-
end
45+
# @test all(lvar_sm_true .== sm.meta.lvar)
46+
# @test all(uvar_sm_true .== sm.meta.uvar)
47+
# @test all(sparse(sm.data.Arows, sm.data.Acols, sm.data.Avals, 2, 5) .== A_sm_true)
48+
# @test all(sparse(sm.data.Hrows, sm.data.Hcols, sm.data.Hvals, 5, 5) .== H_sm_true)
49+
# end
5050

51-
@testset "SlackModel" begin
52-
H = [
53-
6.0 2.0 1.0
54-
2.0 5.0 2.0
55-
1.0 2.0 4.0
56-
]
57-
c = [-8.0; -3; -3]
58-
A = [
59-
1.0 0.0 1.0
60-
0.0 2.0 1.0
61-
]
62-
b = [0.0; 3]
63-
l = [0.0; 0; 0]
64-
u = [Inf; Inf; Inf]
65-
T = eltype(c)
66-
qp = QuadraticModel(
67-
c,
68-
sparse(H),
69-
A = A,
70-
lcon = [-3.0; -4.0],
71-
ucon = [-2.0; Inf],
72-
lvar = l,
73-
uvar = u,
74-
c0 = 0.0,
75-
name = "QM1",
76-
)
77-
sm = SlackModel(qp)
78-
testSM(sm)
51+
# @testset "SlackModel" begin
52+
# H = [
53+
# 6.0 2.0 1.0
54+
# 2.0 5.0 2.0
55+
# 1.0 2.0 4.0
56+
# ]
57+
# c = [-8.0; -3; -3]
58+
# A = [
59+
# 1.0 0.0 1.0
60+
# 0.0 2.0 1.0
61+
# ]
62+
# b = [0.0; 3]
63+
# l = [0.0; 0; 0]
64+
# u = [Inf; Inf; Inf]
65+
# T = eltype(c)
66+
# qp = QuadraticModel(
67+
# c,
68+
# sparse(H),
69+
# A = A,
70+
# lcon = [-3.0; -4.0],
71+
# ucon = [-2.0; Inf],
72+
# lvar = l,
73+
# uvar = u,
74+
# c0 = 0.0,
75+
# name = "QM1",
76+
# )
77+
# sm = SlackModel(qp)
78+
# testSM(sm)
79+
80+
# SlackModel!(qp)
81+
# testSM(qp)
7982

80-
SlackModel!(qp)
81-
testSM(qp)
83+
# qpdense = QuadraticModel(
84+
# c,
85+
# H,
86+
# A = A,
87+
# lcon = [-3.0; -4.0],
88+
# ucon = [-2.0; Inf],
89+
# lvar = l,
90+
# uvar = u,
91+
# c0 = 0.0,
92+
# name = "QM1",
93+
# )
94+
# smdense = SlackModel(qpdense)
95+
# testSM(smdense)
96+
# end
8297

83-
qpdense = QuadraticModel(
98+
# @testset "sort cols COO" begin
99+
# Hrows = [1; 2; 1; 3]
100+
# Hcols = [2; 1; 4; 4]
101+
# Hvals = [1.0; 2.0; 3.0; 4.0]
102+
# Arows = [2; 2; 1; 4]
103+
# Acols = [4; 3; 1; 2]
104+
# Avals = [-1.0; -2.0; -3.0; -4.0]
105+
# c = [-8.0; -3; -3; 2.0]
106+
# l = [0.0; 0; 0; 0]
107+
# u = [Inf; Inf; Inf; Inf]
108+
# qp = QuadraticModel(
109+
# c,
110+
# Hrows,
111+
# Hcols,
112+
# Hvals,
113+
# Arows = Arows,
114+
# Acols = Acols,
115+
# Avals = Avals,
116+
# lcon = [-3.0; -4.0; 2.0; 1.0],
117+
# ucon = [-2.0; Inf; Inf; Inf],
118+
# lvar = l,
119+
# uvar = u,
120+
# c0 = 0.0,
121+
# name = "QM1",
122+
# sortcols = true,
123+
# )
124+
# @test issorted(Hcols)
125+
# @test issorted(Acols)
126+
# end
127+
128+
@testset "LinearOperators" begin
129+
nvar, ncon = 10, 7
130+
T = Float64
131+
H = Symmetric(tril!(sprand(T, nvar, nvar, 0.3)))
132+
A = sprand(T, ncon, nvar, 0.4)
133+
c = rand(nvar)
134+
lvar = fill(-Inf, nvar)
135+
uvar = fill(0., nvar)
136+
lcon = rand(ncon)
137+
ucon = lcon .+ 100.0
138+
qp = QuadraticModel(
84139
c,
85140
H,
86141
A = A,
87-
lcon = [-3.0; -4.0],
88-
ucon = [-2.0; Inf],
89-
lvar = l,
90-
uvar = u,
142+
lcon = lcon,
143+
ucon = ucon,
144+
lvar = lvar,
145+
uvar = uvar,
91146
c0 = 0.0,
92-
name = "QM1",
147+
name = "QM",
93148
)
94-
smdense = SlackModel(qpdense)
95-
testSM(smdense)
96-
end
97-
98-
@testset "sort cols COO" begin
99-
Hrows = [1; 2; 1; 3]
100-
Hcols = [2; 1; 4; 4]
101-
Hvals = [1.0; 2.0; 3.0; 4.0]
102-
Arows = [2; 2; 1; 4]
103-
Acols = [4; 3; 1; 2]
104-
Avals = [-1.0; -2.0; -3.0; -4.0]
105-
c = [-8.0; -3; -3; 2.0]
106-
l = [0.0; 0; 0; 0]
107-
u = [Inf; Inf; Inf; Inf]
108-
qp = QuadraticModel(
149+
qpLO = QuadraticModel(
109150
c,
110-
Hrows,
111-
Hcols,
112-
Hvals,
113-
Arows = Arows,
114-
Acols = Acols,
115-
Avals = Avals,
116-
lcon = [-3.0; -4.0; 2.0; 1.0],
117-
ucon = [-2.0; Inf; Inf; Inf],
118-
lvar = l,
119-
uvar = u,
151+
LinearOperator(H),
152+
A = LinearOperator(A),
153+
lcon = lcon,
154+
ucon = ucon,
155+
lvar = lvar,
156+
uvar = uvar,
120157
c0 = 0.0,
121-
name = "QM1",
122-
sortcols = true,
158+
name = "QMLO",
123159
)
124-
@test issorted(Hcols)
125-
@test issorted(Acols)
160+
x = ones(10)
161+
@test obj(qp, x) == obj(qpLO, x)
162+
@test grad(qp, x) == grad(qpLO, x)
163+
@test cons(qp, x) == cons(qpLO, x)
164+
165+
SM = SlackModel(qp)
166+
SMLO = SlackModel(qpLO)
167+
nfix = length(qp.meta.jfix)
168+
ns = qp.meta.ncon - nfix
169+
x = rand(SM.meta.nvar)
170+
@test SM.meta.nvar == qp.meta.nvar + ns
171+
@test obj(SM, x) == obj(SMLO, x)
172+
@test grad(SM, x) == grad(SMLO, x)
173+
@test cons(SM, x) == cons(SMLO, x)
126174
end
127175

128176
include("test_presolve.jl")

0 commit comments

Comments
 (0)