speedup glop; better respect the time limit
This commit is contained in:
@@ -286,6 +286,7 @@ cc_library(
|
||||
"//ortools/lp_data:lp_utils",
|
||||
"//ortools/lp_data:scattered_vector",
|
||||
"//ortools/util:stats",
|
||||
"//ortools/util:time_limit",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -310,6 +310,10 @@ class BasisFactorization {
|
||||
// Returns the number of updates since last refactorization.
|
||||
int NumUpdates() const { return num_updates_; }
|
||||
|
||||
EntryIndex NumberOfEntriesInLU() const {
|
||||
return lu_factorization_.NumberOfEntries();
|
||||
}
|
||||
|
||||
private:
|
||||
// Called by ForceRefactorization() or Refactorize() or Initialize().
|
||||
Status ComputeFactorization();
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <cstdlib>
|
||||
|
||||
#include "ortools/lp_data/lp_utils.h"
|
||||
#include "ortools/lp_data/permutation.h"
|
||||
|
||||
namespace operations_research {
|
||||
namespace glop {
|
||||
@@ -42,8 +43,8 @@ DenseColumn::ConstView DualEdgeNorms::GetEdgeSquaredNorms() {
|
||||
void DualEdgeNorms::UpdateDataOnBasisPermutation(
|
||||
const ColumnPermutation& col_perm) {
|
||||
if (recompute_edge_squared_norms_) return;
|
||||
ApplyColumnPermutationToRowIndexedVector(col_perm, &edge_squared_norms_,
|
||||
&tmp_edge_squared_norms_);
|
||||
ApplyColumnPermutationToRowIndexedVector(
|
||||
col_perm.const_view(), &edge_squared_norms_, &tmp_edge_squared_norms_);
|
||||
}
|
||||
|
||||
bool DualEdgeNorms::TestPrecision(RowIndex leaving_row,
|
||||
|
||||
@@ -404,12 +404,14 @@ bool LuFactorization::LeftSolveLWithNonZeros(
|
||||
// use a different algorithm.
|
||||
ClearAndResizeVectorWithNonZeros(x->size(), result_before_permutation);
|
||||
x->swap(result_before_permutation->values);
|
||||
const auto input = result_before_permutation->values.const_view();
|
||||
const auto inverse_row_perm = inverse_row_perm_.const_view();
|
||||
if (nz->empty()) {
|
||||
const RowIndex num_rows = inverse_row_perm_.size();
|
||||
const RowIndex num_rows = inverse_row_perm.size();
|
||||
for (RowIndex row(0); row < num_rows; ++row) {
|
||||
const Fractional value = (*result_before_permutation)[row];
|
||||
const Fractional value = input[row];
|
||||
if (value != 0.0) {
|
||||
const RowIndex permuted_row = inverse_row_perm_[row];
|
||||
const RowIndex permuted_row = inverse_row_perm[row];
|
||||
(*x)[permuted_row] = value;
|
||||
}
|
||||
}
|
||||
@@ -417,8 +419,8 @@ bool LuFactorization::LeftSolveLWithNonZeros(
|
||||
nz->swap(result_before_permutation->non_zeros);
|
||||
nz->reserve(result_before_permutation->non_zeros.size());
|
||||
for (const RowIndex row : result_before_permutation->non_zeros) {
|
||||
const Fractional value = (*result_before_permutation)[row];
|
||||
const RowIndex permuted_row = inverse_row_perm_[row];
|
||||
const Fractional value = input[row];
|
||||
const RowIndex permuted_row = inverse_row_perm[row];
|
||||
(*x)[permuted_row] = value;
|
||||
nz->push_back(permuted_row);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,8 @@ Status Markowitz::ComputeRowAndColumnPermutation(
|
||||
// Initialize residual_matrix_non_zero_ with the submatrix left after we
|
||||
// removed the singleton and residual singleton columns.
|
||||
residual_matrix_non_zero_.InitializeFromMatrixSubset(
|
||||
basis_matrix, *row_perm, *col_perm, &singleton_column_, &singleton_row_);
|
||||
basis_matrix, row_perm->const_view(), col_perm->const_view(),
|
||||
&singleton_column_, &singleton_row_);
|
||||
|
||||
// Perform Gaussian elimination.
|
||||
const int end_index = std::min(num_rows.value(), num_cols.value());
|
||||
@@ -216,9 +217,9 @@ void Markowitz::ExtractSingletonColumns(
|
||||
basis_matrix.num_rows().value());
|
||||
}
|
||||
|
||||
bool Markowitz::IsResidualSingletonColumn(const ColumnView& column,
|
||||
const RowPermutation& row_perm,
|
||||
RowIndex* row) {
|
||||
bool Markowitz::IsResidualSingletonColumn(
|
||||
const ColumnView& column, StrictITISpan<RowIndex, const RowIndex> row_perm,
|
||||
RowIndex* row) {
|
||||
int residual_degree = 0;
|
||||
for (const auto e : column) {
|
||||
if (row_perm[e.row()] != kInvalidRow) continue;
|
||||
@@ -238,7 +239,9 @@ void Markowitz::ExtractResidualSingletonColumns(
|
||||
for (ColIndex col(0); col < num_cols; ++col) {
|
||||
if ((*col_perm)[col] != kInvalidCol) continue;
|
||||
const ColumnView column = basis_matrix.column(col);
|
||||
if (!IsResidualSingletonColumn(column, *row_perm, &row)) continue;
|
||||
if (!IsResidualSingletonColumn(column, row_perm->const_view(), &row)) {
|
||||
continue;
|
||||
}
|
||||
(*col_perm)[col] = ColIndex(*index);
|
||||
(*row_perm)[row] = RowIndex(*index);
|
||||
lower_.AddDiagonalOnlyColumn(1.0);
|
||||
@@ -569,8 +572,10 @@ void MatrixNonZeroPattern::Reset(RowIndex num_rows, ColIndex num_cols) {
|
||||
}
|
||||
|
||||
void MatrixNonZeroPattern::InitializeFromMatrixSubset(
|
||||
const CompactSparseMatrixView& basis_matrix, const RowPermutation& row_perm,
|
||||
const ColumnPermutation& col_perm, std::vector<ColIndex>* singleton_columns,
|
||||
const CompactSparseMatrixView& basis_matrix,
|
||||
StrictITISpan<RowIndex, const RowIndex> row_perm,
|
||||
StrictITISpan<ColIndex, const ColIndex> col_perm,
|
||||
std::vector<ColIndex>* singleton_columns,
|
||||
std::vector<RowIndex>* singleton_rows) {
|
||||
const ColIndex num_cols = basis_matrix.num_cols();
|
||||
const RowIndex num_rows = basis_matrix.num_rows();
|
||||
|
||||
@@ -116,11 +116,12 @@ class MatrixNonZeroPattern {
|
||||
// Resets the pattern to the one of the given matrix but only for the
|
||||
// rows/columns whose given permutation is kInvalidRow or kInvalidCol.
|
||||
// This also fills the singleton columns/rows with the corresponding entries.
|
||||
void InitializeFromMatrixSubset(const CompactSparseMatrixView& basis_matrix,
|
||||
const RowPermutation& row_perm,
|
||||
const ColumnPermutation& col_perm,
|
||||
std::vector<ColIndex>* singleton_columns,
|
||||
std::vector<RowIndex>* singleton_rows);
|
||||
void InitializeFromMatrixSubset(
|
||||
const CompactSparseMatrixView& basis_matrix,
|
||||
StrictITISpan<RowIndex, const RowIndex> row_perm,
|
||||
StrictITISpan<ColIndex, const ColIndex> col_perm,
|
||||
std::vector<ColIndex>* singleton_columns,
|
||||
std::vector<RowIndex>* singleton_rows);
|
||||
|
||||
// Adds a non-zero entry to the matrix. There should be no duplicates.
|
||||
void AddEntry(RowIndex row, ColIndex col);
|
||||
@@ -377,8 +378,9 @@ class Markowitz {
|
||||
|
||||
// Helper function for determining if a column is a residual singleton column.
|
||||
// If it is, RowIndex* row contains the index of the single residual edge.
|
||||
bool IsResidualSingletonColumn(const ColumnView& column,
|
||||
const RowPermutation& row_perm, RowIndex* row);
|
||||
bool IsResidualSingletonColumn(
|
||||
const ColumnView& column,
|
||||
StrictITISpan<RowIndex, const RowIndex> row_perm, RowIndex* row);
|
||||
|
||||
// Returns the column of the current residual matrix with an index 'col' in
|
||||
// the initial matrix. We compute it by solving a linear system with the
|
||||
|
||||
@@ -156,16 +156,27 @@ void PrimalEdgeNorms::ComputeMatrixColumnNorms() {
|
||||
void PrimalEdgeNorms::ComputeEdgeSquaredNorms() {
|
||||
SCOPED_TIME_STAT(&stats_);
|
||||
|
||||
// time_limit_->LimitReached() can be costly sometimes, so we only do that
|
||||
// if we feel this will be slow anyway.
|
||||
const bool test_limit = (time_limit_ != nullptr) &&
|
||||
basis_factorization_.NumberOfEntriesInLU() > 10'000;
|
||||
|
||||
// Since we will do a lot of inversions, it is better to be as efficient and
|
||||
// precise as possible by refactorizing the basis.
|
||||
DCHECK(basis_factorization_.IsRefactorized());
|
||||
edge_squared_norms_.resize(compact_matrix_.num_cols(), 0.0);
|
||||
edge_squared_norms_.resize(compact_matrix_.num_cols(), 1.0);
|
||||
for (const ColIndex col : variables_info_.GetIsRelevantBitRow()) {
|
||||
// Note the +1.0 in the squared norm for the component of the edge on the
|
||||
// 'entering_col'.
|
||||
edge_squared_norms_[col] = 1.0 + basis_factorization_.RightSolveSquaredNorm(
|
||||
compact_matrix_.column(col));
|
||||
|
||||
// This operation can be costly, and we abort if we are stuck here.
|
||||
// Note that we still mark edges as "recomputed" otherwise we can runs into
|
||||
// some DCHECK before we actually abort the solve.
|
||||
if (test_limit && time_limit_->LimitReached()) break;
|
||||
}
|
||||
|
||||
recompute_edge_squared_norms_ = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "ortools/lp_data/scattered_vector.h"
|
||||
#include "ortools/lp_data/sparse.h"
|
||||
#include "ortools/util/stats.h"
|
||||
#include "ortools/util/time_limit.h"
|
||||
|
||||
namespace operations_research {
|
||||
namespace glop {
|
||||
@@ -146,6 +147,8 @@ class PrimalEdgeNorms {
|
||||
return DeterministicTimeForFpOperations(num_operations_);
|
||||
}
|
||||
|
||||
void SetTimeLimit(TimeLimit* time_limit) { time_limit_ = time_limit; }
|
||||
|
||||
private:
|
||||
// Statistics about this class.
|
||||
struct Stats : public StatsGroup {
|
||||
@@ -192,6 +195,7 @@ class PrimalEdgeNorms {
|
||||
const CompactSparseMatrix& compact_matrix_;
|
||||
const VariablesInfo& variables_info_;
|
||||
const BasisFactorization& basis_factorization_;
|
||||
TimeLimit* time_limit_ = nullptr;
|
||||
|
||||
// Internal data.
|
||||
GlopParameters parameters_;
|
||||
|
||||
@@ -177,7 +177,7 @@ class RankOneUpdateFactorization {
|
||||
}
|
||||
|
||||
// y->is_non_zero is always all false before and after this code.
|
||||
DCHECK(IsAllFalse(y->is_non_zero));
|
||||
DCHECK(y->is_non_zero.IsAllFalse());
|
||||
y->RepopulateSparseMask();
|
||||
bool use_dense = y->ShouldUseDenseIteration(hypersparse_ratio_);
|
||||
for (int i = elementary_matrices_.size() - 1; i >= 0; --i) {
|
||||
@@ -213,7 +213,7 @@ class RankOneUpdateFactorization {
|
||||
}
|
||||
|
||||
// d->is_non_zero is always all false before and after this code.
|
||||
DCHECK(IsAllFalse(d->is_non_zero));
|
||||
DCHECK(d->is_non_zero.IsAllFalse());
|
||||
d->RepopulateSparseMask();
|
||||
bool use_dense = d->ShouldUseDenseIteration(hypersparse_ratio_);
|
||||
const size_t end = elementary_matrices_.size();
|
||||
|
||||
@@ -220,6 +220,7 @@ ABSL_MUST_USE_RESULT Status RevisedSimplex::SolveInternal(
|
||||
[this, time_limit]() { AdvanceDeterministicTime(time_limit); });
|
||||
|
||||
SOLVER_LOG(logger_, "");
|
||||
primal_edge_norms_.SetTimeLimit(time_limit);
|
||||
if (logger_->LoggingIsEnabled()) {
|
||||
DisplayBasicVariableStatistics();
|
||||
}
|
||||
@@ -2563,13 +2564,15 @@ void RevisedSimplex::PermuteBasis() {
|
||||
if (col_perm.empty()) return;
|
||||
|
||||
// Permute basis_.
|
||||
ApplyColumnPermutationToRowIndexedVector(col_perm, &basis_, &tmp_basis_);
|
||||
ApplyColumnPermutationToRowIndexedVector(col_perm.const_view(), &basis_,
|
||||
&tmp_basis_);
|
||||
|
||||
// Permute dual_pricing_vector_ if needed.
|
||||
if (!dual_pricing_vector_.empty()) {
|
||||
// TODO(user): We need to permute dual_prices_ too now, we recompute
|
||||
// everything one each basis factorization, so this don't matter.
|
||||
ApplyColumnPermutationToRowIndexedVector(col_perm, &dual_pricing_vector_,
|
||||
ApplyColumnPermutationToRowIndexedVector(col_perm.const_view(),
|
||||
&dual_pricing_vector_,
|
||||
&tmp_dual_pricing_vector_);
|
||||
}
|
||||
|
||||
|
||||
@@ -106,20 +106,21 @@ void UpdateRow::ComputeUpdateRow(RowIndex leaving_row) {
|
||||
// small entries (no complexity changes).
|
||||
const Fractional drop_tolerance = parameters_.drop_tolerance();
|
||||
unit_row_left_inverse_filtered_non_zeros_.clear();
|
||||
const auto view = transposed_matrix_.view();
|
||||
const auto matrix_view = transposed_matrix_.view();
|
||||
if (unit_row_left_inverse_.non_zeros.empty()) {
|
||||
const ColIndex size = unit_row_left_inverse_.values.size();
|
||||
const auto values = unit_row_left_inverse_.values.view();
|
||||
for (ColIndex col(0); col < size; ++col) {
|
||||
if (std::abs(unit_row_left_inverse_.values[col]) > drop_tolerance) {
|
||||
if (std::abs(values[col]) > drop_tolerance) {
|
||||
unit_row_left_inverse_filtered_non_zeros_.push_back(col);
|
||||
num_row_wise_entries += view.ColumnNumEntries(col);
|
||||
num_row_wise_entries += matrix_view.ColumnNumEntries(col);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto e : unit_row_left_inverse_) {
|
||||
if (std::abs(e.coefficient()) > drop_tolerance) {
|
||||
unit_row_left_inverse_filtered_non_zeros_.push_back(e.column());
|
||||
num_row_wise_entries += view.ColumnNumEntries(e.column());
|
||||
num_row_wise_entries += matrix_view.ColumnNumEntries(e.column());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,12 +249,17 @@ inline void PermuteWithScratchpad(
|
||||
const IndexType size = input_output->size();
|
||||
zero_scratchpad->swap(*input_output);
|
||||
input_output->resize(size, 0.0);
|
||||
for (IndexType index(0); index < size; ++index) {
|
||||
const Fractional value = (*zero_scratchpad)[index];
|
||||
|
||||
// Caching the pointers help.
|
||||
const Fractional* const input = zero_scratchpad->data();
|
||||
const IndexType* const perm = permutation.data();
|
||||
Fractional* const output = input_output->data();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
DCHECK_GE(perm[i], 0);
|
||||
DCHECK_LT(perm[i], size);
|
||||
const Fractional value = input[i];
|
||||
if (value != 0.0) {
|
||||
const IndexType permuted_index(
|
||||
permutation[PermutationIndexType(index.value())].value());
|
||||
(*input_output)[permuted_index] = value;
|
||||
output[perm[i].value()] = value;
|
||||
}
|
||||
}
|
||||
zero_scratchpad->assign(size, 0.0);
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
#ifndef OR_TOOLS_LP_DATA_PERMUTATION_H_
|
||||
#define OR_TOOLS_LP_DATA_PERMUTATION_H_
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "absl/log/check.h"
|
||||
#include "absl/random/random.h"
|
||||
#include "ortools/base/strong_vector.h"
|
||||
#include "ortools/lp_data/lp_types.h"
|
||||
@@ -45,13 +48,13 @@ class Permutation {
|
||||
public:
|
||||
Permutation() : perm_() {}
|
||||
|
||||
explicit Permutation(IndexType size) : perm_(size.value(), IndexType(0)) {}
|
||||
explicit Permutation(IndexType size) : perm_(size, IndexType(0)) {}
|
||||
|
||||
// This type is neither copyable nor movable.
|
||||
Permutation(const Permutation&) = delete;
|
||||
Permutation& operator=(const Permutation&) = delete;
|
||||
|
||||
IndexType size() const { return IndexType(perm_.size()); }
|
||||
IndexType size() const { return perm_.size(); }
|
||||
bool empty() const { return perm_.empty(); }
|
||||
|
||||
void clear() { perm_.clear(); }
|
||||
@@ -60,9 +63,7 @@ class Permutation {
|
||||
perm_.resize(size.value(), value);
|
||||
}
|
||||
|
||||
void assign(IndexType size, IndexType value) {
|
||||
perm_.assign(size.value(), value);
|
||||
}
|
||||
void assign(IndexType size, IndexType value) { perm_.assign(size, value); }
|
||||
|
||||
IndexType& operator[](IndexType i) { return perm_[i]; }
|
||||
|
||||
@@ -89,8 +90,16 @@ class Permutation {
|
||||
// is -1. (Remembering hint: the signature of a swap (a 2-cycle) is -1.)
|
||||
int ComputeSignature() const;
|
||||
|
||||
// For hot-loops it might be slighlty faster to cache the pointer and avoid
|
||||
// bound checking on each [] access.
|
||||
const IndexType* data() const { return perm_.data(); }
|
||||
|
||||
StrictITISpan<IndexType, const IndexType> const_view() const {
|
||||
return perm_.view();
|
||||
}
|
||||
|
||||
private:
|
||||
util_intops::StrongVector<IndexType, IndexType> perm_;
|
||||
StrictITIVector<IndexType, IndexType> perm_;
|
||||
};
|
||||
|
||||
typedef Permutation<RowIndex> RowPermutation;
|
||||
@@ -116,16 +125,21 @@ void ApplyInversePermutation(const Permutation<IndexType>& perm,
|
||||
// row-indexed vector v.
|
||||
template <typename RowIndexedVector>
|
||||
void ApplyColumnPermutationToRowIndexedVector(
|
||||
const Permutation<ColIndex>& col_perm, RowIndexedVector* v) {
|
||||
RowIndexedVector temp_v = *v;
|
||||
ApplyPermutation(col_perm, temp_v, v);
|
||||
}
|
||||
|
||||
template <typename RowIndexedVector>
|
||||
void ApplyColumnPermutationToRowIndexedVector(
|
||||
const Permutation<ColIndex>& col_perm, RowIndexedVector* v,
|
||||
StrictITISpan<ColIndex, const ColIndex> col_perm, RowIndexedVector* v,
|
||||
RowIndexedVector* tmp) {
|
||||
ApplyPermutation(col_perm, *v, tmp);
|
||||
// Empty size means identity.
|
||||
const int size = col_perm.size().value();
|
||||
if (size == 0) return;
|
||||
|
||||
// Permute into tmp and swap.
|
||||
DCHECK_EQ(size, v->size());
|
||||
|
||||
tmp->resize(RowIndex(size));
|
||||
const auto* from = v->data();
|
||||
auto* to = tmp->data();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
to[col_perm[ColIndex(i)].value()] = from[i];
|
||||
}
|
||||
std::swap(*tmp, *v);
|
||||
}
|
||||
|
||||
@@ -135,7 +149,7 @@ void ApplyColumnPermutationToRowIndexedVector(
|
||||
|
||||
template <typename IndexType>
|
||||
void Permutation<IndexType>::PopulateFromInverse(const Permutation& inverse) {
|
||||
const size_t size = inverse.perm_.size();
|
||||
const IndexType size = inverse.perm_.size();
|
||||
perm_.resize(size);
|
||||
for (IndexType i(0); i < size; ++i) {
|
||||
perm_[inverse[i]] = i;
|
||||
@@ -144,7 +158,7 @@ void Permutation<IndexType>::PopulateFromInverse(const Permutation& inverse) {
|
||||
|
||||
template <typename IndexType>
|
||||
void Permutation<IndexType>::PopulateFromIdentity() {
|
||||
const size_t size = perm_.size();
|
||||
const IndexType size = perm_.size();
|
||||
perm_.resize(size, IndexType(0));
|
||||
for (IndexType i(0); i < size; ++i) {
|
||||
perm_[i] = i;
|
||||
@@ -159,7 +173,7 @@ void Permutation<IndexType>::PopulateRandomly() {
|
||||
|
||||
template <typename IndexType>
|
||||
bool Permutation<IndexType>::Check() const {
|
||||
const size_t size = perm_.size();
|
||||
const size_t size = perm_.size().value();
|
||||
util_intops::StrongVector<IndexType, bool> visited(size, false);
|
||||
for (IndexType i(0); i < size; ++i) {
|
||||
if (perm_[i] < 0 || perm_[i] >= size) {
|
||||
@@ -177,7 +191,7 @@ bool Permutation<IndexType>::Check() const {
|
||||
|
||||
template <typename IndexType>
|
||||
int Permutation<IndexType>::ComputeSignature() const {
|
||||
const size_t size = perm_.size();
|
||||
const size_t size = perm_.size().value();
|
||||
util_intops::StrongVector<IndexType, bool> visited(size);
|
||||
DCHECK(Check());
|
||||
int signature = 1;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "absl/log/check.h"
|
||||
#include "ortools/lp_data/lp_types.h"
|
||||
#include "ortools/util/bitset.h"
|
||||
|
||||
namespace operations_research {
|
||||
namespace glop {
|
||||
@@ -61,7 +62,7 @@ struct ScatteredVector {
|
||||
// Temporary vector used in some sparse computation on the ScatteredVector.
|
||||
// True indicates a possible non-zero value. Note that its state is not always
|
||||
// consistent.
|
||||
StrictITIVector<Index, bool> is_non_zero;
|
||||
Bitset64<Index> is_non_zero;
|
||||
|
||||
// In many cases there is a choice between treating the ScatteredVector as
|
||||
// dense or as sparse. By default, dense algorithms are used when the
|
||||
@@ -95,7 +96,7 @@ struct ScatteredVector {
|
||||
void Add(Index index, Fractional value) {
|
||||
values[index] += value;
|
||||
if (!is_non_zero[index] && value != 0.0) {
|
||||
is_non_zero[index] = true;
|
||||
is_non_zero.Set(index);
|
||||
non_zeros.push_back(index);
|
||||
non_zeros_are_sorted = false;
|
||||
}
|
||||
@@ -128,20 +129,23 @@ struct ScatteredVector {
|
||||
// Efficiently clears the is_non_zero vector.
|
||||
void ClearSparseMask() {
|
||||
if (ShouldUseDenseIteration()) {
|
||||
is_non_zero.assign(values.size(), false);
|
||||
is_non_zero.ClearAndResize(values.size());
|
||||
} else {
|
||||
is_non_zero.resize(values.size(), false);
|
||||
is_non_zero.Resize(values.size());
|
||||
for (const Index index : non_zeros) {
|
||||
is_non_zero[index] = false;
|
||||
is_non_zero.ClearBucket(index);
|
||||
}
|
||||
DCHECK(IsAllFalse(is_non_zero));
|
||||
DCHECK(is_non_zero.IsAllFalse());
|
||||
}
|
||||
}
|
||||
|
||||
// Update the is_non_zero vector to be consistent with the non_zeros vector.
|
||||
void RepopulateSparseMask() {
|
||||
ClearSparseMask();
|
||||
for (const Index index : non_zeros) is_non_zero[index] = true;
|
||||
for (const Index index : non_zeros) {
|
||||
// is_non_zero[index] = true;
|
||||
is_non_zero.Set(index);
|
||||
}
|
||||
}
|
||||
|
||||
// If the proportion of non-zero entries is too large, clears the vector of
|
||||
|
||||
@@ -1481,10 +1481,11 @@ void TriangularMatrix::ComputeRowsToConsiderInSortedOrder(
|
||||
stored.Set(row);
|
||||
}
|
||||
|
||||
const auto matrix_view = view();
|
||||
const auto entry_rows = rows_.view();
|
||||
for (int i = 0; i < non_zero_rows->size(); ++i) {
|
||||
const RowIndex row = (*non_zero_rows)[i];
|
||||
for (const EntryIndex index : Column(RowToColIndex(row))) {
|
||||
for (const EntryIndex index : matrix_view.Column(RowToColIndex(row))) {
|
||||
++num_ops;
|
||||
const RowIndex entry_row = entry_rows[index];
|
||||
if (!stored[entry_row]) {
|
||||
|
||||
@@ -592,11 +592,13 @@ class Bitset64 {
|
||||
// the higher order bits are assumed to be 0.
|
||||
void Intersection(const Bitset64<IndexType>& other) {
|
||||
const int min_size = std::min(data_.size(), other.data_.size());
|
||||
uint64_t* const d = data_.data();
|
||||
const uint64_t* const o = other.data_.data();
|
||||
for (int i = 0; i < min_size; ++i) {
|
||||
data_[i] &= other.data_[i];
|
||||
d[i] &= o[i];
|
||||
}
|
||||
for (int i = min_size; i < data_.size(); ++i) {
|
||||
data_[i] = 0;
|
||||
d[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -725,6 +727,11 @@ class Bitset64 {
|
||||
return output;
|
||||
}
|
||||
|
||||
bool IsAllFalse() const {
|
||||
return std::all_of(data_.begin(), data_.end(),
|
||||
[](uint64_t v) { return v == 0; });
|
||||
}
|
||||
|
||||
private:
|
||||
// Returns the value of the index type.
|
||||
// This function is specialized below to work with IntType and int64_t.
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
* Enables changing the behavior of the TimeLimit class to use -b usertime
|
||||
* instead of \b walltime. This is mainly useful for benchmarks.
|
||||
*/
|
||||
OR_DLL ABSL_DECLARE_FLAG(bool, time_limit_use_usertime);
|
||||
ABSL_DECLARE_FLAG(bool, time_limit_use_usertime);
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace operations_research {
|
||||
*/
|
||||
// TODO(user): The expression "deterministic time" should be replaced with
|
||||
// "number of operations" to avoid confusion with "real" time.
|
||||
class OR_DLL TimeLimit {
|
||||
class TimeLimit {
|
||||
public:
|
||||
static const double kSafetyBufferSeconds; // See the .cc for the value.
|
||||
static const int kHistorySize;
|
||||
|
||||
Reference in New Issue
Block a user