OR-Tools  9.2
preprocessor.h
Go to the documentation of this file.
1 // Copyright 2010-2021 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 //
15 // This file contains the presolving code for a LinearProgram.
16 //
17 // A classical reference is:
18 // E. D. Andersen, K. D. Andersen, "Presolving in linear programming.",
19 // Mathematical Programming 71 (1995) 221-245.
20 
21 #ifndef OR_TOOLS_GLOP_PREPROCESSOR_H_
22 #define OR_TOOLS_GLOP_PREPROCESSOR_H_
23 
24 #include <memory>
25 
32 
33 namespace operations_research {
34 namespace glop {
35 
36 // --------------------------------------------------------
37 // Preprocessor
38 // --------------------------------------------------------
39 // This is the base class for preprocessors.
40 //
41 // TODO(user): On most preprocessors, calling Run() more than once will not work
42 // as expected. Fix? or document and crash in debug if this happens.
43 class Preprocessor {
44  public:
45  explicit Preprocessor(const GlopParameters* parameters);
46  Preprocessor(const Preprocessor&) = delete;
47  Preprocessor& operator=(const Preprocessor&) = delete;
48  virtual ~Preprocessor();
49 
50  // Runs the preprocessor by modifying the given linear program. Returns true
51  // if a postsolve step will be needed (i.e. RecoverSolution() is not the
52  // identity function). Also updates status_ to something different from
53  // ProblemStatus::INIT if the problem was solved (including bad statuses
54  // like ProblemStatus::ABNORMAL, ProblemStatus::INFEASIBLE, etc.).
55  virtual bool Run(LinearProgram* lp) = 0;
56 
57  // Stores the optimal solution of the linear program that was passed to
58  // Run(). The given solution needs to be set to the optimal solution of the
59  // linear program "modified" by Run().
60  virtual void RecoverSolution(ProblemSolution* solution) const = 0;
61 
62  // Returns the status of the preprocessor.
63  // A status different from ProblemStatus::INIT means that the problem is
64  // solved and there is not need to call subsequent preprocessors.
65  ProblemStatus status() const { return status_; }
66 
67  // Some preprocessors only need minimal changes when used with integer
68  // variables in a MIP context. Setting this to true allows to consider integer
69  // variables as integer in these preprocessors.
70  //
71  // Not all preprocessors handle integer variables correctly, calling this
72  // function on them will cause a LOG(FATAL).
73  virtual void UseInMipContext() { in_mip_context_ = true; }
74 
76 
77  protected:
78  // Returns true if a is less than b (or slighlty greater than b with a given
79  // tolerance).
83  }
85  Fractional b) const {
86  // TODO(user): use an absolute tolerance here to be even more defensive?
89  }
90 
94  std::unique_ptr<TimeLimit> infinite_time_limit_;
96 };
97 
98 // --------------------------------------------------------
99 // MainLpPreprocessor
100 // --------------------------------------------------------
101 // This is the main LP preprocessor responsible for calling all the other
102 // preprocessors in this file, possibly more than once.
104  public:
107  MainLpPreprocessor(const MainLpPreprocessor&) = delete;
109  ~MainLpPreprocessor() override {}
110 
111  bool Run(LinearProgram* lp) final;
112  void RecoverSolution(ProblemSolution* solution) const override;
113 
114  // Like RecoverSolution but destroys data structures as it goes to reduce peak
115  // RAM use. After calling this the MainLpPreprocessor object may no longer be
116  // used.
118 
119  void SetLogger(SolverLogger* logger) { logger_ = logger; }
120 
121  private:
122  // Runs the given preprocessor and push it on preprocessors_ for the postsolve
123  // step when needed.
124  void RunAndPushIfRelevant(std::unique_ptr<Preprocessor> preprocessor,
125  const std::string& name, TimeLimit* time_limit,
126  LinearProgram* lp);
127 
128  // Stack of preprocessors currently applied to the lp that needs postsolve.
129  std::vector<std::unique_ptr<Preprocessor>> preprocessors_;
130 
131  // Helpers for logging during presolve.
132  SolverLogger default_logger_;
133  SolverLogger* logger_ = &default_logger_;
134 
135  // Initial dimension of the lp given to Run(), for displaying purpose.
136  EntryIndex initial_num_entries_;
137  RowIndex initial_num_rows_;
138  ColIndex initial_num_cols_;
139 };
140 
141 // --------------------------------------------------------
142 // ColumnDeletionHelper
143 // --------------------------------------------------------
144 
145 // Some preprocessors need to save columns/rows of the matrix for the postsolve.
146 // This class helps them do that.
147 //
148 // Note that we used to simply use a SparseMatrix, which is like a vector of
149 // SparseColumn. However on large problem with 10+ millions columns, each empty
150 // SparseColumn take 48 bytes, so if we run like 10 presolve step that save as
151 // little as 1 columns, we already are at 4GB memory for nothing!
153  public:
154  // Saves a column. The first version CHECKs that it is not already done.
155  void SaveColumn(ColIndex col, const SparseColumn& column);
156  void SaveColumnIfNotAlreadyDone(ColIndex col, const SparseColumn& column);
157 
158  // Returns the saved column. The first version CHECKs that it was saved.
159  const SparseColumn& SavedColumn(ColIndex col) const;
160  const SparseColumn& SavedOrEmptyColumn(ColIndex col) const;
161 
162  private:
163  SparseColumn empty_column_;
164  absl::flat_hash_map<ColIndex, int> saved_columns_index_;
165 
166  // TODO(user): We could optimize further since all these are read only, we
167  // could use a CompactSparseMatrix instead.
168  std::deque<SparseColumn> saved_columns_;
169 };
170 
171 // Help preprocessors deal with column deletion.
173  public:
177 
178  // Remember the given column as "deleted" so that it can later be restored
179  // by RestoreDeletedColumns(). Optionally, the caller may indicate the
180  // value and status of the corresponding variable so that it is automatically
181  // restored; if they don't then the restored value and status will be junk
182  // and must be set by the caller.
183  //
184  // The actual deletion is done by LinearProgram::DeleteColumns().
185  void MarkColumnForDeletion(ColIndex col);
187  VariableStatus status);
188 
189  // From a solution omitting the deleted column, expands it and inserts the
190  // deleted columns. If values and statuses for the corresponding variables
191  // were saved, they'll be restored.
192  void RestoreDeletedColumns(ProblemSolution* solution) const;
193 
194  // Returns whether or not the given column is marked for deletion.
195  bool IsColumnMarked(ColIndex col) const {
196  return col < is_column_deleted_.size() && is_column_deleted_[col];
197  }
198 
199  // Returns a Boolean vector of the column to be deleted.
200  const DenseBooleanRow& GetMarkedColumns() const { return is_column_deleted_; }
201 
202  // Returns true if no columns have been marked for deletion.
203  bool IsEmpty() const { return is_column_deleted_.empty(); }
204 
205  // Restores the class to its initial state.
206  void Clear();
207 
208  // Returns the value that will be restored by
209  // RestoreDeletedColumnInSolution(). Note that only the marked position value
210  // make sense.
211  const DenseRow& GetStoredValue() const { return stored_value_; }
212 
213  private:
214  DenseBooleanRow is_column_deleted_;
215 
216  // Note that this vector has the same size as is_column_deleted_ and that
217  // the value of the variable corresponding to a deleted column col is stored
218  // at position col. Values of columns not deleted are not used. We use this
219  // data structure so columns can be deleted in any order if needed.
220  DenseRow stored_value_;
221  VariableStatusRow stored_status_;
222 };
223 
224 // --------------------------------------------------------
225 // RowDeletionHelper
226 // --------------------------------------------------------
227 // Help preprocessors deal with row deletion.
229  public:
231  RowDeletionHelper(const RowDeletionHelper&) = delete;
233 
234  // Returns true if no rows have been marked for deletion.
235  bool IsEmpty() const { return is_row_deleted_.empty(); }
236 
237  // Restores the class to its initial state.
238  void Clear();
239 
240  // Adds a deleted row to the helper.
241  void MarkRowForDeletion(RowIndex row);
242 
243  // If the given row was marked for deletion, unmark it.
244  void UnmarkRow(RowIndex row);
245 
246  // Returns a Boolean vector of the row to be deleted.
247  const DenseBooleanColumn& GetMarkedRows() const;
248 
249  // Returns whether or not the given row is marked for deletion.
250  bool IsRowMarked(RowIndex row) const {
251  return row < is_row_deleted_.size() && is_row_deleted_[row];
252  }
253 
254  // From a solution without the deleted rows, expand it by restoring
255  // the deleted rows to a VariableStatus::BASIC status with 0.0 value.
256  // This latter value is important, many preprocessors rely on it.
257  void RestoreDeletedRows(ProblemSolution* solution) const;
258 
259  private:
260  DenseBooleanColumn is_row_deleted_;
261 };
262 
263 // --------------------------------------------------------
264 // EmptyColumnPreprocessor
265 // --------------------------------------------------------
266 // Removes the empty columns from the problem.
268  public:
274  bool Run(LinearProgram* lp) final;
275  void RecoverSolution(ProblemSolution* solution) const final;
276 
277  private:
278  ColumnDeletionHelper column_deletion_helper_;
279 };
280 
281 // --------------------------------------------------------
282 // ProportionalColumnPreprocessor
283 // --------------------------------------------------------
284 // Removes the proportional columns from the problem when possible. Two columns
285 // are proportional if one is a non-zero scalar multiple of the other.
286 //
287 // Note that in the linear programming literature, two proportional columns are
288 // usually called duplicates. The notion is the same once the problem has been
289 // scaled. However, during presolve the columns can't be assumed to be scaled,
290 // so it makes sense to use the more general notion of proportional columns.
292  public:
296  delete;
298  const ProportionalColumnPreprocessor&) = delete;
300  bool Run(LinearProgram* lp) final;
301  void RecoverSolution(ProblemSolution* solution) const final;
302  void UseInMipContext() final { LOG(FATAL) << "Not implemented."; }
303 
304  private:
305  // Postsolve information about proportional columns with the same scaled cost
306  // that were merged during presolve.
307 
308  // The proportionality factor of each column. If two columns are proportional
309  // with factor p1 and p2 then p1 times the first column is the same as p2
310  // times the second column.
311  DenseRow column_factors_;
312 
313  // If merged_columns_[col] != kInvalidCol, then column col has been merged
314  // into the column merged_columns_[col].
315  ColMapping merged_columns_;
316 
317  // The old and new variable bounds.
318  DenseRow lower_bounds_;
319  DenseRow upper_bounds_;
320  DenseRow new_lower_bounds_;
321  DenseRow new_upper_bounds_;
322 
323  ColumnDeletionHelper column_deletion_helper_;
324 };
325 
326 // --------------------------------------------------------
327 // ProportionalRowPreprocessor
328 // --------------------------------------------------------
329 // Removes the proportional rows from the problem.
330 // The linear programming literature also calls such rows duplicates, see the
331 // same remark above for columns in ProportionalColumnPreprocessor.
333  public:
338  delete;
340  bool Run(LinearProgram* lp) final;
341  void RecoverSolution(ProblemSolution* solution) const final;
342 
343  private:
344  // Informations about proportional rows, only filled for such rows.
345  DenseColumn row_factors_;
346  RowMapping upper_bound_sources_;
347  RowMapping lower_bound_sources_;
348 
349  bool lp_is_maximization_problem_;
350  RowDeletionHelper row_deletion_helper_;
351 };
352 
353 // --------------------------------------------------------
354 // SingletonPreprocessor
355 // --------------------------------------------------------
356 // Removes as many singleton rows and singleton columns as possible from the
357 // problem. Note that not all types of singleton columns can be removed. See the
358 // comments below on the SingletonPreprocessor functions for more details.
359 //
360 // TODO(user): Generalize the design used in this preprocessor to a general
361 // "propagation" framework in order to apply as many reductions as possible in
362 // an efficient manner.
363 
364 // Holds a triplet (row, col, coefficient).
365 struct MatrixEntry {
366  MatrixEntry(RowIndex _row, ColIndex _col, Fractional _coeff)
367  : row(_row), col(_col), coeff(_coeff) {}
368  RowIndex row;
369  ColIndex col;
371 };
372 
373 // Stores the information needed to undo a singleton row/column deletion.
375  public:
376  // The type of a given operation.
377  typedef enum {
382  } OperationType;
383 
384  // Stores the information, which together with the field deleted_columns_ and
385  // deleted_rows_ of SingletonPreprocessor, are needed to undo an operation
386  // with the given type. Note that all the arguments must refer to the linear
387  // program BEFORE the operation is applied.
388  SingletonUndo(OperationType type, const LinearProgram& lp, MatrixEntry e,
389  ConstraintStatus status);
390 
391  // Undo the operation saved in this class, taking into account the saved
392  // column and row (at the row/col given by Entry()) passed by the calling
393  // instance of SingletonPreprocessor. Note that the operations must be undone
394  // in the reverse order of the one in which they were applied.
395  void Undo(const GlopParameters& parameters, const SparseColumn& saved_column,
396  const SparseColumn& saved_row, ProblemSolution* solution) const;
397 
398  const MatrixEntry& Entry() const { return e_; }
399 
400  private:
401  // Actual undo functions for each OperationType.
402  // Undo() just calls the correct one.
403  void SingletonRowUndo(const SparseColumn& saved_column,
404  ProblemSolution* solution) const;
405  void ZeroCostSingletonColumnUndo(const GlopParameters& parameters,
406  const SparseColumn& saved_row,
407  ProblemSolution* solution) const;
408  void SingletonColumnInEqualityUndo(const GlopParameters& parameters,
409  const SparseColumn& saved_row,
410  ProblemSolution* solution) const;
411  void MakeConstraintAnEqualityUndo(ProblemSolution* solution) const;
412 
413  // All the information needed during undo.
414  OperationType type_;
415  bool is_maximization_;
416  MatrixEntry e_;
417  Fractional cost_;
418 
419  // TODO(user): regroup the pair (lower bound, upper bound) in a bound class?
420  Fractional variable_lower_bound_;
421  Fractional variable_upper_bound_;
422  Fractional constraint_lower_bound_;
423  Fractional constraint_upper_bound_;
424 
425  // This in only used with MAKE_CONSTRAINT_AN_EQUALITY undo.
426  // TODO(user): Clean that up using many Undo classes and virtual functions.
427  ConstraintStatus constraint_status_;
428 };
429 
430 // Deletes as many singleton rows or singleton columns as possible. Note that
431 // each time we delete a row or a column, new singletons may be created.
433  public:
439  bool Run(LinearProgram* lp) final;
440  void RecoverSolution(ProblemSolution* solution) const final;
441 
442  private:
443  // Returns the MatrixEntry of the given singleton row or column, taking into
444  // account the rows and columns that were already deleted.
445  MatrixEntry GetSingletonColumnMatrixEntry(ColIndex col,
446  const SparseMatrix& matrix);
447  MatrixEntry GetSingletonRowMatrixEntry(RowIndex row,
448  const SparseMatrix& matrix_transpose);
449 
450  // A singleton row can always be removed by changing the corresponding
451  // variable bounds to take into account the bounds on this singleton row.
452  void DeleteSingletonRow(MatrixEntry e, LinearProgram* lp);
453 
454  // Internal operation when removing a zero-cost singleton column corresponding
455  // to the given entry. This modifies the constraint bounds to take into acount
456  // the bounds of the corresponding variable.
457  void UpdateConstraintBoundsWithVariableBounds(MatrixEntry e,
458  LinearProgram* lp);
459 
460  // Checks if all other variables in the constraint are integer and the
461  // coefficients are divisible by the coefficient of the singleton variable.
462  bool IntegerSingletonColumnIsRemovable(const MatrixEntry& matrix_entry,
463  const LinearProgram& lp) const;
464 
465  // A singleton column with a cost of zero can always be removed by changing
466  // the corresponding constraint bounds to take into acount the bound of this
467  // singleton column.
468  void DeleteZeroCostSingletonColumn(const SparseMatrix& matrix_transpose,
469  MatrixEntry e, LinearProgram* lp);
470 
471  // Returns true if the constraint associated to the given singleton column was
472  // an equality or could be made one:
473  // If a singleton variable is free in a direction that improves the cost, then
474  // we can always move it as much as possible in this direction. Only the
475  // constraint will stop us, making it an equality. If the constraint doesn't
476  // stop us, then the program is unbounded (provided that there is a feasible
477  // solution).
478  //
479  // Note that this operation does not need any "undo" during the post-solve. At
480  // optimality, the dual value on the constraint row will be of the correct
481  // sign, and relaxing the constraint bound will not impact the dual
482  // feasibility of the solution.
483  //
484  // TODO(user): this operation can be generalized to columns with just one
485  // blocking constraint. Investigate how to use this. The 'reverse' can
486  // probably also be done, relaxing a constraint that is blocking a
487  // unconstrained variable.
488  bool MakeConstraintAnEqualityIfPossible(const SparseMatrix& matrix_transpose,
489  MatrixEntry e, LinearProgram* lp);
490 
491  // If a singleton column appears in an equality, we can remove its cost by
492  // changing the other variables cost using the constraint. We can then delete
493  // the column like in DeleteZeroCostSingletonColumn().
494  void DeleteSingletonColumnInEquality(const SparseMatrix& matrix_transpose,
495  MatrixEntry e, LinearProgram* lp);
496 
497  ColumnDeletionHelper column_deletion_helper_;
498  RowDeletionHelper row_deletion_helper_;
499  std::vector<SingletonUndo> undo_stack_;
500 
501  // This is used as a "cache" by MakeConstraintAnEqualityIfPossible() to avoid
502  // scanning more than once each row. See the code to see how this is used.
503  absl::StrongVector<RowIndex, bool> row_sum_is_cached_;
505  row_lb_sum_;
507  row_ub_sum_;
508 
509  // TODO(user): It is annoying that we need to store a part of the matrix that
510  // is not deleted here. This extra memory usage might show the limit of our
511  // presolve architecture that does not require a new matrix factorization on
512  // the original problem to reconstruct the solution.
513  ColumnsSaver columns_saver_;
514  ColumnsSaver rows_saver_;
515 };
516 
517 // --------------------------------------------------------
518 // FixedVariablePreprocessor
519 // --------------------------------------------------------
520 // Removes the fixed variables from the problem.
522  public:
527  delete;
529  bool Run(LinearProgram* lp) final;
530  void RecoverSolution(ProblemSolution* solution) const final;
531 
532  private:
533  ColumnDeletionHelper column_deletion_helper_;
534 };
535 
536 // --------------------------------------------------------
537 // ForcingAndImpliedFreeConstraintPreprocessor
538 // --------------------------------------------------------
539 // This preprocessor computes for each constraint row the bounds that are
540 // implied by the variable bounds and applies one of the following reductions:
541 //
542 // * If the intersection of the implied bounds and the current constraint bounds
543 // is empty (modulo some tolerance), the problem is INFEASIBLE.
544 //
545 // * If the intersection of the implied bounds and the current constraint bounds
546 // is a singleton (modulo some tolerance), then the constraint is said to be
547 // forcing and all the variables that appear in it can be fixed to one of their
548 // bounds. All these columns and the constraint row is removed.
549 //
550 // * If the implied bounds are included inside the current constraint bounds
551 // (modulo some tolerance) then the constraint is said to be redundant or
552 // implied free. Its bounds are relaxed and the constraint will be removed
553 // later by the FreeConstraintPreprocessor.
554 //
555 // * Otherwise, wo do nothing.
557  public:
559  const GlopParameters* parameters)
566  bool Run(LinearProgram* lp) final;
567  void RecoverSolution(ProblemSolution* solution) const final;
568 
569  private:
570  bool lp_is_maximization_problem_;
571  DenseRow costs_;
572  DenseBooleanColumn is_forcing_up_;
573  ColumnDeletionHelper column_deletion_helper_;
574  RowDeletionHelper row_deletion_helper_;
575  ColumnsSaver columns_saver_;
576 };
577 
578 // --------------------------------------------------------
579 // ImpliedFreePreprocessor
580 // --------------------------------------------------------
581 // It is possible to compute "implied" bounds on a variable from the bounds of
582 // all the other variables and the constraints in which this variable take
583 // place. If such "implied" bounds are inside the variable bounds, then the
584 // variable bounds can be relaxed and the variable is said to be "implied free".
585 //
586 // This preprocessor detects the implied free variables and make as many as
587 // possible free with a priority towards low-degree columns. This transformation
588 // will make the simplex algorithm more efficient later, but will also make it
589 // possible to reduce the problem by applying subsequent transformations:
590 //
591 // * The SingletonPreprocessor already deals with implied free singleton
592 // variables and removes the columns and the rows in which they appear.
593 //
594 // * Any multiple of the column of a free variable can be added to any other
595 // column without changing the linear program solution. This is the dual
596 // counterpart of the fact that any multiple of an equality row can be added to
597 // any row.
598 //
599 // TODO(user): Only process doubleton columns so we have more chance in the
600 // later passes to create more doubleton columns? Such columns lead to a smaller
601 // problem thanks to the DoubletonFreeColumnPreprocessor.
603  public:
609  bool Run(LinearProgram* lp) final;
610  void RecoverSolution(ProblemSolution* solution) const final;
611 
612  private:
613  // This preprocessor adds fixed offsets to some variables. We remember those
614  // here to un-offset them in RecoverSolution().
615  DenseRow variable_offsets_;
616 
617  // This preprocessor causes some variables who would normally be
618  // AT_{LOWER,UPPER}_BOUND to be VariableStatus::FREE. We store the restore
619  // value of these variables; which will only be used (eg. restored) if the
620  // variable actually turns out to be VariableStatus::FREE.
621  VariableStatusRow postsolve_status_of_free_variables_;
622 };
623 
624 // --------------------------------------------------------
625 // DoubletonFreeColumnPreprocessor
626 // --------------------------------------------------------
627 // This preprocessor removes one of the two rows in which a doubleton column of
628 // a free variable appears. Since we can add any multiple of such a column to
629 // any other column, the way this works is that we can always remove all the
630 // entries on one row.
631 //
632 // Actually, we can remove all the entries except the one of the free column.
633 // But we will be left with a singleton row that we can delete in the same way
634 // as what is done in SingletonPreprocessor. That is by reporting the constraint
635 // bounds into the one of the originally free variable. After this operation,
636 // the doubleton free column will become a singleton and may or may not be
637 // removed later by the SingletonPreprocessor.
638 //
639 // Note that this preprocessor can be seen as the dual of the
640 // DoubletonEqualityRowPreprocessor since when taking the dual, an equality row
641 // becomes a free variable and vice versa.
642 //
643 // Note(user): As far as I know, this doubleton free column procedure is more
644 // general than what can be found in the research papers or in any of the linear
645 // solver open source codes as of July 2013. All of them only process such
646 // columns if one of the two rows is also an equality which is not actually
647 // required. Most probably, commercial solvers do use it though.
649  public:
653  delete;
655  const DoubletonFreeColumnPreprocessor&) = delete;
657  bool Run(LinearProgram* lp) final;
658  void RecoverSolution(ProblemSolution* solution) const final;
659 
660  private:
661  enum RowChoice {
662  DELETED = 0,
663  MODIFIED = 1,
664  // This is just a constant for the number of rows in a doubleton column.
665  // That is 2, one will be DELETED, the other MODIFIED.
666  NUM_ROWS = 2,
667  };
668  struct RestoreInfo {
669  // The index of the original free doubleton column and its objective.
670  ColIndex col;
671  Fractional objective_coefficient;
672 
673  // The row indices of the two involved rows and their coefficients on
674  // column col.
675  RowIndex row[NUM_ROWS];
676  Fractional coeff[NUM_ROWS];
677 
678  // The deleted row as a column.
679  SparseColumn deleted_row_as_column;
680  };
681 
682  std::vector<RestoreInfo> restore_stack_;
683  RowDeletionHelper row_deletion_helper_;
684 };
685 
686 // --------------------------------------------------------
687 // UnconstrainedVariablePreprocessor
688 // --------------------------------------------------------
689 // If for a given variable, none of the constraints block it in one direction
690 // and this direction improves the objective, then this variable can be fixed to
691 // its bound in this direction. If this bound is infinite and the variable cost
692 // is non-zero, then the problem is unbounded.
693 //
694 // More generally, by using the constraints and the variables that are unbounded
695 // on one side, one can derive bounds on the dual values. These can be
696 // translated into bounds on the reduced costs or the columns, which may force
697 // variables to their bounds. This is called forcing and dominated columns in
698 // the Andersen & Andersen paper.
700  public:
704  delete;
706  const UnconstrainedVariablePreprocessor&) = delete;
708  bool Run(LinearProgram* lp) final;
709  void RecoverSolution(ProblemSolution* solution) const final;
710 
711  // Removes the given variable and all the rows in which it appears: If a
712  // variable is unconstrained with a zero cost, then all the constraints in
713  // which it appears can be made free! More precisely, during postsolve, if
714  // such a variable is unconstrained towards +kInfinity, for any activity value
715  // of the involved constraints, an M exists such that for each value of the
716  // variable >= M the problem will be feasible.
717  //
718  // The algorithm during postsolve is to find a feasible value for all such
719  // variables while trying to keep their magnitudes small (for better numerical
720  // behavior). target_bound should take only two possible values: +/-kInfinity.
723  LinearProgram* lp);
724 
725  private:
726  // Lower/upper bounds on the feasible dual value. We use constraints and
727  // variables unbounded in one direction to derive these bounds. We use these
728  // bounds to compute bounds on the reduced costs of the problem variables.
729  // Note that any finite bounds on a reduced cost means that the variable
730  // (ignoring its domain) can move freely in one direction.
731  DenseColumn dual_lb_;
732  DenseColumn dual_ub_;
733 
734  // Indicates if a given column may have participated in the current lb/ub
735  // on the reduced cost of the same column.
736  DenseBooleanRow may_have_participated_ub_;
737  DenseBooleanRow may_have_participated_lb_;
738 
739  ColumnDeletionHelper column_deletion_helper_;
740  RowDeletionHelper row_deletion_helper_;
741  ColumnsSaver rows_saver_;
742  DenseColumn rhs_;
743  DenseColumn activity_sign_correction_;
744  DenseBooleanRow is_unbounded_;
745 };
746 
747 // --------------------------------------------------------
748 // FreeConstraintPreprocessor
749 // --------------------------------------------------------
750 // Removes the constraints with no bounds from the problem.
752  public:
757  delete;
759  bool Run(LinearProgram* lp) final;
760  void RecoverSolution(ProblemSolution* solution) const final;
761 
762  private:
763  RowDeletionHelper row_deletion_helper_;
764 };
765 
766 // --------------------------------------------------------
767 // EmptyConstraintPreprocessor
768 // --------------------------------------------------------
769 // Removes the constraints with no coefficients from the problem.
771  public:
776  delete;
778  bool Run(LinearProgram* lp) final;
779  void RecoverSolution(ProblemSolution* solution) const final;
780 
781  private:
782  RowDeletionHelper row_deletion_helper_;
783 };
784 
785 // --------------------------------------------------------
786 // RemoveNearZeroEntriesPreprocessor
787 // --------------------------------------------------------
788 // Removes matrix entries that have only a negligible impact on the solution.
789 // Using the variable bounds, we derive a maximum possible impact, and remove
790 // the entries whose impact is under a given tolerance.
791 //
792 // TODO(user): This preprocessor doesn't work well on badly scaled problems. In
793 // particular, it will set the objective to zero if all the objective
794 // coefficients are small! Run it after ScalingPreprocessor or fix the code.
796  public:
800  delete;
802  const RemoveNearZeroEntriesPreprocessor&) = delete;
804  bool Run(LinearProgram* lp) final;
805  void RecoverSolution(ProblemSolution* solution) const final;
806 
807  private:
808 };
809 
810 // --------------------------------------------------------
811 // SingletonColumnSignPreprocessor
812 // --------------------------------------------------------
813 // Make sure that the only coefficient of all singleton columns (i.e. column
814 // with only one entry) is positive. This is because this way the column will
815 // be transformed in an identity column by the scaling. This will lead to more
816 // efficient solve when this column is involved.
818  public:
822  delete;
824  const SingletonColumnSignPreprocessor&) = delete;
826  bool Run(LinearProgram* lp) final;
827  void RecoverSolution(ProblemSolution* solution) const final;
828 
829  private:
830  std::vector<ColIndex> changed_columns_;
831 };
832 
833 // --------------------------------------------------------
834 // DoubletonEqualityRowPreprocessor
835 // --------------------------------------------------------
836 // Reduce equality constraints involving two variables (i.e. aX + bY = c),
837 // by substitution (and thus removal) of one of the variables by the other
838 // in all the constraints that it is involved in.
840  public:
844  delete;
846  const DoubletonEqualityRowPreprocessor&) = delete;
848  bool Run(LinearProgram* lp) final;
849  void RecoverSolution(ProblemSolution* solution) const final;
850 
851  private:
852  enum ColChoice {
853  DELETED = 0,
854  MODIFIED = 1,
855  // For for() loops iterating over the ColChoice values, and/or arrays.
856  NUM_DOUBLETON_COLS = 2,
857  };
858  static ColChoice OtherColChoice(ColChoice x) {
859  return x == DELETED ? MODIFIED : DELETED;
860  }
861 
862  ColumnDeletionHelper column_deletion_helper_;
863  RowDeletionHelper row_deletion_helper_;
864 
865  struct RestoreInfo {
866  // The row index of the doubleton equality constraint, and its constant.
867  RowIndex row;
868  Fractional rhs; // The constant c in the equality aX + bY = c.
869 
870  // The indices and the data of the two columns that we touched, exactly
871  // as they were beforehand.
872  ColIndex col[NUM_DOUBLETON_COLS];
873  Fractional coeff[NUM_DOUBLETON_COLS];
874  Fractional lb[NUM_DOUBLETON_COLS];
875  Fractional ub[NUM_DOUBLETON_COLS];
876  Fractional objective_coefficient[NUM_DOUBLETON_COLS];
877 
878  // If the modified variable has status AT_[LOWER,UPPER]_BOUND, then we'll
879  // set one of the two original variables to one of its bounds, and set the
880  // other to VariableStatus::BASIC. We store this information (which variable
881  // will be set to one of its bounds, and which bound) for each possible
882  // outcome.
884  ColChoice col_choice;
889  : col_choice(c), status(s), value(v) {}
890  };
891  ColChoiceAndStatus bound_backtracking_at_lower_bound;
892  ColChoiceAndStatus bound_backtracking_at_upper_bound;
893  };
894  void SwapDeletedAndModifiedVariableRestoreInfo(RestoreInfo* r);
895 
896  std::vector<RestoreInfo> restore_stack_;
897  DenseColumn saved_row_lower_bounds_;
898  DenseColumn saved_row_upper_bounds_;
899 
900  ColumnsSaver columns_saver_;
901  DenseRow saved_objective_;
902 };
903 
904 // Because of numerical imprecision, a preprocessor like
905 // DoubletonEqualityRowPreprocessor can transform a constraint/variable domain
906 // like [1, 1+1e-7] to a fixed domain (for ex by multiplying the above domain by
907 // 1e9). This causes an issue because at postsolve, a FIXED_VALUE status now
908 // needs to be transformed to a AT_LOWER_BOUND/AT_UPPER_BOUND status. This is
909 // what this function is doing for the constraint statuses only.
910 //
911 // TODO(user): A better solution would simply be to get rid of the FIXED status
912 // altogether, it is better to simply use AT_LOWER_BOUND/AT_UPPER_BOUND
913 // depending on the constraining bound in the optimal solution. Note that we can
914 // always at the end transform any variable/constraint with a fixed domain to
915 // FIXED_VALUE if needed to keep the same external API.
916 void FixConstraintWithFixedStatuses(const DenseColumn& row_lower_bounds,
917  const DenseColumn& row_upper_bounds,
918  ProblemSolution* solution);
919 
920 // --------------------------------------------------------
921 // DualizerPreprocessor
922 // --------------------------------------------------------
923 // DualizerPreprocessor may change the given program to its dual depending
924 // on the value of the parameter solve_dual_problem.
925 //
926 // IMPORTANT: FreeConstraintPreprocessor() must be called first since this
927 // preprocessor does not deal correctly with free constraints.
929  public:
935  bool Run(LinearProgram* lp) final;
936  void RecoverSolution(ProblemSolution* solution) const final;
937  void UseInMipContext() final {
938  LOG(FATAL) << "In the presence of integer variables, "
939  << "there is no notion of a dual problem.";
940  }
941 
942  // Convert the given problem status to the one of its dual.
944 
945  private:
946  DenseRow variable_lower_bounds_;
947  DenseRow variable_upper_bounds_;
948 
949  RowIndex primal_num_rows_;
950  ColIndex primal_num_cols_;
951  bool primal_is_maximization_problem_;
952  RowToColMapping duplicated_rows_;
953 
954  // For postsolving the variable/constraint statuses.
955  VariableStatusRow dual_status_correspondence_;
956  ColMapping slack_or_surplus_mapping_;
957 };
958 
959 // --------------------------------------------------------
960 // ShiftVariableBoundsPreprocessor
961 // --------------------------------------------------------
962 // For each variable, inspects its bounds and "shift" them if necessary, so that
963 // its domain contains zero. A variable that was shifted will always have at
964 // least one of its bounds to zero. Doing it all at once allows to have a better
965 // precision when modifying the constraint bounds by using an accurate summation
966 // algorithm.
967 //
968 // Example:
969 // - A variable with bound [1e10, infinity] will be shifted to [0, infinity].
970 // - A variable with domain [-1e10, 1e10] will not be shifted. Note that
971 // compared to the first case, doing so here may introduce unnecessary
972 // numerical errors if the variable value in the final solution is close to
973 // zero.
974 //
975 // The expected impact of this is:
976 // - Better behavior of the scaling.
977 // - Better precision and numerical accuracy of the simplex method.
978 // - Slightly improved speed (because adding a column with a variable value of
979 // zero takes no work later).
980 //
981 // TODO(user): Having for each variable one of their bounds at zero is a
982 // requirement for the DualizerPreprocessor and for the implied free column in
983 // the ImpliedFreePreprocessor. However, shifting a variable with a domain like
984 // [-1e10, 1e10] may introduce numerical issues. Relax the definition of
985 // a free variable so that only having a domain containing 0.0 is enough?
987  public:
991  delete;
993  const ShiftVariableBoundsPreprocessor&) = delete;
995  bool Run(LinearProgram* lp) final;
996  void RecoverSolution(ProblemSolution* solution) const final;
997 
998  const DenseRow& offsets() const { return offsets_; }
999 
1000  private:
1001  // Contains for each variable by how much its bounds where shifted during
1002  // presolve. Note that the shift was negative (new bound = initial bound -
1003  // offset).
1004  DenseRow offsets_;
1005  // Contains the initial problem bounds. They are needed to get the perfect
1006  // numerical accuracy for variables at their bound after postsolve.
1007  DenseRow variable_initial_lbs_;
1008  DenseRow variable_initial_ubs_;
1009 };
1010 
1011 // --------------------------------------------------------
1012 // ScalingPreprocessor
1013 // --------------------------------------------------------
1014 // Scales the SparseMatrix of the linear program using a SparseMatrixScaler.
1015 // This is only applied if the parameter use_scaling is true.
1017  public:
1019  : Preprocessor(parameters) {}
1020  ScalingPreprocessor(const ScalingPreprocessor&) = delete;
1023  bool Run(LinearProgram* lp) final;
1024  void RecoverSolution(ProblemSolution* solution) const final;
1025  void UseInMipContext() final { LOG(FATAL) << "Not implemented."; }
1026 
1027  private:
1028  DenseRow variable_lower_bounds_;
1029  DenseRow variable_upper_bounds_;
1030  Fractional cost_scaling_factor_;
1031  Fractional bound_scaling_factor_;
1032  SparseMatrixScaler scaler_;
1033 };
1034 
1035 // --------------------------------------------------------
1036 // ToMinimizationPreprocessor
1037 // --------------------------------------------------------
1038 // Changes the problem from maximization to minimization (if applicable).
1040  public:
1042  : Preprocessor(parameters) {}
1045  delete;
1047  bool Run(LinearProgram* lp) final;
1048  void RecoverSolution(ProblemSolution* solution) const final;
1049 };
1050 
1051 // --------------------------------------------------------
1052 // AddSlackVariablesPreprocessor
1053 // --------------------------------------------------------
1054 // Transforms the linear program to the equation form
1055 // min c.x, s.t. A.x = 0. This is done by:
1056 // 1. Introducing slack variables for all constraints; all these variables are
1057 // introduced with coefficient 1.0, and their bounds are set to be negative
1058 // bounds of the corresponding constraint.
1059 // 2. Changing the bounds of all constraints to (0, 0) to make them an equality.
1060 //
1061 // As a consequence, the matrix of the linear program always has full row rank
1062 // after this preprocessor. Note that the slack variables are always added last,
1063 // so that the rightmost square sub-matrix is always the identity matrix.
1064 //
1065 // TODO(user): Do not require this step to talk to the revised simplex. On large
1066 // LPs like supportcase11.mps, this step alone can add 1.5 GB to the solver peak
1067 // memory for no good reason. The internal matrix representation used in glop is
1068 // a lot more efficient, and there is no point keeping the slacks in
1069 // LinearProgram. It is also bad for incrementaly modifying the LP.
1071  public:
1073  : Preprocessor(parameters) {}
1076  const AddSlackVariablesPreprocessor&) = delete;
1078  bool Run(LinearProgram* lp) final;
1079  void RecoverSolution(ProblemSolution* solution) const final;
1080 
1081  private:
1082  ColIndex first_slack_col_;
1083 };
1084 
1085 } // namespace glop
1086 } // namespace operations_research
1087 
1088 #endif // OR_TOOLS_GLOP_PREPROCESSOR_H_
virtual void RecoverSolution(ProblemSolution *solution) const =0
ProportionalColumnPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:293
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
DualizerPreprocessor & operator=(const DualizerPreprocessor &)=delete
void RecoverSolution(ProblemSolution *solution) const final
ColumnDeletionHelper & operator=(const ColumnDeletionHelper &)=delete
RemoveNearZeroEntriesPreprocessor & operator=(const RemoveNearZeroEntriesPreprocessor &)=delete
SingletonColumnSignPreprocessor & operator=(const SingletonColumnSignPreprocessor &)=delete
FixedVariablePreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:523
void RecoverSolution(ProblemSolution *solution) const final
FixedVariablePreprocessor & operator=(const FixedVariablePreprocessor &)=delete
void RecoverSolution(ProblemSolution *solution) const final
void RemoveZeroCostUnconstrainedVariable(ColIndex col, Fractional target_bound, LinearProgram *lp)
ToMinimizationPreprocessor(const GlopParameters *parameters)
FreeConstraintPreprocessor & operator=(const FreeConstraintPreprocessor &)=delete
const int FATAL
Definition: log_severity.h:32
ShiftVariableBoundsPreprocessor & operator=(const ShiftVariableBoundsPreprocessor &)=delete
ModelSharedTimeLimit * time_limit
ForcingAndImpliedFreeConstraintPreprocessor & operator=(const ForcingAndImpliedFreeConstraintPreprocessor &)=delete
Preprocessor & operator=(const Preprocessor &)=delete
const DenseBooleanRow & GetMarkedColumns() const
Definition: preprocessor.h:200
ShiftVariableBoundsPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:988
bool IsSmallerWithinTolerance(FloatType x, FloatType y, FloatType tolerance)
Definition: fp_utils.h:157
DoubletonFreeColumnPreprocessor & operator=(const DoubletonFreeColumnPreprocessor &)=delete
DoubletonEqualityRowPreprocessor & operator=(const DoubletonEqualityRowPreprocessor &)=delete
const std::string name
ScalingPreprocessor(const GlopParameters *parameters)
#define LOG(severity)
Definition: base/logging.h:420
ColIndex col
Definition: markowitz.cc:183
void RecoverSolution(ProblemSolution *solution) const final
RowIndex row
Definition: preprocessor.h:368
ColIndex col
Definition: preprocessor.h:369
SingletonUndo(OperationType type, const LinearProgram &lp, MatrixEntry e, ConstraintStatus status)
SingletonColumnSignPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:819
UnconstrainedVariablePreprocessor & operator=(const UnconstrainedVariablePreprocessor &)=delete
RowIndex row
Definition: markowitz.cc:182
void RecoverSolution(ProblemSolution *solution) const final
DoubletonFreeColumnPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:650
int64_t b
ProportionalRowPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:334
void RecoverSolution(ProblemSolution *solution) const final
DualizerPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:930
MainLpPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:105
MatrixEntry(RowIndex _row, ColIndex _col, Fractional _coeff)
Definition: preprocessor.h:366
void RecoverSolution(ProblemSolution *solution) const final
StrictITIVector< RowIndex, Fractional > DenseColumn
Definition: lp_types.h:332
EmptyConstraintPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:772
EmptyConstraintPreprocessor & operator=(const EmptyConstraintPreprocessor &)=delete
bool empty() const
void SetTimeLimit(TimeLimit *time_limit)
Definition: preprocessor.h:75
ScalingPreprocessor & operator=(const ScalingPreprocessor &)=delete
EmptyColumnPreprocessor & operator=(const EmptyColumnPreprocessor &)=delete
std::unique_ptr< TimeLimit > infinite_time_limit_
Definition: preprocessor.h:94
void DestructiveRecoverSolution(ProblemSolution *solution)
ImpliedFreePreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:604
ForcingAndImpliedFreeConstraintPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:558
const MatrixEntry & Entry() const
Definition: preprocessor.h:398
void RecoverSolution(ProblemSolution *solution) const final
MainLpPreprocessor & operator=(const MainLpPreprocessor &)=delete
Fractional target_bound
void FixConstraintWithFixedStatuses(const DenseColumn &row_lower_bounds, const DenseColumn &row_upper_bounds, ProblemSolution *solution)
void RecoverSolution(ProblemSolution *solution) const final
virtual bool Run(LinearProgram *lp)=0
FreeConstraintPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:753
SingletonPreprocessor & operator=(const SingletonPreprocessor &)=delete
Fractional coeff
Definition: preprocessor.h:370
UnconstrainedVariablePreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:701
void MarkColumnForDeletionWithState(ColIndex col, Fractional value, VariableStatus status)
RemoveNearZeroEntriesPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:797
DoubletonEqualityRowPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:841
void RecoverSolution(ProblemSolution *solution) const final
const SparseColumn & SavedColumn(ColIndex col) const
void RecoverSolution(ProblemSolution *solution) const final
const DenseBooleanColumn & GetMarkedRows() const
void RecoverSolution(ProblemSolution *solution) const final
RowDeletionHelper & operator=(const RowDeletionHelper &)=delete
ProportionalRowPreprocessor & operator=(const ProportionalRowPreprocessor &)=delete
AddSlackVariablesPreprocessor(const GlopParameters *parameters)
void RestoreDeletedRows(ProblemSolution *solution) const
Collection of objects used to extend the Constraint Solver library.
ProblemStatus ChangeStatusToDualStatus(ProblemStatus status) const
SingletonPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:434
AddSlackVariablesPreprocessor & operator=(const AddSlackVariablesPreprocessor &)=delete
const SparseColumn & SavedOrEmptyColumn(ColIndex col) const
SatParameters parameters
ProportionalColumnPreprocessor & operator=(const ProportionalColumnPreprocessor &)=delete
bool IsSmallerWithinPreprocessorZeroTolerance(Fractional a, Fractional b) const
Definition: preprocessor.h:84
void RecoverSolution(ProblemSolution *solution) const final
ToMinimizationPreprocessor & operator=(const ToMinimizationPreprocessor &)=delete
void RecoverSolution(ProblemSolution *solution) const final
void RecoverSolution(ProblemSolution *solution) const override
void Undo(const GlopParameters &parameters, const SparseColumn &saved_column, const SparseColumn &saved_row, ProblemSolution *solution) const
void RecoverSolution(ProblemSolution *solution) const final
ImpliedFreePreprocessor & operator=(const ImpliedFreePreprocessor &)=delete
void RecoverSolution(ProblemSolution *solution) const final
bool IsSmallerWithinFeasibilityTolerance(Fractional a, Fractional b) const
Definition: preprocessor.h:80
void RestoreDeletedColumns(ProblemSolution *solution) const
int64_t value
void SaveColumn(ColIndex col, const SparseColumn &column)
Preprocessor(const GlopParameters *parameters)
Definition: preprocessor.cc:48
const GlopParameters & parameters_
Definition: preprocessor.h:92
void SaveColumnIfNotAlreadyDone(ColIndex col, const SparseColumn &column)
EmptyColumnPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:269
void RecoverSolution(ProblemSolution *solution) const final
void RecoverSolution(ProblemSolution *solution) const final
int64_t a