Skip to content

Commit 305b6fc

Browse files
committed
speedup glop; better respect the time limit
1 parent cba9f72 commit 305b6fc

17 files changed

+131
-66
lines changed

ortools/glop/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ cc_library(
286286
"//ortools/lp_data:lp_utils",
287287
"//ortools/lp_data:scattered_vector",
288288
"//ortools/util:stats",
289+
"//ortools/util:time_limit",
289290
],
290291
)
291292

ortools/glop/basis_representation.h

+4
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,10 @@ class BasisFactorization {
310310
// Returns the number of updates since last refactorization.
311311
int NumUpdates() const { return num_updates_; }
312312

313+
EntryIndex NumberOfEntriesInLU() const {
314+
return lu_factorization_.NumberOfEntries();
315+
}
316+
313317
private:
314318
// Called by ForceRefactorization() or Refactorize() or Initialize().
315319
Status ComputeFactorization();

ortools/glop/dual_edge_norms.cc

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <cstdlib>
1717

1818
#include "ortools/lp_data/lp_utils.h"
19+
#include "ortools/lp_data/permutation.h"
1920

2021
namespace operations_research {
2122
namespace glop {
@@ -42,8 +43,8 @@ DenseColumn::ConstView DualEdgeNorms::GetEdgeSquaredNorms() {
4243
void DualEdgeNorms::UpdateDataOnBasisPermutation(
4344
const ColumnPermutation& col_perm) {
4445
if (recompute_edge_squared_norms_) return;
45-
ApplyColumnPermutationToRowIndexedVector(col_perm, &edge_squared_norms_,
46-
&tmp_edge_squared_norms_);
46+
ApplyColumnPermutationToRowIndexedVector(
47+
col_perm.const_view(), &edge_squared_norms_, &tmp_edge_squared_norms_);
4748
}
4849

4950
bool DualEdgeNorms::TestPrecision(RowIndex leaving_row,

ortools/glop/lu_factorization.cc

+7-5
Original file line numberDiff line numberDiff line change
@@ -404,21 +404,23 @@ bool LuFactorization::LeftSolveLWithNonZeros(
404404
// use a different algorithm.
405405
ClearAndResizeVectorWithNonZeros(x->size(), result_before_permutation);
406406
x->swap(result_before_permutation->values);
407+
const auto input = result_before_permutation->values.const_view();
408+
const auto inverse_row_perm = inverse_row_perm_.const_view();
407409
if (nz->empty()) {
408-
const RowIndex num_rows = inverse_row_perm_.size();
410+
const RowIndex num_rows = inverse_row_perm.size();
409411
for (RowIndex row(0); row < num_rows; ++row) {
410-
const Fractional value = (*result_before_permutation)[row];
412+
const Fractional value = input[row];
411413
if (value != 0.0) {
412-
const RowIndex permuted_row = inverse_row_perm_[row];
414+
const RowIndex permuted_row = inverse_row_perm[row];
413415
(*x)[permuted_row] = value;
414416
}
415417
}
416418
} else {
417419
nz->swap(result_before_permutation->non_zeros);
418420
nz->reserve(result_before_permutation->non_zeros.size());
419421
for (const RowIndex row : result_before_permutation->non_zeros) {
420-
const Fractional value = (*result_before_permutation)[row];
421-
const RowIndex permuted_row = inverse_row_perm_[row];
422+
const Fractional value = input[row];
423+
const RowIndex permuted_row = inverse_row_perm[row];
422424
(*x)[permuted_row] = value;
423425
nz->push_back(permuted_row);
424426
}

ortools/glop/markowitz.cc

+12-7
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ Status Markowitz::ComputeRowAndColumnPermutation(
6363
// Initialize residual_matrix_non_zero_ with the submatrix left after we
6464
// removed the singleton and residual singleton columns.
6565
residual_matrix_non_zero_.InitializeFromMatrixSubset(
66-
basis_matrix, *row_perm, *col_perm, &singleton_column_, &singleton_row_);
66+
basis_matrix, row_perm->const_view(), col_perm->const_view(),
67+
&singleton_column_, &singleton_row_);
6768

6869
// Perform Gaussian elimination.
6970
const int end_index = std::min(num_rows.value(), num_cols.value());
@@ -216,9 +217,9 @@ void Markowitz::ExtractSingletonColumns(
216217
basis_matrix.num_rows().value());
217218
}
218219

219-
bool Markowitz::IsResidualSingletonColumn(const ColumnView& column,
220-
const RowPermutation& row_perm,
221-
RowIndex* row) {
220+
bool Markowitz::IsResidualSingletonColumn(
221+
const ColumnView& column, StrictITISpan<RowIndex, const RowIndex> row_perm,
222+
RowIndex* row) {
222223
int residual_degree = 0;
223224
for (const auto e : column) {
224225
if (row_perm[e.row()] != kInvalidRow) continue;
@@ -238,7 +239,9 @@ void Markowitz::ExtractResidualSingletonColumns(
238239
for (ColIndex col(0); col < num_cols; ++col) {
239240
if ((*col_perm)[col] != kInvalidCol) continue;
240241
const ColumnView column = basis_matrix.column(col);
241-
if (!IsResidualSingletonColumn(column, *row_perm, &row)) continue;
242+
if (!IsResidualSingletonColumn(column, row_perm->const_view(), &row)) {
243+
continue;
244+
}
242245
(*col_perm)[col] = ColIndex(*index);
243246
(*row_perm)[row] = RowIndex(*index);
244247
lower_.AddDiagonalOnlyColumn(1.0);
@@ -569,8 +572,10 @@ void MatrixNonZeroPattern::Reset(RowIndex num_rows, ColIndex num_cols) {
569572
}
570573

571574
void MatrixNonZeroPattern::InitializeFromMatrixSubset(
572-
const CompactSparseMatrixView& basis_matrix, const RowPermutation& row_perm,
573-
const ColumnPermutation& col_perm, std::vector<ColIndex>* singleton_columns,
575+
const CompactSparseMatrixView& basis_matrix,
576+
StrictITISpan<RowIndex, const RowIndex> row_perm,
577+
StrictITISpan<ColIndex, const ColIndex> col_perm,
578+
std::vector<ColIndex>* singleton_columns,
574579
std::vector<RowIndex>* singleton_rows) {
575580
const ColIndex num_cols = basis_matrix.num_cols();
576581
const RowIndex num_rows = basis_matrix.num_rows();

ortools/glop/markowitz.h

+9-7
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,12 @@ class MatrixNonZeroPattern {
116116
// Resets the pattern to the one of the given matrix but only for the
117117
// rows/columns whose given permutation is kInvalidRow or kInvalidCol.
118118
// This also fills the singleton columns/rows with the corresponding entries.
119-
void InitializeFromMatrixSubset(const CompactSparseMatrixView& basis_matrix,
120-
const RowPermutation& row_perm,
121-
const ColumnPermutation& col_perm,
122-
std::vector<ColIndex>* singleton_columns,
123-
std::vector<RowIndex>* singleton_rows);
119+
void InitializeFromMatrixSubset(
120+
const CompactSparseMatrixView& basis_matrix,
121+
StrictITISpan<RowIndex, const RowIndex> row_perm,
122+
StrictITISpan<ColIndex, const ColIndex> col_perm,
123+
std::vector<ColIndex>* singleton_columns,
124+
std::vector<RowIndex>* singleton_rows);
124125

125126
// Adds a non-zero entry to the matrix. There should be no duplicates.
126127
void AddEntry(RowIndex row, ColIndex col);
@@ -377,8 +378,9 @@ class Markowitz {
377378

378379
// Helper function for determining if a column is a residual singleton column.
379380
// If it is, RowIndex* row contains the index of the single residual edge.
380-
bool IsResidualSingletonColumn(const ColumnView& column,
381-
const RowPermutation& row_perm, RowIndex* row);
381+
bool IsResidualSingletonColumn(
382+
const ColumnView& column,
383+
StrictITISpan<RowIndex, const RowIndex> row_perm, RowIndex* row);
382384

383385
// Returns the column of the current residual matrix with an index 'col' in
384386
// the initial matrix. We compute it by solving a linear system with the

ortools/glop/primal_edge_norms.cc

+12-1
Original file line numberDiff line numberDiff line change
@@ -156,16 +156,27 @@ void PrimalEdgeNorms::ComputeMatrixColumnNorms() {
156156
void PrimalEdgeNorms::ComputeEdgeSquaredNorms() {
157157
SCOPED_TIME_STAT(&stats_);
158158

159+
// time_limit_->LimitReached() can be costly sometimes, so we only do that
160+
// if we feel this will be slow anyway.
161+
const bool test_limit = (time_limit_ != nullptr) &&
162+
basis_factorization_.NumberOfEntriesInLU() > 10'000;
163+
159164
// Since we will do a lot of inversions, it is better to be as efficient and
160165
// precise as possible by refactorizing the basis.
161166
DCHECK(basis_factorization_.IsRefactorized());
162-
edge_squared_norms_.resize(compact_matrix_.num_cols(), 0.0);
167+
edge_squared_norms_.resize(compact_matrix_.num_cols(), 1.0);
163168
for (const ColIndex col : variables_info_.GetIsRelevantBitRow()) {
164169
// Note the +1.0 in the squared norm for the component of the edge on the
165170
// 'entering_col'.
166171
edge_squared_norms_[col] = 1.0 + basis_factorization_.RightSolveSquaredNorm(
167172
compact_matrix_.column(col));
173+
174+
// This operation can be costly, and we abort if we are stuck here.
175+
// Note that we still mark edges as "recomputed" otherwise we can runs into
176+
// some DCHECK before we actually abort the solve.
177+
if (test_limit && time_limit_->LimitReached()) break;
168178
}
179+
169180
recompute_edge_squared_norms_ = false;
170181
}
171182

ortools/glop/primal_edge_norms.h

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "ortools/lp_data/scattered_vector.h"
2828
#include "ortools/lp_data/sparse.h"
2929
#include "ortools/util/stats.h"
30+
#include "ortools/util/time_limit.h"
3031

3132
namespace operations_research {
3233
namespace glop {
@@ -146,6 +147,8 @@ class PrimalEdgeNorms {
146147
return DeterministicTimeForFpOperations(num_operations_);
147148
}
148149

150+
void SetTimeLimit(TimeLimit* time_limit) { time_limit_ = time_limit; }
151+
149152
private:
150153
// Statistics about this class.
151154
struct Stats : public StatsGroup {
@@ -192,6 +195,7 @@ class PrimalEdgeNorms {
192195
const CompactSparseMatrix& compact_matrix_;
193196
const VariablesInfo& variables_info_;
194197
const BasisFactorization& basis_factorization_;
198+
TimeLimit* time_limit_ = nullptr;
195199

196200
// Internal data.
197201
GlopParameters parameters_;

ortools/glop/rank_one_update.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ class RankOneUpdateFactorization {
177177
}
178178

179179
// y->is_non_zero is always all false before and after this code.
180-
DCHECK(IsAllFalse(y->is_non_zero));
180+
DCHECK(y->is_non_zero.IsAllFalse());
181181
y->RepopulateSparseMask();
182182
bool use_dense = y->ShouldUseDenseIteration(hypersparse_ratio_);
183183
for (int i = elementary_matrices_.size() - 1; i >= 0; --i) {
@@ -213,7 +213,7 @@ class RankOneUpdateFactorization {
213213
}
214214

215215
// d->is_non_zero is always all false before and after this code.
216-
DCHECK(IsAllFalse(d->is_non_zero));
216+
DCHECK(d->is_non_zero.IsAllFalse());
217217
d->RepopulateSparseMask();
218218
bool use_dense = d->ShouldUseDenseIteration(hypersparse_ratio_);
219219
const size_t end = elementary_matrices_.size();

ortools/glop/revised_simplex.cc

+5-2
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ ABSL_MUST_USE_RESULT Status RevisedSimplex::SolveInternal(
220220
[this, time_limit]() { AdvanceDeterministicTime(time_limit); });
221221

222222
SOLVER_LOG(logger_, "");
223+
primal_edge_norms_.SetTimeLimit(time_limit);
223224
if (logger_->LoggingIsEnabled()) {
224225
DisplayBasicVariableStatistics();
225226
}
@@ -2563,13 +2564,15 @@ void RevisedSimplex::PermuteBasis() {
25632564
if (col_perm.empty()) return;
25642565

25652566
// Permute basis_.
2566-
ApplyColumnPermutationToRowIndexedVector(col_perm, &basis_, &tmp_basis_);
2567+
ApplyColumnPermutationToRowIndexedVector(col_perm.const_view(), &basis_,
2568+
&tmp_basis_);
25672569

25682570
// Permute dual_pricing_vector_ if needed.
25692571
if (!dual_pricing_vector_.empty()) {
25702572
// TODO(user): We need to permute dual_prices_ too now, we recompute
25712573
// everything one each basis factorization, so this don't matter.
2572-
ApplyColumnPermutationToRowIndexedVector(col_perm, &dual_pricing_vector_,
2574+
ApplyColumnPermutationToRowIndexedVector(col_perm.const_view(),
2575+
&dual_pricing_vector_,
25732576
&tmp_dual_pricing_vector_);
25742577
}
25752578

ortools/glop/update_row.cc

+5-4
Original file line numberDiff line numberDiff line change
@@ -106,20 +106,21 @@ void UpdateRow::ComputeUpdateRow(RowIndex leaving_row) {
106106
// small entries (no complexity changes).
107107
const Fractional drop_tolerance = parameters_.drop_tolerance();
108108
unit_row_left_inverse_filtered_non_zeros_.clear();
109-
const auto view = transposed_matrix_.view();
109+
const auto matrix_view = transposed_matrix_.view();
110110
if (unit_row_left_inverse_.non_zeros.empty()) {
111111
const ColIndex size = unit_row_left_inverse_.values.size();
112+
const auto values = unit_row_left_inverse_.values.view();
112113
for (ColIndex col(0); col < size; ++col) {
113-
if (std::abs(unit_row_left_inverse_.values[col]) > drop_tolerance) {
114+
if (std::abs(values[col]) > drop_tolerance) {
114115
unit_row_left_inverse_filtered_non_zeros_.push_back(col);
115-
num_row_wise_entries += view.ColumnNumEntries(col);
116+
num_row_wise_entries += matrix_view.ColumnNumEntries(col);
116117
}
117118
}
118119
} else {
119120
for (const auto e : unit_row_left_inverse_) {
120121
if (std::abs(e.coefficient()) > drop_tolerance) {
121122
unit_row_left_inverse_filtered_non_zeros_.push_back(e.column());
122-
num_row_wise_entries += view.ColumnNumEntries(e.column());
123+
num_row_wise_entries += matrix_view.ColumnNumEntries(e.column());
123124
}
124125
}
125126
}

ortools/lp_data/lp_utils.h

+10-5
Original file line numberDiff line numberDiff line change
@@ -249,12 +249,17 @@ inline void PermuteWithScratchpad(
249249
const IndexType size = input_output->size();
250250
zero_scratchpad->swap(*input_output);
251251
input_output->resize(size, 0.0);
252-
for (IndexType index(0); index < size; ++index) {
253-
const Fractional value = (*zero_scratchpad)[index];
252+
253+
// Caching the pointers help.
254+
const Fractional* const input = zero_scratchpad->data();
255+
const IndexType* const perm = permutation.data();
256+
Fractional* const output = input_output->data();
257+
for (int i = 0; i < size; ++i) {
258+
DCHECK_GE(perm[i], 0);
259+
DCHECK_LT(perm[i], size);
260+
const Fractional value = input[i];
254261
if (value != 0.0) {
255-
const IndexType permuted_index(
256-
permutation[PermutationIndexType(index.value())].value());
257-
(*input_output)[permuted_index] = value;
262+
output[perm[i].value()] = value;
258263
}
259264
}
260265
zero_scratchpad->assign(size, 0.0);

0 commit comments

Comments
 (0)