19#include "absl/strings/str_format.h"
32 const RowIndex num_rows = basis_matrix.
num_rows();
33 const ColIndex num_cols = basis_matrix.
num_cols();
39 basis_matrix_ = &basis_matrix;
42 lower_.
Reset(num_rows, num_cols);
43 upper_.
Reset(num_rows, num_cols);
44 permuted_lower_.
Reset(num_cols);
45 permuted_upper_.
Reset(num_cols);
46 permuted_lower_column_needs_solve_.
assign(num_cols,
false);
47 contains_only_singleton_columns_ =
true;
53 ExtractSingletonColumns(basis_matrix, row_perm, col_perm, &
index);
54 ExtractResidualSingletonColumns(basis_matrix, row_perm, col_perm, &
index);
55 int stats_num_pivots_without_fill_in =
index;
56 int stats_degree_two_pivot_columns = 0;
61 basis_matrix, *row_perm, *col_perm, &singleton_column_, &singleton_row_);
64 const int end_index =
std::min(num_rows.value(), num_cols.value());
67 while (
index < end_index) {
76 const int64_t min_markowitz = FindPivot(*row_perm, *col_perm, &pivot_row,
77 &pivot_col, &pivot_coefficient);
84 std::abs(pivot_coefficient) <= singularity_threshold) {
85 const std::string error_message = absl::StrFormat(
86 "The matrix is singular! pivot = %E", pivot_coefficient);
87 VLOG(1) <<
"ERROR_LU: " << error_message;
97 const int pivot_col_degree = residual_matrix_non_zero_.
ColDegree(pivot_col);
98 const int pivot_row_degree = residual_matrix_non_zero_.
RowDegree(pivot_row);
100 if (min_markowitz == 0) {
101 ++stats_num_pivots_without_fill_in;
102 if (pivot_col_degree == 1) {
103 RemoveRowFromResidualMatrix(pivot_row, pivot_col);
106 RemoveColumnFromResidualMatrix(pivot_row, pivot_col);
113 if (pivot_col_degree == 2) { ++stats_degree_two_pivot_columns; });
114 UpdateResidualMatrix(pivot_row, pivot_col);
117 if (contains_only_singleton_columns_) {
123 pivot_row, pivot_coefficient);
127 permuted_upper_.
column(pivot_col), pivot_row, pivot_coefficient);
132 (*col_perm)[pivot_col] = ColIndex(
index);
133 (*row_perm)[pivot_row] = RowIndex(
index);
139 num_fp_operations_ += 10 * lower_.
num_entries().value();
140 num_fp_operations_ += 10 * upper_.
num_entries().value();
142 stats_.pivots_without_fill_in_ratio.Add(
143 1.0 * stats_num_pivots_without_fill_in / num_rows.value());
144 stats_.degree_two_pivot_columns.Add(1.0 * stats_degree_two_pivot_columns /
171 permuted_lower_.
Clear();
172 permuted_upper_.
Clear();
173 residual_matrix_non_zero_.
Clear();
174 col_by_degree_.
Clear();
175 examined_col_.clear();
176 num_fp_operations_ = 0;
177 is_col_by_degree_initialized_ =
false;
185 MatrixEntry(RowIndex r, ColIndex c,
Fractional coeff)
187 bool operator<(
const MatrixEntry& o)
const {
188 return (
row == o.row) ?
col < o.col :
row < o.row;
194void Markowitz::ExtractSingletonColumns(
195 const CompactSparseMatrixView& basis_matrix,
RowPermutation* row_perm,
198 std::vector<MatrixEntry> singleton_entries;
199 const ColIndex num_cols = basis_matrix.num_cols();
200 for (ColIndex
col(0);
col < num_cols; ++
col) {
201 const ColumnView& column = basis_matrix.column(
col);
202 if (column.num_entries().value() == 1) {
203 singleton_entries.push_back(
204 MatrixEntry(column.GetFirstRow(),
col, column.GetFirstCoefficient()));
210 std::sort(singleton_entries.begin(), singleton_entries.end());
211 for (
const MatrixEntry e : singleton_entries) {
213 (*col_perm)[e.col] = ColIndex(*
index);
214 (*row_perm)[e.row] = RowIndex(*
index);
220 stats_.basis_singleton_column_ratio.Add(
static_cast<double>(*
index) /
221 basis_matrix.num_rows().value());
224bool Markowitz::IsResidualSingletonColumn(
const ColumnView& column,
227 int residual_degree = 0;
228 for (
const auto e : column) {
231 if (residual_degree > 1)
return false;
234 return residual_degree == 1;
237void Markowitz::ExtractResidualSingletonColumns(
238 const CompactSparseMatrixView& basis_matrix,
RowPermutation* row_perm,
241 const ColIndex num_cols = basis_matrix.num_cols();
243 for (ColIndex
col(0);
col < num_cols; ++
col) {
245 const ColumnView& column = basis_matrix.column(
col);
246 if (!IsResidualSingletonColumn(column, *row_perm, &
row))
continue;
247 (*col_perm)[
col] = ColIndex(*
index);
248 (*row_perm)[
row] = RowIndex(*
index);
253 stats_.basis_residual_singleton_column_ratio.Add(
254 static_cast<double>(*
index) / basis_matrix.num_rows().value());
257const SparseColumn& Markowitz::ComputeColumn(
const RowPermutation& row_perm,
272 if (permuted_lower_column_needs_solve_[
col]) {
276 const ColumnView&
input =
277 first_time ? basis_matrix_->
column(
col) : ColumnView(*lower_column);
280 permuted_lower_column_needs_solve_[
col] =
false;
281 num_fp_operations_ +=
283 return *lower_column;
289 if (lower_column->num_entries() == residual_matrix_non_zero_.
ColDegree(
col)) {
290 return *lower_column;
300 for (
const auto e : basis_matrix_->
column(
col)) {
301 lower_column->SetCoefficient(e.row(), e.coefficient());
304 num_fp_operations_ += lower_column->num_entries().value();
305 lower_column->MoveTaggedEntriesTo(row_perm,
307 return *lower_column;
312 RowIndex* pivot_row, ColIndex* pivot_col,
317 while (!singleton_column_.empty()) {
318 const ColIndex
col = singleton_column_.back();
319 singleton_column_.pop_back();
328 if (residual_matrix_non_zero_.
ColDegree(
col) != 1)
continue;
333 if (contains_only_singleton_columns_) {
337 *pivot_row = e.row();
338 *pivot_coefficient = e.coefficient();
344 const SparseColumn& column = ComputeColumn(row_perm,
col);
345 if (column.IsEmpty())
continue;
347 *pivot_row = column.GetFirstRow();
348 *pivot_coefficient = column.GetFirstCoefficient();
351 contains_only_singleton_columns_ =
false;
356 while (!singleton_row_.empty()) {
357 const RowIndex
row = singleton_row_.back();
358 singleton_row_.pop_back();
366 if (residual_matrix_non_zero_.
RowDegree(
row) != 1)
continue;
370 const SparseColumn& column = ComputeColumn(row_perm,
col);
371 if (column.IsEmpty())
continue;
375 *pivot_coefficient = column.LookUpCoefficient(
row);
381 if (!is_col_by_degree_initialized_) {
382 is_col_by_degree_initialized_ =
true;
383 const ColIndex num_cols = col_perm.size();
384 col_by_degree_.
Reset(row_perm.size().value(), num_cols);
385 for (ColIndex
col(0);
col < num_cols; ++
col) {
387 const int degree = residual_matrix_non_zero_.
ColDegree(
col);
389 UpdateDegree(
col, degree);
396 examined_col_.clear();
399 while (examined_col_.size() < num_columns_to_examine) {
400 const ColIndex
col = col_by_degree_.
Pop();
403 const int col_degree = residual_matrix_non_zero_.
ColDegree(
col);
404 examined_col_.push_back(
col);
415 const int64_t markowitz_lower_bound = col_degree - 1;
416 if (min_markowitz_number < markowitz_lower_bound)
break;
422 const SparseColumn& column = ComputeColumn(row_perm,
col);
423 DCHECK_EQ(column.num_entries(), col_degree);
427 max_magnitude =
std::max(max_magnitude, std::abs(e.coefficient()));
429 if (max_magnitude == 0.0) {
432 examined_col_.pop_back();
436 const Fractional skip_threshold = threshold * max_magnitude;
438 const Fractional magnitude = std::abs(e.coefficient());
439 if (magnitude < skip_threshold)
continue;
441 const int row_degree = residual_matrix_non_zero_.
RowDegree(e.row());
442 const int64_t markowitz_number = (col_degree - 1) * (row_degree - 1);
444 if (markowitz_number < min_markowitz_number ||
445 ((markowitz_number == min_markowitz_number) &&
446 magnitude > std::abs(*pivot_coefficient))) {
447 min_markowitz_number = markowitz_number;
449 *pivot_row = e.row();
450 *pivot_coefficient = e.coefficient();
459 DCHECK_GE(min_markowitz_number, markowitz_lower_bound);
473 for (
const ColIndex
col : examined_col_) {
474 if (
col != *pivot_col) {
475 const int degree = residual_matrix_non_zero_.
ColDegree(
col);
479 return min_markowitz_number;
482void Markowitz::UpdateDegree(ColIndex
col,
int degree) {
483 DCHECK(is_col_by_degree_initialized_);
495 singleton_column_.push_back(
col);
501void Markowitz::RemoveRowFromResidualMatrix(RowIndex pivot_row,
502 ColIndex pivot_col) {
508 if (is_col_by_degree_initialized_) {
509 for (
const ColIndex
col : residual_matrix_non_zero_.
RowNonZero(pivot_row)) {
514 for (
const ColIndex
col : residual_matrix_non_zero_.
RowNonZero(pivot_row)) {
517 singleton_column_.push_back(
col);
523void Markowitz::RemoveColumnFromResidualMatrix(RowIndex pivot_row,
524 ColIndex pivot_col) {
534 const RowIndex
row = e.row();
536 singleton_row_.push_back(
row);
541void Markowitz::UpdateResidualMatrix(RowIndex pivot_row, ColIndex pivot_col) {
543 const SparseColumn& pivot_column = permuted_lower_.
column(pivot_col);
544 residual_matrix_non_zero_.
Update(pivot_row, pivot_col, pivot_column);
545 for (
const ColIndex
col : residual_matrix_non_zero_.
RowNonZero(pivot_row)) {
548 permuted_lower_column_needs_solve_[
col] =
true;
550 RemoveColumnFromResidualMatrix(pivot_row, pivot_col);
560 row_non_zero_.clear();
561 deleted_columns_.
clear();
562 bool_scratchpad_.
clear();
563 num_non_deleted_columns_ = 0;
567 row_degree_.AssignToZero(num_rows);
568 col_degree_.AssignToZero(num_cols);
569 row_non_zero_.clear();
570 row_non_zero_.resize(num_rows.value());
571 deleted_columns_.
assign(num_cols,
false);
572 bool_scratchpad_.
assign(num_cols,
false);
573 num_non_deleted_columns_ = num_cols;
579 std::vector<RowIndex>* singleton_rows) {
580 const ColIndex num_cols = basis_matrix.
num_cols();
581 const RowIndex num_rows = basis_matrix.
num_rows();
584 Reset(num_rows, num_cols);
585 singleton_columns->clear();
586 singleton_rows->clear();
589 for (ColIndex
col(0);
col < num_cols; ++
col) {
591 deleted_columns_[
col] =
true;
592 --num_non_deleted_columns_;
596 ++row_degree_[e.row()];
601 for (RowIndex
row(0);
row < num_rows; ++
row) {
603 row_non_zero_[
row].reserve(row_degree_[
row]);
604 if (row_degree_[
row] == 1) singleton_rows->push_back(
row);
608 row_degree_[
row] = 0;
613 for (ColIndex
col(0);
col < num_cols; ++
col) {
615 int32_t col_degree = 0;
617 const RowIndex
row = e.row();
620 row_non_zero_[
row].push_back(
col);
623 col_degree_[
col] = col_degree;
624 if (col_degree == 1) singleton_columns->push_back(
col);
631 row_non_zero_[
row].push_back(
col);
635 return --col_degree_[
col];
639 return --row_degree_[
row];
643 ColIndex pivot_col) {
644 DCHECK(!deleted_columns_[pivot_col]);
645 deleted_columns_[pivot_col] =
true;
646 --num_non_deleted_columns_;
649 row_degree_[pivot_row] = 0;
653 return deleted_columns_[
col];
657 auto& ref = row_non_zero_[
row];
659 const int end = ref.size();
660 for (
int i = 0; i < end; ++i) {
661 const ColIndex
col = ref[i];
662 if (!deleted_columns_[
col]) {
663 ref[new_index] =
col;
667 ref.resize(new_index);
671 RowIndex
row)
const {
683 DCHECK(deleted_columns_[pivot_col]);
684 const int max_row_degree = num_non_deleted_columns_.value() + 1;
687 for (
const ColIndex
col : row_non_zero_[pivot_row]) {
689 bool_scratchpad_[
col] =
false;
697 const RowIndex
row = e.row();
698 if (
row == pivot_row)
continue;
703 if (e.coefficient() == 0.0 || row_degree_[
row] == max_row_degree)
continue;
712 const int kDeletionThreshold = 4;
713 if (row_non_zero_[
row].size() > row_degree_[
row] + kDeletionThreshold) {
718 MergeInto(pivot_row,
row);
726 MergeIntoSorted(pivot_row,
row);
731void MatrixNonZeroPattern::MergeInto(RowIndex pivot_row, RowIndex
row) {
734 for (
const ColIndex
col : row_non_zero_[
row]) {
735 bool_scratchpad_[
col] =
true;
738 auto& non_zero = row_non_zero_[
row];
739 const int old_size = non_zero.size();
740 for (
const ColIndex
col : row_non_zero_[pivot_row]) {
741 if (bool_scratchpad_[
col]) {
742 bool_scratchpad_[
col] =
false;
748 row_degree_[
row] += non_zero.size() - old_size;
756template <
typename V,
typename W>
757void MergeSortedVectors(
const V& input_a, W* out) {
758 if (input_a.empty())
return;
759 const auto& input_b = *out;
760 int index_a = input_a.size() - 1;
761 int index_b = input_b.size() - 1;
762 int index_out = input_a.size() + input_b.size();
763 out->resize(index_out);
764 while (index_a >= 0) {
766 while (index_a >= 0) {
768 (*out)[index_out] = input_a[index_a];
774 if (input_a[index_a] > input_b[index_b]) {
775 (*out)[index_out] = input_a[index_a];
778 (*out)[index_out] = input_b[index_b];
789void MatrixNonZeroPattern::MergeIntoSorted(RowIndex pivot_row, RowIndex
row) {
791 const auto&
input = row_non_zero_[pivot_row];
792 const auto& output = row_non_zero_[
row];
795 col_scratchpad_.resize(
input.size());
796 col_scratchpad_.resize(std::set_difference(
input.begin(),
input.end(),
797 output.begin(), output.end(),
798 col_scratchpad_.begin()) -
799 col_scratchpad_.begin());
802 for (
const ColIndex
col : col_scratchpad_) {
805 row_degree_[
row] += col_scratchpad_.size();
806 MergeSortedVectors(col_scratchpad_, &row_non_zero_[
row]);
812 col_by_degree_.clear();
817 col_degree_.assign(num_cols, 0);
818 col_index_.assign(num_cols, -1);
819 col_by_degree_.resize(max_degree + 1);
820 min_degree_ = max_degree + 1;
825 DCHECK_LT(degree, col_by_degree_.size());
829 const int32_t old_degree = col_degree_[
col];
830 if (degree != old_degree) {
831 const int32_t old_index = col_index_[
col];
832 if (old_index != -1) {
833 col_by_degree_[old_degree][old_index] = col_by_degree_[old_degree].back();
834 col_index_[col_by_degree_[old_degree].back()] = old_index;
835 col_by_degree_[old_degree].pop_back();
838 col_index_[
col] = col_by_degree_[degree].size();
839 col_degree_[
col] = degree;
840 col_by_degree_[degree].push_back(
col);
841 min_degree_ =
std::min(min_degree_, degree);
843 col_index_[
col] = -1;
844 col_degree_[
col] = 0;
851 DCHECK_LE(min_degree_, col_by_degree_.size());
853 if (min_degree_ == col_by_degree_.size())
return kInvalidCol;
854 if (!col_by_degree_[min_degree_].empty())
break;
857 const ColIndex
col = col_by_degree_[min_degree_].back();
858 col_by_degree_[min_degree_].pop_back();
859 col_index_[
col] = -1;
860 col_degree_[
col] = 0;
865 mapping_.assign(num_cols.value(), -1);
866 free_columns_.clear();
871 ColIndex
col)
const {
872 if (mapping_[
col] == -1)
return empty_column_;
873 return columns_[mapping_[
col]];
878 if (mapping_[
col] != -1)
return &columns_[mapping_[
col]];
880 if (free_columns_.empty()) {
881 new_col_index = columns_.size();
884 new_col_index = free_columns_.back();
885 free_columns_.pop_back();
887 mapping_[
col] = new_col_index;
888 return &columns_[new_col_index];
893 free_columns_.push_back(mapping_[
col]);
894 columns_[mapping_[
col]].Clear();
900 free_columns_.clear();
#define DCHECK_LE(val1, val2)
#define DCHECK_NE(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define DCHECK_EQ(val1, val2)
#define VLOG(verboselevel)
void push_back(const value_type &x)
void Reset(int32_t max_degree, ColIndex num_cols)
void PushOrAdjust(ColIndex col, int32_t degree)
EntryIndex num_entries() const
ColIndex num_cols() const
const ColumnView column(ColIndex col) const
RowIndex num_rows() const
double markowitz_singularity_threshold() const
::PROTOBUF_NAMESPACE_ID::int32 markowitz_zlatev_parameter() const
double lu_factorization_pivot_threshold() const
double DeterministicTimeOfLastFactorization() const
ABSL_MUST_USE_RESULT Status ComputeLU(const CompactSparseMatrixView &basis_matrix, RowPermutation *row_perm, ColumnPermutation *col_perm, TriangularMatrix *lower, TriangularMatrix *upper)
ABSL_MUST_USE_RESULT Status ComputeRowAndColumnPermutation(const CompactSparseMatrixView &basis_matrix, RowPermutation *row_perm, ColumnPermutation *col_perm)
bool IsColumnDeleted(ColIndex col) const
void RemoveDeletedColumnsFromRow(RowIndex row)
int32_t DecreaseColDegree(ColIndex col)
const absl::InlinedVector< ColIndex, 6 > & RowNonZero(RowIndex row) const
void DeleteRowAndColumn(RowIndex pivot_row, ColIndex pivot_col)
int32_t RowDegree(RowIndex row) const
void AddEntry(RowIndex row, ColIndex col)
void Reset(RowIndex num_rows, ColIndex num_cols)
void Update(RowIndex pivot_row, ColIndex pivot_col, const SparseColumn &column)
int32_t DecreaseRowDegree(RowIndex row)
int32_t ColDegree(ColIndex col) const
ColIndex GetFirstNonDeletedColumnFromRow(RowIndex row) const
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 assign(IndexType size, IndexType value)
void Reset(ColIndex num_cols)
void ClearAndReleaseColumn(ColIndex col)
SparseColumn * mutable_column(ColIndex col)
const SparseColumn & column(ColIndex col) const
typename Iterator::Entry Entry
void assign(IntType size, const T &v)
void AddTriangularColumnWithGivenDiagonalEntry(const SparseColumn &column, RowIndex diagonal_row, Fractional diagonal_value)
void Swap(TriangularMatrix *other)
bool IsLowerTriangular() const
void AddAndNormalizeTriangularColumn(const SparseColumn &column, RowIndex diagonal_row, Fractional diagonal_coefficient)
bool IsUpperTriangular() const
void AddTriangularColumn(const ColumnView &column, RowIndex diagonal_row)
void PermutedLowerSparseSolve(const ColumnView &rhs, const RowPermutation &row_perm, SparseColumn *lower, SparseColumn *upper)
void ApplyRowPermutationToNonDiagonalEntries(const RowPermutation &row_perm)
void AddDiagonalOnlyColumn(Fractional diagonal_value)
int64_t NumFpOperationsInLastPermutedLowerSparseSolve() const
void Reset(RowIndex num_rows, ColIndex col_capacity)
EntryIndex num_entries() const
const RowIndex kInvalidRow(-1)
Permutation< ColIndex > ColumnPermutation
Permutation< RowIndex > RowPermutation
static double DeterministicTimeForFpOperations(int64_t n)
const ColIndex kInvalidCol(-1)
Collection of objects used to extend the Constraint Solver library.
static int input(yyscan_t yyscanner)
#define IF_STATS_ENABLED(instructions)
#define SCOPED_TIME_STAT(stats)
#define GLOP_RETURN_IF_ERROR(function_call)