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