OR-Tools  9.6
basis_representation.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_BASIS_REPRESENTATION_H_
15 #define OR_TOOLS_GLOP_BASIS_REPRESENTATION_H_
16 
17 #include <string>
18 #include <vector>
19 
20 #include "ortools/base/logging.h"
22 #include "ortools/glop/parameters.pb.h"
24 #include "ortools/glop/status.h"
28 #include "ortools/lp_data/sparse.h"
29 #include "ortools/util/stats.h"
30 
31 namespace operations_research {
32 namespace glop {
33 
34 // An eta matrix E corresponds to the identity matrix except for one column e of
35 // index j. In particular, B.E is the matrix of the new basis obtained from B by
36 // replacing the j-th vector of B by B.e, note that this is exactly what happens
37 // during a "pivot" of the current basis in the simplex algorithm.
38 //
39 // E = [ 1 ... 0 e_0 0 ... 0
40 // ... ... ... ... ... ... ...
41 // 0 ... 1 e_{j-1} 0 ... 0
42 // 0 ... 0 e_j 0 ... 0
43 // 0 ... 0 e_{j+1} 1 ... 0
44 // ... ... ... ... ... ... ...
45 // 0 ... 0 e_{n-1} 0 ... 1 ]
46 //
47 // The inverse of the eta matrix is:
48 // E^{-1} = [ 1 ... 0 -e_0/e_j 0 ... 0
49 // ... ... ... ... ... ... ...
50 // 0 ... 1 -e_{j-1}/e_j 0 ... 0
51 // 0 ... 0 1/e_j 0 ... 0
52 // 0 ... 0 -e_{j+1}/e_j 1 ... 0
53 // ... ... ... ... ... ... ...
54 // 0 ... 0 -e_{n-1}/e_j 0 ... 1 ]
55 class EtaMatrix {
56  public:
57  EtaMatrix(ColIndex eta_col, const ScatteredColumn& direction);
58  virtual ~EtaMatrix();
59 
60  // Solves the system y.E = c, 'c' beeing the initial value of 'y'.
61  // Then y = c.E^{-1}, so y is equal to c except for
62  // y_j = (c_j - \sum_{i != j}{c_i * e_i}) / e_j.
63  void LeftSolve(DenseRow* y) const;
64 
65  // Same as LeftSolve(), but 'pos' contains the non-zero positions of c. The
66  // order of the positions is not important, but there must be no duplicates.
67  // The values not in 'pos' are not used. If eta_col_ was not already in 'pos',
68  // it is added.
69  void SparseLeftSolve(DenseRow* y, ColIndexVector* pos) const;
70 
71  // Solves the system E.d = a, 'a' beeing the initial value of 'd'.
72  // Then d = E^{-1}.a = [ a_0 - e_0 * a_j / e_j
73  // ...
74  // a_{j-1} - e_{j-1} * a_j / e_j
75  // a_j / e_j
76  // a_{j+1} - e_{j+1} * a_j / e_j
77  // ...
78  // a_{n-1} - e_{n-1} * a_j / e_j ]
79  void RightSolve(DenseColumn* d) const;
80 
81  private:
82  // Internal RightSolve() and LeftSolve() implementations using either the
83  // dense or the sparse representation of the eta vector.
84  void LeftSolveWithDenseEta(DenseRow* y) const;
85  void LeftSolveWithSparseEta(DenseRow* y) const;
86  void RightSolveWithDenseEta(DenseColumn* d) const;
87  void RightSolveWithSparseEta(DenseColumn* d) const;
88 
89  // If an eta vector density is smaller than this threshold, we use the
90  // sparse version of the Solve() functions rather than the dense version.
91  // TODO(user): Detect automatically a good parameter? 0.5 is a good value on
92  // the Netlib (I only did a few experiments though). Note that in the future
93  // we may not even keep the dense representation at all.
94  static const Fractional kSparseThreshold;
95 
96  const ColIndex eta_col_;
97  const Fractional eta_col_coefficient_;
98 
99  // Note that to optimize solves, the position eta_col_ is set to 0.0 and
100  // stored in eta_col_coefficient_ instead.
101  DenseColumn eta_coeff_;
102  SparseColumn sparse_eta_coeff_;
103 
104  DISALLOW_COPY_AND_ASSIGN(EtaMatrix);
105 };
106 
107 // An eta factorization corresponds to the product of k eta matrices,
108 // i.e. E = E_0.E_1. ... .E_{k-1}
109 // It is used to solve two systems:
110 // - E.d = a (where a is usually the entering column).
111 // - y.E = c (where c is usually the objective row).
113  public:
115  virtual ~EtaFactorization();
116 
117  // Deletes all eta matrices.
118  void Clear();
119 
120  // Updates the eta factorization, i.e. adds the new eta matrix defined by
121  // the leaving variable and the corresponding eta column.
122  void Update(ColIndex entering_col, RowIndex leaving_variable_row,
123  const ScatteredColumn& direction);
124 
125  // Left solves all systems from right to left, i.e. y_i = y_{i+1}.(E_i)^{-1}
126  void LeftSolve(DenseRow* y) const;
127 
128  // Same as LeftSolve(), but 'pos' contains the non-zero positions of c. The
129  // order of the positions is not important, but there must be no duplicates.
130  // The values not in 'pos' are not used. If eta_col_ was not already in 'pos',
131  // it is added.
132  void SparseLeftSolve(DenseRow* y, ColIndexVector* pos) const;
133 
134  // Right solves all systems from left to right, i.e. E_i.d_{i+1} = d_i
135  void RightSolve(DenseColumn* d) const;
136 
137  private:
138  std::vector<EtaMatrix*> eta_matrix_;
139 
140  DISALLOW_COPY_AND_ASSIGN(EtaFactorization);
141 };
142 
143 // A basis factorization is the product of an eta factorization and
144 // a L.U decomposition, i.e. B = L.U.E_0.E_1. ... .E_{k-1}
145 // It is used to solve two systems:
146 // - B.d = a where a is the entering column.
147 // - y.B = c where c is the objective row.
148 //
149 // To speed-up and improve stability the factorization is refactorized at least
150 // every 'refactorization_period' updates.
151 //
152 // This class does not take ownership of the underlying matrix and basis, and
153 // thus they must outlive this class (and keep the same address in memory).
155  public:
156  BasisFactorization(const CompactSparseMatrix* compact_matrix,
157  const RowToColMapping* basis);
158  virtual ~BasisFactorization();
159 
160  // Sets the parameters for this component.
161  void SetParameters(const GlopParameters& parameters) {
162  max_num_updates_ = parameters.basis_refactorization_period();
163  use_middle_product_form_update_ =
164  parameters.use_middle_product_form_update();
165  parameters_ = parameters;
166  lu_factorization_.SetParameters(parameters);
167  }
168 
169  // Returns the column permutation used by the LU factorization.
170  // This call only makes sense if the basis was just refactorized.
172  DCHECK(IsRefactorized());
173  return lu_factorization_.GetColumnPermutation();
174  }
175 
176  // Sets the column permutation used by the LU factorization to the identity.
177  // Hense the Solve() results will be computed without this permutation.
178  // This call only makes sense if the basis was just refactorized.
180  DCHECK(IsRefactorized());
181  lu_factorization_.SetColumnPermutationToIdentity();
182  }
183 
184  // Clears the factorization and resets it to an identity matrix of size given
185  // by matrix_.num_rows().
186  void Clear();
187 
188  // Clears the factorization and initializes the class using the current
189  // matrix_ and basis_. This is fast if IsIdentityBasis() is true, otherwise
190  // it will trigger a refactorization and will return an error if the matrix
191  // could not be factorized.
192  ABSL_MUST_USE_RESULT Status Initialize();
193 
194  // This mainly forward the call to LuFactorization::ComputeInitialBasis().
195  //
196  // Note that once this is called, one would need to call Initialize() to
197  // actually create the factorization. The only side effect of this is to
198  // update the deterministic time.
199  //
200  // TODO(user): This "double" factorization is a bit inefficient, and we should
201  // probably Initialize() right away the factorization with the new basis, but
202  // more code is needed for that. It is also not that easy also because we want
203  // to permute all the added slack first.
204  RowToColMapping ComputeInitialBasis(const std::vector<ColIndex>& candidates);
205 
206  // Return the number of rows in the basis.
207  RowIndex GetNumberOfRows() const { return compact_matrix_.num_rows(); }
208 
209  // Clears eta factorization and refactorizes LU.
210  // Nothing happens if this is called on an already refactorized basis.
211  // Returns an error if the matrix could not be factorized: i.e. not a basis.
212  ABSL_MUST_USE_RESULT Status Refactorize();
213 
214  // Like Refactorize(), but do it even if IsRefactorized() is true.
215  // Call this if the underlying basis_ changed and Update() wasn't called.
216  ABSL_MUST_USE_RESULT Status ForceRefactorization();
217 
218  // Returns true if the factorization was just recomputed.
219  bool IsRefactorized() const;
220 
221  // Updates the factorization. The 'eta' column will be modified with a swap to
222  // avoid a copy (only if the standard eta update is used). Returns an error if
223  // the matrix could not be factorized: i.e. not a basis.
224  ABSL_MUST_USE_RESULT Status Update(ColIndex entering_col,
225  RowIndex leaving_variable_row,
226  const ScatteredColumn& direction);
227 
228  // Left solves the system y.B = rhs, where y initially contains rhs.
229  void LeftSolve(ScatteredRow* y) const;
230 
231  // Left solves the system y.B = e_j, where e_j has only 1 non-zero
232  // coefficient of value 1.0 at position 'j'.
233  void LeftSolveForUnitRow(ColIndex j, ScatteredRow* y) const;
234 
235  // Same as LeftSolveForUnitRow() but does not update any internal data.
236  void TemporaryLeftSolveForUnitRow(ColIndex j, ScatteredRow* y) const;
237 
238  // Right solves the system B.d = a where the input is the initial value of d.
239  void RightSolve(ScatteredColumn* d) const;
240 
241  // Same as RightSolve() for matrix.column(col). This also exploits its
242  // sparsity.
243  void RightSolveForProblemColumn(ColIndex col, ScatteredColumn* d) const;
244 
245  // Specialized version for ComputeTau() in DualEdgeNorms. This reuses an
246  // intermediate result of the last LeftSolveForUnitRow() in order to save a
247  // permutation if it is available. Note that the input 'a' should always be
248  // equal to the last result of LeftSolveForUnitRow() and will be used for a
249  // DCHECK() or if the intermediate result wasn't kept.
250  const DenseColumn& RightSolveForTau(const ScatteredColumn& a) const;
251 
252  // Returns the norm of B^{-1}.a, this is a specific function because
253  // it is a bit faster and it avoids polluting the stats of RightSolve().
254  // It can be called only when IsRefactorized() is true.
256 
257  // Returns the norm of (B^T)^{-1}.e_row where e is an unit vector.
258  // This is a bit faster and avoids polluting the stats of LeftSolve().
259  // It can be called only when IsRefactorized() is true.
260  Fractional DualEdgeSquaredNorm(RowIndex row) const;
261 
262  // Computes the condition number of B.
263  // For a given norm, this is the matrix norm times the norm of its inverse.
264  // A condition number greater than 1E7 will lead to precision problems.
268 
269  // Computes the 1-norm of B.
270  // The 1-norm |A| is defined as max_j sum_i |a_ij|
271  // http://en.wikipedia.org/wiki/Matrix_norm
272  Fractional ComputeOneNorm() const;
273 
274  // Computes the infinity-norm of B.
275  // The infinity-norm |A| is defined as max_i sum_j |a_ij|
276  // http://en.wikipedia.org/wiki/Matrix_norm
278 
279  // Computes the 1-norm of the inverse of B.
280  // For this we iteratively solve B.x = e_j, where e_j is the jth unit vector.
281  // The result of this computation is the jth column of B^-1.
283 
284  // Computes the infinity-norm of the inverse of B.
286 
287  // Stats related function.
288  // Note that ResetStats() could be const, but until needed it is not to
289  // prevent anyone holding a const BasisFactorization& to call it.
290  std::string StatString() const {
291  return stats_.StatString() + lu_factorization_.StatString();
292  }
293  void ResetStats() { stats_.Reset(); }
294 
295  // The deterministic time used by this class. It is incremented for each
296  // solve and each factorization.
297  double DeterministicTime() const;
298 
299  // Returns the number of updates since last refactorization.
300  int NumUpdates() const { return num_updates_; }
301 
302  private:
303  // Called by ForceRefactorization() or Refactorize() or Initialize().
304  Status ComputeFactorization();
305 
306  // Return true if the submatrix of matrix_ given by basis_ is exactly the
307  // identity (without permutation).
308  bool IsIdentityBasis() const;
309 
310  // Updates the factorization using the middle product form update.
311  // Qi Huangfu, J. A. Julian Hall, "Novel update techniques for the revised
312  // simplex method", 28 january 2013, Technical Report ERGO-13-0001
313  ABSL_MUST_USE_RESULT Status
314  MiddleProductFormUpdate(ColIndex entering_col, RowIndex leaving_variable_row);
315 
316  // Increases the deterministic time for a solve operation with a vector having
317  // this number of non-zero entries (it can be an approximation).
318  void BumpDeterministicTimeForSolve(int num_entries) const;
319 
320  // Stats about this class.
321  struct Stats : public StatsGroup {
322  Stats()
323  : StatsGroup("BasisFactorization"),
324  refactorization_interval("refactorization_interval", this) {}
325  IntegerDistribution refactorization_interval;
326  };
327 
328  // Mutable because we track the running time of const method like
329  // RightSolve() and LeftSolve().
330  mutable Stats stats_;
331  GlopParameters parameters_;
332 
333  // References to the basis subpart of the linear program matrix.
334  const CompactSparseMatrix& compact_matrix_;
335  const RowToColMapping& basis_;
336 
337  // Middle form product update factorization and scratchpad_ used to construct
338  // new rank one matrices.
339  RankOneUpdateFactorization rank_one_factorization_;
340  mutable DenseColumn scratchpad_;
341  mutable std::vector<RowIndex> scratchpad_non_zeros_;
342 
343  // This is used by RightSolveForTau(). It holds an intermediate result from
344  // the last LeftSolveForUnitRow() and also the final result of
345  // RightSolveForTau().
346  mutable ScatteredColumn tau_;
347 
348  // Booleans controlling the interaction between LeftSolveForUnitRow() that may
349  // or may not keep its intermediate results for the optimized
350  // RightSolveForTau().
351  //
352  // tau_computation_can_be_optimized_ will be true iff LeftSolveForUnitRow()
353  // kept its intermediate result when it was called and the factorization
354  // didn't change since then. If it is true, then RightSolveForTau() can use
355  // this result for a faster computation.
356  //
357  // tau_is_computed_ is used as an heuristic by LeftSolveForUnitRow() to decide
358  // if it is worth keeping its intermediate result (which is sligthly slower).
359  // It is simply set to true by RightSolveForTau() and to false by
360  // LeftSolveForUnitRow(), this way the optimization will automatically switch
361  // itself on when switching from the primal simplex (where RightSolveForTau()
362  // is never called) to the dual where it is called after each
363  // LeftSolveForUnitRow(), and back off again in the other direction.
364  mutable bool tau_computation_can_be_optimized_;
365  mutable bool tau_is_computed_;
366 
367  // Data structure to store partial solve results for the middle form product
368  // update. See LeftSolveForUnitRow() and RightSolveForProblemColumn(). We use
369  // two CompactSparseMatrix to have a better cache behavior when solving with
370  // the rank_one_factorization_.
371  mutable CompactSparseMatrix storage_;
372  mutable CompactSparseMatrix right_storage_;
373  mutable ColMapping left_pool_mapping_;
374  mutable ColMapping right_pool_mapping_;
375 
376  bool use_middle_product_form_update_;
377  int max_num_updates_;
378  int num_updates_;
379  EtaFactorization eta_factorization_;
380  LuFactorization lu_factorization_;
381 
382  // mutable because the Solve() functions are const but need to update this.
383  double last_factorization_deterministic_time_ = 0.0;
384  mutable double deterministic_time_;
385 
386  DISALLOW_COPY_AND_ASSIGN(BasisFactorization);
387 };
388 
389 } // namespace glop
390 } // namespace operations_research
391 
392 #endif // OR_TOOLS_GLOP_BASIS_REPRESENTATION_H_
BasisFactorization(const CompactSparseMatrix *compact_matrix, const RowToColMapping *basis)
const DenseColumn & RightSolveForTau(const ScatteredColumn &a) const
void LeftSolveForUnitRow(ColIndex j, ScatteredRow *y) const
Fractional RightSolveSquaredNorm(const ColumnView &a) const
const ColumnPermutation & GetColumnPermutation() const
void TemporaryLeftSolveForUnitRow(ColIndex j, ScatteredRow *y) const
ABSL_MUST_USE_RESULT Status Update(ColIndex entering_col, RowIndex leaving_variable_row, const ScatteredColumn &direction)
Fractional DualEdgeSquaredNorm(RowIndex row) const
RowToColMapping ComputeInitialBasis(const std::vector< ColIndex > &candidates)
void RightSolveForProblemColumn(ColIndex col, ScatteredColumn *d) const
void SetParameters(const GlopParameters &parameters)
void SparseLeftSolve(DenseRow *y, ColIndexVector *pos) const
void Update(ColIndex entering_col, RowIndex leaving_variable_row, const ScatteredColumn &direction)
EtaMatrix(ColIndex eta_col, const ScatteredColumn &direction)
void SparseLeftSolve(DenseRow *y, ColIndexVector *pos) const
const ColumnPermutation & GetColumnPermutation() const
void SetParameters(const GlopParameters &parameters)
int64_t a
SatParameters parameters
ColIndex col
Definition: markowitz.cc:186
RowIndex row
Definition: markowitz.cc:185
std::vector< ColIndex > ColIndexVector
Definition: lp_types.h:350
StrictITIVector< RowIndex, ColIndex > RowToColMapping
Definition: lp_types.h:384
StrictITIVector< RowIndex, Fractional > DenseColumn
Definition: lp_types.h:370
StrictITIVector< ColIndex, ColIndex > ColMapping
Definition: lp_types.h:347
Collection of objects used to extend the Constraint Solver library.
EntryIndex num_entries