Google OR-Tools v9.14
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
sparse.h
Go to the documentation of this file.
1// Copyright 2010-2025 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
14// The following are very good references for terminology, data structures,
15// and algorithms:
16//
17// I.S. Duff, A.M. Erisman and J.K. Reid, "Direct Methods for Sparse Matrices",
18// Clarendon, Oxford, UK, 1987, ISBN 0-19-853421-3,
19// http://www.amazon.com/dp/0198534213.
20//
21//
22// T.A. Davis, "Direct methods for Sparse Linear Systems", SIAM, Philadelphia,
23// 2006, ISBN-13: 978-0-898716-13, http://www.amazon.com/dp/0898716136.
24//
25//
26// Both books also contain a wealth of references.
27
28#ifndef OR_TOOLS_LP_DATA_SPARSE_H_
29#define OR_TOOLS_LP_DATA_SPARSE_H_
30
31#include <algorithm>
32#include <cstdint>
33#include <string>
34#include <vector>
35
36#include "absl/log/check.h"
37#include "absl/types/span.h"
38#include "ortools/base/types.h"
44#include "ortools/util/bitset.h"
46
47namespace operations_research {
48namespace glop {
49
51
52// --------------------------------------------------------
53// SparseMatrix
54// --------------------------------------------------------
55// SparseMatrix is a class for sparse matrices suitable for computation.
56// Data is represented using the so-called compressed-column storage scheme.
57// Entries (row, col, value) are stored by column using a SparseColumn.
58//
59// Citing [Duff et al, 1987], a matrix is sparse if many of its coefficients are
60// zero and if there is an advantage in exploiting its zeros.
61// For practical reasons, not all zeros are exploited (for example those that
62// result from calculations.) The term entry refers to those coefficients that
63// are handled explicitly. All non-zeros are entries while some zero
64// coefficients may also be entries.
65//
66// Note that no special ordering of entries is assumed.
67class SparseMatrix {
68 public:
70
71 // Useful for testing. This makes it possible to write:
72 // SparseMatrix matrix {
73 // {1, 2, 3},
74 // {4, 5, 6},
75 // {7, 8, 9}};
76#if (!defined(_MSC_VER) || _MSC_VER >= 1800)
78 std::initializer_list<std::initializer_list<Fractional>> init_list);
79
80 // This type is neither copyable nor movable.
81 SparseMatrix(const SparseMatrix&) = delete;
82 SparseMatrix& operator=(const SparseMatrix&) = delete;
84#endif
85 // Clears internal data structure, i.e. erases all the columns and set
86 // the number of rows to zero.
87 void Clear();
88
89 // Returns true if the matrix is empty.
90 // That is if num_rows() OR num_cols() are zero.
91 bool IsEmpty() const;
92
93 // Cleans the columns, i.e. removes zero-values entries, removes duplicates
94 // entries and sorts remaining entries in increasing row order.
95 // Call with care: Runs in O(num_cols * column_cleanup), with each column
96 // cleanup running in O(num_entries * log(num_entries)).
97 void CleanUp();
98
99 // Call CheckNoDuplicates() on all columns, useful for doing a DCHECK.
100 bool CheckNoDuplicates() const;
101
102 // Call IsCleanedUp() on all columns, useful for doing a DCHECK.
103 bool IsCleanedUp() const;
104
105 // Change the number of row of this matrix.
106 void SetNumRows(RowIndex num_rows);
107
108 // Appends an empty column and returns its index.
109 ColIndex AppendEmptyColumn();
110
111 // Appends a unit vector defined by the single entry (row, value).
112 // Note that the row should be smaller than the number of rows of the matrix.
113 void AppendUnitVector(RowIndex row, Fractional value);
114
115 // Swaps the content of this SparseMatrix with the one passed as argument.
116 // Works in O(1).
117 void Swap(SparseMatrix* matrix);
118
119 // Populates the matrix with num_cols columns of zeros. As the number of rows
120 // is specified by num_rows, the matrix is not necessarily square.
121 // Previous columns/values are deleted.
122 void PopulateFromZero(RowIndex num_rows, ColIndex num_cols);
123
124 // Populates the matrix from the Identity matrix of size num_cols.
125 // Previous columns/values are deleted.
126 void PopulateFromIdentity(ColIndex num_cols);
127
128 // Populates the matrix from the transposed of the given matrix.
129 // Note that this preserve the property of lower/upper triangular matrix
130 // to have the diagonal coefficients first/last in each columns. It actually
131 // sorts the entries in each columns by their indices.
132 template <typename Matrix>
133 void PopulateFromTranspose(const Matrix& input);
134
135 // Populates a SparseMatrix from another one (copy), note that this run in
136 // O(number of entries in the matrix).
137 void PopulateFromSparseMatrix(const SparseMatrix& matrix);
138
139 // Populates a SparseMatrix from the image of a matrix A through the given
140 // row_perm and inverse_col_perm. See permutation.h for more details.
141 template <typename Matrix>
142 void PopulateFromPermutedMatrix(const Matrix& a,
143 const RowPermutation& row_perm,
144 const ColumnPermutation& inverse_col_perm);
145
146 // Populates a SparseMatrix from the result of alpha * A + beta * B,
147 // where alpha and beta are Fractionals, A and B are sparse matrices.
149 Fractional beta, const SparseMatrix& b);
150
151 // Multiplies SparseMatrix a by SparseMatrix b.
152 void PopulateFromProduct(const SparseMatrix& a, const SparseMatrix& b);
153
154 // Removes the marked columns from the matrix and adjust its size.
155 // This runs in O(num_cols).
156 void DeleteColumns(const DenseBooleanRow& columns_to_delete);
157
158 // Applies the given row permutation and deletes the rows for which
159 // permutation[row] is kInvalidRow. Sets the new number of rows to num_rows.
160 // This runs in O(num_entries).
161 void DeleteRows(RowIndex num_rows, const RowPermutation& permutation);
162
163 // Appends all rows from the given matrix to the calling object after the last
164 // row of the calling object. Both matrices must have the same number of
165 // columns. The method returns true if the rows were added successfully and
166 // false if it can't add the rows because the number of columns of the
167 // matrices are different.
168 bool AppendRowsFromSparseMatrix(const SparseMatrix& matrix);
169
170 // Applies the row permutation.
171 void ApplyRowPermutation(const RowPermutation& row_perm);
172
173 // Returns the coefficient at position row in column col.
174 // Call with care: runs in O(num_entries_in_col) as entries may not be sorted.
175 Fractional LookUpValue(RowIndex row, ColIndex col) const;
176
177 // Returns true if the matrix equals a (with a maximum error smaller than
178 // given the tolerance).
179 bool Equals(const SparseMatrix& a, Fractional tolerance) const;
180
181 // Returns, in min_magnitude and max_magnitude, the minimum and maximum
182 // magnitudes of the non-zero coefficients of the calling object.
183 void ComputeMinAndMaxMagnitudes(Fractional* min_magnitude,
184 Fractional* max_magnitude) const;
185
186 // Return the matrix dimension.
187 RowIndex num_rows() const { return num_rows_; }
188 ColIndex num_cols() const { return ColIndex(columns_.size()); }
190 // Access the underlying sparse columns.
191 const SparseColumn& column(ColIndex col) const { return columns_[col]; }
192 SparseColumn* mutable_column(ColIndex col) { return &(columns_[col]); }
194 // Returns the total numbers of entries in the matrix.
195 // Runs in O(num_cols).
196 EntryIndex num_entries() const;
197
198 // Computes the 1-norm of the matrix.
199 // The 1-norm |A| is defined as max_j sum_i |a_ij| or
200 // max_col sum_row |a(row,col)|.
202
203 // Computes the oo-norm (infinity-norm) of the matrix.
204 // The oo-norm |A| is defined as max_i sum_j |a_ij| or
205 // max_row sum_col |a(row,col)|.
207
208 // Returns a dense representation of the matrix.
209 std::string Dump() const;
210
211 private:
212 // Resets the internal data structure and create an empty rectangular
213 // matrix of size num_rows x num_cols.
214 void Reset(ColIndex num_cols, RowIndex num_rows);
215
216 // Vector of sparse columns.
218
219 // Number of rows. This is needed as sparse columns don't have a maximum
220 // number of rows.
221 RowIndex num_rows_;
222};
223
224// A matrix constructed from a list of already existing SparseColumn. This class
225// does not take ownership of the underlying columns, and thus they must outlive
226// this class (and keep the same address in memory).
227class MatrixView {
228 public:
229 MatrixView() = default;
230 explicit MatrixView(const SparseMatrix& matrix) {
233
234 // Takes all the columns of the given matrix.
235 void PopulateFromMatrix(const SparseMatrix& matrix) {
236 const ColIndex num_cols = matrix.num_cols();
237 columns_.resize(num_cols, nullptr);
238 for (ColIndex col(0); col < num_cols; ++col) {
239 columns_[col] = &matrix.column(col);
240 }
241 num_rows_ = matrix.num_rows();
242 }
243
244 // Takes all the columns of the first matrix followed by the columns of the
245 // second matrix.
246 void PopulateFromMatrixPair(const SparseMatrix& matrix_a,
247 const SparseMatrix& matrix_b) {
248 const ColIndex num_cols = matrix_a.num_cols() + matrix_b.num_cols();
249 columns_.resize(num_cols, nullptr);
250 for (ColIndex col(0); col < matrix_a.num_cols(); ++col) {
251 columns_[col] = &matrix_a.column(col);
252 }
253 for (ColIndex col(0); col < matrix_b.num_cols(); ++col) {
254 columns_[matrix_a.num_cols() + col] = &matrix_b.column(col);
255 }
256 num_rows_ = std::max(matrix_a.num_rows(), matrix_b.num_rows());
257 }
258
259 // Takes only the columns of the given matrix that belongs to the given basis.
260 void PopulateFromBasis(const MatrixView& matrix,
261 const RowToColMapping& basis) {
262 columns_.resize(RowToColIndex(basis.size()), nullptr);
263 for (RowIndex row(0); row < basis.size(); ++row) {
264 columns_[RowToColIndex(row)] = &matrix.column(basis[row]);
265 }
266 num_rows_ = matrix.num_rows();
267 }
268
269 // Same behavior as the SparseMatrix functions above.
270 bool IsEmpty() const { return columns_.empty(); }
271 RowIndex num_rows() const { return num_rows_; }
272 ColIndex num_cols() const { return columns_.size(); }
273 const SparseColumn& column(ColIndex col) const { return *columns_[col]; }
274 EntryIndex num_entries() const;
277
278 private:
279 RowIndex num_rows_;
281};
282
284 const SparseMatrix& input);
286 const SparseMatrix& a, const RowPermutation& row_perm,
287 const ColumnPermutation& inverse_col_perm);
288extern template void
290 const CompactSparseMatrixView& a, const RowPermutation& row_perm,
291 const ColumnPermutation& inverse_col_perm);
292
293// Another matrix representation which is more efficient than a SparseMatrix but
294// doesn't allow matrix modification. It is faster to construct, uses less
295// memory and provides a better cache locality when iterating over the non-zeros
296// of the matrix columns.
298 public:
299 // When iteration performance matter, getting a ConstView allows the compiler
300 // to do better aliasing analysis and not re-read vectors address all the
301 // time.
302 class ConstView {
303 public:
304 explicit ConstView(const CompactSparseMatrix* matrix)
305 : coefficients_(matrix->coefficients_.data()),
306 rows_(matrix->rows_.data()),
307 starts_(matrix->starts_.data()) {}
308
309 // Functions to iterate on the entries of a given column:
310 // const auto view = compact_matrix.view();
311 // for (const EntryIndex i : view.Column(col)) {
312 // const RowIndex row = view.EntryRow(i);
313 // const Fractional coefficient = view.EntryCoefficient(i);
314 // }
315 ::util::IntegerRange<EntryIndex> Column(ColIndex col) const {
316 return ::util::IntegerRange<EntryIndex>(starts_[col.value()],
317 starts_[col.value() + 1]);
318 }
319 Fractional EntryCoefficient(EntryIndex i) const {
320 return coefficients_[i.value()];
322 RowIndex EntryRow(EntryIndex i) const { return rows_[i.value()]; }
323
324 EntryIndex ColumnNumEntries(ColIndex col) const {
325 return starts_[col.value() + 1] - starts_[col.value()];
327
328 // Returns the scalar product of the given row vector with the column of
329 // index col of this matrix.
330 Fractional ColumnScalarProduct(ColIndex col,
331 DenseRow::ConstView vector) const;
332
333 private:
334 const Fractional* const coefficients_;
335 const RowIndex* const rows_;
336 const EntryIndex* const starts_;
337 };
338
339 CompactSparseMatrix() = default;
340 ConstView view() const { return ConstView(this); }
342 // Convenient constructors for tests.
343 // TODO(user): If this is needed in production code, it can be done faster.
344 explicit CompactSparseMatrix(const SparseMatrix& matrix) {
347
348 // This type is neither copyable nor movable.
350 CompactSparseMatrix& operator=(const CompactSparseMatrix&) = delete;
352 // Creates a CompactSparseMatrix from the given MatrixView. The matrices are
353 // the same, only the representation differ. Note that the entry order in
354 // each column is preserved.
356
357 // Creates a CompactSparseMatrix by copying the input and adding an identity
358 // matrix to the left of it.
360
361 // Creates a CompactSparseMatrix from the transpose of the given
362 // CompactSparseMatrix. Note that the entries in each columns will be ordered
363 // by row indices.
365
366 // Clears the matrix and sets its number of rows. If none of the Populate()
367 // function has been called, Reset() must be called before calling any of the
368 // Add*() functions below.
369 void Reset(RowIndex num_rows);
370
371 // Api to add columns one at the time.
372 void AddEntryToCurrentColumn(RowIndex row, Fractional coeff);
373 void CloseCurrentColumn();
374
375 // Adds a dense column to the CompactSparseMatrix (only the non-zero will be
376 // actually stored). This work in O(input.size()) and returns the index of the
377 // added column.
378 ColIndex AddDenseColumn(const DenseColumn& dense_column);
379
380 // Same as AddDenseColumn(), but only adds the non-zero from the given start.
381 ColIndex AddDenseColumnPrefix(DenseColumn::ConstView dense_column,
382 RowIndex start);
383
384 // Same as AddDenseColumn(), but uses the given non_zeros pattern of input.
385 // If non_zeros is empty, this actually calls AddDenseColumn().
386 ColIndex AddDenseColumnWithNonZeros(const DenseColumn& dense_column,
387 absl::Span<const RowIndex> non_zeros);
388
389 // Adds a dense column for which we know the non-zero positions and clears it.
390 // Note that this function supports duplicate indices in non_zeros. The
391 // complexity is in O(non_zeros.size()). Only the indices present in non_zeros
392 // will be cleared. Returns the index of the added column.
394 std::vector<RowIndex>* non_zeros);
395
396 // Returns the number of entries (i.e. degree) of the given column.
397 EntryIndex ColumnNumEntries(ColIndex col) const {
398 return starts_[col + 1] - starts_[col];
400
401 // Returns the matrix dimensions. See same functions in SparseMatrix.
402 EntryIndex num_entries() const {
403 DCHECK_EQ(coefficients_.size(), rows_.size());
404 return coefficients_.size();
405 }
406 RowIndex num_rows() const { return num_rows_; }
407 ColIndex num_cols() const { return num_cols_; }
409 // Returns whether or not this matrix contains any non-zero entries.
410 bool IsEmpty() const {
411 DCHECK_EQ(coefficients_.size(), rows_.size());
412 return coefficients_.empty();
413 }
414
415 // Alternative iteration API compatible with the one from SparseMatrix.
416 // The ConstView alternative should be faster.
417 ColumnView column(ColIndex col) const {
418 DCHECK_LT(col, num_cols_);
420 // Note that the start may be equal to row.size() if the last columns
421 // are empty, it is why we don't use &row[start].
422 const EntryIndex start = starts_[col];
423 return ColumnView(starts_[col + 1] - start, rows_.data() + start.value(),
424 coefficients_.data() + start.value());
425 }
426
427 // Returns true if the given column is empty. Note that for triangular matrix
428 // this does not include the diagonal coefficient (see below).
429 bool ColumnIsEmpty(ColIndex col) const {
430 return starts_[col + 1] == starts_[col];
432
433 // Returns the scalar product of the given row vector with the column of index
434 // col of this matrix.
435 Fractional ColumnScalarProduct(ColIndex col, const DenseRow& vector) const {
436 return view().ColumnScalarProduct(col, vector.const_view());
438
439 // Adds a multiple of the given column of this matrix to the given
440 // dense_column. If multiplier is 0.0, this function does nothing. This
441 // function is declared in the .h for efficiency.
442 void ColumnAddMultipleToDenseColumn(ColIndex col, Fractional multiplier,
443 DenseColumn::View dense_column) const {
444 if (multiplier == 0.0) return;
445 const auto entry_rows = rows_.view();
446 const auto entry_coeffs = coefficients_.view();
447 for (const EntryIndex i : Column(col)) {
448 dense_column[entry_rows[i]] += multiplier * entry_coeffs[i];
449 }
450 }
451 void ColumnAddMultipleToDenseColumn(ColIndex col, Fractional multiplier,
452 DenseColumn* dense_column) const {
453 return ColumnAddMultipleToDenseColumn(col, multiplier,
454 dense_column->view());
455 }
456
457 // Same as ColumnAddMultipleToDenseColumn() but also adds the new non-zeros to
458 // the non_zeros vector. A non-zero is "new" if is_non_zero[row] was false,
459 // and we update dense_column[row]. This function also updates is_non_zero.
460 void ColumnAddMultipleToSparseScatteredColumn(ColIndex col,
461 Fractional multiplier,
464 if (multiplier == 0.0) return;
465 const auto entry_rows = rows_.view();
466 const auto entry_coeffs = coefficients_.view();
467 for (const EntryIndex i : Column(col)) {
468 column->Add(entry_rows[i], multiplier * entry_coeffs[i]);
469 }
470 }
471
472 // Copies the given column of this matrix into the given dense_column.
473 // This function is declared in the .h for efficiency.
474 void ColumnCopyToDenseColumn(ColIndex col, DenseColumn* dense_column) const {
475 RETURN_IF_NULL(dense_column);
476 dense_column->AssignToZero(num_rows_);
477 ColumnCopyToClearedDenseColumn(col, dense_column);
478 }
479
480 // Same as ColumnCopyToDenseColumn() but assumes the column to be initially
481 // all zero.
482 void ColumnCopyToClearedDenseColumn(ColIndex col,
483 DenseColumn* dense_column) const {
484 RETURN_IF_NULL(dense_column);
485 dense_column->resize(num_rows_, 0.0);
486 const auto entry_rows = rows_.view();
487 const auto entry_coeffs = coefficients_.view();
488 for (const EntryIndex i : Column(col)) {
489 (*dense_column)[entry_rows[i]] = entry_coeffs[i];
490 }
491 }
492
493 // Same as ColumnCopyToClearedDenseColumn() but also fills non_zeros.
494 void ColumnCopyToClearedDenseColumnWithNonZeros(
495 ColIndex col, DenseColumn* dense_column,
496 RowIndexVector* non_zeros) const {
497 RETURN_IF_NULL(dense_column);
498 dense_column->resize(num_rows_, 0.0);
499 non_zeros->clear();
500 const auto entry_rows = rows_.view();
501 const auto entry_coeffs = coefficients_.view();
502 for (const EntryIndex i : Column(col)) {
503 const RowIndex row = entry_rows[i];
504 (*dense_column)[row] = entry_coeffs[i];
505 non_zeros->push_back(row);
506 }
507 }
508
509 void Swap(CompactSparseMatrix* other);
510
511 protected:
512 // Functions to iterate on the entries of a given column.
513 ::util::IntegerRange<EntryIndex> Column(ColIndex col) const {
514 return ::util::IntegerRange<EntryIndex>(starts_[col], starts_[col + 1]);
516
517 // The matrix dimensions, properly updated by full and incremental builders.
518 RowIndex num_rows_;
519 ColIndex num_cols_;
521 // Holds the columns non-zero coefficients and row positions.
522 // The entries for the column of index col are stored in the entries
523 // [starts_[col], starts_[col + 1]).
530 ColIndex col, DenseRow::ConstView vector) const {
531 // We expand ourselves since we don't really care about the floating
532 // point order of operation and this seems faster.
533 int i = starts_[col.value()].value();
534 const int end = starts_[col.value() + 1].value();
535 const int shifted_end = end - 3;
536 Fractional result1 = 0.0;
537 Fractional result2 = 0.0;
538 Fractional result3 = 0.0;
539 Fractional result4 = 0.0;
540 for (; i < shifted_end; i += 4) {
541 result1 += coefficients_[i] * vector[RowToColIndex(rows_[i])];
542 result2 += coefficients_[i + 1] * vector[RowToColIndex(rows_[i + 1])];
543 result3 += coefficients_[i + 2] * vector[RowToColIndex(rows_[i + 2])];
544 result4 += coefficients_[i + 3] * vector[RowToColIndex(rows_[i + 3])];
545 }
546 Fractional result = result1 + result2 + result3 + result4;
547 if (i < end) {
548 result += coefficients_[i] * vector[RowToColIndex(rows_[i])];
549 if (i + 1 < end) {
550 result += coefficients_[i + 1] * vector[RowToColIndex(rows_[i + 1])];
551 if (i + 2 < end) {
552 result += coefficients_[i + 2] * vector[RowToColIndex(rows_[i + 2])];
553 }
554 }
555 }
556 return result;
557}
558
559// A matrix view of the basis columns of a CompactSparseMatrix, with basis
560// specified as a RowToColMapping. This class does not take ownership of the
561// underlying matrix or basis, and thus they must outlive this class (and keep
562// the same address in memory).
563class CompactSparseMatrixView {
564 public:
566 const RowToColMapping* basis)
567 : compact_matrix_(*compact_matrix),
568 columns_(basis->data(), basis->size().value()) {}
569 CompactSparseMatrixView(const CompactSparseMatrix* compact_matrix,
570 const std::vector<ColIndex>* columns)
571 : compact_matrix_(*compact_matrix), columns_(*columns) {}
572
573 // Same behavior as the SparseMatrix functions above.
574 bool IsEmpty() const { return compact_matrix_.IsEmpty(); }
575 RowIndex num_rows() const { return compact_matrix_.num_rows(); }
576 ColIndex num_cols() const { return ColIndex(columns_.size()); }
577 ColumnView column(ColIndex col) const {
578 return compact_matrix_.column(columns_[col.value()]);
580 EntryIndex num_entries() const;
581 Fractional ComputeOneNorm() const;
582 Fractional ComputeInfinityNorm() const;
583
584 private:
585 // We require that the underlying CompactSparseMatrix and RowToColMapping
586 // continue to own the (potentially large) data accessed via this view.
587 const CompactSparseMatrix& compact_matrix_;
588 const absl::Span<const ColIndex> columns_;
589};
590
591// Specialization of a CompactSparseMatrix used for triangular matrices.
592// To be able to solve triangular systems as efficiently as possible, the
593// diagonal entries are stored in a separate vector and not in the underlying
594// CompactSparseMatrix.
595//
596// Advanced usage: this class also support matrices that can be permuted into a
597// triangular matrix and some functions work directly on such matrices.
598class TriangularMatrix : private CompactSparseMatrix {
599 public:
600 TriangularMatrix() : all_diagonal_coefficients_are_one_(true) {}
601
602 // This type is neither copyable nor movable.
603 TriangularMatrix(const TriangularMatrix&) = delete;
606 // Only a subset of the functions from CompactSparseMatrix are exposed (note
607 // the private inheritance). They are extended to deal with diagonal
608 // coefficients properly.
610 void Swap(TriangularMatrix* other);
611 bool IsEmpty() const { return diagonal_coefficients_.empty(); }
612 RowIndex num_rows() const { return num_rows_; }
613 ColIndex num_cols() const { return num_cols_; }
614 EntryIndex num_entries() const {
615 return EntryIndex(num_cols_.value()) + coefficients_.size();
617
618 // On top of the CompactSparseMatrix functionality, TriangularMatrix::Reset()
619 // also pre-allocates space of size col_size for a number of internal vectors.
620 // This helps reduce costly push_back operations for large problems.
621 //
622 // WARNING: Reset() must be called with a sufficiently large col_capacity
623 // prior to any Add* calls (e.g., AddTriangularColumn).
624 void Reset(RowIndex num_rows, ColIndex col_capacity);
625
626 // Constructs a triangular matrix from the given SparseMatrix. The input is
627 // assumed to be lower or upper triangular without any permutations. This is
628 // checked in debug mode.
629 void PopulateFromTriangularSparseMatrix(const SparseMatrix& input);
630
631 // Functions to create a triangular matrix incrementally, column by column.
632 // A client needs to call Reset(num_rows) first, and then each column must be
633 // added by calling one of the 3 functions below.
634 //
635 // Note that the row indices of the columns are allowed to be permuted: the
636 // diagonal entry of the column #col not being necessarily on the row #col.
637 // This is why these functions require the 'diagonal_row' parameter. The
638 // permutation can be fixed at the end by a call to
639 // ApplyRowPermutationToNonDiagonalEntries() or accounted directly in the case
640 // of PermutedLowerSparseSolve().
641 void AddTriangularColumn(const ColumnView& column, RowIndex diagonal_row);
642 void AddTriangularColumnWithGivenDiagonalEntry(const SparseColumn& column,
643 RowIndex diagonal_row,
644 Fractional diagonal_value);
645 void AddDiagonalOnlyColumn(Fractional diagonal_value);
646
647 // Adds the given sparse column divided by diagonal_coefficient.
648 // The diagonal_row is assumed to be present and its value should be the
649 // same as the one given in diagonal_coefficient. Note that this function
650 // tests for zero coefficients in the input column and removes them.
651 void AddAndNormalizeTriangularColumn(const SparseColumn& column,
652 RowIndex diagonal_row,
653 Fractional diagonal_coefficient);
654
655 // Applies the given row permutation to all entries except the diagonal ones.
656 void ApplyRowPermutationToNonDiagonalEntries(const RowPermutation& row_perm);
657
658 // Copy a triangular column with its diagonal entry to the given SparseColumn.
659 void CopyColumnToSparseColumn(ColIndex col, SparseColumn* output) const;
660
661 // Copy a triangular matrix to the given SparseMatrix.
662 void CopyToSparseMatrix(SparseMatrix* output) const;
663
664 // Returns the index of the first column which is not an identity column (i.e.
665 // a column j with only one entry of value 1 at the j-th row). This is always
666 // zero if the matrix is not triangular.
667 ColIndex GetFirstNonIdentityColumn() const {
668 return first_non_identity_column_;
670
671 // Returns the diagonal coefficient of the given column.
672 Fractional GetDiagonalCoefficient(ColIndex col) const {
673 return diagonal_coefficients_[col];
675
676 // Returns true iff the column contains no non-diagonal entries.
677 bool ColumnIsDiagonalOnly(ColIndex col) const {
680
681 // --------------------------------------------------------------------------
682 // Triangular solve functions.
683 //
684 // All the functions containing the word Lower (resp. Upper) require the
685 // matrix to be lower (resp. upper_) triangular without any permutation.
686 // --------------------------------------------------------------------------
687
688 // Solve the system L.x = rhs for a lower triangular matrix.
689 // The result overwrite rhs.
690 void LowerSolve(DenseColumn* rhs) const;
691
692 // Solves the system U.x = rhs for an upper triangular matrix.
693 void UpperSolve(DenseColumn* rhs) const;
694
695 // Solves the system Transpose(U).x = rhs where U is upper triangular.
696 // This can be used to do a left-solve for a row vector (i.e. y.Y = rhs).
697 void TransposeUpperSolve(DenseColumn* rhs) const;
698
699 // This assumes that the rhs is all zero before the given position.
700 void LowerSolveStartingAt(ColIndex start, DenseColumn* rhs) const;
701
702 // Solves the system Transpose(L).x = rhs, where L is lower triangular.
703 // This can be used to do a left-solve for a row vector (i.e., y.Y = rhs).
704 void TransposeLowerSolve(DenseColumn* rhs) const;
705
706 // Hyper-sparse version of the triangular solve functions. The passed
707 // non_zero_rows should contain the positions of the symbolic non-zeros of the
708 // result in the order in which they need to be accessed (or in the reverse
709 // order for the Reverse*() versions).
710 //
711 // The non-zero vector is mutable so that the symbolic non-zeros that are
712 // actually zero because of numerical cancellations can be removed.
713 //
714 // The non-zeros can be computed by one of these two methods:
715 // - ComputeRowsToConsiderWithDfs() which will give them in the reverse order
716 // of the one they need to be accessed in. This is only a topological order,
717 // and it will not necessarily be "sorted".
718 // - ComputeRowsToConsiderInSortedOrder() which will always give them in
719 // increasing order.
720 //
721 // Note that if the non-zeros are given in a sorted order, then the
722 // hyper-sparse functions will return EXACTLY the same results as the non
723 // hyper-sparse version above.
724 //
725 // For a given solve, here is the required order:
726 // - For a lower solve, increasing non-zeros order.
727 // - For an upper solve, decreasing non-zeros order.
728 // - for a transpose lower solve, decreasing non-zeros order.
729 // - for a transpose upper solve, increasing non_zeros order.
730 //
731 // For a general discussion of hyper-sparsity in LP, see:
732 // J.A.J. Hall, K.I.M. McKinnon, "Exploiting hyper-sparsity in the revised
733 // simplex method", December 1999, MS 99-014.
734 // http://www.maths.ed.ac.uk/hall/MS-99/MS9914.pdf
735 void HyperSparseSolve(DenseColumn* rhs, RowIndexVector* non_zero_rows) const;
736 void HyperSparseSolveWithReversedNonZeros(
737 DenseColumn* rhs, RowIndexVector* non_zero_rows) const;
738 void TransposeHyperSparseSolve(DenseColumn* rhs,
739 RowIndexVector* non_zero_rows) const;
740 void TransposeHyperSparseSolveWithReversedNonZeros(
741 DenseColumn* rhs, RowIndexVector* non_zero_rows) const;
742
743 // Given the positions of the non-zeros of a vector, computes the non-zero
744 // positions of the vector after a solve by this triangular matrix. The order
745 // of the returned non-zero positions will be in the REVERSE elimination
746 // order. If the function detects that there are too many non-zeros, then it
747 // aborts early and non_zero_rows is cleared.
748 void ComputeRowsToConsiderWithDfs(RowIndexVector* non_zero_rows) const;
749
750 // Same as TriangularComputeRowsToConsider() but always returns the non-zeros
751 // sorted by rows. It is up to the client to call the direct or reverse
752 // hyper-sparse solve function depending if the matrix is upper or lower
753 // triangular.
754 void ComputeRowsToConsiderInSortedOrder(RowIndexVector* non_zero_rows) const;
755
756 // This is currently only used for testing. It achieves the same result as
757 // PermutedLowerSparseSolve() below, but the latter exploits the sparsity of
758 // rhs and is thus faster for our use case.
759 //
760 // Note that partial_inverse_row_perm only permutes the first k rows, where k
761 // is the same as partial_inverse_row_perm.size(). It is the inverse
762 // permutation of row_perm which only permutes k rows into is [0, k), the
763 // other row images beeing kInvalidRow. The other arguments are the same as
764 // for PermutedLowerSparseSolve() and described there.
765 //
766 // IMPORTANT: lower will contain all the "symbolic" non-zero entries.
767 // A "symbolic" zero entry is one that will be zero whatever the coefficients
768 // of the rhs entries. That is it only depends on the position of its
769 // entries, not on their values. Thus, some of its coefficients may be zero.
770 // This fact is exploited by the LU factorization code. The zero coefficients
771 // of upper will be cleaned, however.
772 void PermutedLowerSolve(const SparseColumn& rhs,
773 const RowPermutation& row_perm,
774 const RowMapping& partial_inverse_row_perm,
775 SparseColumn* lower, SparseColumn* upper) const;
776
777 // This solves a lower triangular system with only ones on the diagonal where
778 // the matrix and the input rhs are permuted by the inverse of row_perm. Note
779 // that the output will also be permuted by the inverse of row_perm. The
780 // function also supports partial permutation. That is if row_perm[i] < 0 then
781 // column row_perm[i] is assumed to be an identity column.
782 //
783 // The output is given as follow:
784 // - lower is cleared, and receives the rows for which row_perm[row] < 0
785 // meaning not yet examined as a pivot (see markowitz.cc).
786 // - upper is NOT cleared, and the other rows (row_perm[row] >= 0) are
787 // appended to it.
788 // - Note that lower and upper can point to the same SparseColumn.
789 //
790 // Note: This function is non-const because ComputeRowsToConsider() also
791 // prunes the underlying dependency graph of the lower matrix while doing a
792 // solve. See marked_ and pruned_ends_ below.
793 void PermutedLowerSparseSolve(const ColumnView& rhs,
794 const RowPermutation& row_perm,
795 SparseColumn* lower, SparseColumn* upper);
796
797 // This is used to compute the deterministic time of a matrix factorization.
798 int64_t NumFpOperationsInLastPermutedLowerSparseSolve() const {
799 return num_fp_operations_;
800 }
801
802 // To be used in DEBUG mode by the client code. This check that the matrix is
803 // lower- (resp. upper-) triangular without any permutation and that there is
804 // no zero on the diagonal. We can't do that on each Solve() that require so,
805 // otherwise it will be too slow in debug.
806 bool IsLowerTriangular() const;
807 bool IsUpperTriangular() const;
808
809 // Visible for testing. This is used by PermutedLowerSparseSolve() to compute
810 // the non-zero indices of the result. The output is as follow:
811 // - lower_column_rows will contains the rows for which row_perm[row] < 0.
812 // - upper_column_rows will contains the other rows in the reverse topological
813 // order in which they should be considered in PermutedLowerSparseSolve().
814 //
815 // This function is non-const because it prunes the underlying dependency
816 // graph of the lower matrix while doing a solve. See marked_ and pruned_ends_
817 // below.
818 //
819 // Pruning the graph at the same time is slower but not by too much (< 2x) and
820 // seems worth doing. Note that when the lower matrix is dense, most of the
821 // graph will likely be pruned. As a result, the symbolic phase will be
822 // negligible compared to the numerical phase so we don't really need a dense
823 // version of PermutedLowerSparseSolve().
825 const RowPermutation& row_perm,
826 RowIndexVector* lower_column_rows,
827 RowIndexVector* upper_column_rows);
828
829 // The upper bound is computed using one of the algorithm presented in
830 // "A Survey of Condition Number Estimation for Triangular Matrices"
831 // https:epubs.siam.org/doi/pdf/10.1137/1029112/
834
835 private:
836 // Internal versions of some Solve() functions to avoid code duplication.
837 template <bool diagonal_of_ones>
838 void LowerSolveStartingAtInternal(ColIndex start,
839 DenseColumn::View rhs) const;
840 template <bool diagonal_of_ones>
841 void UpperSolveInternal(DenseColumn::View rhs) const;
842 template <bool diagonal_of_ones>
843 void TransposeLowerSolveInternal(DenseColumn::View rhs) const;
844 template <bool diagonal_of_ones>
845 void TransposeUpperSolveInternal(DenseColumn::View rhs) const;
846 template <bool diagonal_of_ones>
847 void HyperSparseSolveInternal(DenseColumn::View rhs,
848 RowIndexVector* non_zero_rows) const;
849 template <bool diagonal_of_ones>
850 void HyperSparseSolveWithReversedNonZerosInternal(
851 DenseColumn::View rhs, RowIndexVector* non_zero_rows) const;
852 template <bool diagonal_of_ones>
853 void TransposeHyperSparseSolveInternal(DenseColumn::View rhs,
854 RowIndexVector* non_zero_rows) const;
855 template <bool diagonal_of_ones>
856 void TransposeHyperSparseSolveWithReversedNonZerosInternal(
857 DenseColumn::View rhs, RowIndexVector* non_zero_rows) const;
858
859 // Internal function used by the Add*() functions to finish adding
860 // a new column to a triangular matrix.
861 void CloseCurrentColumn(Fractional diagonal_value);
862
863 // Extra data for "triangular" matrices. The diagonal coefficients are
864 // stored in a separate vector instead of beeing stored in each column.
865 StrictITIVector<ColIndex, Fractional> diagonal_coefficients_;
866
867 // Index of the first column which is not a diagonal only column with a
868 // coefficient of 1. This is used to optimize the solves.
869 ColIndex first_non_identity_column_;
870
871 // This common case allows for more efficient Solve() functions.
872 // TODO(user): Do not even construct diagonal_coefficients_ in this case?
873 bool all_diagonal_coefficients_are_one_;
874
875 // For the hyper-sparse version. These are used to implement a DFS, see
876 // TriangularComputeRowsToConsider() for more details.
877 mutable Bitset64<RowIndex> stored_;
878 mutable std::vector<RowIndex> nodes_to_explore_;
879
880 // For PermutedLowerSparseSolve().
881 int64_t num_fp_operations_;
882 mutable std::vector<RowIndex> lower_column_rows_;
883 mutable std::vector<RowIndex> upper_column_rows_;
884 mutable DenseColumn initially_all_zero_scratchpad_;
885
886 // This boolean vector is used to detect entries that can be pruned during
887 // the DFS used for the symbolic phase of ComputeRowsToConsider().
888 //
889 // Problem: We have a DAG where each node has outgoing arcs towards other
890 // nodes (this adjacency list is NOT sorted by any order). We want to compute
891 // the reachability of a set of nodes S and its topological order. While doing
892 // this, we also want to prune the adjacency lists to exploit the simple fact
893 // that if a -> (b, c) and b -> (c) then c can be removed from the adjacency
894 // list of a since it will be implied through b. Note that this doesn't change
895 // the reachability of any set nor a valid topological ordering of such a set.
896 //
897 // The concept is known as the transitive reduction of a DAG, see
898 // http://en.wikipedia.org/wiki/Transitive_reduction.
899 //
900 // Heuristic algorithm: While doing the DFS to compute Reach(S) and its
901 // topological order, each time we process a node, we mark all its adjacent
902 // node while going down in the DFS, and then we unmark all of them when we go
903 // back up. During the un-marking, if a node is already un-marked, it means
904 // that it was implied by some other path starting at the current node and we
905 // can prune it and remove it from the adjacency list of the current node.
906 //
907 // Note(user): I couldn't find any reference for this algorithm, even though
908 // I suspect I am not the first one to need something similar.
909 mutable DenseBooleanColumn marked_;
910
911 // This is used to represent a pruned sub-matrix of the current matrix that
912 // corresponds to the pruned DAG as described in the comment above for
913 // marked_. This vector is used to encode the sub-matrix as follow:
914 // - Both the rows and the coefficients of the pruned matrix are still stored
915 // in rows_ and coefficients_.
916 // - The data of column 'col' is still stored starting at starts_[col].
917 // - But, its end is given by pruned_ends_[col] instead of starts_[col + 1].
918 //
919 // The idea of using a smaller graph for the symbolic phase is well known in
920 // sparse linear algebra. See:
921 // - John R. Gilbert and Joseph W. H. Liu, "Elimination structures for
922 // unsymmetric sparse LU factors", Tech. Report CS-90-11. Departement of
923 // Computer Science, York University, North York. Ontario, Canada, 1990.
924 // - Stanley C. Eisenstat and Joseph W. H. Liu, "Exploiting structural
925 // symmetry in a sparse partial pivoting code". SIAM J. Sci. Comput. Vol
926 // 14, No 1, pp. 253-257, January 1993.
927 //
928 // Note that we use an original algorithm and prune the graph while performing
929 // the symbolic phase. Hence the pruning will only benefit the next symbolic
930 // phase. This is different from Eisenstat-Liu's symmetric pruning. It is
931 // still a heuristic and will not necessarily find the minimal graph that
932 // has the same result for the symbolic phase though.
933 //
934 // TODO(user): Use this during the "normal" hyper-sparse solves so that
935 // we can benefit from the pruned lower matrix there?
937};
938
939} // namespace glop
940} // namespace operations_research
941
942#endif // OR_TOOLS_LP_DATA_SPARSE_H_
CompactSparseMatrixView(const CompactSparseMatrix *compact_matrix, const RowToColMapping *basis)
Definition sparse.h:567
EntryIndex ColumnNumEntries(ColIndex col) const
Definition sparse.h:326
ConstView(const CompactSparseMatrix *matrix)
Definition sparse.h:306
Fractional ColumnScalarProduct(ColIndex col, DenseRow::ConstView vector) const
Definition sparse.h:531
void PopulateFromMatrixView(const MatrixView &input)
Definition sparse.cc:447
CompactSparseMatrix & operator=(const CompactSparseMatrix &)=delete
ColIndex AddDenseColumnPrefix(DenseColumn::ConstView dense_column, RowIndex start)
Same as AddDenseColumn(), but only adds the non-zero from the given start.
Definition sparse.cc:597
StrictITIVector< EntryIndex, Fractional > coefficients_
Definition sparse.h:526
void ColumnAddMultipleToDenseColumn(ColIndex col, Fractional multiplier, DenseColumn *dense_column) const
Definition sparse.h:453
ColIndex AddDenseColumn(const DenseColumn &dense_column)
Definition sparse.cc:593
void PopulateFromSparseMatrixAndAddSlacks(const SparseMatrix &input)
Definition sparse.cc:466
StrictITIVector< ColIndex, EntryIndex > starts_
Definition sparse.h:528
EntryIndex num_entries() const
Returns the matrix dimensions. See same functions in SparseMatrix.
Definition sparse.h:404
ColIndex AddAndClearColumnWithNonZeros(DenseColumn *column, std::vector< RowIndex > *non_zeros)
Definition sparse.cc:626
bool IsEmpty() const
Returns whether or not this matrix contains any non-zero entries.
Definition sparse.h:412
ColIndex AddDenseColumnWithNonZeros(const DenseColumn &dense_column, absl::Span< const RowIndex > non_zeros)
Definition sparse.cc:611
void AddEntryToCurrentColumn(RowIndex row, Fractional coeff)
Api to add columns one at the time.
Definition sparse.cc:582
bool ColumnIsEmpty(ColIndex col) const
Definition sparse.h:431
EntryIndex ColumnNumEntries(ColIndex col) const
Returns the number of entries (i.e. degree) of the given column.
Definition sparse.h:399
StrictITIVector< EntryIndex, RowIndex > rows_
Definition sparse.h:527
void Swap(CompactSparseMatrix *other)
Definition sparse.cc:642
void ColumnCopyToClearedDenseColumn(ColIndex col, DenseColumn *dense_column) const
Definition sparse.h:484
Fractional ColumnScalarProduct(ColIndex col, const DenseRow &vector) const
Definition sparse.h:437
void PopulateFromTranspose(const CompactSparseMatrix &input)
Definition sparse.cc:495
RowIndex num_rows_
The matrix dimensions, properly updated by full and incremental builders.
Definition sparse.h:520
::util::IntegerRange< EntryIndex > Column(ColIndex col) const
Functions to iterate on the entries of a given column.
Definition sparse.h:515
ColumnView column(ColIndex col) const
Definition sparse.h:419
void PopulateFromMatrixPair(const SparseMatrix &matrix_a, const SparseMatrix &matrix_b)
Definition sparse.h:248
Fractional ComputeInfinityNorm() const
Definition sparse.cc:433
void PopulateFromMatrix(const SparseMatrix &matrix)
Takes all the columns of the given matrix.
Definition sparse.h:237
bool IsEmpty() const
Same behavior as the SparseMatrix functions above.
Definition sparse.h:272
Fractional ComputeOneNorm() const
Definition sparse.cc:430
void PopulateFromBasis(const MatrixView &matrix, const RowToColMapping &basis)
Takes only the columns of the given matrix that belongs to the given basis.
Definition sparse.h:262
const SparseColumn & column(ColIndex col) const
Definition sparse.h:275
void ComputeMinAndMaxMagnitudes(Fractional *min_magnitude, Fractional *max_magnitude) const
Definition sparse.cc:379
void PopulateFromProduct(const SparseMatrix &a, const SparseMatrix &b)
Multiplies SparseMatrix a by SparseMatrix b.
Definition sparse.cc:260
std::string Dump() const
Returns a dense representation of the matrix.
Definition sparse.cc:409
void PopulateFromPermutedMatrix(const Matrix &a, const RowPermutation &row_perm, const ColumnPermutation &inverse_col_perm)
Definition sparse.cc:222
const SparseColumn & column(ColIndex col) const
Access the underlying sparse columns.
Definition sparse.h:193
SparseMatrix & operator=(const SparseMatrix &)=delete
void PopulateFromTranspose(const Matrix &input)
Instantiate needed templates.
Definition sparse.cc:191
bool CheckNoDuplicates() const
Call CheckNoDuplicates() on all columns, useful for doing a DCHECK.
Definition sparse.cc:136
void PopulateFromLinearCombination(Fractional alpha, const SparseMatrix &a, Fractional beta, const SparseMatrix &b)
Definition sparse.cc:235
SparseColumn * mutable_column(ColIndex col)
Definition sparse.h:194
void AppendUnitVector(RowIndex row, Fractional value)
Definition sparse.cc:161
void Swap(SparseMatrix *matrix)
Definition sparse.cc:168
bool IsCleanedUp() const
Call IsCleanedUp() on all columns, useful for doing a DCHECK.
Definition sparse.cc:145
void ApplyRowPermutation(const RowPermutation &row_perm)
Applies the row permutation.
Definition sparse.cc:326
void DeleteRows(RowIndex num_rows, const RowPermutation &permutation)
Definition sparse.cc:299
void PopulateFromIdentity(ColIndex num_cols)
Definition sparse.cc:182
RowIndex num_rows() const
Return the matrix dimension.
Definition sparse.h:189
Fractional ComputeInfinityNorm() const
Definition sparse.cc:405
void PopulateFromZero(RowIndex num_rows, ColIndex num_cols)
Definition sparse.cc:174
bool AppendRowsFromSparseMatrix(const SparseMatrix &matrix)
Definition sparse.cc:312
void DeleteColumns(const DenseBooleanRow &columns_to_delete)
Definition sparse.cc:286
void PopulateFromSparseMatrix(const SparseMatrix &matrix)
Definition sparse.cc:216
bool Equals(const SparseMatrix &a, Fractional tolerance) const
Definition sparse.cc:337
ColIndex AppendEmptyColumn()
Appends an empty column and returns its index.
Definition sparse.cc:155
void SetNumRows(RowIndex num_rows)
Change the number of row of this matrix.
Definition sparse.cc:153
Fractional LookUpValue(RowIndex row, ColIndex col) const
Definition sparse.cc:333
StrictITISpan< ColIndex, const Fractional > ConstView
Definition lp_types.h:291
Fractional ComputeInverseInfinityNormUpperBound() const
Definition sparse.cc:1516
void PermutedComputeRowsToConsider(const ColumnView &rhs, const RowPermutation &row_perm, RowIndexVector *lower_column_rows, RowIndexVector *upper_column_rows)
Definition sparse.cc:1273
StrictITIVector< RowIndex, ColIndex > RowToColMapping
Definition lp_types.h:394
std::vector< RowIndex > RowIndexVector
Definition lp_types.h:361
StrictITIVector< RowIndex, bool > DenseBooleanColumn
Column of booleans.
Definition lp_types.h:383
Permutation< ColIndex > ColumnPermutation
Permutation< RowIndex > RowPermutation
ColIndex RowToColIndex(RowIndex row)
Get the ColIndex corresponding to the column # row.
Definition lp_types.h:54
StrictITIVector< RowIndex, RowIndex > RowMapping
Column of row indices. Used to represent mappings between rows.
Definition lp_types.h:389
StrictITIVector< RowIndex, Fractional > DenseColumn
Column-vector types. Column-vector types are indexed by a row index.
Definition lp_types.h:380
StrictITIVector< ColIndex, Fractional > DenseRow
Row-vector types. Row-vector types are indexed by a column index.
Definition lp_types.h:351
StrictITIVector< ColIndex, bool > DenseBooleanRow
Row of booleans.
Definition lp_types.h:354
In SWIG mode, we don't want anything besides these top-level includes.
ClosedInterval::Iterator end(ClosedInterval interval)
static int input(yyscan_t yyscanner)
#define RETURN_IF_NULL(x)