Skip to content

Commit 5369c94

Browse files
empty rows
1 parent d5ae43d commit 5369c94

File tree

4 files changed

+137
-8
lines changed

4 files changed

+137
-8
lines changed

src/presolve/empty_rows.jl

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
function row_cnt!(Arows, row_cnt::Vector{Int})
2+
for k=1:length(Arows)
3+
i = Arows[k]
4+
row_cnt[i] += 1
5+
end
6+
end
7+
8+
function removed_rows(row_cnt::Vector{Int})
9+
return findall(isequal(0), row_cnt)
10+
end
11+
12+
function empty_rows!(Arows, lcon::Vector{T}, ucon::Vector{T}, ncon, row_cnt::Vector{Int},
13+
rows_rm::Vector{Int}, Arows_sortperm::Vector{Int}) where {T}
14+
new_ncon = 0
15+
for i=1:ncon
16+
if row_cnt[i] == 0
17+
@assert lcon[i] zero(T) ucon[i]
18+
else
19+
new_ncon += 1
20+
lcon[new_ncon] = lcon[i]
21+
ucon[new_ncon] = ucon[i]
22+
end
23+
end
24+
resize!(lcon, new_ncon)
25+
resize!(ucon, new_ncon)
26+
27+
Arows_s = @views Arows[Arows_sortperm]
28+
c_rm = 1
29+
nrm = length(rows_rm)
30+
for k=1:length(Arows)
31+
while c_rm nrm && Arows_s[k] rows_rm[c_rm]
32+
c_rm += 1
33+
end
34+
Arows_s[k] -= c_rm - 1
35+
end
36+
return new_ncon
37+
end
38+
39+
function restore_y!(y::Vector{T}, yout::Vector{T}, row_cnt, ncon) where {T}
40+
c_y = 0
41+
for i = 1:ncon
42+
if row_cnt[i] == 0
43+
yout[i] = zero(T)
44+
else
45+
c_y += 1
46+
yout[i] = y[c_y]
47+
end
48+
end
49+
end

src/presolve/presolve.jl

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
include("remove_ifix.jl")
2+
include("empty_rows.jl")
3+
4+
mutable struct PresolvedData{T, S}
5+
xrm::S
6+
row_cnt::Vector{Int}
7+
nconps::Int
8+
end
29

310
mutable struct PresolvedQuadraticModel{T, S, M1, M2} <: AbstractQuadraticModel{T, S}
411
meta::NLPModelMeta{T, S}
512
counters::Counters
613
data::QPData{T, S, M1, M2}
7-
xrm::S
14+
psd::PresolvedData{T, S}
815
end
916

