18#include <initializer_list>
23#include "absl/log/check.h"
24#include "absl/strings/str_format.h"
37template <
typename Matrix>
38EntryIndex ComputeNumEntries(
const Matrix& matrix) {
39 EntryIndex num_entries(0);
40 const ColIndex num_cols(matrix.num_cols());
41 for (ColIndex
col(0);
col < num_cols; ++
col) {
42 num_entries += matrix.column(
col).num_entries();
50template <
typename Matrix>
51Fractional ComputeOneNormTemplate(
const Matrix& matrix) {
53 const ColIndex num_cols(matrix.num_cols());
54 for (ColIndex
col(0);
col < num_cols; ++
col) {
56 for (
const SparseColumn::Entry e : matrix.column(
col)) {
58 column_norm += fabs(e.coefficient());
61 norm = std::max(norm, column_norm);
69template <
typename Matrix>
70Fractional ComputeInfinityNormTemplate(
const Matrix& matrix) {
72 const ColIndex num_cols(matrix.num_cols());
73 for (ColIndex
col(0);
col < num_cols; ++
col) {
74 for (
const SparseColumn::Entry e : matrix.column(
col)) {
76 row_sum[e.row()] += fabs(e.coefficient());
82 const RowIndex num_rows(matrix.num_rows());
83 for (RowIndex
row(0);
row < num_rows; ++
row) {
84 norm = std::max(norm, row_sum[
row]);
96#if (!defined(_MSC_VER) || (_MSC_VER >= 1800))
97SparseMatrix::SparseMatrix(
98 std::initializer_list<std::initializer_list<Fractional>> init_list) {
100 num_rows_ = RowIndex(init_list.size());
102 for (std::initializer_list<Fractional> init_row : init_list) {
103 num_cols = std::max(num_cols, ColIndex(init_row.size()));
117void SparseMatrix::Clear() {
119 num_rows_ = RowIndex(0);
122bool SparseMatrix::IsEmpty()
const {
123 return columns_.empty() || num_rows_ == 0;
126void SparseMatrix::CleanUp() {
127 const ColIndex num_cols(columns_.size());
129 columns_[
col].CleanUp();
133bool SparseMatrix::CheckNoDuplicates()
const {
134 DenseBooleanColumn boolean_column;
135 const ColIndex num_cols(columns_.size());
136 for (ColIndex
col(0);
col < num_cols; ++
col) {
137 if (!columns_[
col].CheckNoDuplicates(&boolean_column))
return false;
142bool SparseMatrix::IsCleanedUp()
const {
143 const ColIndex num_cols(columns_.size());
145 if (!columns_[
col].IsCleanedUp())
return false;
150void SparseMatrix::SetNumRows(RowIndex num_rows) { num_rows_ = num_rows; }
152ColIndex SparseMatrix::AppendEmptyColumn() {
153 const ColIndex result = columns_.size();
159 DCHECK_LT(
row, num_rows_);
162 columns_.push_back(std::move(new_col));
167 columns_.swap(matrix->columns_);
168 std::swap(num_rows_, matrix->num_rows_);
171void SparseMatrix::PopulateFromZero(RowIndex num_rows, ColIndex num_cols) {
174 columns_[
col].Clear();
176 num_rows_ = num_rows;
179void SparseMatrix::PopulateFromIdentity(ColIndex num_cols) {
180 PopulateFromZero(ColToRowIndex(num_cols), num_cols);
187template <
typename Matrix>
188void SparseMatrix::PopulateFromTranspose(
const Matrix&
input) {
189 Reset(RowToColIndex(
input.num_rows()), ColToRowIndex(
input.num_cols()));
195 for (
const SparseColumn::Entry e :
input.column(
col)) {
196 ++row_degree[e.row()];
200 columns_[RowToColIndex(
row)].Reserve(row_degree[
row]);
204 const RowIndex transposed_row = ColToRowIndex(
col);
205 for (
const SparseColumn::Entry e :
input.column(
col)) {
206 const ColIndex transposed_col = RowToColIndex(e.row());
207 columns_[transposed_col].SetCoefficient(transposed_row, e.coefficient());
210 DCHECK(IsCleanedUp());
213void SparseMatrix::PopulateFromSparseMatrix(
const SparseMatrix& matrix) {
214 Reset(ColIndex(0), matrix.num_rows_);
215 columns_ = matrix.columns_;
218template <
typename Matrix>
219void SparseMatrix::PopulateFromPermutedMatrix(
222 const ColIndex num_cols =
a.num_cols();
223 Reset(num_cols,
a.num_rows());
224 for (ColIndex
col(0);
col < num_cols; ++
col) {
225 for (
const auto e :
a.column(inverse_col_perm[
col])) {
226 columns_[
col].SetCoefficient(row_perm[e.row()], e.coefficient());
229 DCHECK(CheckNoDuplicates());
232void SparseMatrix::PopulateFromLinearCombination(
Fractional alpha,
236 DCHECK_EQ(
a.num_cols(),
b.num_cols());
237 DCHECK_EQ(
a.num_rows(),
b.num_rows());
239 const ColIndex num_cols =
a.num_cols();
240 Reset(num_cols,
a.num_rows());
242 const RowIndex num_rows =
a.num_rows();
244 for (ColIndex
col(0);
col < num_cols; ++
col) {
245 for (
const SparseColumn::Entry e :
a.columns_[
col]) {
248 for (
const SparseColumn::Entry e :
b.columns_[
col]) {
252 columns_[
col].CleanUp();
253 dense_column.
Clear();
259 const ColIndex num_cols =
b.num_cols();
260 const RowIndex num_rows =
a.num_rows();
261 Reset(num_cols, num_rows);
264 for (ColIndex col_b(0); col_b < num_cols; ++col_b) {
265 for (
const SparseColumn::Entry eb :
b.columns_[col_b]) {
266 if (eb.coefficient() == 0.0) {
270 for (
const SparseColumn::Entry ea :
a.columns_[col_a]) {
272 tmp_column.AddToCoefficient(ea.row(),
value);
277 tmp_column.PopulateSparseColumn(&columns_[col_b]);
278 columns_[col_b].CleanUp();
283void SparseMatrix::DeleteColumns(
const DenseBooleanRow& columns_to_delete) {
284 if (columns_to_delete.empty())
return;
285 ColIndex new_index(0);
286 const ColIndex num_cols = columns_.size();
287 for (ColIndex
col(0);
col < num_cols; ++
col) {
288 if (
col >= columns_to_delete.size() || !columns_to_delete[
col]) {
289 columns_[
col].Swap(&(columns_[new_index]));
293 columns_.resize(new_index);
296void SparseMatrix::DeleteRows(RowIndex new_num_rows,
297 const RowPermutation& permutation) {
298 DCHECK_EQ(num_rows_, permutation.size());
299 for (RowIndex
row(0);
row < num_rows_; ++
row) {
300 DCHECK_LT(permutation[
row], new_num_rows);
302 const ColIndex
end = num_cols();
304 columns_[
col].ApplyPartialRowPermutation(permutation);
306 SetNumRows(new_num_rows);
309bool SparseMatrix::AppendRowsFromSparseMatrix(
const SparseMatrix& matrix) {
310 const ColIndex
end = num_cols();
311 if (
end != matrix.num_cols()) {
314 const RowIndex offset = num_rows();
319 SetNumRows(offset + matrix.num_rows());
323void SparseMatrix::ApplyRowPermutation(
const RowPermutation& row_perm) {
324 const ColIndex num_cols(columns_.size());
326 columns_[
col].ApplyRowPermutation(row_perm);
330Fractional SparseMatrix::LookUpValue(RowIndex
row, ColIndex
col)
const {
331 return columns_[
col].LookUpCoefficient(
row);
335 if (num_cols() !=
a.num_cols() || num_rows() !=
a.num_rows()) {
341 const ColIndex num_cols =
a.num_cols();
342 for (ColIndex
col(0);
col < num_cols; ++
col) {
344 for (
const SparseColumn::Entry e : columns_[
col]) {
345 dense_column.AddToCoefficient(e.row(), e.coefficient());
349 for (
const SparseColumn::Entry e :
a.columns_[
col]) {
350 if (fabs(e.coefficient() - dense_column.GetCoefficient(e.row())) >
357 for (
const SparseColumn::Entry e :
a.columns_[
col]) {
358 dense_column_a.AddToCoefficient(e.row(), e.coefficient());
362 for (
const SparseColumn::Entry e : columns_[
col]) {
363 if (fabs(e.coefficient() - dense_column_a.GetCoefficient(e.row())) >
369 dense_column.Clear();
370 dense_column_a.Clear();
376void SparseMatrix::ComputeMinAndMaxMagnitudes(Fractional* min_magnitude,
377 Fractional* max_magnitude)
const {
380 *min_magnitude = kInfinity;
381 *max_magnitude = 0.0;
382 for (ColIndex
col(0);
col < num_cols(); ++
col) {
383 for (
const SparseColumn::Entry e : columns_[
col]) {
384 const Fractional magnitude = fabs(e.coefficient());
385 if (magnitude != 0.0) {
386 *min_magnitude = std::min(*min_magnitude, magnitude);
387 *max_magnitude = std::max(*max_magnitude, magnitude);
391 if (*max_magnitude == 0.0) {
392 *min_magnitude = 0.0;
396EntryIndex SparseMatrix::num_entries()
const {
397 return ComputeNumEntries(*
this);
399Fractional SparseMatrix::ComputeOneNorm()
const {
400 return ComputeOneNormTemplate(*
this);
402Fractional SparseMatrix::ComputeInfinityNorm()
const {
403 return ComputeInfinityNormTemplate(*
this);
406std::string SparseMatrix::Dump()
const {
408 const ColIndex num_cols(columns_.size());
410 for (RowIndex
row(0);
row < num_rows_; ++
row) {
412 for (ColIndex
col(0);
col < num_cols; ++
col) {
413 absl::StrAppendFormat(&result,
"%g ", ToDouble(LookUpValue(
row,
col)));
415 result.append(
"}\n");
420void SparseMatrix::Reset(ColIndex num_cols, RowIndex num_rows) {
422 columns_.resize(num_cols, SparseColumn());
423 num_rows_ = num_rows;
426EntryIndex MatrixView::num_entries()
const {
return ComputeNumEntries(*
this); }
427Fractional MatrixView::ComputeOneNorm()
const {
428 return ComputeOneNormTemplate(*
this);
430Fractional MatrixView::ComputeInfinityNorm()
const {
431 return ComputeInfinityNormTemplate(*
this);
435template void SparseMatrix::PopulateFromTranspose<SparseMatrix>(
437template void SparseMatrix::PopulateFromPermutedMatrix<SparseMatrix>(
440template void SparseMatrix::PopulateFromPermutedMatrix<CompactSparseMatrixView>(
444void CompactSparseMatrix::PopulateFromMatrixView(
const MatrixView&
input) {
445 num_cols_ =
input.num_cols();
447 const EntryIndex num_entries =
input.num_entries();
448 starts_.assign(num_cols_ + 1, EntryIndex(0));
449 coefficients_.assign(num_entries, 0.0);
450 rows_.assign(num_entries, RowIndex(0));
454 for (
const SparseColumn::Entry e :
input.column(
col)) {
455 coefficients_[
index] = e.coefficient();
456 rows_[
index] = e.row();
463void CompactSparseMatrix::PopulateFromSparseMatrixAndAddSlacks(
464 const SparseMatrix&
input) {
466 num_rows_ =
input.num_rows();
467 const EntryIndex num_entries =
468 input.num_entries() + EntryIndex(num_rows_.value());
469 starts_.assign(num_cols_ + 1, EntryIndex(0));
470 coefficients_.assign(num_entries, 0.0);
471 rows_.assign(num_entries, RowIndex(0));
475 for (
const SparseColumn::Entry e :
input.column(
col)) {
476 coefficients_[
index] = e.coefficient();
477 rows_[
index] = e.row();
481 for (RowIndex
row(0);
row < num_rows_; ++
row) {
483 coefficients_[
index] = 1.0;
487 starts_[num_cols_] =
index;
490void CompactSparseMatrix::PopulateFromTranspose(
491 const CompactSparseMatrix&
input) {
498 starts_.assign(num_cols_ + 2, EntryIndex(0));
499 for (
const RowIndex
row :
input.rows_) {
502 for (ColIndex
col(2);
col < starts_.size(); ++
col) {
503 starts_[
col] += starts_[
col - 1];
505 coefficients_.resize(starts_.back(), 0.0);
506 rows_.resize(starts_.back(), kInvalidRow);
511 const auto entry_rows = rows_.view();
512 const auto input_entry_rows =
input.rows_.view();
513 const auto entry_coefficients = coefficients_.view();
514 const auto input_entry_coefficients =
input.coefficients_.view();
515 const auto num_cols =
input.num_cols();
516 const auto starts = starts_.view();
517 for (ColIndex
col(0);
col < num_cols; ++
col) {
518 const RowIndex transposed_row = ColToRowIndex(
col);
519 for (
const EntryIndex i :
input.Column(
col)) {
520 const ColIndex transposed_col = RowToColIndex(input_entry_rows[i]);
521 const EntryIndex
index = starts[transposed_col + 1]++;
522 entry_coefficients[
index] = input_entry_coefficients[i];
523 entry_rows[
index] = transposed_row;
527 DCHECK_EQ(starts_.front(), 0);
528 DCHECK_EQ(starts_.back(), rows_.size());
531void TriangularMatrix::PopulateFromTranspose(
const TriangularMatrix&
input) {
532 CompactSparseMatrix::PopulateFromTranspose(
input);
535 diagonal_coefficients_ =
input.diagonal_coefficients_;
536 all_diagonal_coefficients_are_one_ =
input.all_diagonal_coefficients_are_one_;
539 pruned_ends_.resize(num_cols_, EntryIndex(0));
540 for (ColIndex
col(0);
col < num_cols_; ++
col) {
541 pruned_ends_[
col] = starts_[
col + 1];
546 first_non_identity_column_ = 0;
547 const ColIndex
end = diagonal_coefficients_.size();
548 while (first_non_identity_column_ <
end &&
549 ColumnNumEntries(first_non_identity_column_) == 0 &&
550 diagonal_coefficients_[first_non_identity_column_] == 1.0) {
551 ++first_non_identity_column_;
555void CompactSparseMatrix::Reset(RowIndex num_rows) {
556 num_rows_ = num_rows;
559 coefficients_.clear();
561 starts_.push_back(EntryIndex(0));
564void TriangularMatrix::Reset(RowIndex num_rows, ColIndex col_capacity) {
565 CompactSparseMatrix::Reset(num_rows);
566 first_non_identity_column_ = 0;
567 all_diagonal_coefficients_are_one_ =
true;
569 pruned_ends_.resize(col_capacity);
570 diagonal_coefficients_.resize(col_capacity);
571 starts_.resize(col_capacity + 1);
573 starts_[ColIndex(0)] = 0;
576ColIndex CompactSparseMatrix::AddDenseColumn(
const DenseColumn& dense_column) {
577 return AddDenseColumnPrefix(dense_column.
const_view(), RowIndex(0));
580ColIndex CompactSparseMatrix::AddDenseColumnPrefix(
582 const RowIndex num_rows(dense_column.
size());
584 if (dense_column[
row] != 0.0) {
585 rows_.push_back(
row);
586 coefficients_.push_back(dense_column[
row]);
589 starts_.push_back(rows_.size());
591 return num_cols_ - 1;
594ColIndex CompactSparseMatrix::AddDenseColumnWithNonZeros(
595 const DenseColumn& dense_column,
const std::vector<RowIndex>& non_zeros) {
596 if (non_zeros.empty())
return AddDenseColumn(dense_column);
597 for (
const RowIndex
row : non_zeros) {
600 rows_.push_back(
row);
601 coefficients_.push_back(
value);
604 starts_.push_back(rows_.size());
606 return num_cols_ - 1;
609ColIndex CompactSparseMatrix::AddAndClearColumnWithNonZeros(
610 DenseColumn*
column, std::vector<RowIndex>* non_zeros) {
611 for (
const RowIndex
row : *non_zeros) {
614 rows_.push_back(
row);
615 coefficients_.push_back(
value);
616 (*column)[
row] = 0.0;
620 starts_.push_back(rows_.size());
622 return num_cols_ - 1;
625void CompactSparseMatrix::Swap(CompactSparseMatrix* other) {
626 std::swap(num_rows_, other->num_rows_);
627 std::swap(num_cols_, other->num_cols_);
628 coefficients_.swap(other->coefficients_);
629 rows_.swap(other->rows_);
630 starts_.swap(other->starts_);
634 CompactSparseMatrix::Swap(other);
635 diagonal_coefficients_.swap(other->diagonal_coefficients_);
636 std::swap(first_non_identity_column_, other->first_non_identity_column_);
637 std::swap(all_diagonal_coefficients_are_one_,
638 other->all_diagonal_coefficients_are_one_);
641EntryIndex CompactSparseMatrixView::num_entries()
const {
642 return ComputeNumEntries(*
this);
644Fractional CompactSparseMatrixView::ComputeOneNorm()
const {
645 return ComputeOneNormTemplate(*
this);
647Fractional CompactSparseMatrixView::ComputeInfinityNorm()
const {
648 return ComputeInfinityNormTemplate(*
this);
654void TriangularMatrix::CloseCurrentColumn(
Fractional diagonal_value) {
655 DCHECK_NE(diagonal_value, 0.0);
658 DCHECK_LT(num_cols_, diagonal_coefficients_.size());
659 diagonal_coefficients_[num_cols_] = diagonal_value;
663 DCHECK_LT(num_cols_, pruned_ends_.size());
664 pruned_ends_[num_cols_] = coefficients_.size();
666 DCHECK_LT(num_cols_, starts_.size());
667 starts_[num_cols_] = coefficients_.size();
668 if (first_non_identity_column_ == num_cols_ - 1 && coefficients_.empty() &&
669 diagonal_value == 1.0) {
670 first_non_identity_column_ = num_cols_;
672 all_diagonal_coefficients_are_one_ =
673 all_diagonal_coefficients_are_one_ && (diagonal_value == 1.0);
676void TriangularMatrix::AddDiagonalOnlyColumn(Fractional diagonal_value) {
677 CloseCurrentColumn(diagonal_value);
681 RowIndex diagonal_row) {
683 for (
const SparseColumn::Entry e :
column) {
684 if (e.row() == diagonal_row) {
685 diagonal_value = e.coefficient();
687 DCHECK_NE(0.0, e.coefficient());
688 rows_.push_back(e.row());
689 coefficients_.push_back(e.coefficient());
692 CloseCurrentColumn(diagonal_value);
695void TriangularMatrix::AddAndNormalizeTriangularColumn(
699 for (
const SparseColumn::Entry e :
column) {
700 if (e.row() != diagonal_row) {
701 if (e.coefficient() != 0.0) {
702 rows_.push_back(e.row());
703 coefficients_.push_back(e.coefficient() / diagonal_coefficient);
706 DCHECK_EQ(e.coefficient(), diagonal_coefficient);
709 CloseCurrentColumn(1.0);
712void TriangularMatrix::AddTriangularColumnWithGivenDiagonalEntry(
715 for (SparseColumn::Entry e :
column) {
716 DCHECK_NE(e.row(), diagonal_row);
717 rows_.push_back(e.row());
718 coefficients_.push_back(e.coefficient());
720 CloseCurrentColumn(diagonal_value);
723void TriangularMatrix::PopulateFromTriangularSparseMatrix(
729 DCHECK(IsLowerTriangular() || IsUpperTriangular());
732bool TriangularMatrix::IsLowerTriangular()
const {
733 for (ColIndex
col(0);
col < num_cols_; ++
col) {
734 if (diagonal_coefficients_[
col] == 0.0)
return false;
735 for (EntryIndex i : Column(
col)) {
742bool TriangularMatrix::IsUpperTriangular()
const {
743 for (ColIndex
col(0);
col < num_cols_; ++
col) {
744 if (diagonal_coefficients_[
col] == 0.0)
return false;
745 for (EntryIndex i : Column(
col)) {
752void TriangularMatrix::ApplyRowPermutationToNonDiagonalEntries(
753 const RowPermutation& row_perm) {
754 EntryIndex num_entries = rows_.size();
755 for (EntryIndex i(0); i < num_entries; ++i) {
756 rows_[i] = row_perm[rows_[i]];
760void TriangularMatrix::CopyColumnToSparseColumn(ColIndex
col,
763 const auto entry_rows = rows_.view();
764 const auto entry_coefficients = coefficients_.view();
765 for (
const EntryIndex i : Column(
col)) {
766 output->SetCoefficient(entry_rows[i], entry_coefficients[i]);
772void TriangularMatrix::CopyToSparseMatrix(SparseMatrix* output)
const {
773 output->PopulateFromZero(num_rows_, num_cols_);
775 CopyColumnToSparseColumn(
col, output->mutable_column(
col));
779void TriangularMatrix::LowerSolve(DenseColumn* rhs)
const {
780 LowerSolveStartingAt(ColIndex(0), rhs);
783void TriangularMatrix::LowerSolveStartingAt(ColIndex
start,
786 if (all_diagonal_coefficients_are_one_) {
787 LowerSolveStartingAtInternal<true>(
start, rhs->
view());
789 LowerSolveStartingAtInternal<false>(
start, rhs->
view());
793template <
bool diagonal_of_ones>
794void TriangularMatrix::LowerSolveStartingAtInternal(
795 ColIndex
start, DenseColumn::View rhs)
const {
796 const ColIndex begin = std::max(
start, first_non_identity_column_);
797 const auto entry_rows = rows_.view();
798 const auto entry_coefficients = coefficients_.view();
799 const auto diagonal_coefficients = diagonal_coefficients_.view();
800 const ColIndex
end = diagonal_coefficients.size();
802 const Fractional
value = rhs[ColToRowIndex(
col)];
803 if (
value == 0.0)
continue;
804 const Fractional coeff =
805 diagonal_of_ones ?
value :
value / diagonal_coefficients[
col];
806 if (!diagonal_of_ones) {
807 rhs[ColToRowIndex(
col)] = coeff;
809 for (
const EntryIndex i : Column(
col)) {
810 rhs[entry_rows[
i]] -= coeff * entry_coefficients[
i];
815void TriangularMatrix::UpperSolve(DenseColumn* rhs)
const {
817 if (all_diagonal_coefficients_are_one_) {
818 UpperSolveInternal<true>(rhs->view());
820 UpperSolveInternal<false>(rhs->view());
824template <
bool diagonal_of_ones>
825void TriangularMatrix::UpperSolveInternal(DenseColumn::View rhs)
const {
826 const ColIndex
end = first_non_identity_column_;
827 const auto entry_rows = rows_.view();
828 const auto entry_coefficients = coefficients_.view();
829 const auto diagonal_coefficients = diagonal_coefficients_.view();
830 for (ColIndex
col(diagonal_coefficients.size() - 1);
col >=
end; --
col) {
831 const Fractional
value = rhs[ColToRowIndex(
col)];
832 if (
value == 0.0)
continue;
833 const Fractional coeff =
834 diagonal_of_ones ?
value :
value / diagonal_coefficients[
col];
835 if (!diagonal_of_ones) {
836 rhs[ColToRowIndex(
col)] = coeff;
842 const EntryIndex i_end = starts_[
col];
843 for (EntryIndex
i(starts_[
col + 1] - 1);
i >= i_end; --
i) {
844 rhs[entry_rows[
i]] -= coeff * entry_coefficients[
i];
849void TriangularMatrix::TransposeUpperSolve(DenseColumn* rhs)
const {
851 if (all_diagonal_coefficients_are_one_) {
852 TransposeUpperSolveInternal<true>(rhs->view());
854 TransposeUpperSolveInternal<false>(rhs->view());
858template <
bool diagonal_of_ones>
859void TriangularMatrix::TransposeUpperSolveInternal(
860 DenseColumn::View rhs)
const {
861 const ColIndex
end = num_cols_;
862 const auto starts = starts_.view();
863 const auto entry_rows = rows_.view();
864 const auto entry_coefficients = coefficients_.view();
865 const auto diagonal_coefficients = diagonal_coefficients_.view();
867 EntryIndex i = starts_[first_non_identity_column_];
868 for (ColIndex
col(first_non_identity_column_);
col <
end; ++
col) {
869 Fractional sum = rhs[ColToRowIndex(
col)];
876 const EntryIndex i_end = starts[
col + 1];
877 const EntryIndex shifted_end = i_end - 3;
878 for (; i < shifted_end; i += 4) {
879 sum -= entry_coefficients[i] * rhs[entry_rows[i]] +
880 entry_coefficients[i + 1] * rhs[entry_rows[i + 1]] +
881 entry_coefficients[i + 2] * rhs[entry_rows[i + 2]] +
882 entry_coefficients[i + 3] * rhs[entry_rows[i + 3]];
885 sum -= entry_coefficients[
i] * rhs[entry_rows[
i]];
887 sum -= entry_coefficients[
i + 1] * rhs[entry_rows[
i + 1]];
889 sum -= entry_coefficients[
i + 2] * rhs[entry_rows[
i + 2]];
896 diagonal_of_ones ? sum : sum / diagonal_coefficients[
col];
900void TriangularMatrix::TransposeLowerSolve(DenseColumn* rhs)
const {
902 if (all_diagonal_coefficients_are_one_) {
903 TransposeLowerSolveInternal<true>(rhs->view());
905 TransposeLowerSolveInternal<false>(rhs->view());
909template <
bool diagonal_of_ones>
910void TriangularMatrix::TransposeLowerSolveInternal(
911 DenseColumn::View rhs)
const {
912 const ColIndex
end = first_non_identity_column_;
915 ColIndex
col = num_cols_ - 1;
916 while (
col >=
end && rhs[ColToRowIndex(
col)] == 0.0) {
920 const auto starts = starts_.view();
921 const auto diagonal_coeffs = diagonal_coefficients_.view();
922 const auto entry_rows = rows_.view();
923 const auto entry_coefficients = coefficients_.view();
924 EntryIndex
i = starts[
col + 1] - 1;
934 const EntryIndex i_end = starts[
col];
935 const EntryIndex shifted_end = i_end + 3;
936 for (;
i >= shifted_end;
i -= 4) {
937 sum -= entry_coefficients[
i] * rhs[entry_rows[
i]] +
938 entry_coefficients[
i - 1] * rhs[entry_rows[
i - 1]] +
939 entry_coefficients[
i - 2] * rhs[entry_rows[
i - 2]] +
940 entry_coefficients[
i - 3] * rhs[entry_rows[
i - 3]];
943 sum -= entry_coefficients[
i] * rhs[entry_rows[
i]];
944 if (i >= i_end + 1) {
945 sum -= entry_coefficients[
i - 1] * rhs[entry_rows[
i - 1]];
946 if (i >= i_end + 2) {
947 sum -= entry_coefficients[
i - 2] * rhs[entry_rows[
i - 2]];
954 diagonal_of_ones ? sum : sum / diagonal_coeffs[
col];
958void TriangularMatrix::HyperSparseSolve(DenseColumn* rhs,
959 RowIndexVector* non_zero_rows)
const {
961 if (all_diagonal_coefficients_are_one_) {
962 HyperSparseSolveInternal<true>(rhs->view(), non_zero_rows);
964 HyperSparseSolveInternal<false>(rhs->view(), non_zero_rows);
968template <
bool diagonal_of_ones>
969void TriangularMatrix::HyperSparseSolveInternal(
970 DenseColumn::View rhs, RowIndexVector* non_zero_rows)
const {
972 const auto entry_rows = rows_.view();
973 const auto entry_coefficients = coefficients_.view();
974 for (
const RowIndex
row : *non_zero_rows) {
975 if (rhs[
row] == 0.0)
continue;
976 const ColIndex row_as_col = RowToColIndex(
row);
977 const Fractional coeff =
978 diagonal_of_ones ? rhs[
row]
979 : rhs[
row] / diagonal_coefficients_[row_as_col];
981 for (
const EntryIndex i : Column(row_as_col)) {
982 rhs[entry_rows[i]] -= coeff * entry_coefficients[i];
984 (*non_zero_rows)[new_size] =
row;
987 non_zero_rows->resize(new_size);
990void TriangularMatrix::HyperSparseSolveWithReversedNonZeros(
991 DenseColumn* rhs, RowIndexVector* non_zero_rows)
const {
993 if (all_diagonal_coefficients_are_one_) {
994 HyperSparseSolveWithReversedNonZerosInternal<true>(rhs->view(),
997 HyperSparseSolveWithReversedNonZerosInternal<false>(rhs->view(),
1002template <
bool diagonal_of_ones>
1003void TriangularMatrix::HyperSparseSolveWithReversedNonZerosInternal(
1004 DenseColumn::View rhs, RowIndexVector* non_zero_rows)
const {
1005 int new_start = non_zero_rows->size();
1006 const auto entry_rows = rows_.view();
1007 const auto entry_coefficients = coefficients_.view();
1008 for (
const RowIndex
row : Reverse(*non_zero_rows)) {
1009 if (rhs[
row] == 0.0)
continue;
1010 const ColIndex row_as_col = RowToColIndex(
row);
1011 const Fractional coeff =
1012 diagonal_of_ones ? rhs[
row]
1013 : rhs[
row] / diagonal_coefficients_[row_as_col];
1015 for (
const EntryIndex i : Column(row_as_col)) {
1016 rhs[entry_rows[i]] -= coeff * entry_coefficients[i];
1019 (*non_zero_rows)[new_start] =
row;
1021 non_zero_rows->erase(non_zero_rows->begin(),
1022 non_zero_rows->begin() + new_start);
1025void TriangularMatrix::TransposeHyperSparseSolve(
1026 DenseColumn* rhs, RowIndexVector* non_zero_rows)
const {
1028 if (all_diagonal_coefficients_are_one_) {
1029 TransposeHyperSparseSolveInternal<true>(rhs->view(), non_zero_rows);
1031 TransposeHyperSparseSolveInternal<false>(rhs->view(), non_zero_rows);
1035template <
bool diagonal_of_ones>
1036void TriangularMatrix::TransposeHyperSparseSolveInternal(
1037 DenseColumn::View rhs, RowIndexVector* non_zero_rows)
const {
1040 const auto entry_rows = rows_.view();
1041 const auto entry_coefficients = coefficients_.view();
1042 for (
const RowIndex
row : *non_zero_rows) {
1043 Fractional sum = rhs[
row];
1044 const ColIndex row_as_col = RowToColIndex(
row);
1048 EntryIndex i = starts_[row_as_col];
1049 const EntryIndex i_end = starts_[row_as_col + 1];
1050 const EntryIndex shifted_end = i_end - 3;
1051 for (; i < shifted_end; i += 4) {
1052 sum -= entry_coefficients[i] * rhs[entry_rows[i]] +
1053 entry_coefficients[i + 1] * rhs[entry_rows[i + 1]] +
1054 entry_coefficients[i + 2] * rhs[entry_rows[i + 2]] +
1055 entry_coefficients[i + 3] * rhs[entry_rows[i + 3]];
1058 sum -= entry_coefficients[
i] * rhs[entry_rows[
i]];
1059 if (i + 1 < i_end) {
1060 sum -= entry_coefficients[
i + 1] * rhs[entry_rows[
i + 1]];
1061 if (i + 2 < i_end) {
1062 sum -= entry_coefficients[
i + 2] * rhs[entry_rows[
i + 2]];
1068 diagonal_of_ones ? sum : sum / diagonal_coefficients_[row_as_col];
1070 (*non_zero_rows)[new_size] =
row;
1074 non_zero_rows->resize(new_size);
1077void TriangularMatrix::TransposeHyperSparseSolveWithReversedNonZeros(
1078 DenseColumn* rhs, RowIndexVector* non_zero_rows)
const {
1080 if (all_diagonal_coefficients_are_one_) {
1081 TransposeHyperSparseSolveWithReversedNonZerosInternal<true>(rhs->view(),
1084 TransposeHyperSparseSolveWithReversedNonZerosInternal<false>(rhs->view(),
1089template <
bool diagonal_of_ones>
1090void TriangularMatrix::TransposeHyperSparseSolveWithReversedNonZerosInternal(
1091 DenseColumn::View rhs, RowIndexVector* non_zero_rows)
const {
1092 int new_start = non_zero_rows->size();
1093 const auto entry_rows = rows_.view();
1094 const auto entry_coefficients = coefficients_.view();
1095 for (
const RowIndex
row : Reverse(*non_zero_rows)) {
1096 Fractional sum = rhs[
row];
1097 const ColIndex row_as_col = RowToColIndex(
row);
1101 EntryIndex i = starts_[row_as_col + 1] - 1;
1102 const EntryIndex i_end = starts_[row_as_col];
1103 const EntryIndex shifted_end = i_end + 3;
1104 for (; i >= shifted_end; i -= 4) {
1105 sum -= entry_coefficients[i] * rhs[entry_rows[i]] +
1106 entry_coefficients[i - 1] * rhs[entry_rows[i - 1]] +
1107 entry_coefficients[i - 2] * rhs[entry_rows[i - 2]] +
1108 entry_coefficients[i - 3] * rhs[entry_rows[i - 3]];
1111 sum -= entry_coefficients[
i] * rhs[entry_rows[
i]];
1112 if (i >= i_end + 1) {
1113 sum -= entry_coefficients[
i - 1] * rhs[entry_rows[
i - 1]];
1114 if (i >= i_end + 2) {
1115 sum -= entry_coefficients[
i - 2] * rhs[entry_rows[
i - 2]];
1121 diagonal_of_ones ? sum : sum / diagonal_coefficients_[row_as_col];
1124 (*non_zero_rows)[new_start] =
row;
1127 non_zero_rows->erase(non_zero_rows->begin(),
1128 non_zero_rows->begin() + new_start);
1131void TriangularMatrix::PermutedLowerSolve(
1132 const SparseColumn& rhs,
const RowPermutation& row_perm,
1135 DCHECK(all_diagonal_coefficients_are_one_);
1139 initially_all_zero_scratchpad_.resize(num_rows_, 0.0);
1140 for (
const SparseColumn::Entry e : rhs) {
1141 initially_all_zero_scratchpad_[e.row()] = e.coefficient();
1144 const auto entry_rows = rows_.view();
1145 const auto entry_coefficients = coefficients_.view();
1146 const RowIndex end_row(partial_inverse_row_perm.
size());
1149 const RowIndex permuted_row = partial_inverse_row_perm[
row];
1150 const Fractional pivot = initially_all_zero_scratchpad_[permuted_row];
1151 if (pivot == 0.0)
continue;
1154 initially_all_zero_scratchpad_[entry_rows[i]] -=
1155 entry_coefficients[i] * pivot;
1160 const RowIndex num_rows = num_rows_;
1161 for (RowIndex
row(0);
row < num_rows; ++
row) {
1162 if (initially_all_zero_scratchpad_[
row] != 0.0) {
1163 if (row_perm[
row] < 0) {
1164 lower->SetCoefficient(
row, initially_all_zero_scratchpad_[
row]);
1166 upper->SetCoefficient(
row, initially_all_zero_scratchpad_[
row]);
1168 initially_all_zero_scratchpad_[
row] = 0.0;
1171 DCHECK(
lower->CheckNoDuplicates());
1174void TriangularMatrix::PermutedLowerSparseSolve(
const ColumnView& rhs,
1178 DCHECK(all_diagonal_coefficients_are_one_);
1184 PermutedComputeRowsToConsider(rhs, row_perm, &lower_column_rows_,
1185 &upper_column_rows_);
1188 initially_all_zero_scratchpad_.resize(num_rows_, 0.0);
1189 for (
const auto e : rhs) {
1190 initially_all_zero_scratchpad_[e.row()] = e.coefficient();
1195 num_fp_operations_ = 0;
1196 lower_column->
Clear();
1203 EntryIndex(upper_column_rows_.size()));
1204 for (
const RowIndex permuted_row : Reverse(upper_column_rows_)) {
1205 const Fractional pivot = initially_all_zero_scratchpad_[permuted_row];
1206 if (pivot == 0.0)
continue;
1209 initially_all_zero_scratchpad_[permuted_row] = 0.0;
1210 const ColIndex row_as_col =
RowToColIndex(row_perm[permuted_row]);
1211 DCHECK_GE(row_as_col, 0);
1213 DCHECK_EQ(diagonal_coefficients_[row_as_col], 1.0);
1214 num_fp_operations_ += 1 + ColumnNumEntries(row_as_col).value();
1215 for (
const auto e :
column(row_as_col)) {
1216 initially_all_zero_scratchpad_[e.row()] -= e.coefficient() * pivot;
1221 lower_column->
Reserve(EntryIndex(lower_column_rows_.size()));
1222 for (
const RowIndex permuted_row : lower_column_rows_) {
1223 const Fractional pivot = initially_all_zero_scratchpad_[permuted_row];
1224 initially_all_zero_scratchpad_[permuted_row] = 0.0;
1254void TriangularMatrix::PermutedComputeRowsToConsider(
1257 stored_.Resize(num_rows_);
1258 marked_.resize(num_rows_,
false);
1259 lower_column_rows->clear();
1260 upper_column_rows->clear();
1261 nodes_to_explore_.clear();
1263 for (SparseColumn::Entry e : rhs) {
1266 stored_.Set(e.row());
1267 lower_column_rows->push_back(e.row());
1269 nodes_to_explore_.push_back(e.row());
1283 const auto entry_rows = rows_.view();
1284 while (!nodes_to_explore_.empty()) {
1285 const RowIndex
row = nodes_to_explore_.back();
1291 nodes_to_explore_.pop_back();
1292 const RowIndex explored_row = nodes_to_explore_.back();
1293 nodes_to_explore_.pop_back();
1294 DCHECK(!stored_[explored_row]);
1295 stored_.Set(explored_row);
1296 upper_column_rows->push_back(explored_row);
1306 EntryIndex i = starts_[
col];
1307 EntryIndex
end = pruned_ends_[
col];
1309 const RowIndex entry_row = entry_rows[i];
1310 if (!marked_[entry_row]) {
1316 std::swap(rows_[i], rows_[
end]);
1317 std::swap(coefficients_[i], coefficients_[
end]);
1319 marked_[entry_row] =
false;
1329 nodes_to_explore_.pop_back();
1338 lower_column_rows->push_back(
row);
1339 nodes_to_explore_.pop_back();
1346 const EntryIndex
end = pruned_ends_[
col];
1347 for (EntryIndex i = starts_[
col]; i <
end; ++i) {
1348 const RowIndex entry_row = entry_rows[i];
1349 if (!stored_[entry_row]) {
1350 nodes_to_explore_.push_back(entry_row);
1352 marked_[entry_row] =
true;
1356 DCHECK_LE(nodes_to_explore_.size(), 2 * num_rows_.value() + rows_.size());
1360 for (
const RowIndex
row : *lower_column_rows) {
1361 stored_.ClearBucket(
row);
1363 for (
const RowIndex
row : *upper_column_rows) {
1364 stored_.ClearBucket(
row);
1368void TriangularMatrix::ComputeRowsToConsiderWithDfs(
1370 if (non_zero_rows->empty())
return;
1380 const int sparsity_threshold =
1381 static_cast<int>(0.025 *
static_cast<double>(num_rows_.value()));
1382 const int num_ops_threshold =
1383 static_cast<int>(0.05 *
static_cast<double>(num_rows_.value()));
1384 int num_ops = non_zero_rows->size();
1385 if (num_ops > sparsity_threshold) {
1386 non_zero_rows->clear();
1391 stored_.Resize(num_rows_);
1392 nodes_to_explore_.clear();
1393 nodes_to_explore_.swap(*non_zero_rows);
1397 const auto entry_rows = rows_.view();
1398 while (!nodes_to_explore_.empty()) {
1399 const RowIndex
row = nodes_to_explore_.back();
1404 nodes_to_explore_.pop_back();
1405 const RowIndex explored_row = -
row - 1;
1406 stored_.Set(explored_row);
1407 non_zero_rows->push_back(explored_row);
1413 nodes_to_explore_.pop_back();
1422 nodes_to_explore_.back() = -
row - 1;
1423 for (
const EntryIndex i : Column(RowToColIndex(
row))) {
1425 const RowIndex entry_row = entry_rows[i];
1426 if (!stored_[entry_row]) {
1427 nodes_to_explore_.push_back(entry_row);
1434 if (num_ops > num_ops_threshold)
break;
1438 for (
const RowIndex
row : *non_zero_rows) {
1439 stored_.ClearBucket(
row);
1443 if (num_ops > num_ops_threshold) non_zero_rows->clear();
1446void TriangularMatrix::ComputeRowsToConsiderInSortedOrder(
1447 RowIndexVector* non_zero_rows)
const {
1448 if (non_zero_rows->empty())
return;
1451 const int sparsity_threshold =
1452 static_cast<int>(0.025 *
static_cast<double>(num_rows_.value()));
1453 const int num_ops_threshold =
1454 static_cast<int>(0.05 *
static_cast<double>(num_rows_.value()));
1455 int num_ops = non_zero_rows->size();
1456 if (num_ops > sparsity_threshold) {
1457 non_zero_rows->clear();
1461 stored_.Resize(num_rows_);
1462 for (
const RowIndex
row : *non_zero_rows) stored_.Set(
row);
1464 const auto entry_rows = rows_.view();
1465 for (
int i = 0; i < non_zero_rows->size(); ++i) {
1466 const RowIndex
row = (*non_zero_rows)[i];
1469 const RowIndex entry_row = entry_rows[
index];
1470 if (!stored_[entry_row]) {
1471 non_zero_rows->push_back(entry_row);
1472 stored_.Set(entry_row);
1475 if (num_ops > num_ops_threshold)
break;
1478 if (num_ops > num_ops_threshold) {
1480 non_zero_rows->clear();
1482 std::sort(non_zero_rows->begin(), non_zero_rows->end());
1483 for (
const RowIndex
row : *non_zero_rows) stored_.ClearBucket(
row);
1491Fractional TriangularMatrix::ComputeInverseInfinityNormUpperBound()
const {
1492 if (first_non_identity_column_ == num_cols_) {
1497 const bool is_upper = IsUpperTriangular();
1499 const int num_cols = num_cols_.value();
1501 const auto entry_rows = rows_.view();
1502 const auto entry_coefficients = coefficients_.view();
1503 for (
int i = 0; i < num_cols; ++i) {
1504 const ColIndex
col(is_upper ? num_cols - 1 - i : i);
1505 DCHECK_NE(diagonal_coefficients_[
col], 0.0);
1507 std::abs(diagonal_coefficients_[
col]);
1510 for (
const EntryIndex i : Column(
col)) {
1511 row_norm_estimate[entry_rows[i]] +=
1512 coeff * std::abs(entry_coefficients[i]);
1516 return *std::max_element(row_norm_estimate.begin(), row_norm_estimate.end());
1519Fractional TriangularMatrix::ComputeInverseInfinityNorm()
const {
1520 const bool is_upper = IsUpperTriangular();
1524 for (ColIndex
col(0);
col < num_cols_; ++
col) {
1525 right_hand_side.
assign(num_rows_, 0);
1530 UpperSolve(&right_hand_side);
1532 LowerSolve(&right_hand_side);
1536 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1537 row_sum[
row] += std::abs(right_hand_side[
row]);
1542 for (RowIndex
row(0);
row < num_rows_; ++
row) {
1543 norm = std::max(norm, row_sum[
row]);
void PopulateSparseColumn(SparseColumn *sparse_column) const
void AddToCoefficient(RowIndex row, Fractional value)
EntryIndex num_entries() const
Note this method can only be used when the vector has no duplicates.
void Clear()
Clears the vector, i.e. removes all entries.
void AppendEntriesWithOffset(const SparseVector &sparse_vector, Index offset)
void SetCoefficient(Index index, Fractional value)
Defines the coefficient at index, i.e. vector[index] = value;.
void Reserve(EntryIndex new_capacity)
Reserve the underlying storage for the given number of entries.
bool CheckNoDuplicates() const
void assign(IntType size, const T &v)
ConstView const_view() const
std::vector< RowIndex > RowIndexVector
ColIndex RowToColIndex(RowIndex row)
Get the ColIndex corresponding to the column # row.
constexpr RowIndex kInvalidRow(-1)
RowIndex ColToRowIndex(ColIndex col)
Get the RowIndex corresponding to the row # col.
StrictITIVector< RowIndex, Fractional > DenseColumn
Column-vector types. Column-vector types are indexed by a row index.
In SWIG mode, we don't want anything besides these top-level includes.
static int input(yyscan_t yyscanner)
#define RETURN_IF_NULL(x)
std::optional< int64_t > end