18#include <initializer_list>
23#include "absl/log/check.h"
24#include "absl/strings/str_format.h"
25#include "absl/types/span.h"
38template <
typename Matrix>
39EntryIndex ComputeNumEntries(
const Matrix& matrix) {
40 EntryIndex num_entries(0);
41 const ColIndex num_cols(matrix.num_cols());
42 for (ColIndex col(0); col < num_cols; ++col) {
43 num_entries += matrix.column(col).num_entries();
51template <
typename Matrix>
52Fractional ComputeOneNormTemplate(
const Matrix& matrix) {
54 const ColIndex num_cols(matrix.num_cols());
55 for (ColIndex col(0); col < num_cols; ++col) {
57 for (
const SparseColumn::Entry e : matrix.column(col)) {
59 column_norm += fabs(e.coefficient());
62 norm = std::max(norm, column_norm);
70template <
typename Matrix>
71Fractional ComputeInfinityNormTemplate(
const Matrix& matrix) {
73 const ColIndex num_cols(matrix.num_cols());
74 for (ColIndex col(0); col < num_cols; ++col) {
75 for (
const SparseColumn::Entry e : matrix.column(col)) {
77 row_sum[e.row()] += fabs(e.coefficient());
83 const RowIndex num_rows(matrix.num_rows());
84 for (RowIndex row(0); row < num_rows; ++row) {
85 norm = std::max(norm, row_sum[row]);
97#if (!defined(_MSC_VER) || (_MSC_VER >= 1800))
99 std::initializer_list<std::initializer_list<Fractional>> init_list) {
101 num_rows_ = RowIndex(init_list.size());
103 for (std::initializer_list<Fractional> init_row : init_list) {
109 columns_[col].SetCoefficient(row, value);
118void SparseMatrix::Clear() {
120 num_rows_ = RowIndex(0);
124 return columns_.empty() || num_rows_ == 0;
128 const ColIndex num_cols(columns_.size());
130 columns_[col].CleanUp();
134bool SparseMatrix::CheckNoDuplicates()
const {
135 DenseBooleanColumn boolean_column;
137 for (ColIndex col(0); col <
num_cols; ++col) {
143bool SparseMatrix::IsCleanedUp()
const {
144 const ColIndex num_cols(columns_.size());
151void SparseMatrix::SetNumRows(RowIndex num_rows) { num_rows_ = num_rows; }
154 const ColIndex result = columns_.size();
160 DCHECK_LT(row, num_rows_);
163 columns_.push_back(std::move(new_col));
168 columns_.swap(matrix->columns_);
169 std::swap(num_rows_, matrix->num_rows_);
175 columns_[col].Clear();
177 num_rows_ = num_rows;
180void SparseMatrix::PopulateFromIdentity(ColIndex num_cols) {
181 PopulateFromZero(ColToRowIndex(num_cols), num_cols);
184 columns_[col].SetCoefficient(row,
Fractional(1.0));
188template <
typename Matrix>
189void SparseMatrix::PopulateFromTranspose(
const Matrix&
input) {
190 Reset(RowToColIndex(
input.num_rows()), ColToRowIndex(
input.num_cols()));
195 for (ColIndex col(0); col <
input.num_cols(); ++col) {
196 for (
const SparseColumn::Entry e :
input.column(col)) {
197 ++row_degree[e.row()];
200 for (RowIndex row(0); row <
input.num_rows(); ++row) {
201 columns_[RowToColIndex(row)].Reserve(row_degree[row]);
204 for (ColIndex col(0); col <
input.num_cols(); ++col) {
205 const RowIndex transposed_row = ColToRowIndex(col);
206 for (
const SparseColumn::Entry e :
input.column(col)) {
207 const ColIndex transposed_col = RowToColIndex(e.row());
208 columns_[transposed_col].SetCoefficient(transposed_row, e.coefficient());
211 DCHECK(IsCleanedUp());
214void SparseMatrix::PopulateFromSparseMatrix(
const SparseMatrix& matrix) {
215 Reset(ColIndex(0), matrix.num_rows_);
216 columns_ = matrix.columns_;
219template <
typename Matrix>
223 const ColIndex
num_cols = a.num_cols();
225 for (ColIndex col(0); col <
num_cols; ++col) {
226 for (
const auto e : a.column(inverse_col_perm[col])) {
227 columns_[col].SetCoefficient(row_perm[e.row()], e.coefficient());
237 DCHECK_EQ(a.
num_cols(), b.num_cols());
238 DCHECK_EQ(a.
num_rows(), b.num_rows());
245 for (ColIndex col(0); col <
num_cols; ++col) {
246 for (
const SparseColumn::Entry e : a.columns_[col]) {
249 for (
const SparseColumn::Entry e : b.columns_[col]) {
253 columns_[col].CleanUp();
254 dense_column.
Clear();
265 for (ColIndex col_b(0); col_b <
num_cols; ++col_b) {
266 for (
const SparseColumn::Entry eb : b.columns_[col_b]) {
267 if (eb.coefficient() == 0.0) {
271 for (
const SparseColumn::Entry ea : a.columns_[col_a]) {
272 const Fractional value = ea.coefficient() * eb.coefficient();
273 tmp_column.AddToCoefficient(ea.row(), value);
278 tmp_column.PopulateSparseColumn(&columns_[col_b]);
279 columns_[col_b].CleanUp();
284void SparseMatrix::DeleteColumns(
const DenseBooleanRow& columns_to_delete) {
285 if (columns_to_delete.empty())
return;
286 ColIndex new_index(0);
287 const ColIndex
num_cols = columns_.size();
288 for (ColIndex col(0); col <
num_cols; ++col) {
289 if (col >= columns_to_delete.size() || !columns_to_delete[col]) {
290 columns_[col].Swap(&(columns_[new_index]));
294 columns_.resize(new_index);
297void SparseMatrix::DeleteRows(RowIndex new_num_rows,
298 const RowPermutation& permutation) {
299 DCHECK_EQ(num_rows_, permutation.size());
300 for (RowIndex row(0); row < num_rows_; ++row) {
301 DCHECK_LT(permutation[row], new_num_rows);
303 const ColIndex end = num_cols();
304 for (ColIndex col(0); col < end; ++col) {
305 columns_[col].ApplyPartialRowPermutation(permutation);
307 SetNumRows(new_num_rows);
310bool SparseMatrix::AppendRowsFromSparseMatrix(
const SparseMatrix& matrix) {
311 const ColIndex end = num_cols();
312 if (end != matrix.num_cols()) {
315 const RowIndex offset = num_rows();
316 for (ColIndex col(0); col < end; ++col) {
317 const SparseColumn& source_column = matrix.columns_[col];
320 SetNumRows(offset + matrix.num_rows());
324void SparseMatrix::ApplyRowPermutation(
const RowPermutation& row_perm) {
325 const ColIndex num_cols(columns_.size());
327 columns_[col].ApplyRowPermutation(row_perm);
331Fractional SparseMatrix::LookUpValue(RowIndex row, ColIndex col)
const {
332 return columns_[col].LookUpCoefficient(row);
342 const ColIndex num_cols = a.
num_cols();
343 for (ColIndex col(0); col < num_cols; ++col) {
345 for (
const SparseColumn::Entry e : columns_[col]) {
346 dense_column.AddToCoefficient(e.row(), e.coefficient());
350 for (
const SparseColumn::Entry e : a.columns_[col]) {
351 if (fabs(e.coefficient() - dense_column.GetCoefficient(e.row())) >
358 for (
const SparseColumn::Entry e : a.columns_[col]) {
359 dense_column_a.AddToCoefficient(e.row(), e.coefficient());
363 for (
const SparseColumn::Entry e : columns_[col]) {
364 if (fabs(e.coefficient() - dense_column_a.GetCoefficient(e.row())) >
370 dense_column.Clear();
371 dense_column_a.Clear();
377void SparseMatrix::ComputeMinAndMaxMagnitudes(Fractional* min_magnitude,
378 Fractional* max_magnitude)
const {
382 *max_magnitude = 0.0;
383 for (ColIndex col(0); col <
num_cols(); ++col) {
384 for (
const SparseColumn::Entry e : columns_[col]) {
385 const Fractional magnitude = fabs(e.coefficient());
386 if (magnitude != 0.0) {
387 *min_magnitude = std::min(*min_magnitude, magnitude);
388 *max_magnitude = std::max(*max_magnitude, magnitude);
392 if (*max_magnitude == 0.0) {
393 *min_magnitude = 0.0;
397EntryIndex SparseMatrix::num_entries()
const {
398 return ComputeNumEntries(*
this);
401 return ComputeOneNormTemplate(*
this);
404 return ComputeInfinityNormTemplate(*
this);
411 for (RowIndex row(0); row < num_rows_; ++row) {
413 for (ColIndex col(0); col <
num_cols; ++col) {
416 result.append(
"}\n");
421void SparseMatrix::Reset(ColIndex num_cols, RowIndex num_rows) {
423 columns_.resize(num_cols, SparseColumn());
424 num_rows_ = num_rows;
427EntryIndex MatrixView::num_entries()
const {
return ComputeNumEntries(*
this); }
428Fractional MatrixView::ComputeOneNorm()
const {
429 return ComputeOneNormTemplate(*
this);
432 return ComputeInfinityNormTemplate(*
this);
446 num_cols_ =
input.num_cols();
453 for (ColIndex col(0); col <
input.num_cols(); ++col) {
455 for (
const SparseColumn::Entry e :
input.column(col)) {
457 rows_[index] = e.row();
461 starts_[
input.num_cols()] = index;
464void CompactSparseMatrix::PopulateFromSparseMatrixAndAddSlacks(
465 const SparseMatrix&
input) {
466 const int input_num_cols =
input.num_cols().value();
475 for (ColIndex col(0); col < input_num_cols; ++col) {
477 for (
const SparseColumn::Entry e :
input.column(col)) {
479 rows_[index] = e.row();
483 for (RowIndex row(0); row < num_rows_; ++row) {
484 starts_[input_num_cols + RowToColIndex(row)] = index;
485 coefficients_[index] = 1.0;
489 DCHECK_EQ(index, num_entries);
490 starts_[num_cols_] = index;
493void CompactSparseMatrix::PopulateFromTranspose(
494 const CompactSparseMatrix&
input) {
501 const ColIndex start_size =
num_cols_ + 2;
502 starts_.assign(start_size, EntryIndex(0));
503 for (
const RowIndex row :
input.rows_) {
506 for (ColIndex col(2); col < start_size; ++col) {
507 starts_[col] += starts_[col - 1];
509 coefficients_.resize(starts_.back(), 0.0);
510 rows_.resize(starts_.back(), kInvalidRow);
515 const auto entry_rows = rows_.view();
516 const auto input_entry_rows =
input.rows_.view();
517 const auto entry_coefficients = coefficients_.view();
518 const auto input_entry_coefficients =
input.coefficients_.view();
519 const auto num_cols =
input.num_cols();
520 const auto starts = starts_.view();
521 for (ColIndex col(0); col < num_cols; ++col) {
522 const RowIndex transposed_row = ColToRowIndex(col);
523 for (
const EntryIndex i :
input.Column(col)) {
524 const ColIndex transposed_col = RowToColIndex(input_entry_rows[i]);
525 const EntryIndex index = starts[transposed_col + 1]++;
526 entry_coefficients[index] = input_entry_coefficients[i];
527 entry_rows[index] = transposed_row;
531 DCHECK_EQ(starts_.front(), 0);
532 DCHECK_EQ(starts_.back(), rows_.size());
535void TriangularMatrix::PopulateFromTranspose(
const TriangularMatrix&
input) {
536 CompactSparseMatrix::PopulateFromTranspose(
input);
539 diagonal_coefficients_ =
input.diagonal_coefficients_;
540 all_diagonal_coefficients_are_one_ =
input.all_diagonal_coefficients_are_one_;
543 pruned_ends_.resize(
num_cols_, EntryIndex(0));
544 for (ColIndex col(0); col <
num_cols_; ++col) {
545 pruned_ends_[col] =
starts_[col + 1];
550 first_non_identity_column_ = 0;
551 const ColIndex end = diagonal_coefficients_.size();
552 while (first_non_identity_column_ < end &&
553 ColumnNumEntries(first_non_identity_column_) == 0 &&
554 diagonal_coefficients_[first_non_identity_column_] == 1.0) {
555 ++first_non_identity_column_;
559void CompactSparseMatrix::Reset(RowIndex num_rows) {
560 num_rows_ = num_rows;
565 starts_.push_back(EntryIndex(0));
570 first_non_identity_column_ = 0;
571 all_diagonal_coefficients_are_one_ =
true;
573 pruned_ends_.resize(col_capacity);
574 diagonal_coefficients_.resize(col_capacity);
575 starts_.resize(col_capacity + 1);
587 starts_.push_back(rows_.size());
592 return AddDenseColumnPrefix(dense_column.
const_view(), RowIndex(0));
598 for (RowIndex row(start); row <
num_rows; ++row) {
599 if (dense_column[row] != 0.0) {
600 rows_.push_back(row);
604 starts_.push_back(rows_.size());
606 return num_cols_ - 1;
609ColIndex CompactSparseMatrix::AddDenseColumnWithNonZeros(
610 const DenseColumn& dense_column, absl::Span<const RowIndex> non_zeros) {
612 for (
const RowIndex row : non_zeros) {
615 rows_.push_back(row);
619 starts_.push_back(rows_.size());
621 return num_cols_ - 1;
624ColIndex CompactSparseMatrix::AddAndClearColumnWithNonZeros(
625 DenseColumn* column, std::vector<RowIndex>* non_zeros) {
626 for (
const RowIndex row : *non_zeros) {
629 rows_.push_back(row);
631 (*column)[row] = 0.0;
635 starts_.push_back(rows_.size());
637 return num_cols_ - 1;
640void CompactSparseMatrix::Swap(CompactSparseMatrix* other) {
641 std::swap(num_rows_, other->num_rows_);
644 rows_.swap(other->rows_);
650 diagonal_coefficients_.swap(other->diagonal_coefficients_);
651 std::swap(first_non_identity_column_, other->first_non_identity_column_);
652 std::swap(all_diagonal_coefficients_are_one_,
653 other->all_diagonal_coefficients_are_one_);
657 return ComputeNumEntries(*
this);
660 return ComputeOneNormTemplate(*
this);
663 return ComputeInfinityNormTemplate(*
this);
669void TriangularMatrix::CloseCurrentColumn(
Fractional diagonal_value) {
670 DCHECK_NE(diagonal_value, 0.0);
673 DCHECK_LT(num_cols_, diagonal_coefficients_.size());
674 diagonal_coefficients_[num_cols_] = diagonal_value;
678 DCHECK_LT(num_cols_, pruned_ends_.size());
679 const EntryIndex num_entries = coefficients_.size();
680 pruned_ends_[num_cols_] = num_entries;
682 DCHECK_LT(num_cols_, starts_.size());
683 starts_[num_cols_] = num_entries;
684 if (first_non_identity_column_ == num_cols_ - 1 && diagonal_value == 1.0 &&
686 first_non_identity_column_ = num_cols_;
688 all_diagonal_coefficients_are_one_ =
689 all_diagonal_coefficients_are_one_ && (diagonal_value == 1.0);
692void TriangularMatrix::AddDiagonalOnlyColumn(Fractional diagonal_value) {
693 CloseCurrentColumn(diagonal_value);
697 RowIndex diagonal_row) {
699 for (
const SparseColumn::Entry e :
column) {
700 if (e.row() == diagonal_row) {
701 diagonal_value = e.coefficient();
703 DCHECK_NE(0.0, e.coefficient());
704 rows_.push_back(e.row());
708 CloseCurrentColumn(diagonal_value);
711void TriangularMatrix::AddAndNormalizeTriangularColumn(
715 for (
const SparseColumn::Entry e :
column) {
716 if (e.row() != diagonal_row) {
717 if (e.coefficient() != 0.0) {
718 rows_.push_back(e.row());
719 coefficients_.push_back(e.coefficient() / diagonal_coefficient);
722 DCHECK_EQ(e.coefficient(), diagonal_coefficient);
725 CloseCurrentColumn(1.0);
731 for (SparseColumn::Entry e :
column) {
732 DCHECK_NE(e.row(), diagonal_row);
733 rows_.push_back(e.row());
736 CloseCurrentColumn(diagonal_value);
742 for (ColIndex col(0); col <
input.num_cols(); ++col) {
745 DCHECK(IsLowerTriangular() || IsUpperTriangular());
748bool TriangularMatrix::IsLowerTriangular()
const {
749 for (ColIndex col(0); col < num_cols_; ++col) {
750 if (diagonal_coefficients_[col] == 0.0)
return false;
751 for (EntryIndex i :
Column(col)) {
758bool TriangularMatrix::IsUpperTriangular()
const {
759 for (ColIndex col(0); col < num_cols_; ++col) {
760 if (diagonal_coefficients_[col] == 0.0)
return false;
761 for (EntryIndex i :
Column(col)) {
768void TriangularMatrix::ApplyRowPermutationToNonDiagonalEntries(
769 const RowPermutation& row_perm) {
776void TriangularMatrix::CopyColumnToSparseColumn(ColIndex col,
779 const auto entry_rows =
rows_.view();
781 for (
const EntryIndex i :
Column(col)) {
782 output->SetCoefficient(entry_rows[i], entry_coefficients[i]);
784 output->SetCoefficient(
ColToRowIndex(col), diagonal_coefficients_[col]);
788void TriangularMatrix::CopyToSparseMatrix(SparseMatrix* output)
const {
789 output->PopulateFromZero(num_rows_, num_cols_);
795void TriangularMatrix::LowerSolve(DenseColumn* rhs)
const {
796 LowerSolveStartingAt(ColIndex(0), rhs);
802 if (all_diagonal_coefficients_are_one_) {
803 LowerSolveStartingAtInternal<true>(start, rhs->
view());
805 LowerSolveStartingAtInternal<false>(start, rhs->
view());
809template <
bool diagonal_of_ones>
810void TriangularMatrix::LowerSolveStartingAtInternal(
811 ColIndex start, DenseColumn::View rhs)
const {
812 const ColIndex begin = std::max(start, first_non_identity_column_);
813 const auto entry_rows = rows_.view();
814 const auto entry_coefficients = coefficients_.view();
815 const auto diagonal_coefficients = diagonal_coefficients_.view();
816 const ColIndex end = diagonal_coefficients.size();
817 for (ColIndex col(begin); col < end; ++col) {
818 const Fractional value = rhs[ColToRowIndex(col)];
819 if (value == 0.0)
continue;
820 const Fractional coeff =
821 diagonal_of_ones ? value : value / diagonal_coefficients[col];
822 if (!diagonal_of_ones) {
823 rhs[ColToRowIndex(col)] = coeff;
825 for (
const EntryIndex i : Column(col)) {
826 rhs[entry_rows[
i]] -= coeff * entry_coefficients[
i];
831void TriangularMatrix::UpperSolve(DenseColumn* rhs)
const {
833 if (all_diagonal_coefficients_are_one_) {
834 UpperSolveInternal<true>(rhs->view());
836 UpperSolveInternal<false>(rhs->view());
840template <
bool diagonal_of_ones>
841void TriangularMatrix::UpperSolveInternal(DenseColumn::View rhs)
const {
842 const ColIndex end = first_non_identity_column_;
843 const auto entry_rows = rows_.view();
844 const auto entry_coefficients = coefficients_.view();
845 const auto diagonal_coefficients = diagonal_coefficients_.view();
846 const auto starts = starts_.view();
847 for (ColIndex col(diagonal_coefficients.size() - 1); col >= end; --col) {
848 const Fractional value = rhs[ColToRowIndex(col)];
849 if (value == 0.0)
continue;
850 const Fractional coeff =
851 diagonal_of_ones ? value : value / diagonal_coefficients[col];
852 if (!diagonal_of_ones) {
853 rhs[ColToRowIndex(col)] = coeff;
859 const EntryIndex i_end = starts[col];
860 for (EntryIndex
i(starts[col + 1] - 1);
i >= i_end; --
i) {
861 rhs[entry_rows[
i]] -= coeff * entry_coefficients[
i];
866void TriangularMatrix::TransposeUpperSolve(DenseColumn* rhs)
const {
868 if (all_diagonal_coefficients_are_one_) {
869 TransposeUpperSolveInternal<true>(rhs->view());
871 TransposeUpperSolveInternal<false>(rhs->view());
875template <
bool diagonal_of_ones>
876void TriangularMatrix::TransposeUpperSolveInternal(
877 DenseColumn::View rhs)
const {
878 const ColIndex end = num_cols_;
879 const auto starts = starts_.view();
880 const auto entry_rows = rows_.view();
881 const auto entry_coefficients = coefficients_.view();
882 const auto diagonal_coefficients = diagonal_coefficients_.view();
884 EntryIndex i = starts_[first_non_identity_column_];
885 for (ColIndex col(first_non_identity_column_); col < end; ++col) {
886 Fractional sum = rhs[ColToRowIndex(col)];
893 const EntryIndex i_end = starts[col + 1];
894 const EntryIndex shifted_end = i_end - 3;
895 for (; i < shifted_end; i += 4) {
896 sum -= entry_coefficients[i] * rhs[entry_rows[i]] +
897 entry_coefficients[i + 1] * rhs[entry_rows[i + 1]] +
898 entry_coefficients[i + 2] * rhs[entry_rows[i + 2]] +
899 entry_coefficients[i + 3] * rhs[entry_rows[i + 3]];
902 sum -= entry_coefficients[
i] * rhs[entry_rows[
i]];
904 sum -= entry_coefficients[
i + 1] * rhs[entry_rows[
i + 1]];
906 sum -= entry_coefficients[
i + 2] * rhs[entry_rows[
i + 2]];
913 diagonal_of_ones ? sum : sum / diagonal_coefficients[col];
917void TriangularMatrix::TransposeLowerSolve(DenseColumn* rhs)
const {
919 if (all_diagonal_coefficients_are_one_) {
920 TransposeLowerSolveInternal<true>(rhs->view());
922 TransposeLowerSolveInternal<false>(rhs->view());
926template <
bool diagonal_of_ones>
927void TriangularMatrix::TransposeLowerSolveInternal(
928 DenseColumn::View rhs)
const {
929 const ColIndex end = first_non_identity_column_;
932 ColIndex col = num_cols_ - 1;
933 while (col >= end && rhs[ColToRowIndex(col)] == 0.0) {
937 const auto starts = starts_.view();
938 const auto diagonal_coeffs = diagonal_coefficients_.view();
939 const auto entry_rows = rows_.view();
940 const auto entry_coefficients = coefficients_.view();
941 EntryIndex
i = starts[col + 1] - 1;
942 for (; col >= end; --col) {
951 const EntryIndex i_end = starts[col];
952 const EntryIndex shifted_end = i_end + 3;
953 for (;
i >= shifted_end;
i -= 4) {
954 sum -= entry_coefficients[
i] * rhs[entry_rows[
i]] +
955 entry_coefficients[
i - 1] * rhs[entry_rows[
i - 1]] +
956 entry_coefficients[
i - 2] * rhs[entry_rows[
i - 2]] +
957 entry_coefficients[
i - 3] * rhs[entry_rows[
i - 3]];
960 sum -= entry_coefficients[
i] * rhs[entry_rows[
i]];
961 if (i >= i_end + 1) {
962 sum -= entry_coefficients[
i - 1] * rhs[entry_rows[
i - 1]];
963 if (i >= i_end + 2) {
964 sum -= entry_coefficients[
i - 2] * rhs[entry_rows[
i - 2]];
971 diagonal_of_ones ? sum : sum / diagonal_coeffs[col];
975void TriangularMatrix::HyperSparseSolve(DenseColumn* rhs,
976 RowIndexVector* non_zero_rows)
const {
978 if (all_diagonal_coefficients_are_one_) {
979 HyperSparseSolveInternal<true>(rhs->view(), non_zero_rows);
981 HyperSparseSolveInternal<false>(rhs->view(), non_zero_rows);
985template <
bool diagonal_of_ones>
986void TriangularMatrix::HyperSparseSolveInternal(
987 DenseColumn::View rhs, RowIndexVector* non_zero_rows)
const {
989 const auto entry_rows = rows_.view();
990 const auto entry_coefficients = coefficients_.view();
991 for (
const RowIndex row : *non_zero_rows) {
992 if (rhs[row] == 0.0)
continue;
993 const ColIndex row_as_col = RowToColIndex(row);
994 const Fractional coeff =
995 diagonal_of_ones ? rhs[row]
996 : rhs[row] / diagonal_coefficients_[row_as_col];
998 for (
const EntryIndex i : Column(row_as_col)) {
999 rhs[entry_rows[i]] -= coeff * entry_coefficients[i];
1001 (*non_zero_rows)[new_size] = row;
1004 non_zero_rows->resize(new_size);
1007void TriangularMatrix::HyperSparseSolveWithReversedNonZeros(
1008 DenseColumn* rhs, RowIndexVector* non_zero_rows)
const {
1010 if (all_diagonal_coefficients_are_one_) {
1011 HyperSparseSolveWithReversedNonZerosInternal<true>(rhs->view(),
1014 HyperSparseSolveWithReversedNonZerosInternal<false>(rhs->view(),
1019template <
bool diagonal_of_ones>
1020void TriangularMatrix::HyperSparseSolveWithReversedNonZerosInternal(
1021 DenseColumn::View rhs, RowIndexVector* non_zero_rows)
const {
1022 int new_start = non_zero_rows->size();
1023 const auto entry_rows = rows_.view();
1024 const auto entry_coefficients = coefficients_.view();
1025 for (
const RowIndex row : Reverse(*non_zero_rows)) {
1026 if (rhs[row] == 0.0)
continue;
1027 const ColIndex row_as_col = RowToColIndex(row);
1028 const Fractional coeff =
1029 diagonal_of_ones ? rhs[row]
1030 : rhs[row] / diagonal_coefficients_[row_as_col];
1032 for (
const EntryIndex i : Column(row_as_col)) {
1033 rhs[entry_rows[i]] -= coeff * entry_coefficients[i];
1036 (*non_zero_rows)[new_start] = row;
1038 non_zero_rows->erase(non_zero_rows->begin(),
1039 non_zero_rows->begin() + new_start);
1042void TriangularMatrix::TransposeHyperSparseSolve(
1043 DenseColumn* rhs, RowIndexVector* non_zero_rows)
const {
1045 if (all_diagonal_coefficients_are_one_) {
1046 TransposeHyperSparseSolveInternal<true>(rhs->view(), non_zero_rows);
1048 TransposeHyperSparseSolveInternal<false>(rhs->view(), non_zero_rows);
1052template <
bool diagonal_of_ones>
1053void TriangularMatrix::TransposeHyperSparseSolveInternal(
1054 DenseColumn::View rhs, RowIndexVector* non_zero_rows)
const {
1057 const auto entry_rows = rows_.view();
1058 const auto entry_coefficients = coefficients_.view();
1059 for (
const RowIndex row : *non_zero_rows) {
1060 Fractional sum = rhs[row];
1061 const ColIndex row_as_col = RowToColIndex(row);
1065 EntryIndex i = starts_[row_as_col];
1066 const EntryIndex i_end = starts_[row_as_col + 1];
1067 const EntryIndex shifted_end = i_end - 3;
1068 for (; i < shifted_end; i += 4) {
1069 sum -= entry_coefficients[i] * rhs[entry_rows[i]] +
1070 entry_coefficients[i + 1] * rhs[entry_rows[i + 1]] +
1071 entry_coefficients[i + 2] * rhs[entry_rows[i + 2]] +
1072 entry_coefficients[i + 3] * rhs[entry_rows[i + 3]];
1075 sum -= entry_coefficients[
i] * rhs[entry_rows[
i]];
1076 if (i + 1 < i_end) {
1077 sum -= entry_coefficients[
i + 1] * rhs[entry_rows[
i + 1]];
1078 if (i + 2 < i_end) {
1079 sum -= entry_coefficients[
i + 2] * rhs[entry_rows[
i + 2]];
1085 diagonal_of_ones ? sum : sum / diagonal_coefficients_[row_as_col];
1087 (*non_zero_rows)[new_size] = row;
1091 non_zero_rows->resize(new_size);
1094void TriangularMatrix::TransposeHyperSparseSolveWithReversedNonZeros(
1095 DenseColumn* rhs, RowIndexVector* non_zero_rows)
const {
1097 if (all_diagonal_coefficients_are_one_) {
1098 TransposeHyperSparseSolveWithReversedNonZerosInternal<true>(rhs->view(),
1101 TransposeHyperSparseSolveWithReversedNonZerosInternal<false>(rhs->view(),
1106template <
bool diagonal_of_ones>
1107void TriangularMatrix::TransposeHyperSparseSolveWithReversedNonZerosInternal(
1108 DenseColumn::View rhs, RowIndexVector* non_zero_rows)
const {
1109 int new_start = non_zero_rows->size();
1110 const auto entry_rows = rows_.view();
1111 const auto entry_coefficients = coefficients_.view();
1112 for (
const RowIndex row : Reverse(*non_zero_rows)) {
1113 Fractional sum = rhs[row];
1114 const ColIndex row_as_col = RowToColIndex(row);
1118 EntryIndex i = starts_[row_as_col + 1] - 1;
1119 const EntryIndex i_end = starts_[row_as_col];
1120 const EntryIndex shifted_end = i_end + 3;
1121 for (; i >= shifted_end; i -= 4) {
1122 sum -= entry_coefficients[i] * rhs[entry_rows[i]] +
1123 entry_coefficients[i - 1] * rhs[entry_rows[i - 1]] +
1124 entry_coefficients[i - 2] * rhs[entry_rows[i - 2]] +
1125 entry_coefficients[i - 3] * rhs[entry_rows[i - 3]];
1128 sum -= entry_coefficients[
i] * rhs[entry_rows[
i]];
1129 if (i >= i_end + 1) {
1130 sum -= entry_coefficients[
i - 1] * rhs[entry_rows[
i - 1]];
1131 if (i >= i_end + 2) {
1132 sum -= entry_coefficients[
i - 2] * rhs[entry_rows[
i - 2]];
1138 diagonal_of_ones ? sum : sum / diagonal_coefficients_[row_as_col];
1141 (*non_zero_rows)[new_start] = row;
1144 non_zero_rows->erase(non_zero_rows->begin(),
1145 non_zero_rows->begin() + new_start);
1148void TriangularMatrix::PermutedLowerSolve(
1149 const SparseColumn& rhs,
const RowPermutation& row_perm,
1152 DCHECK(all_diagonal_coefficients_are_one_);
1156 initially_all_zero_scratchpad_.resize(
num_rows_, 0.0);
1157 for (
const SparseColumn::Entry e : rhs) {
1158 initially_all_zero_scratchpad_[e.row()] = e.coefficient();
1161 const auto entry_rows =
rows_.view();
1163 const RowIndex end_row(partial_inverse_row_perm.
size());
1164 for (RowIndex row(
ColToRowIndex(first_non_identity_column_)); row < end_row;
1166 const RowIndex permuted_row = partial_inverse_row_perm[row];
1167 const Fractional pivot = initially_all_zero_scratchpad_[permuted_row];
1168 if (pivot == 0.0)
continue;
1171 initially_all_zero_scratchpad_[entry_rows[i]] -=
1172 entry_coefficients[i] * pivot;
1178 for (RowIndex row(0); row <
num_rows; ++row) {
1179 if (initially_all_zero_scratchpad_[row] != 0.0) {
1180 if (row_perm[row] < 0) {
1185 initially_all_zero_scratchpad_[row] = 0.0;
1195 DCHECK(all_diagonal_coefficients_are_one_);
1202 &upper_column_rows_);
1205 initially_all_zero_scratchpad_.resize(
num_rows_, 0.0);
1206 for (
const auto e : rhs) {
1207 initially_all_zero_scratchpad_[e.row()] = e.coefficient();
1212 num_fp_operations_ = 0;
1213 lower_column->
Clear();
1220 EntryIndex(upper_column_rows_.size()));
1221 for (
const RowIndex permuted_row :
Reverse(upper_column_rows_)) {
1222 const Fractional pivot = initially_all_zero_scratchpad_[permuted_row];
1223 if (pivot == 0.0)
continue;
1226 initially_all_zero_scratchpad_[permuted_row] = 0.0;
1227 const ColIndex row_as_col =
RowToColIndex(row_perm[permuted_row]);
1228 DCHECK_GE(row_as_col, 0);
1230 DCHECK_EQ(diagonal_coefficients_[row_as_col], 1.0);
1232 for (
const auto e :
column(row_as_col)) {
1233 initially_all_zero_scratchpad_[e.row()] -= e.coefficient() * pivot;
1238 lower_column->
Reserve(EntryIndex(lower_column_rows_.size()));
1239 for (
const RowIndex permuted_row : lower_column_rows_) {
1240 const Fractional pivot = initially_all_zero_scratchpad_[permuted_row];
1241 initially_all_zero_scratchpad_[permuted_row] = 0.0;
1276 lower_column_rows->clear();
1277 upper_column_rows->clear();
1278 nodes_to_explore_.clear();
1280 for (SparseColumn::Entry e : rhs) {
1283 stored_.Set(e.row());
1284 lower_column_rows->push_back(e.row());
1286 nodes_to_explore_.push_back(e.row());
1300 const auto entry_rows =
rows_.view();
1301 while (!nodes_to_explore_.empty()) {
1302 const RowIndex row = nodes_to_explore_.back();
1308 nodes_to_explore_.pop_back();
1309 const RowIndex explored_row = nodes_to_explore_.back();
1310 nodes_to_explore_.pop_back();
1311 DCHECK(!stored_[explored_row]);
1312 stored_.Set(explored_row);
1313 upper_column_rows->push_back(explored_row);
1324 EntryIndex end = pruned_ends_[col];
1326 const RowIndex entry_row = entry_rows[i];
1327 if (!marked_[entry_row]) {
1336 marked_[entry_row] =
false;
1340 pruned_ends_[col] = end;
1346 nodes_to_explore_.pop_back();
1355 lower_column_rows->push_back(row);
1356 nodes_to_explore_.pop_back();
1363 const EntryIndex end = pruned_ends_[col];
1364 for (EntryIndex i =
starts_[col]; i < end; ++i) {
1365 const RowIndex entry_row = entry_rows[i];
1366 if (!stored_[entry_row]) {
1367 nodes_to_explore_.push_back(entry_row);
1369 marked_[entry_row] =
true;
1373 DCHECK_LE(nodes_to_explore_.size(), 2 *
num_rows_.value() +
rows_.size());
1377 for (
const RowIndex row : *lower_column_rows) {
1378 stored_.ClearBucket(row);
1380 for (
const RowIndex row : *upper_column_rows) {
1381 stored_.ClearBucket(row);
1387 if (non_zero_rows->empty())
return;
1397 const int sparsity_threshold =
1398 static_cast<int>(0.025 *
static_cast<double>(
num_rows_.value()));
1399 const int num_ops_threshold =
1400 static_cast<int>(0.05 *
static_cast<double>(
num_rows_.value()));
1401 int num_ops = non_zero_rows->size();
1402 if (num_ops > sparsity_threshold) {
1403 non_zero_rows->clear();
1408 stored_.Resize(num_rows_);
1409 nodes_to_explore_.clear();
1410 nodes_to_explore_.swap(*non_zero_rows);
1414 const auto entry_rows = rows_.view();
1415 while (!nodes_to_explore_.empty()) {
1416 const RowIndex row = nodes_to_explore_.back();
1421 nodes_to_explore_.pop_back();
1422 const RowIndex explored_row = -row - 1;
1423 stored_.Set(explored_row);
1424 non_zero_rows->push_back(explored_row);
1430 nodes_to_explore_.pop_back();
1439 nodes_to_explore_.back() = -row - 1;
1440 for (
const EntryIndex i : Column(RowToColIndex(row))) {
1442 const RowIndex entry_row = entry_rows[i];
1443 if (!stored_[entry_row]) {
1444 nodes_to_explore_.push_back(entry_row);
1451 if (num_ops > num_ops_threshold)
break;
1455 for (
const RowIndex row : *non_zero_rows) {
1456 stored_.ClearBucket(row);
1460 if (num_ops > num_ops_threshold) non_zero_rows->clear();
1463void TriangularMatrix::ComputeRowsToConsiderInSortedOrder(
1464 RowIndexVector* non_zero_rows)
const {
1465 if (non_zero_rows->empty())
return;
1468 const int sparsity_threshold =
1469 static_cast<int>(0.025 *
static_cast<double>(
num_rows_.value()));
1470 const int num_ops_threshold =
1471 static_cast<int>(0.05 *
static_cast<double>(
num_rows_.value()));
1472 int num_ops = non_zero_rows->size();
1473 if (num_ops > sparsity_threshold) {
1474 non_zero_rows->clear();
1478 stored_.Resize(num_rows_);
1480 for (
const RowIndex row : *non_zero_rows) {
1484 const auto matrix_view = view();
1485 const auto entry_rows = rows_.view();
1486 for (
int i = 0; i < non_zero_rows->size(); ++i) {
1487 const RowIndex row = (*non_zero_rows)[i];
1488 for (
const EntryIndex index : matrix_view.Column(RowToColIndex(row))) {
1490 const RowIndex entry_row = entry_rows[index];
1491 if (!stored[entry_row]) {
1492 non_zero_rows->push_back(entry_row);
1493 stored.
Set(entry_row);
1496 if (num_ops > num_ops_threshold)
break;
1499 if (num_ops > num_ops_threshold) {
1501 non_zero_rows->clear();
1503 std::sort(non_zero_rows->begin(), non_zero_rows->end());
1504 for (
const RowIndex row : *non_zero_rows) {
1505 stored_.ClearBucket(row);
1514Fractional TriangularMatrix::ComputeInverseInfinityNormUpperBound()
const {
1515 if (first_non_identity_column_ == num_cols_) {
1520 const bool is_upper = IsUpperTriangular();
1522 const int num_cols = num_cols_.value();
1524 const auto entry_rows = rows_.view();
1525 const auto entry_coefficients = coefficients_.view();
1526 for (
int i = 0; i < num_cols; ++i) {
1527 const ColIndex col(is_upper ? num_cols - 1 - i : i);
1528 DCHECK_NE(diagonal_coefficients_[col], 0.0);
1530 std::abs(diagonal_coefficients_[col]);
1533 for (
const EntryIndex i : Column(col)) {
1534 row_norm_estimate[entry_rows[i]] +=
1535 coeff * std::abs(entry_coefficients[i]);
1539 return *std::max_element(row_norm_estimate.begin(), row_norm_estimate.end());
1542Fractional TriangularMatrix::ComputeInverseInfinityNorm()
const {
1543 const bool is_upper = IsUpperTriangular();
1547 for (ColIndex col(0); col <
num_cols_; ++col) {
1555 LowerSolve(&right_hand_side);
1559 for (RowIndex row(0); row < num_rows_; ++row) {
1560 row_sum[row] += std::abs(right_hand_side[row]);
1565 for (RowIndex row(0); row < num_rows_; ++row) {
1566 norm = std::max(norm, row_sum[row]);
Fractional ComputeOneNorm() const
EntryIndex num_entries() const
Fractional ComputeInfinityNorm() const
void Reset(RowIndex num_rows)
void PopulateFromMatrixView(const MatrixView &input)
ColIndex AddDenseColumnPrefix(DenseColumn::ConstView dense_column, RowIndex start)
Same as AddDenseColumn(), but only adds the non-zero from the given start.
StrictITIVector< EntryIndex, Fractional > coefficients_
ColIndex AddDenseColumn(const DenseColumn &dense_column)
StrictITIVector< ColIndex, EntryIndex > starts_
EntryIndex num_entries() const
Returns the matrix dimensions. See same functions in SparseMatrix.
RowIndex num_rows() const
void AddEntryToCurrentColumn(RowIndex row, Fractional coeff)
Api to add columns one at the time.
EntryIndex ColumnNumEntries(ColIndex col) const
Returns the number of entries (i.e. degree) of the given column.
StrictITIVector< EntryIndex, RowIndex > rows_
void Swap(CompactSparseMatrix *other)
RowIndex num_rows_
The matrix dimensions, properly updated by full and incremental builders.
::util::IntegerRange< EntryIndex > Column(ColIndex col) const
Functions to iterate on the entries of a given column.
ColumnView column(ColIndex col) const
void CloseCurrentColumn()
Fractional ComputeInfinityNorm() const
void PopulateSparseColumn(SparseColumn *sparse_column) const
void AddToCoefficient(RowIndex row, Fractional value)
void PopulateFromProduct(const SparseMatrix &a, const SparseMatrix &b)
Multiplies SparseMatrix a by SparseMatrix b.
std::string Dump() const
Returns a dense representation of the matrix.
void PopulateFromPermutedMatrix(const Matrix &a, const RowPermutation &row_perm, const ColumnPermutation &inverse_col_perm)
void PopulateFromTranspose(const Matrix &input)
Instantiate needed templates.
bool CheckNoDuplicates() const
Call CheckNoDuplicates() on all columns, useful for doing a DCHECK.
void PopulateFromLinearCombination(Fractional alpha, const SparseMatrix &a, Fractional beta, const SparseMatrix &b)
void AppendUnitVector(RowIndex row, Fractional value)
void Swap(SparseMatrix *matrix)
bool IsCleanedUp() const
Call IsCleanedUp() on all columns, useful for doing a DCHECK.
RowIndex num_rows() const
Return the matrix dimension.
Fractional ComputeInfinityNorm() const
void PopulateFromZero(RowIndex num_rows, ColIndex num_cols)
Fractional ComputeOneNorm() const
bool Equals(const SparseMatrix &a, Fractional tolerance) const
ColIndex num_cols() const
ColIndex AppendEmptyColumn()
Appends an empty column and returns its index.
Fractional LookUpValue(RowIndex row, ColIndex col) const
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)
StrictITISpan< RowIndex, const Fractional > ConstView
ConstView const_view() const
void UpperSolve(DenseColumn *rhs) const
Solves the system U.x = rhs for an upper triangular matrix.
void Reset(RowIndex num_rows, ColIndex col_capacity)
void LowerSolveStartingAt(ColIndex start, DenseColumn *rhs) const
This assumes that the rhs is all zero before the given position.
void AddTriangularColumnWithGivenDiagonalEntry(const SparseColumn &column, RowIndex diagonal_row, Fractional diagonal_value)
void AddTriangularColumn(const ColumnView &column, RowIndex diagonal_row)
void PermutedComputeRowsToConsider(const ColumnView &rhs, const RowPermutation &row_perm, RowIndexVector *lower_column_rows, RowIndexVector *upper_column_rows)
void PopulateFromTriangularSparseMatrix(const SparseMatrix &input)
RowIndex num_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.
void PermutedLowerSparseSolve(const ColumnView &rhs, const RowPermutation &row_perm, SparseColumn *lower, SparseColumn *upper)
void ComputeRowsToConsiderWithDfs(RowIndexVector *non_zero_rows) const
void Swap(TriangularMatrix *other)
constexpr double kInfinity
Infinity for type Fractional.
std::vector< RowIndex > RowIndexVector
BeginEndReverseIteratorWrapper< Container > Reverse(const Container &c)
Permutation< ColIndex > ColumnPermutation
Permutation< RowIndex > RowPermutation
ColIndex RowToColIndex(RowIndex row)
Get the ColIndex corresponding to the column # row.
constexpr RowIndex kInvalidRow(-1)
StrictITIVector< RowIndex, RowIndex > RowMapping
Column of row indices. Used to represent mappings between rows.
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.
static double ToDouble(double f)
In SWIG mode, we don't want anything besides these top-level includes.
util_intops::StrongVector< ColumnEntryIndex, ElementIndex > SparseColumn
BeginEndReverseIteratorWrapper< Container > Reverse(const Container &c)
static int input(yyscan_t yyscanner)
#define RETURN_IF_NULL(x)