1017
"""
@@ -64,6 +71,13 @@ function presolve(
6471
xrm = S(undef, 0)
6572
end
6673

74+
# remove constraints
75+
row_cnt = zeros(Int, ncon)
76+
row_cnt!(psdata.A.rows, row_cnt)
77+
rows_rm = removed_rows(row_cnt)
78+
Arows_sortperm = sortperm(psdata.A.rows)
79+
nconps = empty_rows!(psdata.A.rows, lcon, ucon, ncon, row_cnt, rows_rm, Arows_sortperm)
80+
6781
# form meta
6882
nnzh = length(psdata.H.vals)
6983
if !(nnzh == length(psdata.H.rows) == length(psdata.H.cols))
@@ -86,7 +100,7 @@ function presolve(
86100
qm,
87101
solution = xrm,
88102
objective = obj(qm, xrm),
89-
multipliers = zeros(T, qm.meta.nvar),
103+
multipliers = zeros(T, nconps),
90104
multipliers_L = s_l,
91105
multipliers_U = s_u,
92106
iter = 0,
@@ -98,17 +112,18 @@ function presolve(
98112
nvarps,
99113
lvar = lvar,
100114
uvar = uvar,
101-
ncon = ncon,
115+
ncon = nconps,
102116
lcon = lcon,
103117
ucon = ucon,
104118
nnzj = nnzj,
105119
nnzh = nnzh,
106-
lin = 1:ncon,
120+
lin = 1:nconps,
107121
islp = (nnzh == 0);
108122
minimize = qm.meta.minimize,
109123
kwargs...,
110124
)
111-
ps = PresolvedQuadraticModel(psmeta, Counters(), psdata, xrm)
125+
psd = PresolvedData{T, S}(xrm, row_cnt, nconps)
126+
ps = PresolvedQuadraticModel(psmeta, Counters(), psdata, psd)
112127
return GenericExecutionStats(
113128
:unknown,
114129
ps,
@@ -131,10 +146,15 @@ function postsolve!(
131146
psqm::PresolvedQuadraticModel{T, S},
132147
x_in::S,
133148
x_out::S,
149+
y_in::S,
150+
y_out::S,
134151
) where {T, S}
135152
if length(qm.meta.ifix) > 0
136-
restore_ifix!(qm.meta.ifix, psqm.xrm, x_in, x_out)
153+
restore_ifix!(qm.meta.ifix, psqm.psd.xrm, x_in, x_out)
137154
else
138155
x_out .= @views x_in[1:(qm.meta.nvar)]
139156
end
157+
ncon = length(y_out)
158+
restore_y!(y_in, y_out, psqm.psd.row_cnt, ncon)
159+
140160
end

src/qpmodel.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ function QuadraticModel(
9494
sortcols::Bool = false,
9595
kwargs...,
9696
) where {T, S}
97+
@assert all(lvar .≤ uvar)
98+
@assert all(lcon .≤ ucon)
9799
nnzh = length(Hvals)
98100
if !(nnzh == length(Hrows) == length(Hcols))
99101
error("The length of Hrows, Hcols and Hvals must be the same")
@@ -155,6 +157,8 @@ function QuadraticModel(
155157
c0::T = zero(T),
156158
kwargs...,
157159
) where {T, S}
160+
@assert all(lvar .≤ uvar)
161+
@assert all(lcon .≤ ucon)
158162
ncon, nvar = size(A)
159163
if typeof(H) <: AbstractLinearOperator # convert A to a LinOp if A is a Matrix?
160164
nnzh = 0

test/test_presolve.jl

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,15 @@
4848
@test norm(Aps - sparse(Aps_true)) sqrt(eps(T))
4949
@test psqp.meta.lvar == lvarps_true
5050
@test psqp.meta.uvar == uvarps_true
51-
@test psqp.xrm == [2.0]
51+
@test psqp.psd.xrm == [2.0]
5252
@test psqp.meta.ifix == Int[]
5353
@test psqp.meta.nvar == 2
5454

5555
x_in = [4.0; 7.0]
5656
x_out = zeros(3)
57-
postsolve!(qp, psqp, x_in, x_out)
57+
y_in = [2.0; 2.0]
58+
y_out = zeros(2)
59+
postsolve!(qp, psqp, x_in, x_out, y_in, y_out)
5860
@test x_out == [4.0; 2.0; 7.0]
5961

6062
# test that solves the problem
@@ -68,3 +70,57 @@
6870
stats_ps2 = presolve(qp2)
6971
@test stats_ps2.status == :first_order
7072
end
73+
74+
@testset "presolve empty rows" begin
75+
H = [
76+
6.0 2.0 1.0
77+
2.0 5.0 2.0
78+
1.0 2.0 4.0
79+
]
80+
c = [-8.0; -3; -3]
81+
A = [
82+
1.0 0.0 1.0
83+
0.0 0.0 0.0
84+
0.0 0.0 0.0
85+
3.2 0.0 0.0
86+
0.0 0.0 0.0
87+
0.0 2.0 1.0
88+
]
89+
b = [0.0; 0.0; 0.0; 4.0; 0.0; 3]
90+
l = [0.0; 0.0; 0]
91+
u = [Inf; 2.0; Inf]
92+
T = eltype(c)
93+
qp = QuadraticModel(
94+
c,
95+
SparseMatrixCOO(tril(H)),
96+
A = SparseMatrixCOO(A),
97+
lcon = b,
98+
ucon = b,
99+
lvar = l,
100+
uvar = u,
101+
c0 = 0.0,
102+
name = "QM1",
103+
)
104+
105+
statsps = presolve(qp)
106+
psqp = statsps.solver_specific[:presolvedQM]
107+
108+
Aps_true = [
109+
1.0 0.0 1.0
110+
3.2 0.0 0.0
111+
0.0 2.0 1.0
112+
]
113+
bps_true = [0.0; 4.0; 3.0]
114+
115+
Aps = sparse(psqp.data.A.rows, psqp.data.A.cols, psqp.data.A.vals, psqp.meta.ncon, psqp.meta.nvar)
116+
@test Aps == sparse(Aps_true)
117+
@test psqp.meta.lcon == psqp.meta.ucon == bps_true
118+
@test psqp.meta.ncon == 3
119+
120+
x_in = [4.0; 7.0; 4.0]
121+
x_out = zeros(3)
122+
y_in = [2.0; 2.0; 4.0]
123+
y_out = zeros(6)
124+
postsolve!(qp, psqp, x_in, x_out, y_in, y_out)
125+
@test y_out == [2.0; 0.0; 0.0; 2.0; 0.0; 4.0]
126+
end

0 commit comments

Comments
 (0)