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_));
61 ComputeTransposeUpper();
62 ComputeTransposeLower();
64 is_identity_factorization_ =
false;
80 const std::vector<ColIndex>& candidates) {
95 CHECK_EQ(col_perm_.
size(), candidates.size());
96 for (
int i = 0; i < col_perm_.
size(); ++i) {
111 if (is_identity_factorization_)
return;
121 if (is_identity_factorization_)
return;
136 const std::vector<RowIndex>& non_zeros, absl::Span<Fractional>
column) {
138 if (non_zeros.empty()) {
141 for (
const RowIndex
row : non_zeros) {
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);
166 if (non_zero_rows_.empty()) {
172 if (non_zero_rows_.empty()) {
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);
197 if (non_zero_rows_.empty()) {
199 &dense_zero_scratchpad_);
201 transpose_upper_.
HyperSparseSolve(&dense_zero_scratchpad_, &non_zero_rows_);
204 if (non_zero_rows_.empty()) {
205 transpose_lower_.
UpperSolve(&dense_zero_scratchpad_);
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_));
234 if (
x->non_zeros.empty()) {
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()) {
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()) {
303 x->non_zeros_are_sorted =
true;
304 if (
x->non_zeros.empty()) {
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;
338 y->non_zeros_are_sorted =
true;
348 CHECK(col_perm_.
empty());
349 if (is_identity_factorization_)
return;
355 x->non_zeros_are_sorted =
true;
356 if (
x->non_zeros.empty()) {
360 &
x->values, &
x->non_zeros);
367 if (is_identity_factorization_) {
372 std::vector<RowIndex>* nz =
reinterpret_cast<RowIndexVector*
>(&
y->non_zeros);
376 y->non_zeros_are_sorted =
true;
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 for (RowIndex
row(0);
row < inverse_row_perm_.
size(); ++
row) {
411 const RowIndex permuted_row = inverse_row_perm_[
row];
412 (*x)[permuted_row] =
value;
416 nz->swap(result_before_permutation->
non_zeros);
417 nz->reserve(result_before_permutation->
non_zeros.size());
418 for (
const RowIndex
row : result_before_permutation->
non_zeros) {
420 const RowIndex permuted_row = inverse_row_perm_[
row];
421 (*x)[permuted_row] =
value;
422 nz->push_back(permuted_row);
424 y->non_zeros_are_sorted =
false;
437 DCHECK(
y->non_zeros.empty());
438 if (is_identity_factorization_) {
440 y->non_zeros.push_back(
col);
443 const ColIndex permuted_col = col_perm_.
empty() ?
col : col_perm_[
col];
444 (*y)[permuted_col] = 1.0;
445 y->non_zeros.push_back(permuted_col);
456 y->non_zeros_are_sorted =
true;
457 if (
y->non_zeros.empty()) {
467 if (is_identity_factorization_) {
468 column_of_upper_.
Clear();
470 return column_of_upper_;
474 return column_of_upper_;
479 const int initial_num_entries = matrix.
num_entries().value();
480 const int lu_num_entries =
482 if (is_identity_factorization_ || initial_num_entries == 0)
return 1.0;
483 return static_cast<double>(lu_num_entries) /
484 static_cast<double>(initial_num_entries);
488 return is_identity_factorization_
494 if (is_identity_factorization_)
return 1.0;
505 if (is_identity_factorization_)
return 1.0;
506 const RowIndex num_rows = lower_.
num_rows();
507 const ColIndex num_cols = lower_.
num_cols();
509 for (ColIndex
col(0);
col < num_cols; ++
col) {
516 for (RowIndex
row(0);
row < num_rows; ++
row) {
517 column_norm += std::abs(right_hand_side[
row]);
520 norm = std::max(norm, column_norm);
526 if (is_identity_factorization_)
return 1.0;
527 const RowIndex num_rows = lower_.
num_rows();
528 const ColIndex num_cols = lower_.
num_cols();
530 for (ColIndex
col(0);
col < num_cols; ++
col) {
536 for (RowIndex
row(0);
row < num_rows; ++
row) {
537 row_sum[
row] += std::abs(right_hand_side[
row]);
542 for (RowIndex
row(0);
row < num_rows; ++
row) {
543 norm = std::max(norm, row_sum[
row]);
550 if (is_identity_factorization_)
return 1.0;
556 if (is_identity_factorization_)
return 1.0;
568 double density = 0.0;
569 for (
const SparseColumn::Entry e :
b) {
570 if (row_perm[e.row()] !=
kNonPivotal && e.coefficient() != 0.0) {
574 const RowIndex num_rows = row_perm.
size();
575 return density / num_rows.value();
579void LuFactorization::ComputeTransposeUpper() {
584void LuFactorization::ComputeTransposeLower()
const {
589bool LuFactorization::CheckFactorization(
const CompactSparseMatrixView& matrix,
591 if (is_identity_factorization_)
return true;
595 paq.PopulateFromPermutedMatrix(matrix, row_perm_, inverse_col_perm_);
596 if (!row_perm_.
Check()) {
599 if (!inverse_col_perm_.
Check()) {
603 SparseMatrix should_be_zero;
604 should_be_zero.PopulateFromLinearCombination(
Fractional(1.0), paq,
607 for (ColIndex
col(0);
col < should_be_zero.num_cols(); ++
col) {
608 for (
const SparseColumn::Entry e : should_be_zero.column(
col)) {
609 const Fractional magnitude = std::abs(e.coefficient());
610 if (magnitude > tolerance) {
611 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
ABSL_MUST_USE_RESULT Status ComputeRowAndColumnPermutation(const CompactSparseMatrixView &basis_matrix, RowPermutation *row_perm, ColumnPermutation *col_perm)
double DeterministicTimeOfLastFactorization() const
Returns an estimate of the time spent in the last factorization.
ABSL_MUST_USE_RESULT Status ComputeLU(const CompactSparseMatrixView &basis_matrix, RowPermutation *row_perm, ColumnPermutation *col_perm, TriangularMatrix *lower, TriangularMatrix *upper)
int ComputeSignature() const
void PopulateFromInverse(const Permutation &inverse)
bool Check() const
Returns true if the calling object contains a permutation, false otherwise.
void Clear()
Clears the vector, i.e. removes all entries.
void SetCoefficient(Index index, Fractional value)
Defines the coefficient at index, i.e. vector[index] = value;.
static Status OK()
Improves readability but identical to 0-arg constructor.
@ ERROR_LU
The LU factorization of the current basis couldn't be computed.
void resize(IntType size)
void UpperSolve(DenseColumn *rhs) const
Solves the system U.x = rhs for an upper triangular matrix.
void PopulateFromTranspose(const TriangularMatrix &input)
void Reset(RowIndex num_rows, ColIndex col_capacity)
void LowerSolve(DenseColumn *rhs) const
void LowerSolveStartingAt(ColIndex start, DenseColumn *rhs) const
This assumes that the rhs is all zero before the given position.
ColIndex num_cols() const
Fractional ComputeInverseInfinityNormUpperBound() const
RowIndex num_rows() const
void HyperSparseSolveWithReversedNonZeros(DenseColumn *rhs, RowIndexVector *non_zero_rows) const
void TransposeHyperSparseSolve(DenseColumn *rhs, RowIndexVector *non_zero_rows) const
void ComputeRowsToConsiderInSortedOrder(RowIndexVector *non_zero_rows) const
EntryIndex num_entries() const
void CopyColumnToSparseColumn(ColIndex col, SparseColumn *output) const
Copy a triangular column with its diagonal entry to the given SparseColumn.
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 TransposeHyperSparseSolveWithReversedNonZeros(DenseColumn *rhs, RowIndexVector *non_zero_rows) const
void TransposeUpperSolve(DenseColumn *rhs) const
void HyperSparseSolve(DenseColumn *rhs, RowIndexVector *non_zero_rows) const
void TransposeLowerSolve(DenseColumn *rhs) const
value_type * data()
– Pass-through methods to STL vector ----------------------------------—
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.
std::vector< RowIndex > RowIndexVector
Fractional Square(Fractional f)
bool IsAllZero(const Container &input)
Returns true if the given Fractional container is all zeros.
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)
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.
#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
std::vector< Index > non_zeros