Skip to content

Commit a531b33

Browse files
[PRESOLVE] Avoid copying some rows/cols, fix issue unconstrained reduction zero with objective (#97)
* free memory, fix issue unconstrained reduction zero obj * accelerate computation of presolved A and Q * fix nnz errors, multipliers to dense vectors * remove dropzeros! * multipliers to dense vectors * QuadraticModelPresolvedData * fixed_vars_only * up doc, rm setindex2
1 parent 347ce01 commit a531b33

12 files changed

+392
-403
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ NLPModels = "0.18, 0.19"
1919
NLPModelsModifiers = "0.6"
2020
Requires = "0.3, 0.4, 0.5, 1.0"
2121
SolverCore = "0.2.2"
22-
SparseMatricesCOO = "0.1"
22+
SparseMatricesCOO = "0.1.1"
2323
julia = "^1.0.0"
2424

2525
[extras]

src/presolve/empty_rows.jl

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,21 @@ struct EmptyRow{T, S} <: PresolveOperation{T, S}
33
end
44

55
function empty_rows!(
6+
qmp::QuadraticModelPresolveData{T, S},
67
operations::Vector{PresolveOperation{T, S}},
7-
lcon::S,
8-
ucon::S,
9-
ncon,
10-
row_cnt,
11-
kept_rows::Vector{Bool},
128
) where {T, S}
13-
empty_row_pass = false
14-
for i = 1:ncon
9+
qmp.empty_row_pass = false
10+
lcon, ucon, row_cnt, kept_rows = qmp.lcon, qmp.ucon, qmp.row_cnt, qmp.kept_rows
11+
for i = 1:qmp.ncon
1512
(kept_rows[i] && (row_cnt[i] == 0)) || continue
16-
empty_row_pass = true
13+
qmp.empty_row_pass = true
1714
@assert (lcon[i] zero(T) ucon[i])
1815
row_cnt[i] = -1
1916
kept_rows[i] = false
2017
push!(operations, EmptyRow{T, S}(i))
2118
end
22-
return empty_row_pass
2319
end
2420

25-
postsolve!(sol::QMSolution, operation::EmptyRow) = nothing
21+
function postsolve!(sol::QMSolution, operation::EmptyRow, psd::PresolvedData)
22+
psd.kept_rows[operation.i] = true
23+
end

src/presolve/free_rows.jl

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,21 @@ struct FreeRow{T, S} <: PresolveOperation{T, S}
33
end
44

55
function free_rows!(
6+
qmp::QuadraticModelPresolveData{T, S},
67
operations::Vector{PresolveOperation{T, S}},
7-
lcon::S,
8-
ucon::S,
9-
ncon,
10-
row_cnt,
11-
kept_rows::Vector{Bool},
128
) where {T, S}
13-
free_row_pass = false
14-
for i = 1:ncon
9+
lcon, ucon = qmp.lcon, qmp.ucon
10+
row_cnt, kept_rows = qmp.row_cnt, qmp.kept_rows
11+
qmp.free_row_pass = false
12+
for i = 1:qmp.ncon
1513
(kept_rows[i] && lcon[i] == -T(Inf) && ucon[i] == T(Inf)) || continue
16-
free_row_pass = true
14+
qmp.free_row_pass = true
1715
row_cnt[i] = -1
1816
kept_rows[i] = false
1917
push!(operations, FreeRow{T, S}(i))
2018
end
21-
return free_row_pass
2219
end
2320

24-
postsolve!(sol::QMSolution, operation::FreeRow) = nothing
21+
function postsolve!(sol::QMSolution, operation::FreeRow, psd::PresolvedData)
22+
psd.kept_rows[operation.i] = true
23+
end

src/presolve/linear_singleton_columns.jl

Lines changed: 33 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,92 +2,82 @@ struct FreeLinearSingletonColumn{T, S} <: PresolveOperation{T, S}
22
i::Int
33
j::Int
44
aij::T
5-
arowi::Row{T}
65
yi::T
76
conival::T
87
end
98

109
function free_linear_singleton_columns!(
10+
qmp::QuadraticModelPresolveData{T, S},
1111
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,
2612
) where {T, S}
27-
free_lsc_pass = false
13+
qmp.free_lsc_pass = false
2814
c0_offset = zero(T)
15+
hcols, arows, acols, c = qmp.hcols, qmp.arows, qmp.acols, qmp.c
16+
lcon, ucon, lvar, uvar = qmp.lcon, qmp.ucon, qmp.lvar, qmp.uvar
17+
row_cnt, col_cnt = qmp.row_cnt, qmp.col_cnt
18+
kept_rows, kept_cols = qmp.kept_rows, qmp.kept_cols
2919

30-
for j = 1:nvar
20+
for j = 1:qmp.nvar
3121
(kept_cols[j] && (col_cnt[j] == 1)) || continue
3222
# check infinity bounds and no hessian contribution
3323
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)
24+
# find i the row index of the singleton column j, and aij
25+
aij = T(Inf)
3626
colj = acols[j]
3727
k = 1
3828
i = colj.nzind[k]
3929
while !(kept_rows[i])
4030
k += 1
4131
i = colj.nzind[k]
4232
end
43-
Aij = colj.nzval[k]
33+
aij = colj.nzval[k]
4434

