@@ -6,7 +6,7 @@ include("postsolve_utils.jl")
6
6
mutable struct PresolvedData{T, S}
7
7
ifix:: Vector{Int}
8
8
xrm:: S
9
- row_cnt :: Vector{Int }
9
+ kept_rows :: Vector{Bool }
10
10
nconps:: Int
11
11
end
12
12
@@ -17,6 +17,23 @@ mutable struct PresolvedQuadraticModel{T, S, M1, M2} <: AbstractQuadraticModel{T
17
17
psd:: PresolvedData{T, S}
18
18
end
19
19
20
+ function update_kept_rows! (kept_rows:: Vector{Bool} , vec_to_rm:: Vector{Int} )
21
+ ncon = length (kept_rows)
22
+ n_rm = length (vec_to_rm)
23
+ offset = 0
24
+ c_v = 1
25
+ for i= 1 : ncon
26
+ if ! kept_rows[i]
27
+ offset += 1
28
+ else
29
+ if c_v ≤ n_rm && vec_to_rm[c_v] + offset == i
30
+ kept_rows[i] = false
31
+ c_v += 1
32
+ end
33
+ end
34
+ end
35
+ end
36
+
20
37
"""
21
38
stats_ps = presolve(qm::QuadraticModel{T, S}; kwargs...)
22
39
@@ -25,13 +42,15 @@ Apply a presolve routine to `qm` and returns a
25
42
from the package [`SolverCore.jl`](https://github.com/JuliaSmoothOptimizers/SolverCore.jl).
26
43
The presolve operations currently implemented are:
27
44
45
+ - [`empty_rows!`](@ref) : remove empty rows
46
+ - [`singleton_rows!`](@ref) : remove singleton rows
28
47
- [`remove_ifix!`](@ref) : remove fixed variables
29
48
30
49
The `PresolvedQuadraticModel{T, S} <: AbstractQuadraticModel{T, S}` is located in the `solver_specific` field:
31
50
32
51
psqm = stats_ps.solver_specific[:presolvedQM]
33
52
34
- and should be used to call [`postsolve! `](@ref).
53
+ and should be used to call [`postsolve`](@ref).
35
54
36
55
If the presolved problem has 0 variables, `stats_ps.solution` contains a solution of the primal problem,
37
56
`stats_ps.multipliers` is a zero `SparseVector`, and, if we define
@@ -50,31 +69,39 @@ function presolve(
50
69
lvar, uvar = psqm. meta. lvar, psqm. meta. uvar
51
70
lcon, ucon = psqm. meta. lcon, psqm. meta. ucon
52
71
nvar, ncon = psqm. meta. nvar, psqm. meta. ncon
72
+ # copy if same vector
73
+ lcon === ucon && (lcon = copy (lcon))
74
+ lvar === uvar && (lvar = copy (lvar))
53
75
54
76
# empty rows
55
77
row_cnt = zeros (Int, ncon)
78
+ kept_rows = fill (true , ncon)
56
79
row_cnt! (psdata. A. rows, row_cnt) # number of coefficients per row
57
- rows_rm = removed_empty_rows (row_cnt) # indices of the empty rows
58
- if length (rows_rm) > 0
80
+ empty_rows = find_empty_rows (row_cnt) # indices of the empty rows
81
+ if length (empty_rows) > 0
82
+ empty_row_pass = true
83
+ update_kept_rows! (kept_rows, empty_rows)
59
84
Arows_sortperm = sortperm (psdata. A. rows) # permute rows
60
85
Arows_s = @views psdata. A. rows[Arows_sortperm]
61
- nconps = empty_rows! (psdata. A. rows, lcon, ucon, ncon, row_cnt, rows_rm , Arows_s)
86
+ nconps = empty_rows! (psdata. A. rows, lcon, ucon, ncon, row_cnt, empty_rows , Arows_s)
62
87
else
88
+ empty_row_pass = false
63
89
nconps = ncon
64
90
end
65
91
66
92
# remove singleton rows
67
- if nconps != ncon
68
- row_cnt2 = Vector {Int} (undef , nconps)
69
- else
70
- row_cnt2 = row_cnt
93
+ if empty_row_pass
94
+ resize! (row_cnt , nconps)
95
+ row_cnt . = 0
96
+ row_cnt! (psdata . A . rows, row_cnt) # number of coefficients per rows
71
97
end
72
- row_cnt2 .= 0
73
- row_cnt! (psdata. A. rows, row_cnt2) # number of coefficients per rows
74
- singl_rows = removed_singleton_rows (row_cnt2) # indices of the empty rows
98
+ singl_rows = find_singleton_rows (row_cnt) # indices of the singleton rows
75
99
if length (singl_rows) > 0
76
- nconps = singleton_rows! (psdata. A. rows, psdata. A. cols, psdata. A. vals, lcon, ucon, lvar, uvar, nvar, nconps, row_cnt2, singl_rows)
100
+ singl_row_pass = true
101
+ update_kept_rows! (kept_rows, singl_rows)
102
+ nconps = singleton_rows! (psdata. A. rows, psdata. A. cols, psdata. A. vals, lcon, ucon, lvar, uvar, nvar, nconps, row_cnt, singl_rows)
77
103
else
104
+ singl_row_pass = false
78
105
nconps = nconps
79
106
end
80
107
@@ -146,7 +173,7 @@ function presolve(
146
173
minimize = qm. meta. minimize,
147
174
kwargs... ,
148
175
)
149
- psd = PresolvedData {T, S} (ifix, xrm, row_cnt , nconps)
176
+ psd = PresolvedData {T, S} (ifix, xrm, kept_rows , nconps)
150
177
ps = PresolvedQuadraticModel (psmeta, Counters (), psdata, psd)
151
178
return GenericExecutionStats (
152
179
:unknown ,
@@ -158,13 +185,6 @@ function presolve(
158
185
end
159
186
end
160
187
161
- """
162
- postsolve!(qm::QuadraticModel{T, S}, psqm::PresolvedQuadraticModel{T, S},
163
- x_in::S, x_out::S) where {T, S}
164
-
165
- Retrieve the solution `x_out` of the original QP `qm` given the solution of the presolved QP (`psqm`)
166
- `x_in`.
167
- """
168
188
function postsolve! (
169
189
qm:: QuadraticModel{T, S} ,
170
190
psqm:: PresolvedQuadraticModel{T, S} ,
@@ -182,11 +202,34 @@ function postsolve!(
182
202
x_out .= @views x_in[1 : (qm. meta. nvar)]
183
203
end
184
204
ncon = length (y_out)
185
- restore_y! (y_in, y_out, psqm. psd. row_cnt , ncon)
205
+ restore_y! (y_in, y_out, psqm. psd. kept_rows , ncon)
186
206
187
207
ilow, iupp = s_l. nzind, s_u. nzind
188
208
restore_ilow_iupp! (ilow, iupp, ifix)
189
209
s_l_out = SparseVector (qm. meta. nvar, ilow, s_l. nzval)
190
210
s_u_out = SparseVector (qm. meta. nvar, iupp, s_u. nzval)
191
211
return s_l_out, s_u_out
192
212
end
213
+
214
+ """
215
+ x, y, s_l, s_u = postsolve(qm::QuadraticModel{T, S}, psqm::PresolvedQuadraticModel{T, S},
216
+ x_in::S, y_in::S,
217
+ s_l_in::SparseVector{T, Int},
218
+ s_u_in::SparseVector{T, Int}) where {T, S}
219
+
220
+ Retrieve the solution `x, y, s_l, s_u` of the original QP `qm` given the solution of the presolved QP (`psqm`)
221
+ `x_in, y_in, s_l_in, s_u_in`.
222
+ """
223
+ function postsolve (
224
+ qm:: QuadraticModel{T, S} ,
225
+ psqm:: PresolvedQuadraticModel{T, S} ,
226
+ x_in:: S ,
227
+ y_in:: S ,
228
+ s_l:: SparseVector{T, Int} ,
229
+ s_u:: SparseVector{T, Int} ,
230
+ ) where {T, S}
231
+ x_out = similar (qm. meta. x0)
232
+ y_out = similar (qm. meta. y0)
233
+ s_l_out, s_u_out = postsolve! (qm, psqm, x_in, x_out, y_in, y_out, s_l, s_u)
234
+ return x_out, y_out, s_l_out, s_u_out
235
+ end
0 commit comments