OR-Tools  9.3
sparse.cc
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17
18#include "absl/strings/str_format.h"
22
23namespace operations_research {
24namespace glop {
25
26namespace {
27
29
30template <typename Matrix>
31EntryIndex ComputeNumEntries(const Matrix& matrix) {
32 EntryIndex num_entries(0);
33 const ColIndex num_cols(matrix.num_cols());
34 for (ColIndex col(0); col < num_cols; ++col) {
35 num_entries += matrix.column(col).num_entries();
36 }
37 return num_entries;
38}
39
40// Computes the 1-norm of the matrix.
41// The 1-norm |A| is defined as max_j sum_i |a_ij| or
42// max_col sum_row |a(row,col)|.
43template <typename Matrix>
44Fractional ComputeOneNormTemplate(const Matrix& matrix) {
45 Fractional norm(0.0);
46 const ColIndex num_cols(matrix.num_cols());
47 for (ColIndex col(0); col < num_cols; ++col) {
48 Fractional column_norm(0);
49 for (const SparseColumn::Entry e : matrix.column(col)) {
50 // Compute sum_i |a_ij|.
51 column_norm += fabs(e.coefficient());
52 }
53 // Compute max_j sum_i |a_ij|
54 norm = std::max(norm, column_norm);
55 }
56 return norm;
57}
58
59// Computes the oo-norm (infinity-norm) of the matrix.
60// The oo-norm |A| is defined as max_i sum_j |a_ij| or
61// max_row sum_col |a(row,col)|.
62template <typename Matrix>
63Fractional ComputeInfinityNormTemplate(const Matrix& matrix) {
64 DenseColumn row_sum(matrix.num_rows(), 0.0);
65 const ColIndex num_cols(matrix.num_cols());
66 for (ColIndex col(0); col < num_cols; ++col) {
67 for (const SparseColumn::Entry e : matrix.column(col)) {
68 // Compute sum_j |a_ij|.
69 row_sum[e.row()] += fabs(e.coefficient());
70 }
71 }
72
73 // Compute max_i sum_j |a_ij|
74 Fractional norm = 0.0;
75 const RowIndex num_rows(matrix.num_rows());
76 for (RowIndex row(0); row < num_rows; ++row) {
77 norm = std::max(norm, row_sum[row]);
78 }
79 return norm;
80}
81
82} // namespace
83
84// --------------------------------------------------------
85// SparseMatrix
86// --------------------------------------------------------
87SparseMatrix::SparseMatrix() : columns_(), num_rows_(0) {}
88
89#if (!defined(_MSC_VER) || (_MSC_VER >= 1800))
91 std::initializer_list<std::initializer_list<Fractional>> init_list) {
92 ColIndex num_cols(0);
93 num_rows_ = RowIndex(init_list.size());
94 RowIndex row(0);
95 for (std::initializer_list<Fractional> init_row : init_list) {
96 num_cols = std::max(num_cols, ColIndex(init_row.size()));
97 columns_.resize(num_cols, SparseColumn());
98 ColIndex col(0);
99 for (Fractional value : init_row) {
100 if (value != 0.0) {
101 columns_[col].SetCoefficient(row, value);
102 }
103 ++col;
104 }
105 ++row;
106 }
107}
108#endif
109
111 columns_.clear();
112 num_rows_ = RowIndex(0);
113}
114
116 return columns_.empty() || num_rows_ == 0;
117}
118
120 const ColIndex num_cols(columns_.size());
121 for (ColIndex col(0); col < num_cols; ++col) {
122 columns_[col].CleanUp();
123 }
124}
125
127 DenseBooleanColumn boolean_column;
128 const ColIndex num_cols(columns_.size());
129 for (ColIndex col(0); col < num_cols; ++col) {
130 if (!columns_[col].CheckNoDuplicates(&boolean_column)) return false;
131 }
132 return true;
133}
134
136 const ColIndex num_cols(columns_.size());
137 for (ColIndex col(0); col < num_cols; ++col) {
138 if (!columns_[col].IsCleanedUp()) return false;
139 }
140 return true;
141}
142
143void SparseMatrix::SetNumRows(RowIndex num_rows) { num_rows_ = num_rows; }
144
146 const ColIndex result = columns_.size();
147 columns_.push_back(SparseColumn());
148 return result;
149}
150
152 DCHECK_LT(row, num_rows_);
153 SparseColumn new_col;
154 new_col.SetCoefficient(row, value);
155 columns_.push_back(std::move(new_col));
156}
157
159 // We do not need to swap the different mutable scratchpads we use.
160 columns_.swap(matrix->columns_);
161 std::swap(num_rows_, matrix->num_rows_);
162}
163
164void SparseMatrix::PopulateFromZero(RowIndex num_rows, ColIndex num_cols) {
165 columns_.resize(num_cols, SparseColumn());
166 for (ColIndex col(0); col < num_cols; ++col) {
167 columns_[col].Clear();
168 }
169 num_rows_ = num_rows;
170}
171
172void SparseMatrix::PopulateFromIdentity(ColIndex num_cols) {
174 for (ColIndex col(0); col < num_cols; ++col) {
175 const RowIndex row = ColToRowIndex(col);
176 columns_[col].SetCoefficient(row, Fractional(1.0));
177 }
178}
179
180template <typename Matrix>
182 Reset(RowToColIndex(input.num_rows()), ColToRowIndex(input.num_cols()));
183
184 // We do a first pass on the input matrix to resize the new columns properly.
185 StrictITIVector<RowIndex, EntryIndex> row_degree(input.num_rows(),
186 EntryIndex(0));
187 for (ColIndex col(0); col < input.num_cols(); ++col) {
188 for (const SparseColumn::Entry e : input.column(col)) {
189 ++row_degree[e.row()];
190 }
191 }
192 for (RowIndex row(0); row < input.num_rows(); ++row) {
193 columns_[RowToColIndex(row)].Reserve(row_degree[row]);
194 }
195
196 for (ColIndex col(0); col < input.num_cols(); ++col) {
197 const RowIndex transposed_row = ColToRowIndex(col);
198 for (const SparseColumn::Entry e : input.column(col)) {
199 const ColIndex transposed_col = RowToColIndex(e.row());
200 columns_[transposed_col].SetCoefficient(transposed_row, e.coefficient());
201 }
202 }
204}
205
207 Reset(ColIndex(0), matrix.num_rows_);
208 columns_ = matrix.columns_;
209}
210
211template <typename Matrix>
213 const Matrix& a, const RowPermutation& row_perm,
214 const ColumnPermutation& inverse_col_perm) {
215 const ColIndex num_cols = a.num_cols();
216 Reset(num_cols, a.num_rows());
217 for (ColIndex col(0); col < num_cols; ++col) {
218 for (const auto e : a.column(inverse_col_perm[col])) {
219 columns_[col].SetCoefficient(row_perm[e.row()], e.coefficient());
220 }
221 }
223}
224
226 const SparseMatrix& a,
227 Fractional beta,
228 const SparseMatrix& b) {
229 DCHECK_EQ(a.num_cols(), b.num_cols());
230 DCHECK_EQ(a.num_rows(), b.num_rows());
231
232 const ColIndex num_cols = a.num_cols();
233 Reset(num_cols, a.num_rows());
234
235 const RowIndex num_rows = a.num_rows();
237 for (ColIndex col(0); col < num_cols; ++col) {
238 for (const SparseColumn::Entry e : a.columns_[col]) {
239 dense_column.AddToCoefficient(e.row(), alpha * e.coefficient());
240 }
241 for (const SparseColumn::Entry e : b.columns_[col]) {
242 dense_column.AddToCoefficient(e.row(), beta * e.coefficient());
243 }
244 dense_column.PopulateSparseColumn(&columns_[col]);
245 columns_[col].CleanUp();
246 dense_column.Clear();
247 }
248}
249
251 const SparseMatrix& b) {
252 const ColIndex num_cols = b.num_cols();
253 const RowIndex num_rows = a.num_rows();
254 Reset(num_cols, num_rows);
255
257 for (ColIndex col_b(0); col_b < num_cols; ++col_b) {
258 for (const SparseColumn::Entry eb : b.columns_[col_b]) {
259 if (eb.coefficient() == 0.0) {
260 continue;
261 }
262 const ColIndex col_a = RowToColIndex(eb.row());
263 for (const SparseColumn::Entry ea : a.columns_[col_a]) {
264 const Fractional value = ea.coefficient() * eb.coefficient();
265 tmp_column.AddToCoefficient(ea.row(), value);
266 }
267 }
268
269 // Populate column col_b.
270 tmp_column.PopulateSparseColumn(&columns_[col_b]);
271 columns_[col_b].CleanUp();
272 tmp_column.Clear();
273 }
274}
275
276void SparseMatrix::DeleteColumns(const DenseBooleanRow& columns_to_delete) {
277 if (columns_to_delete.empty()) return;
278 ColIndex new_index(0);
279 const ColIndex num_cols = columns_.size();
280 for (ColIndex col(0); col < num_cols; ++col) {
281 if (col >= columns_to_delete.size() || !columns_to_delete[col]) {
282 columns_[col].Swap(&(columns_[new_index]));
283 ++new_index;
284 }
285 }
286 columns_.resize(new_index);
287}
288
289void SparseMatrix::DeleteRows(RowIndex new_num_rows,
290 const RowPermutation& permutation) {
291 DCHECK_EQ(num_rows_, permutation.size());
292 for (RowIndex row(0); row < num_rows_; ++row) {
293 DCHECK_LT(permutation[row], new_num_rows);
294 }
295 const ColIndex end = num_cols();
296 for (ColIndex col(0); col < end; ++col) {
297 columns_[col].ApplyPartialRowPermutation(permutation);
298 }
299 SetNumRows(new_num_rows);
300}
301
303 const ColIndex end = num_cols();
304 if (end != matrix.num_cols()) {
305 return false;
306 }
307 const RowIndex offset = num_rows();
308 for (ColIndex col(0); col < end; ++col) {
309 const SparseColumn& source_column = matrix.columns_[col];
310 columns_[col].AppendEntriesWithOffset(source_column, offset);
311 }
312 SetNumRows(offset + matrix.num_rows());
313 return true;
314}
315
317 const ColIndex num_cols(columns_.size());
318 for (ColIndex col(0); col < num_cols; ++col) {
319 columns_[col].ApplyRowPermutation(row_perm);
320 }
321}
322
323Fractional SparseMatrix::LookUpValue(RowIndex row, ColIndex col) const {
324 return columns_[col].LookUpCoefficient(row);
325}
326
327bool SparseMatrix::Equals(const SparseMatrix& a, Fractional tolerance) const {
328 if (num_cols() != a.num_cols() || num_rows() != a.num_rows()) {
329 return false;
330 }
331
332 RandomAccessSparseColumn dense_column(num_rows());
333 RandomAccessSparseColumn dense_column_a(num_rows());
334 const ColIndex num_cols = a.num_cols();
335 for (ColIndex col(0); col < num_cols; ++col) {
336 // Store all entries of current matrix in a dense column.
337 for (const SparseColumn::Entry e : columns_[col]) {
338 dense_column.AddToCoefficient(e.row(), e.coefficient());
339 }
340
341 // Check all entries of a are those stored in the dense column.
342 for (const SparseColumn::Entry e : a.columns_[col]) {
343 if (fabs(e.coefficient() - dense_column.GetCoefficient(e.row())) >
344 tolerance) {
345 return false;
346 }
347 }
348
349 // Store all entries of matrix a in a dense column.
350 for (const SparseColumn::Entry e : a.columns_[col]) {
351 dense_column_a.AddToCoefficient(e.row(), e.coefficient());
352 }
353
354 // Check all entries are those stored in the dense column a.
355 for (const SparseColumn::Entry e : columns_[col]) {
356 if (fabs(e.coefficient() - dense_column_a.GetCoefficient(e.row())) >
357 tolerance) {
358 return false;
359 }
360 }
361
362 dense_column.Clear();
363 dense_column_a.Clear();
364 }
365
366 return true;
367}
368
370 Fractional* max_magnitude) const {
371 RETURN_IF_NULL(min_magnitude);
372 RETURN_IF_NULL(max_magnitude);
373 *min_magnitude = kInfinity;
374 *max_magnitude = 0.0;
375 for (ColIndex col(0); col < num_cols(); ++col) {
376 for (const SparseColumn::Entry e : columns_[col]) {
377 const Fractional magnitude = fabs(e.coefficient());
378 if (magnitude != 0.0) {
379 *min_magnitude = std::min(*min_magnitude, magnitude);
380 *max_magnitude = std::max(*max_magnitude, magnitude);
381 }
382 }
383 }
384 if (*max_magnitude == 0.0) {
385 *min_magnitude = 0.0;
386 }
387}
388
389EntryIndex SparseMatrix::num_entries() const {
390 return ComputeNumEntries(*this);
391}
393 return ComputeOneNormTemplate(*this);
394}
396 return ComputeInfinityNormTemplate(*this);
397}
398
399std::string SparseMatrix::Dump() const {
400 std::string result;
401 const ColIndex num_cols(columns_.size());
402
403 for (RowIndex row(0); row < num_rows_; ++row) {
404 result.append("{ ");
405 for (ColIndex col(0); col < num_cols; ++col) {
406 absl::StrAppendFormat(&result, "%g ", ToDouble(LookUpValue(row, col)));
407 }
408 result.append("}\n");
409 }
410 return result;
411}
412
413void SparseMatrix::Reset(ColIndex num_cols, RowIndex num_rows) {
414 Clear();
415 columns_.resize(num_cols, SparseColumn());
416 num_rows_ = num_rows;
417}
418
419EntryIndex MatrixView::num_entries() const { return ComputeNumEntries(*this); }
421 return ComputeOneNormTemplate(*this);
422}
424 return ComputeInfinityNormTemplate(*this);
425}
426
427// Instantiate needed templates.
428template void SparseMatrix::PopulateFromTranspose<SparseMatrix>(
429 const SparseMatrix& input);
430template void SparseMatrix::PopulateFromPermutedMatrix<SparseMatrix>(
431 const SparseMatrix& a, const RowPermutation& row_perm,
432 const ColumnPermutation& inverse_col_perm);
433template void SparseMatrix::PopulateFromPermutedMatrix<CompactSparseMatrixView>(
434 const CompactSparseMatrixView& a, const RowPermutation& row_perm,
435 const ColumnPermutation& inverse_col_perm);
436
438 num_cols_ = input.num_cols();
439 num_rows_ = input.num_rows();
440 const EntryIndex num_entries = input.num_entries();
441 starts_.assign(num_cols_ + 1, EntryIndex(0));
443 rows_.assign(num_entries, RowIndex(0));
444 EntryIndex index(0);
445 for (ColIndex col(0); col < input.num_cols(); ++col) {
446 starts_[col] = index;
447 for (const SparseColumn::Entry e : input.column(col)) {
448 coefficients_[index] = e.coefficient();
449 rows_[index] = e.row();
450 ++index;
451 }
452 }
453 starts_[input.num_cols()] = index;
454}
455
457 const SparseMatrix& input) {
458 num_cols_ = input.num_cols() + RowToColIndex(input.num_rows());
459 num_rows_ = input.num_rows();
460 const EntryIndex num_entries =
461 input.num_entries() + EntryIndex(num_rows_.value());
462 starts_.assign(num_cols_ + 1, EntryIndex(0));
464 rows_.assign(num_entries, RowIndex(0));
465 EntryIndex index(0);
466 for (ColIndex col(0); col < input.num_cols(); ++col) {
467 starts_[col] = index;
468 for (const SparseColumn::Entry e : input.column(col)) {
469 coefficients_[index] = e.coefficient();
470 rows_[index] = e.row();
471 ++index;
472 }
473 }
474 for (RowIndex row(0); row < num_rows_; ++row) {
475 starts_[input.num_cols() + RowToColIndex(row)] = index;
476 coefficients_[index] = 1.0;
477 rows_[index] = row;
478 ++index;
479 }
481}
482
484 const CompactSparseMatrix& input) {
485 num_cols_ = RowToColIndex(input.num_rows());
486 num_rows_ = ColToRowIndex(input.num_cols());
487
488 // Fill the starts_ vector by computing the number of entries of each rows and
489 // then doing a cummulative sum. After this step starts_[col + 1] will be the
490 // actual start of the column col when we are done.
491 starts_.assign(num_cols_ + 2, EntryIndex(0));
492 for (const RowIndex row : input.rows_) {
493 ++starts_[RowToColIndex(row) + 2];
494 }
495 for (ColIndex col(2); col < starts_.size(); ++col) {
496 starts_[col] += starts_[col - 1];
497 }
501
502 // Use starts_ to fill the matrix. Note that starts_ is modified so that at
503 // the end it has its final values.
504 for (ColIndex col(0); col < input.num_cols(); ++col) {
505 const RowIndex transposed_row = ColToRowIndex(col);
506 for (const EntryIndex i : input.Column(col)) {
507 const ColIndex transposed_col = RowToColIndex(input.EntryRow(i));
508 const EntryIndex index = starts_[transposed_col + 1]++;
509 coefficients_[index] = input.EntryCoefficient(i);
510 rows_[index] = transposed_row;
511 }
512 }
513
514 DCHECK_EQ(starts_.front(), 0);
516}
517
520
521 // This takes care of the triangular special case.
522 diagonal_coefficients_ = input.diagonal_coefficients_;
523 all_diagonal_coefficients_are_one_ = input.all_diagonal_coefficients_are_one_;
524
525 // The elimination structure of the transpose is not the same.
526 pruned_ends_.resize(num_cols_, EntryIndex(0));
527 for (ColIndex col(0); col < num_cols_; ++col) {
528 pruned_ends_[col] = starts_[col + 1];
529 }
530
531 // Compute first_non_identity_column_. Note that this is not necessarily the
532 // same as input.first_non_identity_column_ for an upper triangular matrix.
533 first_non_identity_column_ = 0;
534 const ColIndex end = diagonal_coefficients_.size();
535 while (first_non_identity_column_ < end &&
536 ColumnNumEntries(first_non_identity_column_) == 0 &&
537 diagonal_coefficients_[first_non_identity_column_] == 1.0) {
538 ++first_non_identity_column_;
539 }
540}
541
542void CompactSparseMatrix::Reset(RowIndex num_rows) {
544 num_cols_ = 0;
545 rows_.clear();
547 starts_.clear();
548 starts_.push_back(EntryIndex(0));
549}
550
551void TriangularMatrix::Reset(RowIndex num_rows, ColIndex col_capacity) {
553 first_non_identity_column_ = 0;
554 all_diagonal_coefficients_are_one_ = true;
555
556 pruned_ends_.resize(col_capacity);
557 diagonal_coefficients_.resize(col_capacity);
558 starts_.resize(col_capacity + 1);
559 // Non-zero entries in the first column always have an offset of 0.
560 starts_[ColIndex(0)] = 0;
561}
562
564 return AddDenseColumnPrefix(dense_column, RowIndex(0));
565}
566
568 const DenseColumn& dense_column, RowIndex start) {
569 const RowIndex num_rows(dense_column.size());
570 for (RowIndex row(start); row < num_rows; ++row) {
571 if (dense_column[row] != 0.0) {
573 coefficients_.push_back(dense_column[row]);
574 }
575 }
577 ++num_cols_;
578 return num_cols_ - 1;
579}
580
582 const DenseColumn& dense_column, const std::vector<RowIndex>& non_zeros) {
583 if (non_zeros.empty()) return AddDenseColumn(dense_column);
584 for (const RowIndex row : non_zeros) {
585 const Fractional value = dense_column[row];
586 if (value != 0.0) {
589 }
590 }
592 ++num_cols_;
593 return num_cols_ - 1;
594}
595
597 DenseColumn* column, std::vector<RowIndex>* non_zeros) {
598 for (const RowIndex row : *non_zeros) {
599 const Fractional value = (*column)[row];
600 if (value != 0.0) {
603 (*column)[row] = 0.0;
604 }
605 }
606 non_zeros->clear();
608 ++num_cols_;
609 return num_cols_ - 1;
610}
611
616 rows_.swap(other->rows_);
617 starts_.swap(other->starts_);
618}
619
622 diagonal_coefficients_.swap(other->diagonal_coefficients_);
623 std::swap(first_non_identity_column_, other->first_non_identity_column_);
624 std::swap(all_diagonal_coefficients_are_one_,
625 other->all_diagonal_coefficients_are_one_);
626}
627
629 return ComputeNumEntries(*this);
630}
632 return ComputeOneNormTemplate(*this);
633}
635 return ComputeInfinityNormTemplate(*this);
636}
637
638// Internal function used to finish adding one column to a triangular matrix.
639// This sets the diagonal coefficient to the given value, and prepares the
640// matrix for the next column addition.
641void TriangularMatrix::CloseCurrentColumn(Fractional diagonal_value) {
642 DCHECK_NE(diagonal_value, 0.0);
643 // The vectors diagonal_coefficients, pruned_ends, and starts_ should have all
644 // been preallocated by a call to SetTotalNumberOfColumns().
645 DCHECK_LT(num_cols_, diagonal_coefficients_.size());
646 diagonal_coefficients_[num_cols_] = diagonal_value;
647
648 // TODO(user): This is currently not used by all matrices. It will be good
649 // to fill it only when needed.
650 DCHECK_LT(num_cols_, pruned_ends_.size());
651 pruned_ends_[num_cols_] = coefficients_.size();
652 ++num_cols_;
655 if (first_non_identity_column_ == num_cols_ - 1 && coefficients_.empty() &&
656 diagonal_value == 1.0) {
657 first_non_identity_column_ = num_cols_;
658 }
659 all_diagonal_coefficients_are_one_ =
660 all_diagonal_coefficients_are_one_ && (diagonal_value == 1.0);
661}
662
664 CloseCurrentColumn(diagonal_value);
665}
666
668 RowIndex diagonal_row) {
669 Fractional diagonal_value = 0.0;
670 for (const SparseColumn::Entry e : column) {
671 if (e.row() == diagonal_row) {
672 diagonal_value = e.coefficient();
673 } else {
674 DCHECK_NE(0.0, e.coefficient());
675 rows_.push_back(e.row());
676 coefficients_.push_back(e.coefficient());
677 }
678 }
679 CloseCurrentColumn(diagonal_value);
680}
681
683 const SparseColumn& column, RowIndex diagonal_row,
684 Fractional diagonal_coefficient) {
685 // TODO(user): use division by a constant using multiplication.
686 for (const SparseColumn::Entry e : column) {
687 if (e.row() != diagonal_row) {
688 if (e.coefficient() != 0.0) {
689 rows_.push_back(e.row());
690 coefficients_.push_back(e.coefficient() / diagonal_coefficient);
691 }
692 } else {
693 DCHECK_EQ(e.coefficient(), diagonal_coefficient);
694 }
695 }
696 CloseCurrentColumn(1.0);
697}
698
700 const SparseColumn& column, RowIndex diagonal_row,
701 Fractional diagonal_value) {
702 for (SparseColumn::Entry e : column) {
703 DCHECK_NE(e.row(), diagonal_row);
704 rows_.push_back(e.row());
705 coefficients_.push_back(e.coefficient());
706 }
707 CloseCurrentColumn(diagonal_value);
708}
709
711 const SparseMatrix& input) {
712 Reset(input.num_rows(), input.num_cols());
713 for (ColIndex col(0); col < input.num_cols(); ++col) {
715 }
717}
718
720 for (ColIndex col(0); col < num_cols_; ++col) {
721 if (diagonal_coefficients_[col] == 0.0) return false;
722 for (EntryIndex i : Column(col)) {
723 if (EntryRow(i) <= ColToRowIndex(col)) return false;
724 }
725 }
726 return true;
727}
728
730 for (ColIndex col(0); col < num_cols_; ++col) {
731 if (diagonal_coefficients_[col] == 0.0) return false;
732 for (EntryIndex i : Column(col)) {
733 if (EntryRow(i) >= ColToRowIndex(col)) return false;
734 }
735 }
736 return true;
737}
738
740 const RowPermutation& row_perm) {
741 EntryIndex num_entries = rows_.size();
742 for (EntryIndex i(0); i < num_entries; ++i) {
743 rows_[i] = row_perm[rows_[i]];
744 }
745}
746
748 SparseColumn* output) const {
749 output->Clear();
750 for (const EntryIndex i : Column(col)) {
752 }
753 output->SetCoefficient(ColToRowIndex(col), diagonal_coefficients_[col]);
754 output->CleanUp();
755}
756
759 for (ColIndex col(0); col < num_cols_; ++col) {
761 }
762}
763
765 LowerSolveStartingAt(ColIndex(0), rhs);
766}
767
769 DenseColumn* rhs) const {
770 if (all_diagonal_coefficients_are_one_) {
771 LowerSolveStartingAtInternal<true>(start, rhs);
772 } else {
773 LowerSolveStartingAtInternal<false>(start, rhs);
774 }
775}
776
777template <bool diagonal_of_ones>
778void TriangularMatrix::LowerSolveStartingAtInternal(ColIndex start,
779 DenseColumn* rhs) const {
780 RETURN_IF_NULL(rhs);
781 const ColIndex begin = std::max(start, first_non_identity_column_);
782 const ColIndex end = diagonal_coefficients_.size();
783 for (ColIndex col(begin); col < end; ++col) {
784 const Fractional value = (*rhs)[ColToRowIndex(col)];
785 if (value == 0.0) continue;
786 const Fractional coeff =
787 diagonal_of_ones ? value : value / diagonal_coefficients_[col];
788 if (!diagonal_of_ones) {
789 (*rhs)[ColToRowIndex(col)] = coeff;
790 }
791 for (const EntryIndex i : Column(col)) {
792 (*rhs)[EntryRow(i)] -= coeff * EntryCoefficient(i);
793 }
794 }
795}
796
798 if (all_diagonal_coefficients_are_one_) {
799 UpperSolveInternal<true>(rhs);
800 } else {
801 UpperSolveInternal<false>(rhs);
802 }
803}
804
805template <bool diagonal_of_ones>
806void TriangularMatrix::UpperSolveInternal(DenseColumn* rhs) const {
807 RETURN_IF_NULL(rhs);
808 const ColIndex end = first_non_identity_column_;
809 for (ColIndex col(diagonal_coefficients_.size() - 1); col >= end; --col) {
810 const Fractional value = (*rhs)[ColToRowIndex(col)];
811 if (value == 0.0) continue;
812 const Fractional coeff =
813 diagonal_of_ones ? value : value / diagonal_coefficients_[col];
814 if (!diagonal_of_ones) {
815 (*rhs)[ColToRowIndex(col)] = coeff;
816 }
817
818 // It is faster to iterate this way (instead of i : Column(col)) because of
819 // cache locality. Note that the floating-point computations are exactly the
820 // same in both cases.
821 const EntryIndex i_end = starts_[col];
822 for (EntryIndex i(starts_[col + 1] - 1); i >= i_end; --i) {
823 (*rhs)[EntryRow(i)] -= coeff * EntryCoefficient(i);
824 }
825 }
826}
827
829 if (all_diagonal_coefficients_are_one_) {
830 TransposeUpperSolveInternal<true>(rhs);
831 } else {
832 TransposeUpperSolveInternal<false>(rhs);
833 }
834}
835
836template <bool diagonal_of_ones>
837void TriangularMatrix::TransposeUpperSolveInternal(DenseColumn* rhs) const {
838 RETURN_IF_NULL(rhs);
839 const ColIndex end = num_cols_;
840 EntryIndex i = starts_[first_non_identity_column_];
841 for (ColIndex col(first_non_identity_column_); col < end; ++col) {
842 Fractional sum = (*rhs)[ColToRowIndex(col)];
843
844 // Note that this is a bit faster than the simpler
845 // for (const EntryIndex i : Column(col)) {
846 // EntryIndex i is explicitly not modified in outer iterations, since
847 // the last entry in column col is stored contiguously just before the
848 // first entry in column col+1.
849 const EntryIndex i_end = starts_[col + 1];
850 for (; i < i_end; ++i) {
851 sum -= EntryCoefficient(i) * (*rhs)[EntryRow(i)];
852 }
853 (*rhs)[ColToRowIndex(col)] =
854 diagonal_of_ones ? sum : sum / diagonal_coefficients_[col];
855 }
856}
857
859 if (all_diagonal_coefficients_are_one_) {
860 TransposeLowerSolveInternal<true>(rhs);
861 } else {
862 TransposeLowerSolveInternal<false>(rhs);
863 }
864}
865
866template <bool diagonal_of_ones>
867void TriangularMatrix::TransposeLowerSolveInternal(DenseColumn* rhs) const {
868 RETURN_IF_NULL(rhs);
869 const ColIndex end = first_non_identity_column_;
870
871 // We optimize a bit the solve by skipping the last 0.0 positions.
872 ColIndex col = num_cols_ - 1;
873 while (col >= end && (*rhs)[ColToRowIndex(col)] == 0.0) {
874 --col;
875 }
876
877 EntryIndex i = starts_[col + 1] - 1;
878 for (; col >= end; --col) {
879 Fractional sum = (*rhs)[ColToRowIndex(col)];
880
881 // Note that this is a bit faster than the simpler
882 // for (const EntryIndex i : Column(col)) {
883 // mainly because we iterate in a good direction for the cache.
884 // EntryIndex i is explicitly not modified in outer iterations, since
885 // the last entry in column col is stored contiguously just before the
886 // first entry in column col+1.
887 const EntryIndex i_end = starts_[col];
888 for (; i >= i_end; --i) {
889 sum -= EntryCoefficient(i) * (*rhs)[EntryRow(i)];
890 }
891 (*rhs)[ColToRowIndex(col)] =
892 diagonal_of_ones ? sum : sum / diagonal_coefficients_[col];
893 }
894}
895
897 RowIndexVector* non_zero_rows) const {
898 if (all_diagonal_coefficients_are_one_) {
899 HyperSparseSolveInternal<true>(rhs, non_zero_rows);
900 } else {
901 HyperSparseSolveInternal<false>(rhs, non_zero_rows);
902 }
903}
904
905template <bool diagonal_of_ones>
906void TriangularMatrix::HyperSparseSolveInternal(
907 DenseColumn* rhs, RowIndexVector* non_zero_rows) const {
908 RETURN_IF_NULL(rhs);
909 int new_size = 0;
910 for (const RowIndex row : *non_zero_rows) {
911 if ((*rhs)[row] == 0.0) continue;
912 const ColIndex row_as_col = RowToColIndex(row);
913 const Fractional coeff =
914 diagonal_of_ones ? (*rhs)[row]
915 : (*rhs)[row] / diagonal_coefficients_[row_as_col];
916 (*rhs)[row] = coeff;
917 for (const EntryIndex i : Column(row_as_col)) {
918 (*rhs)[EntryRow(i)] -= coeff * EntryCoefficient(i);
919 }
920 (*non_zero_rows)[new_size] = row;
921 ++new_size;
922 }
923 non_zero_rows->resize(new_size);
924}
925
927 DenseColumn* rhs, RowIndexVector* non_zero_rows) const {
928 if (all_diagonal_coefficients_are_one_) {
929 HyperSparseSolveWithReversedNonZerosInternal<true>(rhs, non_zero_rows);
930 } else {
931 HyperSparseSolveWithReversedNonZerosInternal<false>(rhs, non_zero_rows);
932 }
933}
934
935template <bool diagonal_of_ones>
936void TriangularMatrix::HyperSparseSolveWithReversedNonZerosInternal(
937 DenseColumn* rhs, RowIndexVector* non_zero_rows) const {
938 RETURN_IF_NULL(rhs);
939 int new_start = non_zero_rows->size();
940 for (const RowIndex row : Reverse(*non_zero_rows)) {
941 if ((*rhs)[row] == 0.0) continue;
942 const ColIndex row_as_col = RowToColIndex(row);
943 const Fractional coeff =
944 diagonal_of_ones ? (*rhs)[row]
945 : (*rhs)[row] / diagonal_coefficients_[row_as_col];
946 (*rhs)[row] = coeff;
947 for (const EntryIndex i : Column(row_as_col)) {
948 (*rhs)[EntryRow(i)] -= coeff * EntryCoefficient(i);
949 }
950 --new_start;
951 (*non_zero_rows)[new_start] = row;
952 }
953 non_zero_rows->erase(non_zero_rows->begin(),
954 non_zero_rows->begin() + new_start);
955}
956
958 DenseColumn* rhs, RowIndexVector* non_zero_rows) const {
959 if (all_diagonal_coefficients_are_one_) {
960 TransposeHyperSparseSolveInternal<true>(rhs, non_zero_rows);
961 } else {
962 TransposeHyperSparseSolveInternal<false>(rhs, non_zero_rows);
963 }
964}
965
966template <bool diagonal_of_ones>
967void TriangularMatrix::TransposeHyperSparseSolveInternal(
968 DenseColumn* rhs, RowIndexVector* non_zero_rows) const {
969 RETURN_IF_NULL(rhs);
970 int new_size = 0;
971 for (const RowIndex row : *non_zero_rows) {
972 Fractional sum = (*rhs)[row];
973 const ColIndex row_as_col = RowToColIndex(row);
974 for (const EntryIndex i : Column(row_as_col)) {
975 sum -= EntryCoefficient(i) * (*rhs)[EntryRow(i)];
976 }
977 (*rhs)[row] =
978 diagonal_of_ones ? sum : sum / diagonal_coefficients_[row_as_col];
979 if (sum != 0.0) {
980 (*non_zero_rows)[new_size] = row;
981 ++new_size;
982 }
983 }
984 non_zero_rows->resize(new_size);
985}
986
988 DenseColumn* rhs, RowIndexVector* non_zero_rows) const {
989 if (all_diagonal_coefficients_are_one_) {
990 TransposeHyperSparseSolveWithReversedNonZerosInternal<true>(rhs,
991 non_zero_rows);
992 } else {
993 TransposeHyperSparseSolveWithReversedNonZerosInternal<false>(rhs,
994 non_zero_rows);
995 }
996}
997
998template <bool diagonal_of_ones>
999void TriangularMatrix::TransposeHyperSparseSolveWithReversedNonZerosInternal(
1000 DenseColumn* rhs, RowIndexVector* non_zero_rows) const {
1001 RETURN_IF_NULL(rhs);
1002 int new_start = non_zero_rows->size();
1003 for (const RowIndex row : Reverse(*non_zero_rows)) {
1004 Fractional sum = (*rhs)[row];
1005 const ColIndex row_as_col = RowToColIndex(row);
1006
1007 // We do the loops this way so that the floating point operations are
1008 // exactly the same as the ones performed by TransposeLowerSolveInternal().
1009 EntryIndex i = starts_[row_as_col + 1] - 1;
1010 const EntryIndex i_end = starts_[row_as_col];
1011 for (; i >= i_end; --i) {
1012 sum -= EntryCoefficient(i) * (*rhs)[EntryRow(i)];
1013 }
1014 (*rhs)[row] =
1015 diagonal_of_ones ? sum : sum / diagonal_coefficients_[row_as_col];
1016 if (sum != 0.0) {
1017 --new_start;
1018 (*non_zero_rows)[new_start] = row;
1019 }
1020 }
1021 non_zero_rows->erase(non_zero_rows->begin(),
1022 non_zero_rows->begin() + new_start);
1023}
1024
1026 const SparseColumn& rhs, const RowPermutation& row_perm,
1027 const RowMapping& partial_inverse_row_perm, SparseColumn* lower,
1028 SparseColumn* upper) const {
1029 DCHECK(all_diagonal_coefficients_are_one_);
1032
1033 initially_all_zero_scratchpad_.resize(num_rows_, 0.0);
1034 for (const SparseColumn::Entry e : rhs) {
1035 initially_all_zero_scratchpad_[e.row()] = e.coefficient();
1036 }
1037
1038 const RowIndex end_row(partial_inverse_row_perm.size());
1039 for (RowIndex row(ColToRowIndex(first_non_identity_column_)); row < end_row;
1040 ++row) {
1041 const RowIndex permuted_row = partial_inverse_row_perm[row];
1042 const Fractional pivot = initially_all_zero_scratchpad_[permuted_row];
1043 if (pivot == 0.0) continue;
1044 for (EntryIndex i : Column(RowToColIndex(row))) {
1045 initially_all_zero_scratchpad_[EntryRow(i)] -=
1046 EntryCoefficient(i) * pivot;
1047 }
1048 }
1049
1050 lower->Clear();
1051 const RowIndex num_rows = num_rows_;
1052 for (RowIndex row(0); row < num_rows; ++row) {
1053 if (initially_all_zero_scratchpad_[row] != 0.0) {
1054 if (row_perm[row] < 0) {
1055 lower->SetCoefficient(row, initially_all_zero_scratchpad_[row]);
1056 } else {
1057 upper->SetCoefficient(row, initially_all_zero_scratchpad_[row]);
1058 }
1059 initially_all_zero_scratchpad_[row] = 0.0;
1060 }
1061 }
1062 DCHECK(lower->CheckNoDuplicates());
1063}
1064
1066 const RowPermutation& row_perm,
1067 SparseColumn* lower_column,
1068 SparseColumn* upper_column) {
1069 DCHECK(all_diagonal_coefficients_are_one_);
1070 RETURN_IF_NULL(lower_column);
1071 RETURN_IF_NULL(upper_column);
1072
1073 // Compute the set of rows that will be non zero in the result (lower_column,
1074 // upper_column).
1075 PermutedComputeRowsToConsider(rhs, row_perm, &lower_column_rows_,
1076 &upper_column_rows_);
1077
1078 // Copy rhs into initially_all_zero_scratchpad_.
1079 initially_all_zero_scratchpad_.resize(num_rows_, 0.0);
1080 for (const auto e : rhs) {
1081 initially_all_zero_scratchpad_[e.row()] = e.coefficient();
1082 }
1083
1084 // We clear lower_column first in case upper_column and lower_column point to
1085 // the same underlying SparseColumn.
1086 num_fp_operations_ = 0;
1087 lower_column->Clear();
1088
1089 // rows_to_consider_ contains the row to process in reverse order. Note in
1090 // particular that each "permuted_row" will never be touched again and so its
1091 // value is final. We copy the result in (lower_column, upper_column) and
1092 // clear initially_all_zero_scratchpad_ at the same time.
1093 upper_column->Reserve(upper_column->num_entries() +
1094 EntryIndex(upper_column_rows_.size()));
1095 for (const RowIndex permuted_row : Reverse(upper_column_rows_)) {
1096 const Fractional pivot = initially_all_zero_scratchpad_[permuted_row];
1097 if (pivot == 0.0) continue;
1098 // Note that permuted_row will not appear in the loop below so we
1099 // already know the value of the solution at this position.
1100 initially_all_zero_scratchpad_[permuted_row] = 0.0;
1101 const ColIndex row_as_col = RowToColIndex(row_perm[permuted_row]);
1102 DCHECK_GE(row_as_col, 0);
1103 upper_column->SetCoefficient(permuted_row, pivot);
1104 DCHECK_EQ(diagonal_coefficients_[row_as_col], 1.0);
1105 num_fp_operations_ += 1 + ColumnNumEntries(row_as_col).value();
1106 for (const auto e : column(row_as_col)) {
1107 initially_all_zero_scratchpad_[e.row()] -= e.coefficient() * pivot;
1108 }
1109 }
1110
1111 // TODO(user): The size of lower is exact, so we could be slighly faster here.
1112 lower_column->Reserve(EntryIndex(lower_column_rows_.size()));
1113 for (const RowIndex permuted_row : lower_column_rows_) {
1114 const Fractional pivot = initially_all_zero_scratchpad_[permuted_row];
1115 initially_all_zero_scratchpad_[permuted_row] = 0.0;
1116 lower_column->SetCoefficient(permuted_row, pivot);
1117 }
1118 DCHECK(lower_column->CheckNoDuplicates());
1119 DCHECK(upper_column->CheckNoDuplicates());
1120}
1121
1122// The goal is to find which rows of the working column we will need to look
1123// at in PermutedLowerSparseSolve() when solving P^{-1}.L.P.x = rhs, 'P' being a
1124// row permutation, 'L' a lower triangular matrix and 'this' being 'P^{-1}.L'.
1125// Note that the columns of L that are identity columns (this is the case for
1126// the ones corresponding to a kNonPivotal in P) can be skipped since they will
1127// leave the working column unchanged.
1128//
1129// Let G denote the graph G = (V,E) of the column-to-row adjacency of A:
1130// - 'V' is the set of nodes, one node i corresponds to a both a row
1131// and a column (the matrix is square).
1132// - 'E' is the set of arcs. There is an arc from node i to node j iff the
1133// coefficient of i-th column, j-th row of A = P^{-1}.L.P is non zero.
1134//
1135// Let S denote the set of nodes i such that rhs_i != 0.
1136// Let R denote the set of all accessible nodes from S in G.
1137// x_k is possibly non-zero iff k is in R, i.e. if k is not in R then x_k = 0
1138// for sure, and there is no need to look a the row k during the solve.
1139//
1140// So, to solve P^{-1}.L.P.x = rhs, only rows corresponding to P.R have to be
1141// considered (ignoring the one that map to identity column of L). A topological
1142// sort of P.R is used to decide in which order one should iterate on them. This
1143// will be given by upper_column_rows_ and it will be populated in reverse
1144// order.
1146 const ColumnView& rhs, const RowPermutation& row_perm,
1147 RowIndexVector* lower_column_rows, RowIndexVector* upper_column_rows) {
1148 stored_.resize(num_rows_, false);
1149 marked_.resize(num_rows_, false);
1150 lower_column_rows->clear();
1151 upper_column_rows->clear();
1152 nodes_to_explore_.clear();
1153
1154 for (SparseColumn::Entry e : rhs) {
1155 const ColIndex col = RowToColIndex(row_perm[e.row()]);
1156 if (col < 0) {
1157 stored_[e.row()] = true;
1158 lower_column_rows->push_back(e.row());
1159 } else {
1160 nodes_to_explore_.push_back(e.row());
1161 }
1162 }
1163
1164 // Topological sort based on Depth-First-Search.
1165 // A few notes:
1166 // - By construction, if the matrix can be permuted into a lower triangular
1167 // form, there is no cycle. This code does nothing to test for cycles, but
1168 // there is a DCHECK() to detect them during debugging.
1169 // - This version uses sentinels (kInvalidRow) on nodes_to_explore_ to know
1170 // when a node has been explored (i.e. when the recursive dfs goes back in
1171 // the call stack). This is faster than an alternate implementation that
1172 // uses another Boolean array to detect when we go back in the
1173 // depth-first search.
1174 while (!nodes_to_explore_.empty()) {
1175 const RowIndex row = nodes_to_explore_.back();
1176
1177 // If the depth-first search from the current node is finished (i.e. there
1178 // is a sentinel on the stack), we store the node (which is just before on
1179 // the stack). This will store the nodes in reverse topological order.
1180 if (row < 0) {
1181 nodes_to_explore_.pop_back();
1182 const RowIndex explored_row = nodes_to_explore_.back();
1183 nodes_to_explore_.pop_back();
1184 DCHECK(!stored_[explored_row]);
1185 stored_[explored_row] = true;
1186 upper_column_rows->push_back(explored_row);
1187
1188 // Unmark and prune the nodes that are already unmarked. See the header
1189 // comment on marked_ for the algorithm description.
1190 //
1191 // Complexity note: The only difference with the "normal" DFS doing no
1192 // pruning is this extra loop here and the marked_[entry_row] = true in
1193 // the loop later in this function. On an already pruned graph, this is
1194 // probably between 1 and 2 times slower than the "normal" DFS.
1195 const ColIndex col = RowToColIndex(row_perm[explored_row]);
1196 EntryIndex i = starts_[col];
1197 EntryIndex end = pruned_ends_[col];
1198 while (i < end) {
1199 const RowIndex entry_row = EntryRow(i);
1200 if (!marked_[entry_row]) {
1201 --end;
1202
1203 // Note that we could keep the pruned row in a separate vector and
1204 // not touch the triangular matrix. But the current solution seems
1205 // better cache-wise and memory-wise.
1206 std::swap(rows_[i], rows_[end]);
1208 } else {
1209 marked_[entry_row] = false;
1210 ++i;
1211 }
1212 }
1213 pruned_ends_[col] = end;
1214 continue;
1215 }
1216
1217 // If the node is already stored, skip.
1218 if (stored_[row]) {
1219 nodes_to_explore_.pop_back();
1220 continue;
1221 }
1222
1223 // Expand only if we are not on a kNonPivotal row.
1224 // Otherwise we can store the node right away.
1225 const ColIndex col = RowToColIndex(row_perm[row]);
1226 if (col < 0) {
1227 stored_[row] = true;
1228 lower_column_rows->push_back(row);
1229 nodes_to_explore_.pop_back();
1230 continue;
1231 }
1232
1233 // Go one level forward in the depth-first search, and store the 'adjacent'
1234 // node on nodes_to_explore_ for further processing.
1235 nodes_to_explore_.push_back(kInvalidRow);
1236 const EntryIndex end = pruned_ends_[col];
1237 for (EntryIndex i = starts_[col]; i < end; ++i) {
1238 const RowIndex entry_row = EntryRow(i);
1239 if (!stored_[entry_row]) {
1240 nodes_to_explore_.push_back(entry_row);
1241 }
1242 marked_[entry_row] = true;
1243 }
1244
1245 // The graph contains cycles? this is not supposed to happen.
1246 DCHECK_LE(nodes_to_explore_.size(), 2 * num_rows_.value() + rows_.size());
1247 }
1248
1249 // Clear stored_.
1250 for (const RowIndex row : *lower_column_rows) {
1251 stored_[row] = false;
1252 }
1253 for (const RowIndex row : *upper_column_rows) {
1254 stored_[row] = false;
1255 }
1256}
1257
1259 RowIndexVector* non_zero_rows) const {
1260 if (non_zero_rows->empty()) return;
1261
1262 // We don't start the DFS if the initial number of non-zeros is under the
1263 // sparsity_threshold. During the DFS, we abort it if the number of floating
1264 // points operations get larger than the num_ops_threshold.
1265 //
1266 // In both cases, we make sure to clear non_zero_rows so that the solving part
1267 // will use the non-hypersparse version of the code.
1268 //
1269 // TODO(user): Investigate the best thresholds.
1270 const int sparsity_threshold =
1271 static_cast<int>(0.025 * static_cast<double>(num_rows_.value()));
1272 const int num_ops_threshold =
1273 static_cast<int>(0.05 * static_cast<double>(num_rows_.value()));
1274 int num_ops = non_zero_rows->size();
1275 if (num_ops > sparsity_threshold) {
1276 non_zero_rows->clear();
1277 return;
1278 }
1279
1280 // Initialize using the non-zero positions of the input.
1281 stored_.resize(num_rows_, false);
1282 nodes_to_explore_.clear();
1283 nodes_to_explore_.swap(*non_zero_rows);
1284
1285 // Topological sort based on Depth-First-Search.
1286 // Same remarks as the version implemented in PermutedComputeRowsToConsider().
1287 while (!nodes_to_explore_.empty()) {
1288 const RowIndex row = nodes_to_explore_.back();
1289
1290 // If the depth-first search from the current node is finished, we store the
1291 // node. This will store the node in reverse topological order.
1292 if (row < 0) {
1293 nodes_to_explore_.pop_back();
1294 const RowIndex explored_row = -row - 1;
1295 stored_[explored_row] = true;
1296 non_zero_rows->push_back(explored_row);
1297 continue;
1298 }
1299
1300 // If the node is already stored, skip.
1301 if (stored_[row]) {
1302 nodes_to_explore_.pop_back();
1303 continue;
1304 }
1305
1306 // Go one level forward in the depth-first search, and store the 'adjacent'
1307 // node on nodes_to_explore_ for further processing.
1308 //
1309 // We reverse the sign of nodes_to_explore_.back() to detect when the
1310 // DFS will be back on this node.
1311 nodes_to_explore_.back() = -row - 1;
1312 for (const EntryIndex i : Column(RowToColIndex(row))) {
1313 ++num_ops;
1314 const RowIndex entry_row = EntryRow(i);
1315 if (!stored_[entry_row]) {
1316 nodes_to_explore_.push_back(entry_row);
1317 }
1318 }
1319
1320 // Abort if the number of operations is not negligible compared to the
1321 // number of rows. Note that this test also prevents the code from cycling
1322 // in case the matrix is actually not triangular.
1323 if (num_ops > num_ops_threshold) break;
1324 }
1325
1326 // Clear stored_.
1327 for (const RowIndex row : *non_zero_rows) {
1328 stored_[row] = false;
1329 }
1330
1331 // If we aborted, clear the result.
1332 if (num_ops > num_ops_threshold) non_zero_rows->clear();
1333}
1334
1336 RowIndexVector* non_zero_rows) const {
1337 static const Fractional kDefaultSparsityRatio = 0.025;
1338 static const Fractional kDefaultNumOpsRatio = 0.05;
1339 ComputeRowsToConsiderInSortedOrder(non_zero_rows, kDefaultSparsityRatio,
1340 kDefaultNumOpsRatio);
1341}
1342
1344 RowIndexVector* non_zero_rows, Fractional sparsity_ratio,
1345 Fractional num_ops_ratio) const {
1346 if (non_zero_rows->empty()) return;
1347
1348 // TODO(user): Investigate the best thresholds.
1349 const int sparsity_threshold =
1350 static_cast<int>(0.025 * static_cast<double>(num_rows_.value()));
1351 const int num_ops_threshold =
1352 static_cast<int>(0.05 * static_cast<double>(num_rows_.value()));
1353 int num_ops = non_zero_rows->size();
1354 if (num_ops > sparsity_threshold) {
1355 non_zero_rows->clear();
1356 return;
1357 }
1358
1359 stored_.resize(num_rows_, false);
1360 for (const RowIndex row : *non_zero_rows) stored_[row] = true;
1361 for (int i = 0; i < non_zero_rows->size(); ++i) {
1362 const RowIndex row = (*non_zero_rows)[i];
1363 for (const EntryIndex i : Column(RowToColIndex(row))) {
1364 ++num_ops;
1365 const RowIndex entry_row = EntryRow(i);
1366 if (!stored_[entry_row]) {
1367 non_zero_rows->push_back(entry_row);
1368 stored_[entry_row] = true;
1369 }
1370 }
1371 if (num_ops > num_ops_threshold) break;
1372 }
1373
1374 for (const RowIndex row : *non_zero_rows) stored_[row] = false;
1375 if (num_ops > num_ops_threshold) {
1376 non_zero_rows->clear();
1377 } else {
1378 std::sort(non_zero_rows->begin(), non_zero_rows->end());
1379 }
1380}
1381
1382// A known upper bound for the infinity norm of T^{-1} is the
1383// infinity norm of y where T'*y = x with:
1384// - x the all 1s vector.
1385// - Each entry in T' is the absolute value of the same entry in T.
1387 if (first_non_identity_column_ == num_cols_) {
1388 // Identity matrix
1389 return 1.0;
1390 }
1391
1392 const bool is_upper = IsUpperTriangular();
1393 DenseColumn row_norm_estimate(num_rows_, 1.0);
1394 const int num_cols = num_cols_.value();
1395
1396 for (int i = 0; i < num_cols; ++i) {
1397 const ColIndex col(is_upper ? num_cols - 1 - i : i);
1398 DCHECK_NE(diagonal_coefficients_[col], 0.0);
1399 const Fractional coeff = row_norm_estimate[ColToRowIndex(col)] /
1400 std::abs(diagonal_coefficients_[col]);
1401
1402 row_norm_estimate[ColToRowIndex(col)] = coeff;
1403 for (const EntryIndex i : Column(col)) {
1404 row_norm_estimate[EntryRow(i)] += coeff * std::abs(EntryCoefficient(i));
1405 }
1406 }
1407
1408 return *std::max_element(row_norm_estimate.begin(), row_norm_estimate.end());
1409}
1410
1412 const bool is_upper = IsUpperTriangular();
1413
1414 DenseColumn row_sum(num_rows_, 0.0);
1415 DenseColumn right_hand_side;
1416 for (ColIndex col(0); col < num_cols_; ++col) {
1417 right_hand_side.assign(num_rows_, 0);
1418 right_hand_side[ColToRowIndex(col)] = 1.0;
1419
1420 // Get the col-th column of the matrix inverse.
1421 if (is_upper) {
1422 UpperSolve(&right_hand_side);
1423 } else {
1424 LowerSolve(&right_hand_side);
1425 }
1426
1427 // Compute sum_j |inverse_ij|.
1428 for (RowIndex row(0); row < num_rows_; ++row) {
1429 row_sum[row] += std::abs(right_hand_side[row]);
1430 }
1431 }
1432 // Compute max_i sum_j |inverse_ij|.
1433 Fractional norm = 0.0;
1434 for (RowIndex row(0); row < num_rows_; ++row) {
1435 norm = std::max(norm, row_sum[row]);
1436 }
1437
1438 return norm;
1439}
1440} // namespace glop
1441} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:893
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:892
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:895
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:894
#define DCHECK(condition)
Definition: base/logging.h:890
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:891
bool empty() const
void push_back(const value_type &x)
void swap(StrongVector &x)
ColIndex AddDenseColumn(const DenseColumn &dense_column)
Definition: sparse.cc:563
StrictITIVector< ColIndex, EntryIndex > starts_
Definition: sparse.h:466
ColIndex AddDenseColumnWithNonZeros(const DenseColumn &dense_column, const std::vector< RowIndex > &non_zeros)
Definition: sparse.cc:581
ColIndex AddAndClearColumnWithNonZeros(DenseColumn *column, std::vector< RowIndex > *non_zeros)
Definition: sparse.cc:596
void Swap(CompactSparseMatrix *other)
Definition: sparse.cc:612
StrictITIVector< EntryIndex, RowIndex > rows_
Definition: sparse.h:465
ColIndex AddDenseColumnPrefix(const DenseColumn &dense_column, RowIndex start)
Definition: sparse.cc:567
Fractional EntryCoefficient(EntryIndex i) const
Definition: sparse.h:366
StrictITIVector< EntryIndex, Fractional > coefficients_
Definition: sparse.h:464
void PopulateFromTranspose(const CompactSparseMatrix &input)
Definition: sparse.cc:483
::util::IntegerRange< EntryIndex > Column(ColIndex col) const
Definition: sparse.h:363
void PopulateFromSparseMatrixAndAddSlacks(const SparseMatrix &input)
Definition: sparse.cc:456
void PopulateFromMatrixView(const MatrixView &input)
Definition: sparse.cc:437
RowIndex EntryRow(EntryIndex i) const
Definition: sparse.h:367
ColumnView column(ColIndex col) const
Definition: sparse.h:369
EntryIndex ColumnNumEntries(ColIndex col) const
Definition: sparse.h:340
Fractional ComputeInfinityNorm() const
Definition: sparse.cc:423
Fractional ComputeOneNorm() const
Definition: sparse.cc:420
EntryIndex num_entries() const
Definition: sparse.cc:419
void AddToCoefficient(RowIndex row, Fractional value)
void PopulateSparseColumn(SparseColumn *sparse_column) const
void AppendUnitVector(RowIndex row, Fractional value)
Definition: sparse.cc:151
void PopulateFromLinearCombination(Fractional alpha, const SparseMatrix &a, Fractional beta, const SparseMatrix &b)
Definition: sparse.cc:225
void PopulateFromPermutedMatrix(const Matrix &a, const RowPermutation &row_perm, const ColumnPermutation &inverse_col_perm)
Definition: sparse.cc:212
void PopulateFromTranspose(const Matrix &input)
Definition: sparse.cc:181
void PopulateFromIdentity(ColIndex num_cols)
Definition: sparse.cc:172
Fractional ComputeInfinityNorm() const
Definition: sparse.cc:395
void SetNumRows(RowIndex num_rows)
Definition: sparse.cc:143
SparseColumn * mutable_column(ColIndex col)
Definition: sparse.h:182
Fractional LookUpValue(RowIndex row, ColIndex col) const
Definition: sparse.cc:323
void Swap(SparseMatrix *matrix)
Definition: sparse.cc:158
void ComputeMinAndMaxMagnitudes(Fractional *min_magnitude, Fractional *max_magnitude) const
Definition: sparse.cc:369
void DeleteRows(RowIndex num_rows, const RowPermutation &permutation)
Definition: sparse.cc:289
void PopulateFromProduct(const SparseMatrix &a, const SparseMatrix &b)
Definition: sparse.cc:250
bool AppendRowsFromSparseMatrix(const SparseMatrix &matrix)
Definition: sparse.cc:302
void DeleteColumns(const DenseBooleanRow &columns_to_delete)
Definition: sparse.cc:276
void PopulateFromSparseMatrix(const SparseMatrix &matrix)
Definition: sparse.cc:206
void ApplyRowPermutation(const RowPermutation &row_perm)
Definition: sparse.cc:316
void PopulateFromZero(RowIndex num_rows, ColIndex num_cols)
Definition: sparse.cc:164
bool Equals(const SparseMatrix &a, Fractional tolerance) const
Definition: sparse.cc:327
void SetCoefficient(Index index, Fractional value)
void assign(IntType size, const T &v)
Definition: lp_types.h:278
void TransposeHyperSparseSolve(DenseColumn *rhs, RowIndexVector *non_zero_rows) const
Definition: sparse.cc:957
void CopyToSparseMatrix(SparseMatrix *output) const
Definition: sparse.cc:757
void AddTriangularColumnWithGivenDiagonalEntry(const SparseColumn &column, RowIndex diagonal_row, Fractional diagonal_value)
Definition: sparse.cc:699
void UpperSolve(DenseColumn *rhs) const
Definition: sparse.cc:797
void HyperSparseSolve(DenseColumn *rhs, RowIndexVector *non_zero_rows) const
Definition: sparse.cc:896
Fractional ComputeInverseInfinityNorm() const
Definition: sparse.cc:1411
void Swap(TriangularMatrix *other)
Definition: sparse.cc:620
void PopulateFromTriangularSparseMatrix(const SparseMatrix &input)
Definition: sparse.cc:710
void LowerSolve(DenseColumn *rhs) const
Definition: sparse.cc:764
void TransposeHyperSparseSolveWithReversedNonZeros(DenseColumn *rhs, RowIndexVector *non_zero_rows) const
Definition: sparse.cc:987
void LowerSolveStartingAt(ColIndex start, DenseColumn *rhs) const
Definition: sparse.cc:768
void PopulateFromTranspose(const TriangularMatrix &input)
Definition: sparse.cc:518
void CopyColumnToSparseColumn(ColIndex col, SparseColumn *output) const
Definition: sparse.cc:747
void AddAndNormalizeTriangularColumn(const SparseColumn &column, RowIndex diagonal_row, Fractional diagonal_coefficient)
Definition: sparse.cc:682
void ComputeRowsToConsiderInSortedOrder(RowIndexVector *non_zero_rows, Fractional sparsity_ratio, Fractional num_ops_ratio) const
Definition: sparse.cc:1343
void AddTriangularColumn(const ColumnView &column, RowIndex diagonal_row)
Definition: sparse.cc:667
void TransposeLowerSolve(DenseColumn *rhs) const
Definition: sparse.cc:858
void PermutedLowerSparseSolve(const ColumnView &rhs, const RowPermutation &row_perm, SparseColumn *lower, SparseColumn *upper)
Definition: sparse.cc:1065
void PermutedComputeRowsToConsider(const ColumnView &rhs, const RowPermutation &row_perm, RowIndexVector *lower_column_rows, RowIndexVector *upper_column_rows)
Definition: sparse.cc:1145
void ApplyRowPermutationToNonDiagonalEntries(const RowPermutation &row_perm)
Definition: sparse.cc:739
void TransposeUpperSolve(DenseColumn *rhs) const
Definition: sparse.cc:828
void ComputeRowsToConsiderWithDfs(RowIndexVector *non_zero_rows) const
Definition: sparse.cc:1258
Fractional ComputeInverseInfinityNormUpperBound() const
Definition: sparse.cc:1386
void PermutedLowerSolve(const SparseColumn &rhs, const RowPermutation &row_perm, const RowMapping &partial_inverse_row_perm, SparseColumn *lower, SparseColumn *upper) const
Definition: sparse.cc:1025
void AddDiagonalOnlyColumn(Fractional diagonal_value)
Definition: sparse.cc:663
void Reset(RowIndex num_rows, ColIndex col_capacity)
Definition: sparse.cc:551
void HyperSparseSolveWithReversedNonZeros(DenseColumn *rhs, RowIndexVector *non_zero_rows) const
Definition: sparse.cc:926
int64_t b
int64_t a
int64_t value
double lower
Definition: glpk_solver.cc:75
double upper
Definition: glpk_solver.cc:76
int index
ColIndex col
Definition: markowitz.cc:183
RowIndex row
Definition: markowitz.cc:182
const RowIndex kInvalidRow(-1)
ColIndex RowToColIndex(RowIndex row)
Definition: lp_types.h:49
RowIndex ColToRowIndex(ColIndex col)
Definition: lp_types.h:52
std::vector< RowIndex > RowIndexVector
Definition: lp_types.h:313
StrictITIVector< RowIndex, Fractional > DenseColumn
Definition: lp_types.h:332
const double kInfinity
Definition: lp_types.h:84
static double ToDouble(double f)
Definition: lp_types.h:69
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:262
Collection of objects used to extend the Constraint Solver library.
BeginEndReverseIteratorWrapper< Container > Reverse(const Container &c)
Definition: iterators.h:98
static int input(yyscan_t yyscanner)
EntryIndex num_entries
#define RETURN_IF_NULL(x)
Definition: return_macros.h:20
std::optional< int64_t > end
int64_t start
const double coeff