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