21#include "absl/log/check.h"
22#include "absl/types/span.h"
30 : is_identity_factorization_(true),
34 inverse_row_perm_() {}
38 lower_.Reset(RowIndex(0), ColIndex(0));
39 upper_.Reset(RowIndex(0), ColIndex(0));
40 transpose_upper_.Reset(RowIndex(0), ColIndex(0));
41 transpose_lower_.Reset(RowIndex(0), ColIndex(0));
42 is_identity_factorization_ =
true;
45 inverse_row_perm_.clear();
46 inverse_col_perm_.clear();
58 markowitz_.ComputeLU(matrix, &row_perm_, &col_perm_, &lower_, &upper_));
59 inverse_col_perm_.PopulateFromInverse(col_perm_);
60 inverse_row_perm_.PopulateFromInverse(row_perm_);
61 ComputeTransposeUpper();
62 ComputeTransposeLower();
64 is_identity_factorization_ =
false;
67 stats_.basis_num_entries.Add(matrix.
num_entries().value());
80 const std::vector<ColIndex>& candidates) {
82 (void)markowitz_.ComputeRowAndColumnPermutation(view, &row_perm_, &col_perm_);
86 for (RowIndex row(0); row < matrix.
num_rows(); ++row) {
95 CHECK_EQ(col_perm_.size(), candidates.size());
96 for (
int i = 0; i < col_perm_.size(); ++i) {
106 return markowitz_.DeterministicTimeOfLastFactorization();
111 if (is_identity_factorization_)
return;
114 lower_.LowerSolve(&dense_column_scratchpad_);
115 upper_.UpperSolve(&dense_column_scratchpad_);
121 if (is_identity_factorization_)
return;
126 upper_.TransposeUpperSolve(&dense_column_scratchpad_);
127 lower_.TransposeLowerSolve(&dense_column_scratchpad_);
136 absl::Span<const RowIndex> non_zeros, absl::Span<Fractional> column) {
138 if (non_zeros.empty()) {
141 for (
const RowIndex row : non_zeros) {
142 sum +=
Square(column[row.value()]);
143 (column)[row.value()] = 0.0;
152 if (is_identity_factorization_)
return SquaredNorm(a);
154 non_zero_rows_.clear();
155 const RowIndex num_rows = lower_.num_rows();
156 dense_zero_scratchpad_.resize(num_rows, 0.0);
157 DCHECK(
IsAllZero(dense_zero_scratchpad_));
159 for (
const SparseColumn::Entry e : a) {
160 const RowIndex permuted_row = row_perm_[e.row()];
161 dense_zero_scratchpad_[permuted_row] = e.coefficient();
162 non_zero_rows_.push_back(permuted_row);
165 lower_.ComputeRowsToConsiderInSortedOrder(&non_zero_rows_);
166 if (non_zero_rows_.empty()) {
167 lower_.LowerSolve(&dense_zero_scratchpad_);
169 lower_.HyperSparseSolve(&dense_zero_scratchpad_, &non_zero_rows_);
170 upper_.ComputeRowsToConsiderInSortedOrder(&non_zero_rows_);
172 if (non_zero_rows_.empty()) {
173 upper_.UpperSolve(&dense_zero_scratchpad_);
175 upper_.HyperSparseSolveWithReversedNonZeros(&dense_zero_scratchpad_,
178 return ComputeSquaredNormAndResetToZero(
180 absl::MakeSpan(dense_zero_scratchpad_.data(), num_rows.value()));
184 if (is_identity_factorization_)
return 1.0;
186 const RowIndex permuted_row =
189 non_zero_rows_.clear();
190 const RowIndex num_rows = lower_.num_rows();
191 dense_zero_scratchpad_.resize(num_rows, 0.0);
192 DCHECK(
IsAllZero(dense_zero_scratchpad_));
193 dense_zero_scratchpad_[permuted_row] = 1.0;
194 non_zero_rows_.push_back(permuted_row);
196 transpose_upper_.ComputeRowsToConsiderInSortedOrder(&non_zero_rows_);
197 if (non_zero_rows_.empty()) {
198 transpose_upper_.LowerSolveStartingAt(
RowToColIndex(permuted_row),
199 &dense_zero_scratchpad_);
201 transpose_upper_.HyperSparseSolve(&dense_zero_scratchpad_, &non_zero_rows_);
202 transpose_lower_.ComputeRowsToConsiderInSortedOrder(&non_zero_rows_);
204 if (non_zero_rows_.empty()) {
205 transpose_lower_.UpperSolve(&dense_zero_scratchpad_);
207 transpose_lower_.HyperSparseSolveWithReversedNonZeros(
208 &dense_zero_scratchpad_, &non_zero_rows_);
210 return ComputeSquaredNormAndResetToZero(
212 absl::MakeSpan(dense_zero_scratchpad_.data(), num_rows.value()));
220 const RowIndex num_rows = perm.
size();
221 for (RowIndex row(0); row < num_rows; ++row) {
222 if (a[row] != b[perm[row]])
return false;
231 if (!is_identity_factorization_) {
232 DCHECK(AreEqualWithPermutation(a, x->values, row_perm_));
233 lower_.ComputeRowsToConsiderInSortedOrder(&x->non_zeros);
234 if (x->non_zeros.empty()) {
235 lower_.LowerSolve(&x->values);
237 lower_.HyperSparseSolve(&x->values, &x->non_zeros);
242template <
typename Column>
243void LuFactorization::RightSolveLInternal(
const Column& b,
250 ColIndex first_column_to_consider(
RowToColIndex(x->values.size()));
252 for (
const auto e : b) {
253 const RowIndex permuted_row = row_perm_[e.row()];
254 (*x)[permuted_row] = e.coefficient();
255 x->non_zeros.push_back(permuted_row);
264 first_column_to_consider = std::min(first_column_to_consider, col);
268 x->non_zeros_are_sorted =
true;
269 if (x->non_zeros.empty()) {
272 lower_.HyperSparseSolve(&
x->values, &
x->non_zeros);
280 x->non_zeros.clear();
281 if (is_identity_factorization_) {
283 (*x)[e.row()] = e.coefficient();
284 x->non_zeros.push_back(e.row());
289 RightSolveLInternal(b, x);
293 if (is_identity_factorization_)
return;
294 if (x->non_zeros.empty()) {
296 lower_.LowerSolve(&x->values);
302 lower_.ComputeRowsToConsiderInSortedOrder(&x->non_zeros);
303 x->non_zeros_are_sorted =
true;
304 if (x->non_zeros.empty()) {
305 lower_.LowerSolve(&x->values);
307 lower_.HyperSparseSolve(&x->values, &x->non_zeros);
315 x->non_zeros.clear();
317 if (is_identity_factorization_) {
322 if (b.non_zeros.empty()) {
327 RightSolveLInternal(b, x);
332 CHECK(col_perm_.empty());
333 if (is_identity_factorization_)
return;
337 transpose_upper_.ComputeRowsToConsiderInSortedOrder(nz);
340 upper_.TransposeUpperSolve(x);
342 upper_.TransposeHyperSparseSolve(x, nz);
348 CHECK(col_perm_.empty());
349 if (is_identity_factorization_)
return;
354 upper_.ComputeRowsToConsiderInSortedOrder(&x->non_zeros);
355 x->non_zeros_are_sorted =
true;
356 if (x->non_zeros.empty()) {
357 transpose_upper_.TransposeLowerSolve(&x->values);
359 transpose_upper_.TransposeHyperSparseSolveWithReversedNonZeros(
360 &x->values, &x->non_zeros);
367 if (is_identity_factorization_) {
375 transpose_lower_.ComputeRowsToConsiderInSortedOrder(nz);
378 lower_.TransposeLowerSolve(x);
380 lower_.TransposeHyperSparseSolveWithReversedNonZeros(x, nz);
383 if (result_before_permutation ==
nullptr) {
395 for (
const RowIndex row : *nz) {
396 DCHECK_NE((*x)[row], 0.0);
406 x->swap(result_before_permutation->
values);
408 const auto inverse_row_perm = inverse_row_perm_.const_view();
410 const RowIndex num_rows = inverse_row_perm.size();
411 for (RowIndex row(0); row < num_rows; ++row) {
414 const RowIndex permuted_row = inverse_row_perm[row];
415 (*x)[permuted_row] = value;
419 nz->swap(result_before_permutation->
non_zeros);
420 nz->reserve(result_before_permutation->
non_zeros.size());
421 for (
const RowIndex row : result_before_permutation->
non_zeros) {
423 const RowIndex permuted_row = inverse_row_perm[row];
424 (*x)[permuted_row] = value;
425 nz->push_back(permuted_row);
441 if (is_identity_factorization_) {
446 const ColIndex permuted_col = col_perm_.empty() ? col : col_perm_[col];
447 (*y)[permuted_col] = 1.0;
453 if (transpose_upper_.ColumnIsDiagonalOnly(permuted_col)) {
454 (*y)[permuted_col] /= transpose_upper_.GetDiagonalCoefficient(permuted_col);
458 transpose_upper_.ComputeRowsToConsiderInSortedOrder(nz);
461 transpose_upper_.LowerSolveStartingAt(permuted_col, x);
463 transpose_upper_.HyperSparseSolve(x, nz);
470 if (is_identity_factorization_) {
471 column_of_upper_.Clear();
473 return column_of_upper_;
475 upper_.CopyColumnToSparseColumn(col_perm_.empty() ? col : col_perm_[col],
477 return column_of_upper_;
482 const int initial_num_entries = matrix.
num_entries().value();
483 const int lu_num_entries =
484 (lower_.num_entries() + upper_.num_entries()).value();
485 if (is_identity_factorization_ || initial_num_entries == 0)
return 1.0;
486 return static_cast<double>(lu_num_entries) /
487 static_cast<double>(initial_num_entries);
491 return is_identity_factorization_
493 : lower_.num_entries() + upper_.num_entries();
497 if (is_identity_factorization_)
return 1.0;
498 DCHECK_EQ(upper_.num_rows().value(), upper_.num_cols().value());
500 for (ColIndex col(0); col < upper_.num_cols(); ++col) {
501 product *= upper_.GetDiagonalCoefficient(col);
503 return product * row_perm_.ComputeSignature() *
504 inverse_col_perm_.ComputeSignature();
508 if (is_identity_factorization_)
return 1.0;
509 const RowIndex num_rows = lower_.num_rows();
510 const ColIndex num_cols = lower_.num_cols();
512 for (ColIndex col(0); col < num_cols; ++col) {
519 for (RowIndex row(0); row < num_rows; ++row) {
520 column_norm += std::abs(right_hand_side[row]);
523 norm = std::max(norm, column_norm);
529 if (is_identity_factorization_)
return 1.0;
530 const RowIndex num_rows = lower_.num_rows();
531 const ColIndex num_cols = lower_.num_cols();
533 for (ColIndex col(0); col < num_cols; ++col) {
539 for (RowIndex row(0); row < num_rows; ++row) {
540 row_sum[row] += std::abs(right_hand_side[row]);
545 for (RowIndex row(0); row < num_rows; ++row) {
546 norm = std::max(norm, row_sum[row]);
553 if (is_identity_factorization_)
return 1.0;
559 if (is_identity_factorization_)
return 1.0;
564 return lower_.ComputeInverseInfinityNormUpperBound() *
565 upper_.ComputeInverseInfinityNormUpperBound();
571 double density = 0.0;
572 for (
const SparseColumn::Entry e : b) {
573 if (row_perm[e.row()] !=
kNonPivotal && e.coefficient() != 0.0) {
577 const RowIndex num_rows = row_perm.
size();
578 return density / num_rows.value();
582void LuFactorization::ComputeTransposeUpper() {
584 transpose_upper_.PopulateFromTranspose(upper_);
587void LuFactorization::ComputeTransposeLower()
const {
589 transpose_lower_.PopulateFromTranspose(lower_);
594 if (is_identity_factorization_)
return true;
598 paq.PopulateFromPermutedMatrix(matrix, row_perm_, inverse_col_perm_);
599 if (!row_perm_.Check()) {
602 if (!inverse_col_perm_.Check()) {
606 SparseMatrix should_be_zero;
607 should_be_zero.PopulateFromLinearCombination(
Fractional(1.0), paq,
610 for (ColIndex col(0); col < should_be_zero.num_cols(); ++col) {
611 for (
const SparseColumn::Entry e : should_be_zero.column(col)) {
612 const Fractional magnitude = std::abs(e.coefficient());
613 if (magnitude > tolerance) {
614 VLOG(2) << magnitude <<
" != 0, at column " << col;
Fractional ComputeOneNorm() const
RowIndex num_rows() const
ColIndex num_cols() const
EntryIndex num_entries() const
Fractional ComputeInfinityNorm() const
RowIndex num_rows() const
ColIndex num_cols() const
void RightSolveLForColumnView(const ColumnView &b, ScatteredColumn *x) const
void RightSolve(DenseColumn *x) const
void RightSolveUWithNonZeros(ScatteredColumn *x) const
ColIndex LeftSolveUForUnitRow(ColIndex col, ScatteredRow *y) const
double DeterministicTimeOfLastFactorization() const
Returns the deterministic time of the last factorization.
void LeftSolve(DenseRow *y) const
void RightSolveLForScatteredColumn(const ScatteredColumn &b, ScatteredColumn *x) const
void RightSolveLWithPermutedInput(const DenseColumn &a, ScatteredColumn *x) const
Fractional DualEdgeSquaredNorm(RowIndex row) const
Returns the norm of (B^T)^{-1}.e_row where e is an unit vector.
Fractional ComputeInverseInfinityNormUpperBound() const
ABSL_MUST_USE_RESULT Status ComputeFactorization(const CompactSparseMatrixView &compact_matrix)
Fractional ComputeInverseOneNorm() const
EntryIndex NumberOfEntries() const
void RightSolveLWithNonZeros(ScatteredColumn *x) const
Fractional ComputeInverseInfinityNorm() const
bool LeftSolveLWithNonZeros(ScatteredRow *y, ScatteredColumn *result_before_permutation) const
Fractional ComputeInfinityNormConditionNumber(const CompactSparseMatrixView &matrix) const
Fractional RightSolveSquaredNorm(const ColumnView &a) const
Returns the norm of B^{-1}.a.
RowToColMapping ComputeInitialBasis(const CompactSparseMatrix &matrix, const std::vector< ColIndex > &candidates)
Fractional ComputeOneNormConditionNumber(const CompactSparseMatrixView &matrix) const
double GetFillInPercentage(const CompactSparseMatrixView &matrix) const
void ComputeLowerTimesUpper(SparseMatrix *product) const
const SparseColumn & GetColumnOfU(ColIndex col) const
void LeftSolveUWithNonZeros(ScatteredRow *y) const
Fractional ComputeDeterminant() const
static Status OK()
Improves readability but identical to 0-arg constructor.
@ ERROR_LU
The LU factorization of the current basis couldn't be computed.
ConstView const_view() const
void LowerSolveStartingAt(ColIndex start, DenseColumn *rhs) const
This assumes that the rhs is all zero before the given position.
void ComputeRowsToConsiderInSortedOrder(RowIndexVector *non_zero_rows) const
Fractional GetDiagonalCoefficient(ColIndex col) const
Returns the diagonal coefficient of the given column.
bool ColumnIsDiagonalOnly(ColIndex col) const
Returns true iff the column contains no non-diagonal entries.
ColIndex GetFirstNonIdentityColumn() const
void push_back(const value_type &val)
constexpr ColIndex kInvalidCol(-1)
void PermuteWithScratchpad(const Permutation< PermutationIndexType > &permutation, StrictITIVector< IndexType, Fractional > *zero_scratchpad, StrictITIVector< IndexType, Fractional > *input_output)
Permutes the given dense vector. It uses for this an all zero scratchpad.
StrictITIVector< RowIndex, ColIndex > RowToColMapping
std::vector< RowIndex > RowIndexVector
Fractional Square(Fractional f)
bool IsAllZero(const Container &input)
Returns true if the given Fractional container is all zeros.
Permutation< RowIndex > RowPermutation
void PermuteWithKnownNonZeros(const Permutation< IndexType > &permutation, StrictITIVector< IndexType, Fractional > *zero_scratchpad, StrictITIVector< IndexType, Fractional > *output, std::vector< IndexType > *non_zeros)
ColIndex RowToColIndex(RowIndex row)
Get the ColIndex corresponding to the column # row.
constexpr RowIndex kInvalidRow(-1)
void ClearAndResizeVectorWithNonZeros(IndexType size, ScatteredRowOrCol *v)
Sets a dense vector for which the non zeros are known to be non_zeros.
RowIndex ColToRowIndex(ColIndex col)
Get the RowIndex corresponding to the row # col.
const RowIndex kNonPivotal(-1)
Fractional SquaredNorm(const SparseColumn &v)
void ApplyPermutation(const Permutation< IndexType > &perm, const ITIVectorType &b, ITIVectorType *result)
StrictITIVector< RowIndex, Fractional > DenseColumn
Column-vector types. Column-vector types are indexed by a row index.
StrictITIVector< ColIndex, Fractional > DenseRow
Row-vector types. Row-vector types are indexed by a column index.
Fractional SquaredNormAndResetToZero(absl::Span< Fractional > data)
void ApplyInversePermutation(const Permutation< IndexType > &perm, const ITIVectorType &b, ITIVectorType *result)
In SWIG mode, we don't want anything besides these top-level includes.
static int input(yyscan_t yyscanner)
#define IF_STATS_ENABLED(instructions)
#define SCOPED_TIME_STAT(stats)
#define GLOP_RETURN_IF_ERROR(function_call)
Macro to simplify error propagation between function returning Status.
#define GLOP_RETURN_AND_LOG_ERROR(error_code, message)
Macro to simplify the creation of an error.
StrictITIVector< Index, Fractional > values
bool non_zeros_are_sorted
std::vector< Index > non_zeros