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