OR-Tools  9.6
lu_factorization.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 #ifndef OR_TOOLS_GLOP_LU_FACTORIZATION_H_
15 #define OR_TOOLS_GLOP_LU_FACTORIZATION_H_
16 
17 #include <string>
18 #include <vector>
19 
20 #include "ortools/glop/markowitz.h"
21 #include "ortools/glop/parameters.pb.h"
22 #include "ortools/glop/status.h"
26 #include "ortools/lp_data/sparse.h"
28 #include "ortools/util/stats.h"
29 
30 namespace operations_research {
31 namespace glop {
32 
33 // An LU-Factorization class encapsulating the LU factorization data and
34 // algorithms. The actual algorithm is in markowitz.h and .cc. This class holds
35 // all the Solve() functions that deal with the permutations and the L and U
36 // factors once they are computed.
38  public:
40 
41  // Returns true if the LuFactorization is a factorization of the identity
42  // matrix. In this state, all the Solve() functions will work for any
43  // vector dimension.
44  bool IsIdentityFactorization() { return is_identity_factorization_; }
45 
46  // Clears internal data structure and reset this class to the factorization
47  // of an identity matrix.
48  void Clear();
49 
50  // Computes an LU-decomposition for a given matrix B. If for some reason,
51  // there was an error, then the factorization is reset to the one of the
52  // identity matrix, and an error is reported.
53  //
54  // Note(user): Since a client must use the result, there is little chance of
55  // it being confused by this revert to identity factorization behavior. The
56  // reason behind it is that this way, calling any public function of this
57  // class will never cause a crash of the program.
58  ABSL_MUST_USE_RESULT Status
59  ComputeFactorization(const CompactSparseMatrixView& compact_matrix);
60 
61  // Given a set of columns, find a maximum linearly independent subset that can
62  // be factorized in a stable way, and complete it into a square matrix using
63  // slack columns. The initial set can have less, more or the same number of
64  // columns as the number of rows.
66  const std::vector<ColIndex>& candidates);
67 
68  // Returns the column permutation used by the LU factorization.
69  const ColumnPermutation& GetColumnPermutation() const { return col_perm_; }
70 
71  // Sets the column permutation to the identity permutation. The idea is that
72  // the column permutation can be incorporated in the basis RowToColMapping,
73  // and once this is done, then a client can call this and effectively remove
74  // the need for a column permutation on each solve.
76  col_perm_.clear();
77  inverse_col_perm_.clear();
78  }
79 
80  // Solves 'B.x = b', x initially contains b, and is replaced by 'B^{-1}.b'.
81  // Since P.B.Q^{-1} = L.U, we have B = P^{-1}.L.U.Q.
82  // 1/ Solve P^{-1}.y = b for y by computing y = P.b,
83  // 2/ solve L.z = y for z,
84  // 3/ solve U.t = z for t,
85  // 4/ finally solve Q.x = t, by computing x = Q^{-1}.t.
86  void RightSolve(DenseColumn* x) const;
87 
88  // Solves 'y.B = r', y initially contains r, and is replaced by r.B^{-1}.
89  // Internally, it takes x = y^T, b = r^T and solves B^T.x = b.
90  // We have P.B.Q^{-1} = P.B.Q^T = L.U, thus (L.U)^T = Q.B^T.P^T.
91  // Therefore B^T = Q^{-1}.U^T.L^T.P^T.P^{-1} = Q^{-1}.U^T.L^T.P
92  // The procedure is thus:
93  // 1/ Solve Q^{-1}.y = b for y, by computing y = Q.b,
94  // 2/ solve U^T.z = y for z,
95  // 3/ solve L^T.t = z for t,
96  // 4/ finally, solve P.x = t for x by computing x = P^{-1}.t.
97  void LeftSolve(DenseRow* y) const;
98 
99  // More fine-grained right/left solve functions that may exploit the initial
100  // non-zeros of the input vector if non-empty. Note that a solve involving L
101  // actually solves P^{-1}.L and a solve involving U actually solves U.Q. To
102  // solve a system with the initial matrix B, one needs to call:
103  // - RightSolveL() and then RightSolveU() for a right solve (B.x = initial x).
104  // - LeftSolveU() and then LeftSolveL() for a left solve (y.B = initial y).
107  void LeftSolveUWithNonZeros(ScatteredRow* y) const;
108 
109  // Specialized version of LeftSolveL() that may exploit the initial non_zeros
110  // of y if it is non empty. Moreover, if result_before_permutation is not
111  // NULL, it might be filled with the result just before row_perm_ is applied
112  // to it and true is returned. If result_before_permutation is not filled,
113  // then false is returned.
115  ScatteredColumn* result_before_permutation) const;
116  void LeftSolveLWithNonZeros(ScatteredRow* y) const;
117 
118  // Specialized version of RightSolveLWithNonZeros() that takes a SparseColumn
119  // or a ScatteredColumn as input. non_zeros will either be cleared or set to
120  // the non zeros of the result. Important: the output x must be of the correct
121  // size and all zero.
122  void RightSolveLForColumnView(const ColumnView& b, ScatteredColumn* x) const;
124  ScatteredColumn* x) const;
125 
126  // Specialized version of RightSolveLWithNonZeros() where x is originally
127  // equal to 'a' permuted by row_perm_. Note that 'a' is only used for DCHECK.
129  ScatteredColumn* x) const;
130 
131  // Specialized version of LeftSolveU() for an unit right-hand side.
132  // non_zeros will either be cleared or set to the non zeros of the results.
133  // It also returns the value of col permuted by Q (which is the position
134  // of the unit-vector rhs in the solve system: y.U = rhs).
135  // Important: the output y must be of the correct size and all zero.
136  ColIndex LeftSolveUForUnitRow(ColIndex col, ScatteredRow* y) const;
137 
138  // Returns the given column of U.
139  // It will only be valid until the next call to GetColumnOfU().
140  const SparseColumn& GetColumnOfU(ColIndex col) const;
141 
142  // Returns the norm of B^{-1}.a
144 
145  // Returns the norm of (B^T)^{-1}.e_row where e is an unit vector.
146  Fractional DualEdgeSquaredNorm(RowIndex row) const;
147 
148  // The fill-in of the LU-factorization is defined as the sum of the number
149  // of entries of both the lower- and upper-triangular matrices L and U minus
150  // the number of entries in the initial matrix B.
151  //
152  // This returns the number of entries in lower + upper as the percentage of
153  // the number of entries in B.
154  double GetFillInPercentage(const CompactSparseMatrixView& matrix) const;
155 
156  // Returns the number of entries in L + U.
157  // If the factorization is the identity, this returns 0.
158  EntryIndex NumberOfEntries() const;
159 
160  // Computes the determinant of the input matrix B.
161  // Since P.B.Q^{-1} = L.U, det(P) * det(B) * det(Q^{-1}) = det(L) * det(U).
162  // det(L) = 1 since L is a lower-triangular matrix with 1 on the diagonal.
163  // det(P) = +1 or -1 (by definition it is the sign of the permutation P)
164  // det(Q^{-1}) = +1 or -1 (the sign of the permutation Q^{-1})
165  // Finally det(U) = product of the diagonal elements of U, since U is an
166  // upper-triangular matrix.
167  // Taking all this into account:
168  // det(B) = sign(P) * sign(Q^{-1}) * prod_i u_ii .
170 
171  // Computes the 1-norm of the inverse of the input matrix B.
172  // For this we iteratively solve B.x = e_j, where e_j is the jth unit vector.
173  // The result of this computation is the jth column of B^-1.
174  // The 1-norm |B| is defined as max_j sum_i |a_ij|
175  // http://en.wikipedia.org/wiki/Matrix_norm
177 
178  // Computes the infinity-norm of the inverse of the input matrix B.
179  // The infinity-norm |B| is defined as max_i sum_j |a_ij|
180  // http://en.wikipedia.org/wiki/Matrix_norm
182 
183  // Computes the condition number of the input matrix B.
184  // For a given norm, this is the matrix norm times the norm of its inverse.
185  //
186  // Note that because the LuFactorization class does not keep the
187  // non-factorized matrix in memory, it needs to be passed to these functions.
188  // It is up to the client to pass exactly the same matrix as the one used
189  // for ComputeFactorization().
190  //
191  // TODO(user): separate this from LuFactorization.
193  const CompactSparseMatrixView& matrix) const;
195  const CompactSparseMatrixView& matrix) const;
197 
198  // Sets the current parameters.
199  void SetParameters(const GlopParameters& parameters) {
200  parameters_ = parameters;
201  markowitz_.SetParameters(parameters);
202  }
203 
204  // Returns a string containing the statistics for this class.
205  std::string StatString() const {
206  return stats_.StatString() + markowitz_.StatString();
207  }
208 
209  // This is only used for testing and in debug mode.
210  // TODO(user): avoid the matrix conversion by multiplying TriangularMatrix
211  // directly.
212  void ComputeLowerTimesUpper(SparseMatrix* product) const {
213  SparseMatrix temp_lower, temp_upper;
214  lower_.CopyToSparseMatrix(&temp_lower);
215  upper_.CopyToSparseMatrix(&temp_upper);
216  product->PopulateFromProduct(temp_lower, temp_upper);
217  }
218 
219  // Returns the deterministic time of the last factorization.
221 
222  // Visible for testing.
223  const RowPermutation& row_perm() const { return row_perm_; }
225  return inverse_col_perm_;
226  }
227 
228  private:
229  // Statistics about this class.
230  struct Stats : public StatsGroup {
231  Stats()
232  : StatsGroup("LuFactorization"),
233  basis_num_entries("basis_num_entries", this),
234  lu_fill_in("lu_fill_in", this) {}
235  IntegerDistribution basis_num_entries;
236  RatioDistribution lu_fill_in;
237  };
238 
239  // Internal function used in the left solve functions.
240  void LeftSolveScratchpad() const;
241 
242  // Internal function used in the right solve functions
243  template <typename Column>
244  void RightSolveLInternal(const Column& b, ScatteredColumn* x) const;
245 
246  // Fills transpose_upper_ from upper_.
247  void ComputeTransposeUpper();
248 
249  // transpose_lower_ is only needed when we compute dual norms.
250  void ComputeTransposeLower() const;
251 
252  // Computes R = P.B.Q^{-1} - L.U and returns false if the largest magnitude of
253  // the coefficients of P.B.Q^{-1} - L.U is greater than tolerance.
254  bool CheckFactorization(const CompactSparseMatrixView& matrix,
255  Fractional tolerance) const;
256 
257  // Special case where we have nothing to do. This happens at the beginning
258  // when we start the problem with an all-slack basis and gives a good speedup
259  // on really easy problems. It is initially true and set to true each time we
260  // call Clear(). We set it to false if a call to ComputeFactorization()
261  // succeeds.
262  bool is_identity_factorization_;
263 
264  // The triangular factor L and U (and its transpose).
265  TriangularMatrix lower_;
266  TriangularMatrix upper_;
267  TriangularMatrix transpose_upper_;
268 
269  // The transpose of lower_. It is just used by DualEdgeSquaredNorm()
270  // and mutable so it can be lazily initialized.
271  mutable TriangularMatrix transpose_lower_;
272 
273  // The column permutation Q and its inverse Q^{-1} in P.B.Q^{-1} = L.U.
274  ColumnPermutation col_perm_;
275  ColumnPermutation inverse_col_perm_;
276 
277  // The row permutation P and its inverse P^{-1} in P.B.Q^{-1} = L.U.
278  RowPermutation row_perm_;
279  RowPermutation inverse_row_perm_;
280 
281  // Temporary storage used by LeftSolve()/RightSolve().
282  mutable DenseColumn dense_column_scratchpad_;
283 
284  // Temporary storage used by GetColumnOfU().
285  mutable SparseColumn column_of_upper_;
286 
287  // Same as dense_column_scratchpad_ but this vector is always reset to zero by
288  // the functions that use it. non_zero_rows_ is used to track the
289  // non_zero_rows_ position of dense_column_scratchpad_.
290  mutable DenseColumn dense_zero_scratchpad_;
291  mutable std::vector<RowIndex> non_zero_rows_;
292 
293  // Statistics, mutable so const functions can still update it.
294  mutable Stats stats_;
295 
296  // Proto holding all the parameters of this algorithm.
297  GlopParameters parameters_;
298 
299  // The class doing the Markowitz LU factorization.
300  Markowitz markowitz_;
301 
302  DISALLOW_COPY_AND_ASSIGN(LuFactorization);
303 };
304 
305 } // namespace glop
306 } // namespace operations_research
307 #endif // OR_TOOLS_GLOP_LU_FACTORIZATION_H_
void LeftSolveUWithNonZeros(ScatteredRow *y) const
const SparseColumn & GetColumnOfU(ColIndex col) const
RowToColMapping ComputeInitialBasis(const CompactSparseMatrix &matrix, const std::vector< ColIndex > &candidates)
void RightSolveLForColumnView(const ColumnView &b, ScatteredColumn *x) const
void RightSolveLWithPermutedInput(const DenseColumn &a, ScatteredColumn *x) const
double GetFillInPercentage(const CompactSparseMatrixView &matrix) const
Fractional RightSolveSquaredNorm(const ColumnView &a) const
const ColumnPermutation & GetColumnPermutation() const
void RightSolveUWithNonZeros(ScatteredColumn *x) const
const RowPermutation & row_perm() const
bool LeftSolveLWithNonZeros(ScatteredRow *y, ScatteredColumn *result_before_permutation) const
const ColumnPermutation & inverse_col_perm() const
ColIndex LeftSolveUForUnitRow(ColIndex col, ScatteredRow *y) const
Fractional DualEdgeSquaredNorm(RowIndex row) const
void RightSolveLForScatteredColumn(const ScatteredColumn &b, ScatteredColumn *x) const
void RightSolveLWithNonZeros(ScatteredColumn *x) const
Fractional ComputeInfinityNormConditionNumber(const CompactSparseMatrixView &matrix) const
void ComputeLowerTimesUpper(SparseMatrix *product) const
ABSL_MUST_USE_RESULT Status ComputeFactorization(const CompactSparseMatrixView &compact_matrix)
void SetParameters(const GlopParameters &parameters)
Fractional ComputeOneNormConditionNumber(const CompactSparseMatrixView &matrix) const
void SetParameters(const GlopParameters &parameters)
Definition: markowitz.h:315
std::string StatString() const
Definition: markowitz.h:312
void PopulateFromProduct(const SparseMatrix &a, const SparseMatrix &b)
Definition: sparse.cc:255
void CopyToSparseMatrix(SparseMatrix *output) const
Definition: sparse.cc:770
int64_t b
int64_t a
SatParameters parameters
ColIndex col
Definition: markowitz.cc:186
RowIndex row
Definition: markowitz.cc:185
Permutation< ColIndex > ColumnPermutation
StrictITIVector< RowIndex, Fractional > DenseColumn
Definition: lp_types.h:370
Permutation< RowIndex > RowPermutation
Collection of objects used to extend the Constraint Solver library.