OR-Tools  9.1
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  // Like RecoverSolution but destroys data structures as it goes to reduce peak
114  // RAM use. After calling this the MainLpPreprocessor object may no longer be
115  // used.
117 
118  private:
119  // Runs the given preprocessor and push it on preprocessors_ for the postsolve
120  // step when needed.
121  void RunAndPushIfRelevant(std::unique_ptr<Preprocessor> preprocessor,
122  const std::string& name, TimeLimit* time_limit,
123  LinearProgram* lp);
124 
125  // Stack of preprocessors currently applied to the lp that needs postsolve.
126  std::vector<std::unique_ptr<Preprocessor>> preprocessors_;
127 
128  // Initial dimension of the lp given to Run(), for displaying purpose.
129  EntryIndex initial_num_entries_;
130  RowIndex initial_num_rows_;
131  ColIndex initial_num_cols_;
132 };
133 
134 // --------------------------------------------------------
135 // ColumnDeletionHelper
136 // --------------------------------------------------------
137 
138 // Some preprocessors need to save columns/rows of the matrix for the postsolve.
139 // This class helps them do that.
140 //
141 // Note that we used to simply use a SparseMatrix, which is like a vector of
142 // SparseColumn. However on large problem with 10+ millions columns, each empty
143 // SparseColumn take 48 bytes, so if we run like 10 presolve step that save as
144 // little as 1 columns, we already are at 4GB memory for nothing!
146  public:
147  // Saves a column. The first version CHECKs that it is not already done.
148  void SaveColumn(ColIndex col, const SparseColumn& column);
149  void SaveColumnIfNotAlreadyDone(ColIndex col, const SparseColumn& column);
150 
151  // Returns the saved column. The first version CHECKs that it was saved.
152  const SparseColumn& SavedColumn(ColIndex col) const;
153  const SparseColumn& SavedOrEmptyColumn(ColIndex col) const;
154 
155  private:
156  SparseColumn empty_column_;
157  absl::flat_hash_map<ColIndex, int> saved_columns_index_;
158 
159  // TODO(user): We could optimize further since all these are read only, we
160  // could use a CompactSparseMatrix instead.
161  std::deque<SparseColumn> saved_columns_;
162 };
163 
164 // Help preprocessors deal with column deletion.
166  public:
170 
171  // Remember the given column as "deleted" so that it can later be restored
172  // by RestoreDeletedColumns(). Optionally, the caller may indicate the
173  // value and status of the corresponding variable so that it is automatically
174  // restored; if they don't then the restored value and status will be junk
175  // and must be set by the caller.
176  //
177  // The actual deletion is done by LinearProgram::DeleteColumns().
178  void MarkColumnForDeletion(ColIndex col);
180  VariableStatus status);
181 
182  // From a solution omitting the deleted column, expands it and inserts the
183  // deleted columns. If values and statuses for the corresponding variables
184  // were saved, they'll be restored.
185  void RestoreDeletedColumns(ProblemSolution* solution) const;
186 
187  // Returns whether or not the given column is marked for deletion.
188  bool IsColumnMarked(ColIndex col) const {
189  return col < is_column_deleted_.size() && is_column_deleted_[col];
190  }
191 
192  // Returns a Boolean vector of the column to be deleted.
193  const DenseBooleanRow& GetMarkedColumns() const { return is_column_deleted_; }
194 
195  // Returns true if no columns have been marked for deletion.
196  bool IsEmpty() const { return is_column_deleted_.empty(); }
197 
198  // Restores the class to its initial state.
199  void Clear();
200 
201  // Returns the value that will be restored by
202  // RestoreDeletedColumnInSolution(). Note that only the marked position value
203  // make sense.
204  const DenseRow& GetStoredValue() const { return stored_value_; }
205 
206  private:
207  DenseBooleanRow is_column_deleted_;
208 
209  // Note that this vector has the same size as is_column_deleted_ and that
210  // the value of the variable corresponding to a deleted column col is stored
211  // at position col. Values of columns not deleted are not used. We use this
212  // data structure so columns can be deleted in any order if needed.
213  DenseRow stored_value_;
214  VariableStatusRow stored_status_;
215 };
216 
217 // --------------------------------------------------------
218 // RowDeletionHelper
219 // --------------------------------------------------------
220 // Help preprocessors deal with row deletion.
222  public:
224  RowDeletionHelper(const RowDeletionHelper&) = delete;
226 
227  // Returns true if no rows have been marked for deletion.
228  bool IsEmpty() const { return is_row_deleted_.empty(); }
229 
230  // Restores the class to its initial state.
231  void Clear();
232 
233  // Adds a deleted row to the helper.
234  void MarkRowForDeletion(RowIndex row);
235 
236  // If the given row was marked for deletion, unmark it.
237  void UnmarkRow(RowIndex row);
238 
239  // Returns a Boolean vector of the row to be deleted.
240  const DenseBooleanColumn& GetMarkedRows() const;
241 
242  // Returns whether or not the given row is marked for deletion.
243  bool IsRowMarked(RowIndex row) const {
244  return row < is_row_deleted_.size() && is_row_deleted_[row];
245  }
246 
247  // From a solution without the deleted rows, expand it by restoring
248  // the deleted rows to a VariableStatus::BASIC status with 0.0 value.
249  // This latter value is important, many preprocessors rely on it.
250  void RestoreDeletedRows(ProblemSolution* solution) const;
251 
252  private:
253  DenseBooleanColumn is_row_deleted_;
254 };
255 
256 // --------------------------------------------------------
257 // EmptyColumnPreprocessor
258 // --------------------------------------------------------
259 // Removes the empty columns from the problem.
261  public:
267  bool Run(LinearProgram* lp) final;
268  void RecoverSolution(ProblemSolution* solution) const final;
269 
270  private:
271  ColumnDeletionHelper column_deletion_helper_;
272 };
273 
274 // --------------------------------------------------------
275 // ProportionalColumnPreprocessor
276 // --------------------------------------------------------
277 // Removes the proportional columns from the problem when possible. Two columns
278 // are proportional if one is a non-zero scalar multiple of the other.
279 //
280 // Note that in the linear programming literature, two proportional columns are
281 // usually called duplicates. The notion is the same once the problem has been
282 // scaled. However, during presolve the columns can't be assumed to be scaled,
283 // so it makes sense to use the more general notion of proportional columns.
285  public:
289  delete;
291  const ProportionalColumnPreprocessor&) = delete;
293  bool Run(LinearProgram* lp) final;
294  void RecoverSolution(ProblemSolution* solution) const final;
295  void UseInMipContext() final { LOG(FATAL) << "Not implemented."; }
296 
297  private:
298  // Postsolve information about proportional columns with the same scaled cost
299  // that were merged during presolve.
300 
301  // The proportionality factor of each column. If two columns are proportional
302  // with factor p1 and p2 then p1 times the first column is the same as p2
303  // times the second column.
304  DenseRow column_factors_;
305 
306  // If merged_columns_[col] != kInvalidCol, then column col has been merged
307  // into the column merged_columns_[col].
308  ColMapping merged_columns_;
309 
310  // The old and new variable bounds.
311  DenseRow lower_bounds_;
312  DenseRow upper_bounds_;
313  DenseRow new_lower_bounds_;
314  DenseRow new_upper_bounds_;
315 
316  ColumnDeletionHelper column_deletion_helper_;
317 };
318 
319 // --------------------------------------------------------
320 // ProportionalRowPreprocessor
321 // --------------------------------------------------------
322 // Removes the proportional rows from the problem.
323 // The linear programming literature also calls such rows duplicates, see the
324 // same remark above for columns in ProportionalColumnPreprocessor.
326  public:
331  delete;
333  bool Run(LinearProgram* lp) final;
334  void RecoverSolution(ProblemSolution* solution) const final;
335 
336  private:
337  // Informations about proportional rows, only filled for such rows.
338  DenseColumn row_factors_;
339  RowMapping upper_bound_sources_;
340  RowMapping lower_bound_sources_;
341 
342  bool lp_is_maximization_problem_;
343  RowDeletionHelper row_deletion_helper_;
344 };
345 
346 // --------------------------------------------------------
347 // SingletonPreprocessor
348 // --------------------------------------------------------
349 // Removes as many singleton rows and singleton columns as possible from the
350 // problem. Note that not all types of singleton columns can be removed. See the
351 // comments below on the SingletonPreprocessor functions for more details.
352 //
353 // TODO(user): Generalize the design used in this preprocessor to a general
354 // "propagation" framework in order to apply as many reductions as possible in
355 // an efficient manner.
356 
357 // Holds a triplet (row, col, coefficient).
358 struct MatrixEntry {
359  MatrixEntry(RowIndex _row, ColIndex _col, Fractional _coeff)
360  : row(_row), col(_col), coeff(_coeff) {}
361  RowIndex row;
362  ColIndex col;
364 };
365 
366 // Stores the information needed to undo a singleton row/column deletion.
368  public:
369  // The type of a given operation.
370  typedef enum {
375  } OperationType;
376 
377  // Stores the information, which together with the field deleted_columns_ and
378  // deleted_rows_ of SingletonPreprocessor, are needed to undo an operation
379  // with the given type. Note that all the arguments must refer to the linear
380  // program BEFORE the operation is applied.
381  SingletonUndo(OperationType type, const LinearProgram& lp, MatrixEntry e,
382  ConstraintStatus status);
383 
384  // Undo the operation saved in this class, taking into account the saved
385  // column and row (at the row/col given by Entry()) passed by the calling
386  // instance of SingletonPreprocessor. Note that the operations must be undone
387  // in the reverse order of the one in which they were applied.
388  void Undo(const GlopParameters& parameters, const SparseColumn& saved_column,
389  const SparseColumn& saved_row, ProblemSolution* solution) const;
390 
391  const MatrixEntry& Entry() const { return e_; }
392 
393  private:
394  // Actual undo functions for each OperationType.
395  // Undo() just calls the correct one.
396  void SingletonRowUndo(const SparseColumn& saved_column,
397  ProblemSolution* solution) const;
398  void ZeroCostSingletonColumnUndo(const GlopParameters& parameters,
399  const SparseColumn& saved_row,
400  ProblemSolution* solution) const;
401  void SingletonColumnInEqualityUndo(const GlopParameters& parameters,
402  const SparseColumn& saved_row,
403  ProblemSolution* solution) const;
404  void MakeConstraintAnEqualityUndo(ProblemSolution* solution) const;
405 
406  // All the information needed during undo.
407  OperationType type_;
408  bool is_maximization_;
409  MatrixEntry e_;
410  Fractional cost_;
411 
412  // TODO(user): regroup the pair (lower bound, upper bound) in a bound class?
413  Fractional variable_lower_bound_;
414  Fractional variable_upper_bound_;
415  Fractional constraint_lower_bound_;
416  Fractional constraint_upper_bound_;
417 
418  // This in only used with MAKE_CONSTRAINT_AN_EQUALITY undo.
419  // TODO(user): Clean that up using many Undo classes and virtual functions.
420  ConstraintStatus constraint_status_;
421 };
422 
423 // Deletes as many singleton rows or singleton columns as possible. Note that
424 // each time we delete a row or a column, new singletons may be created.
426  public:
432  bool Run(LinearProgram* lp) final;
433  void RecoverSolution(ProblemSolution* solution) const final;
434 
435  private:
436  // Returns the MatrixEntry of the given singleton row or column, taking into
437  // account the rows and columns that were already deleted.
438  MatrixEntry GetSingletonColumnMatrixEntry(ColIndex col,
439  const SparseMatrix& matrix);
440  MatrixEntry GetSingletonRowMatrixEntry(RowIndex row,
441  const SparseMatrix& matrix_transpose);
442 
443  // A singleton row can always be removed by changing the corresponding
444  // variable bounds to take into account the bounds on this singleton row.
445  void DeleteSingletonRow(MatrixEntry e, LinearProgram* lp);
446 
447  // Internal operation when removing a zero-cost singleton column corresponding
448  // to the given entry. This modifies the constraint bounds to take into acount
449  // the bounds of the corresponding variable.
450  void UpdateConstraintBoundsWithVariableBounds(MatrixEntry e,
451  LinearProgram* lp);
452 
453  // Checks if all other variables in the constraint are integer and the
454  // coefficients are divisible by the coefficient of the singleton variable.
455  bool IntegerSingletonColumnIsRemovable(const MatrixEntry& matrix_entry,
456  const LinearProgram& lp) const;
457 
458  // A singleton column with a cost of zero can always be removed by changing
459  // the corresponding constraint bounds to take into acount the bound of this
460  // singleton column.
461  void DeleteZeroCostSingletonColumn(const SparseMatrix& matrix_transpose,
462  MatrixEntry e, LinearProgram* lp);
463 
464  // Returns true if the constraint associated to the given singleton column was
465  // an equality or could be made one:
466  // If a singleton variable is free in a direction that improves the cost, then
467  // we can always move it as much as possible in this direction. Only the
468  // constraint will stop us, making it an equality. If the constraint doesn't
469  // stop us, then the program is unbounded (provided that there is a feasible
470  // solution).
471  //
472  // Note that this operation does not need any "undo" during the post-solve. At
473  // optimality, the dual value on the constraint row will be of the correct
474  // sign, and relaxing the constraint bound will not impact the dual
475  // feasibility of the solution.
476  //
477  // TODO(user): this operation can be generalized to columns with just one
478  // blocking constraint. Investigate how to use this. The 'reverse' can
479  // probably also be done, relaxing a constraint that is blocking a
480  // unconstrained variable.
481  bool MakeConstraintAnEqualityIfPossible(const SparseMatrix& matrix_transpose,
482  MatrixEntry e, LinearProgram* lp);
483 
484  // If a singleton column appears in an equality, we can remove its cost by
485  // changing the other variables cost using the constraint. We can then delete
486  // the column like in DeleteZeroCostSingletonColumn().
487  void DeleteSingletonColumnInEquality(const SparseMatrix& matrix_transpose,
488  MatrixEntry e, LinearProgram* lp);
489 
490  ColumnDeletionHelper column_deletion_helper_;
491  RowDeletionHelper row_deletion_helper_;
492  std::vector<SingletonUndo> undo_stack_;
493 
494  // This is used as a "cache" by MakeConstraintAnEqualityIfPossible() to avoid
495  // scanning more than once each row. See the code to see how this is used.
496  absl::StrongVector<RowIndex, bool> row_sum_is_cached_;
498  row_lb_sum_;
500  row_ub_sum_;
501 
502  // TODO(user): It is annoying that we need to store a part of the matrix that
503  // is not deleted here. This extra memory usage might show the limit of our
504  // presolve architecture that does not require a new matrix factorization on
505  // the original problem to reconstruct the solution.
506  ColumnsSaver columns_saver_;
507  ColumnsSaver rows_saver_;
508 };
509 
510 // --------------------------------------------------------
511 // FixedVariablePreprocessor
512 // --------------------------------------------------------
513 // Removes the fixed variables from the problem.
515  public:
520  delete;
522  bool Run(LinearProgram* lp) final;
523  void RecoverSolution(ProblemSolution* solution) const final;
524 
525  private:
526  ColumnDeletionHelper column_deletion_helper_;
527 };
528 
529 // --------------------------------------------------------
530 // ForcingAndImpliedFreeConstraintPreprocessor
531 // --------------------------------------------------------
532 // This preprocessor computes for each constraint row the bounds that are
533 // implied by the variable bounds and applies one of the following reductions:
534 //
535 // * If the intersection of the implied bounds and the current constraint bounds
536 // is empty (modulo some tolerance), the problem is INFEASIBLE.
537 //
538 // * If the intersection of the implied bounds and the current constraint bounds
539 // is a singleton (modulo some tolerance), then the constraint is said to be
540 // forcing and all the variables that appear in it can be fixed to one of their
541 // bounds. All these columns and the constraint row is removed.
542 //
543 // * If the implied bounds are included inside the current constraint bounds
544 // (modulo some tolerance) then the constraint is said to be redundant or
545 // implied free. Its bounds are relaxed and the constraint will be removed
546 // later by the FreeConstraintPreprocessor.
547 //
548 // * Otherwise, wo do nothing.
550  public:
552  const GlopParameters* parameters)
559  bool Run(LinearProgram* lp) final;
560  void RecoverSolution(ProblemSolution* solution) const final;
561 
562  private:
563  bool lp_is_maximization_problem_;
564  DenseRow costs_;
565  DenseBooleanColumn is_forcing_up_;
566  ColumnDeletionHelper column_deletion_helper_;
567  RowDeletionHelper row_deletion_helper_;
568  ColumnsSaver columns_saver_;
569 };
570 
571 // --------------------------------------------------------
572 // ImpliedFreePreprocessor
573 // --------------------------------------------------------
574 // It is possible to compute "implied" bounds on a variable from the bounds of
575 // all the other variables and the constraints in which this variable take
576 // place. If such "implied" bounds are inside the variable bounds, then the
577 // variable bounds can be relaxed and the variable is said to be "implied free".
578 //
579 // This preprocessor detects the implied free variables and make as many as
580 // possible free with a priority towards low-degree columns. This transformation
581 // will make the simplex algorithm more efficient later, but will also make it
582 // possible to reduce the problem by applying subsequent transformations:
583 //
584 // * The SingletonPreprocessor already deals with implied free singleton
585 // variables and removes the columns and the rows in which they appear.
586 //
587 // * Any multiple of the column of a free variable can be added to any other
588 // column without changing the linear program solution. This is the dual
589 // counterpart of the fact that any multiple of an equality row can be added to
590 // any row.
591 //
592 // TODO(user): Only process doubleton columns so we have more chance in the
593 // later passes to create more doubleton columns? Such columns lead to a smaller
594 // problem thanks to the DoubletonFreeColumnPreprocessor.
596  public:
602  bool Run(LinearProgram* lp) final;
603  void RecoverSolution(ProblemSolution* solution) const final;
604 
605  private:
606  // This preprocessor adds fixed offsets to some variables. We remember those
607  // here to un-offset them in RecoverSolution().
608  DenseRow variable_offsets_;
609 
610  // This preprocessor causes some variables who would normally be
611  // AT_{LOWER,UPPER}_BOUND to be VariableStatus::FREE. We store the restore
612  // value of these variables; which will only be used (eg. restored) if the
613  // variable actually turns out to be VariableStatus::FREE.
614  VariableStatusRow postsolve_status_of_free_variables_;
615 };
616 
617 // --------------------------------------------------------
618 // DoubletonFreeColumnPreprocessor
619 // --------------------------------------------------------
620 // This preprocessor removes one of the two rows in which a doubleton column of
621 // a free variable appears. Since we can add any multiple of such a column to
622 // any other column, the way this works is that we can always remove all the
623 // entries on one row.
624 //
625 // Actually, we can remove all the entries except the one of the free column.
626 // But we will be left with a singleton row that we can delete in the same way
627 // as what is done in SingletonPreprocessor. That is by reporting the constraint
628 // bounds into the one of the originally free variable. After this operation,
629 // the doubleton free column will become a singleton and may or may not be
630 // removed later by the SingletonPreprocessor.
631 //
632 // Note that this preprocessor can be seen as the dual of the
633 // DoubletonEqualityRowPreprocessor since when taking the dual, an equality row
634 // becomes a free variable and vice versa.
635 //
636 // Note(user): As far as I know, this doubleton free column procedure is more
637 // general than what can be found in the research papers or in any of the linear
638 // solver open source codes as of July 2013. All of them only process such
639 // columns if one of the two rows is also an equality which is not actually
640 // required. Most probably, commercial solvers do use it though.
642  public:
646  delete;
648  const DoubletonFreeColumnPreprocessor&) = delete;
650  bool Run(LinearProgram* lp) final;
651  void RecoverSolution(ProblemSolution* solution) const final;
652 
653  private:
654  enum RowChoice {
655  DELETED = 0,
656  MODIFIED = 1,
657  // This is just a constant for the number of rows in a doubleton column.
658  // That is 2, one will be DELETED, the other MODIFIED.
659  NUM_ROWS = 2,
660  };
661  struct RestoreInfo {
662  // The index of the original free doubleton column and its objective.
663  ColIndex col;
664  Fractional objective_coefficient;
665 
666  // The row indices of the two involved rows and their coefficients on
667  // column col.
668  RowIndex row[NUM_ROWS];
669  Fractional coeff[NUM_ROWS];
670 
671  // The deleted row as a column.
672  SparseColumn deleted_row_as_column;
673  };
674 
675  std::vector<RestoreInfo> restore_stack_;
676  RowDeletionHelper row_deletion_helper_;
677 };
678 
679 // --------------------------------------------------------
680 // UnconstrainedVariablePreprocessor
681 // --------------------------------------------------------
682 // If for a given variable, none of the constraints block it in one direction
683 // and this direction improves the objective, then this variable can be fixed to
684 // its bound in this direction. If this bound is infinite and the variable cost
685 // is non-zero, then the problem is unbounded.
686 //
687 // More generally, by using the constraints and the variables that are unbounded
688 // on one side, one can derive bounds on the dual values. These can be
689 // translated into bounds on the reduced costs or the columns, which may force
690 // variables to their bounds. This is called forcing and dominated columns in
691 // the Andersen & Andersen paper.
693  public:
697  delete;
699  const UnconstrainedVariablePreprocessor&) = delete;
701  bool Run(LinearProgram* lp) final;
702  void RecoverSolution(ProblemSolution* solution) const final;
703 
704  // Removes the given variable and all the rows in which it appears: If a
705  // variable is unconstrained with a zero cost, then all the constraints in
706  // which it appears can be made free! More precisely, during postsolve, if
707  // such a variable is unconstrained towards +kInfinity, for any activity value
708  // of the involved constraints, an M exists such that for each value of the
709  // variable >= M the problem will be feasible.
710  //
711  // The algorithm during postsolve is to find a feasible value for all such
712  // variables while trying to keep their magnitudes small (for better numerical
713  // behavior). target_bound should take only two possible values: +/-kInfinity.
716  LinearProgram* lp);
717 
718  private:
719  // Lower/upper bounds on the feasible dual value. We use constraints and
720  // variables unbounded in one direction to derive these bounds. We use these
721  // bounds to compute bounds on the reduced costs of the problem variables.
722  // Note that any finite bounds on a reduced cost means that the variable
723  // (ignoring its domain) can move freely in one direction.
724  DenseColumn dual_lb_;
725  DenseColumn dual_ub_;
726 
727  // Indicates if a given column may have participated in the current lb/ub
728  // on the reduced cost of the same column.
729  DenseBooleanRow may_have_participated_ub_;
730  DenseBooleanRow may_have_participated_lb_;
731 
732  ColumnDeletionHelper column_deletion_helper_;
733  RowDeletionHelper row_deletion_helper_;
734  ColumnsSaver rows_saver_;
735  DenseColumn rhs_;
736  DenseColumn activity_sign_correction_;
737  DenseBooleanRow is_unbounded_;
738 };
739 
740 // --------------------------------------------------------
741 // FreeConstraintPreprocessor
742 // --------------------------------------------------------
743 // Removes the constraints with no bounds from the problem.
745  public:
750  delete;
752  bool Run(LinearProgram* lp) final;
753  void RecoverSolution(ProblemSolution* solution) const final;
754 
755  private:
756  RowDeletionHelper row_deletion_helper_;
757 };
758 
759 // --------------------------------------------------------
760 // EmptyConstraintPreprocessor
761 // --------------------------------------------------------
762 // Removes the constraints with no coefficients from the problem.
764  public:
769  delete;
771  bool Run(LinearProgram* lp) final;
772  void RecoverSolution(ProblemSolution* solution) const final;
773 
774  private:
775  RowDeletionHelper row_deletion_helper_;
776 };
777 
778 // --------------------------------------------------------
779 // RemoveNearZeroEntriesPreprocessor
780 // --------------------------------------------------------
781 // Removes matrix entries that have only a negligible impact on the solution.
782 // Using the variable bounds, we derive a maximum possible impact, and remove
783 // the entries whose impact is under a given tolerance.
784 //
785 // TODO(user): This preprocessor doesn't work well on badly scaled problems. In
786 // particular, it will set the objective to zero if all the objective
787 // coefficients are small! Run it after ScalingPreprocessor or fix the code.
789  public:
793  delete;
795  const RemoveNearZeroEntriesPreprocessor&) = delete;
797  bool Run(LinearProgram* lp) final;
798  void RecoverSolution(ProblemSolution* solution) const final;
799 
800  private:
801 };
802 
803 // --------------------------------------------------------
804 // SingletonColumnSignPreprocessor
805 // --------------------------------------------------------
806 // Make sure that the only coefficient of all singleton columns (i.e. column
807 // with only one entry) is positive. This is because this way the column will
808 // be transformed in an identity column by the scaling. This will lead to more
809 // efficient solve when this column is involved.
811  public:
815  delete;
817  const SingletonColumnSignPreprocessor&) = delete;
819  bool Run(LinearProgram* lp) final;
820  void RecoverSolution(ProblemSolution* solution) const final;
821 
822  private:
823  std::vector<ColIndex> changed_columns_;
824 };
825 
826 // --------------------------------------------------------
827 // DoubletonEqualityRowPreprocessor
828 // --------------------------------------------------------
829 // Reduce equality constraints involving two variables (i.e. aX + bY = c),
830 // by substitution (and thus removal) of one of the variables by the other
831 // in all the constraints that it is involved in.
833  public:
837  delete;
839  const DoubletonEqualityRowPreprocessor&) = delete;
841  bool Run(LinearProgram* lp) final;
842  void RecoverSolution(ProblemSolution* solution) const final;
843 
844  private:
845  enum ColChoice {
846  DELETED = 0,
847  MODIFIED = 1,
848  // For for() loops iterating over the ColChoice values, and/or arrays.
849  NUM_DOUBLETON_COLS = 2,
850  };
851  static ColChoice OtherColChoice(ColChoice x) {
852  return x == DELETED ? MODIFIED : DELETED;
853  }
854 
855  ColumnDeletionHelper column_deletion_helper_;
856  RowDeletionHelper row_deletion_helper_;
857 
858  struct RestoreInfo {
859  // The row index of the doubleton equality constraint, and its constant.
860  RowIndex row;
861  Fractional rhs; // The constant c in the equality aX + bY = c.
862 
863  // The indices and the data of the two columns that we touched, exactly
864  // as they were beforehand.
865  ColIndex col[NUM_DOUBLETON_COLS];
866  Fractional coeff[NUM_DOUBLETON_COLS];
867  Fractional lb[NUM_DOUBLETON_COLS];
868  Fractional ub[NUM_DOUBLETON_COLS];
869  Fractional objective_coefficient[NUM_DOUBLETON_COLS];
870 
871  // If the modified variable has status AT_[LOWER,UPPER]_BOUND, then we'll
872  // set one of the two original variables to one of its bounds, and set the
873  // other to VariableStatus::BASIC. We store this information (which variable
874  // will be set to one of its bounds, and which bound) for each possible
875  // outcome.
877  ColChoice col_choice;
882  : col_choice(c), status(s), value(v) {}
883  };
884  ColChoiceAndStatus bound_backtracking_at_lower_bound;
885  ColChoiceAndStatus bound_backtracking_at_upper_bound;
886  };
887  void SwapDeletedAndModifiedVariableRestoreInfo(RestoreInfo* r);
888 
889  std::vector<RestoreInfo> restore_stack_;
890  DenseColumn saved_row_lower_bounds_;
891  DenseColumn saved_row_upper_bounds_;
892 
893  ColumnsSaver columns_saver_;
894  DenseRow saved_objective_;
895 };
896 
897 // Because of numerical imprecision, a preprocessor like
898 // DoubletonEqualityRowPreprocessor can transform a constraint/variable domain
899 // like [1, 1+1e-7] to a fixed domain (for ex by multiplying the above domain by
900 // 1e9). This causes an issue because at postsolve, a FIXED_VALUE status now
901 // needs to be transformed to a AT_LOWER_BOUND/AT_UPPER_BOUND status. This is
902 // what this function is doing for the constraint statuses only.
903 //
904 // TODO(user): A better solution would simply be to get rid of the FIXED status
905 // altogether, it is better to simply use AT_LOWER_BOUND/AT_UPPER_BOUND
906 // depending on the constraining bound in the optimal solution. Note that we can
907 // always at the end transform any variable/constraint with a fixed domain to
908 // FIXED_VALUE if needed to keep the same external API.
909 void FixConstraintWithFixedStatuses(const DenseColumn& row_lower_bounds,
910  const DenseColumn& row_upper_bounds,
911  ProblemSolution* solution);
912 
913 // --------------------------------------------------------
914 // DualizerPreprocessor
915 // --------------------------------------------------------
916 // DualizerPreprocessor may change the given program to its dual depending
917 // on the value of the parameter solve_dual_problem.
918 //
919 // IMPORTANT: FreeConstraintPreprocessor() must be called first since this
920 // preprocessor does not deal correctly with free constraints.
922  public:
928  bool Run(LinearProgram* lp) final;
929  void RecoverSolution(ProblemSolution* solution) const final;
930  void UseInMipContext() final {
931  LOG(FATAL) << "In the presence of integer variables, "
932  << "there is no notion of a dual problem.";
933  }
934 
935  // Convert the given problem status to the one of its dual.
937 
938  private:
939  DenseRow variable_lower_bounds_;
940  DenseRow variable_upper_bounds_;
941 
942  RowIndex primal_num_rows_;
943  ColIndex primal_num_cols_;
944  bool primal_is_maximization_problem_;
945  RowToColMapping duplicated_rows_;
946 
947  // For postsolving the variable/constraint statuses.
948  VariableStatusRow dual_status_correspondence_;
949  ColMapping slack_or_surplus_mapping_;
950 };
951 
952 // --------------------------------------------------------
953 // ShiftVariableBoundsPreprocessor
954 // --------------------------------------------------------
955 // For each variable, inspects its bounds and "shift" them if necessary, so that
956 // its domain contains zero. A variable that was shifted will always have at
957 // least one of its bounds to zero. Doing it all at once allows to have a better
958 // precision when modifying the constraint bounds by using an accurate summation
959 // algorithm.
960 //
961 // Example:
962 // - A variable with bound [1e10, infinity] will be shifted to [0, infinity].
963 // - A variable with domain [-1e10, 1e10] will not be shifted. Note that
964 // compared to the first case, doing so here may introduce unnecessary
965 // numerical errors if the variable value in the final solution is close to
966 // zero.
967 //
968 // The expected impact of this is:
969 // - Better behavior of the scaling.
970 // - Better precision and numerical accuracy of the simplex method.
971 // - Slightly improved speed (because adding a column with a variable value of
972 // zero takes no work later).
973 //
974 // TODO(user): Having for each variable one of their bounds at zero is a
975 // requirement for the DualizerPreprocessor and for the implied free column in
976 // the ImpliedFreePreprocessor. However, shifting a variable with a domain like
977 // [-1e10, 1e10] may introduce numerical issues. Relax the definition of
978 // a free variable so that only having a domain containing 0.0 is enough?
980  public:
984  delete;
986  const ShiftVariableBoundsPreprocessor&) = delete;
988  bool Run(LinearProgram* lp) final;
989  void RecoverSolution(ProblemSolution* solution) const final;
990 
991  const DenseRow& offsets() const { return offsets_; }
992 
993  private:
994  // Contains for each variable by how much its bounds where shifted during
995  // presolve. Note that the shift was negative (new bound = initial bound -
996  // offset).
997  DenseRow offsets_;
998  // Contains the initial problem bounds. They are needed to get the perfect
999  // numerical accuracy for variables at their bound after postsolve.
1000  DenseRow variable_initial_lbs_;
1001  DenseRow variable_initial_ubs_;
1002 };
1003 
1004 // --------------------------------------------------------
1005 // ScalingPreprocessor
1006 // --------------------------------------------------------
1007 // Scales the SparseMatrix of the linear program using a SparseMatrixScaler.
1008 // This is only applied if the parameter use_scaling is true.
1010  public:
1012  : Preprocessor(parameters) {}
1013  ScalingPreprocessor(const ScalingPreprocessor&) = delete;
1016  bool Run(LinearProgram* lp) final;
1017  void RecoverSolution(ProblemSolution* solution) const final;
1018  void UseInMipContext() final { LOG(FATAL) << "Not implemented."; }
1019 
1020  private:
1021  DenseRow variable_lower_bounds_;
1022  DenseRow variable_upper_bounds_;
1023  Fractional cost_scaling_factor_;
1024  Fractional bound_scaling_factor_;
1025  SparseMatrixScaler scaler_;
1026 };
1027 
1028 // --------------------------------------------------------
1029 // ToMinimizationPreprocessor
1030 // --------------------------------------------------------
1031 // Changes the problem from maximization to minimization (if applicable).
1033  public:
1035  : Preprocessor(parameters) {}
1038  delete;
1040  bool Run(LinearProgram* lp) final;
1041  void RecoverSolution(ProblemSolution* solution) const final;
1042 };
1043 
1044 // --------------------------------------------------------
1045 // AddSlackVariablesPreprocessor
1046 // --------------------------------------------------------
1047 // Transforms the linear program to the equation form
1048 // min c.x, s.t. A.x = 0. This is done by:
1049 // 1. Introducing slack variables for all constraints; all these variables are
1050 // introduced with coefficient 1.0, and their bounds are set to be negative
1051 // bounds of the corresponding constraint.
1052 // 2. Changing the bounds of all constraints to (0, 0) to make them an equality.
1053 //
1054 // As a consequence, the matrix of the linear program always has full row rank
1055 // after this preprocessor. Note that the slack variables are always added last,
1056 // so that the rightmost square sub-matrix is always the identity matrix.
1057 //
1058 // TODO(user): Do not require this step to talk to the revised simplex. On large
1059 // LPs like supportcase11.mps, this step alone can add 1.5 GB to the solver peak
1060 // memory for no good reason. The internal matrix representation used in glop is
1061 // a lot more efficient, and there is no point keeping the slacks in
1062 // LinearProgram. It is also bad for incrementaly modifying the LP.
1064  public:
1066  : Preprocessor(parameters) {}
1069  const AddSlackVariablesPreprocessor&) = delete;
1071  bool Run(LinearProgram* lp) final;
1072  void RecoverSolution(ProblemSolution* solution) const final;
1073 
1074  private:
1075  ColIndex first_slack_col_;
1076 };
1077 
1078 } // namespace glop
1079 } // namespace operations_research
1080 
1081 #endif // OR_TOOLS_GLOP_PREPROCESSOR_H_
virtual void RecoverSolution(ProblemSolution *solution) const =0
ProportionalColumnPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:286
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:105
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:516
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:193
ShiftVariableBoundsPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:981
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:416
ColIndex col
Definition: markowitz.cc:183
void RecoverSolution(ProblemSolution *solution) const final
RowIndex row
Definition: preprocessor.h:361
ColIndex col
Definition: preprocessor.h:362
SingletonUndo(OperationType type, const LinearProgram &lp, MatrixEntry e, ConstraintStatus status)
SingletonColumnSignPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:812
UnconstrainedVariablePreprocessor & operator=(const UnconstrainedVariablePreprocessor &)=delete
RowIndex row
Definition: markowitz.cc:182
void RecoverSolution(ProblemSolution *solution) const final
DoubletonFreeColumnPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:643
int64_t b
ProportionalRowPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:327
void RecoverSolution(ProblemSolution *solution) const final
DualizerPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:923
MainLpPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:105
MatrixEntry(RowIndex _row, ColIndex _col, Fractional _coeff)
Definition: preprocessor.h:359
void RecoverSolution(ProblemSolution *solution) const final
StrictITIVector< RowIndex, Fractional > DenseColumn
Definition: lp_types.h:332
EmptyConstraintPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:765
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:597
ForcingAndImpliedFreeConstraintPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:551
const MatrixEntry & Entry() const
Definition: preprocessor.h:391
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:746
SingletonPreprocessor & operator=(const SingletonPreprocessor &)=delete
Fractional coeff
Definition: preprocessor.h:363
UnconstrainedVariablePreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:694
void MarkColumnForDeletionWithState(ColIndex col, Fractional value, VariableStatus status)
RemoveNearZeroEntriesPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:790
DoubletonEqualityRowPreprocessor(const GlopParameters *parameters)
Definition: preprocessor.h:834
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:427
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:262
void RecoverSolution(ProblemSolution *solution) const final
void RecoverSolution(ProblemSolution *solution) const final
int64_t a