Skip to content

Commit e4bbd73

Browse files
[PRESOLVE] Free linear singleton columns, row/col storage of A and H (#87)
* clean presolve * up postsolve * remove useless storage vectors * store rows and cols * free linear singleton columns
1 parent b002937 commit e4bbd73

File tree

9 files changed

+629
-396
lines changed

9 files changed

+629
-396
lines changed

src/presolve/empty_rows.jl

Lines changed: 20 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,25 @@
1-
"""
2-
new_ncon = empty_rows!(Arows, lcon, ucon, ncon, row_cnt, empty_rows)
3-
4-
Removes the empty rows of A, and the corresponding elements in lcon and ucon that are in `empty_rows`.
5-
`row_cnt` is a vector of the number of elements per row.
1+
struct EmptyRow{T, S} <: PresolveOperation{T, S}
2+
i::Int # idx of empty row
3+
end
64

7-
Returns the new number of constraints `new_ncon` and updates in-place `Arows`, `lcon`, `ucon`.
8-
"""
95
function empty_rows!(
10-
Arows,
11-
lcon::Vector{T},
12-
ucon::Vector{T},
6+
operations::Vector{PresolveOperation{T, S}},
7+
lcon::S,
8+
ucon::S,
139
ncon,
14-
row_cnt::Vector{Int},
15-
empty_rows::Vector{Int},
16-
) where {T}
17-
new_ncon = 0
18-
for i = 1:ncon
19-
if row_cnt[i] == 0
20-
@assert lcon[i] zero(T) ucon[i]
21-
else
22-
new_ncon += 1
23-
lcon[new_ncon] = lcon[i]
24-
ucon[new_ncon] = ucon[i]
25-
end
10+
row_cnt,
11+
kept_rows::Vector{Bool},
12+
) where {T, S}
13+
empty_row_pass = false
14+
for i=1:ncon
15+
(kept_rows[i] && (row_cnt[i] == 0)) || continue
16+
empty_row_pass = true
17+
@assert (lcon[i] zero(T) ucon[i])
18+
row_cnt[i] = -1
19+
kept_rows[i] = false
20+
push!(operations, EmptyRow{T, S}(i))
2621
end
27-
resize!(lcon, new_ncon)
28-
resize!(ucon, new_ncon)
29-
30-
# assume Acols is sorted
31-
Annz = length(Arows)
32-
nempty = length(empty_rows)
33-
34-
for idxempty = 1:nempty
35-
currentiempty = empty_rows[idxempty]
36-
# index of the current singleton row that takes the number of
37-
# already removed variables into account:
38-
newcurrentiempty = currentiempty - idxempty + 1
39-
40-
# remove singleton rows in A rows
41-
Awritepos = 1
42-
k = 1
43-
while k <= Annz
44-
Ai = Arows[k]
45-
Arows[Awritepos] = (Ai < newcurrentiempty) ? Ai : Ai - 1
46-
Awritepos += 1
47-
k += 1
48-
end
49-
end
50-
return new_ncon
22+
return empty_row_pass
5123
end
24+
25+
postsolve!(pt::OutputPoint, operation::EmptyRow) = nothing
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
struct FreeLinearSingletonColumn{T, S} <: PresolveOperation{T, S}
2+
i::Int
3+
j::Int
4+
aij::T
5+
arowi::Row{T}
6+
yi::T
7+
conival::T
8+
end
9+
10+
function free_linear_singleton_columns!(
11+
operations::Vector{PresolveOperation{T, S}},
12+
hcols::Vector{Col{T}},
13+
arows::Vector{Row{T}},
14+
acols::Vector{Col{T}},
15+
c::AbstractVector{T},
16+
c0::T,
17+
lcon::AbstractVector{T},
18+
ucon::AbstractVector{T},
19+
lvar::AbstractVector{T},
20+
uvar::AbstractVector{T},
21+
nvar,
22+
row_cnt,
23+
col_cnt,
24+
kept_rows,
25+
kept_cols,
26+
) where {T, S}
27+
free_lsc_pass = false
28+
c0_offset = zero(T)
29+
30+
for j=1:nvar
31+
(kept_cols[j] && (col_cnt[j] == 1)) || continue
32+
# check infinity bounds and no hessian contribution
33+
if lvar[j] == -T(Inf) && uvar[j] == T(Inf) && isempty(hcols[j].nzind)
34+
# find i the row index of the singleton column j, and Aij
35+
Aij = T(Inf)
36+
colj = acols[j]
37+
k = 1
38+
i = colj.nzind[k]
39+
while !(kept_rows[i])
40+
k += 1
41+
i = colj.nzind[k]
42+
end
43+
Aij = colj.nzval[k]
44+
45+
yi = c[j] / Aij
46+
nzcj = c[j] != zero(T)
47+
if yi < zero(T)
48+
lcon[i] = ucon[i]
49+
elseif yi > zero(T)
50+
ucon[i] = lcon[i]
51+
end
52+
if abs(Aij) > sqrt(eps(T))
53+
nzcj && (c0_offset += yi * ucon[i]) # update c0
54+
nb_elem_i = row_cnt[i] - 1
55+
rowi = arows[i] # i-th row
56+
# new row to store for postsolve:
57+
rowi2 = Row(zeros(Int, nb_elem_i), zeros(T, nb_elem_i))
58+
c_i = 1
59+
for k in 1:length(rowi.nzind)
60+
j2 = rowi.nzind[k]
61+
# add all col elements to rowi2 except the col j2 == j
62+
if kept_cols[j2] && j2 != j
63+
rowi2.nzind[c_i] = j2
64+
Aij2 = rowi.nzval[k]
65+
rowi2.nzval[c_i] = Aij2
66+
nzcj && (c[j2] -= yi * Aij2) # update c if c[j] != 0
67+
col_cnt[j2] -= 1
68+
c_i += 1
69+
end
70+
end
71+
conival = nzcj ? ucon[i] : (lcon[i] + ucon[i]) / 2 # constant for postsolve
72+
push!(operations, FreeLinearSingletonColumn{T, S}(i, j, Aij, rowi2, yi, conival))
73+
kept_cols[j] = false
74+
col_cnt[j] = -1
75+
kept_rows[i] = false
76+
row_cnt[i] = -1
77+
free_lsc_pass = true
78+
end
79+
end
80+
end
81+
return free_lsc_pass, c0 + c0_offset
82+
end
83+
84+
function postsolve!(pt::OutputPoint{T, S}, operation::FreeLinearSingletonColumn{T, S}) where {T, S}
85+
x = pt.x
86+
j = operation.j
87+
# x[j] = (coival - Σₖ Aik x[k]) / Aij , where k ≂̸ j
88+
x[j] = operation.conival
89+
for (i, Aij) in zip(operation.arowi.nzind, operation.arowi.nzval)
90+
x[j] -= Aij * x[i]
91+
end
92+
x[j] /= operation.aij
93+
pt.s_l[j] = zero(T)
94+
pt.s_u[j] = zero(T)
95+
pt.y[operation.i] = operation.yi
96+
end

src/presolve/postsolve_utils.jl

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
1-
function restore_ifix!(kept_cols, xps, x, xout)
1+
function restore_x!(kept_cols, xin::S, xout::S, nvar) where S
22
# put x and xps inside xout according to kept_cols
3-
nvar = length(xout)
43
cx = 0
54
for i = 1:nvar
65
if kept_cols[i]
76
cx += 1
8-
xout[i] = x[cx]
9-
else
10-
xout[i] = xps[i]
7+
xout[i] = xin[cx]
118
end
129
end
1310
end
1411

15-
function restore_y!(y::Vector{T}, yout::Vector{T}, kept_rows::Vector{Bool}, ncon) where {T}
12+
function restore_y!(kept_rows::Vector{Bool}, y::Vector{T}, yout::Vector{T}, ncon) where {T}
1613
c_y = 0
1714
for i = 1:ncon
1815
if !kept_rows[i]

0 commit comments

Comments
 (0)