45-
yi = c[j] / Aij
35+
yi = c[j] / aij
4636
nzcj = c[j] != zero(T)
4737
if yi < zero(T)
4838
lcon[i] = ucon[i]
4939
elseif yi > zero(T)
5040
ucon[i] = lcon[i]
5141
end
52-
if abs(Aij) > sqrt(eps(T))
42+
if abs(aij) > sqrt(eps(T))
5343
nzcj && (c0_offset += yi * ucon[i]) # update c0
54-
nb_elem_i = row_cnt[i] - 1
5544
rowi = arows[i] # i-th row
5645
# 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 = 1:length(rowi.nzind)
60-
j2 = rowi.nzind[k]
46+
for (l, ail) in zip(rowi.nzind, rowi.nzval)
6147
# 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
48+
(kept_cols[l] && l != j) || continue
49+
nzcj && (c[l] -= yi * ail) # update c if c[j] != 0
50+
col_cnt[l] -= 1
7051
end
7152
conival = nzcj ? ucon[i] : (lcon[i] + ucon[i]) / 2 # constant for postsolve
72-
push!(operations, FreeLinearSingletonColumn{T, S}(i, j, Aij, rowi2, yi, conival))
53+
push!(operations, FreeLinearSingletonColumn{T, S}(i, j, aij, yi, conival))
7354
kept_cols[j] = false
7455
col_cnt[j] = -1
7556
kept_rows[i] = false
7657
row_cnt[i] = -1
77-
free_lsc_pass = true
58+
qmp.free_lsc_pass = true
7859
end
7960
end
8061
end
81-
return free_lsc_pass, c0 + c0_offset
62+
qmp.c0 += c0_offset
8263
end
8364

84-
function postsolve!(sol::QMSolution{T, S}, operation::FreeLinearSingletonColumn{T, S}) where {T, S}
65+
function postsolve!(
66+
sol::QMSolution,
67+
operation::FreeLinearSingletonColumn{T, S},
68+
psd::PresolvedData{T, S},
69+
) where {T, S}
8570
x = sol.x
86-
j = operation.j
87-
# x[j] = (coival - Σₖ Aik x[k]) / Aij , where k ≂̸ j
71+
kept_rows, kept_cols = psd.kept_rows, psd.kept_cols
72+
i, j = operation.i, operation.j
73+
arowi = psd.arows[i]
74+
kept_rows[i] = true
75+
kept_cols[j] = true
76+
# x[j] = (coival - Σₖ Aik x[k]) / aij , where k ≂̸ j
8877
x[j] = operation.conival
89-
for (i, Aij) in zip(operation.arowi.nzind, operation.arowi.nzval)
90-
x[j] -= Aij * x[i]
78+
for (l, ail) in zip(arowi.nzind, arowi.nzval)
79+
(l != j && kept_cols[l]) || continue
80+
x[j] -= ail * x[l]
9181
end
9282
x[j] /= operation.aij
9383
sol.s_l[j] = zero(T)

src/presolve/postsolve_utils.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,28 @@ function restore_ilow_iupp!(ilow, iupp, kept_cols)
4141
end
4242
end
4343
end
44+
45+
function restore_s!(
46+
s_l::AbstractVector{T},
47+
s_u::AbstractVector{T},
48+
s_l_in::SparseVector{T, Int},
49+
s_u_in::SparseVector{T, Int},
50+
kept_cols::Vector{Bool},
51+
) where {T}
52+
ilow, iupp = copy(s_l_in.nzind), copy(s_u_in.nzind)
53+
restore_ilow_iupp!(ilow, iupp, kept_cols)
54+
s_l[ilow] .= s_l_in.nzval
55+
s_u[iupp] .= s_u_in.nzval
56+
end
57+
58+
function restore_s!(
59+
s_l::S,
60+
s_u::S,
61+
s_l_in::S,
62+
s_u_in::S,
63+
kept_cols::Vector{Bool},
64+
) where {S <: DenseVector}
65+
nvar = length(s_l)
66+
restore_x!(kept_cols, s_l_in, s_l, nvar)
67+
restore_x!(kept_cols, s_u_in, s_u, nvar)
68+
end

0 commit comments

Comments
 (0)