OR-Tools  9.2
reduced_costs.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 #ifndef OR_TOOLS_GLOP_REDUCED_COSTS_H_
15 #define OR_TOOLS_GLOP_REDUCED_COSTS_H_
16 
17 #include "absl/random/bit_gen_ref.h"
20 #include "ortools/glop/pricing.h"
22 #include "ortools/glop/status.h"
28 #include "ortools/util/stats.h"
29 
30 namespace operations_research {
31 namespace glop {
32 
33 // Maintains the reduced costs of the non-basic variables and some related
34 // quantities.
35 //
36 // Terminology:
37 // - To each non-basic column 'a' of A, we can associate an "edge" in the
38 // kernel of A equal to 1.0 on the index of 'a' and '-B^{-1}.a' on the basic
39 // variables.
40 // - 'B^{-1}.a' is called the "right inverse" of 'a'.
41 // - The reduced cost of a column is equal to the scalar product of this
42 // column's edge with the cost vector (objective_), and corresponds to the
43 // variation in the objective function when we add this edge to the current
44 // solution.
45 // - The dual values are the "left inverse" of the basic objective by B.
46 // That is 'basic_objective_.B^{-1}'
47 // - The reduced cost of a column is also equal to the scalar product of this
48 // column with the vector of the dual values.
49 class ReducedCosts {
50  public:
51  // Takes references to the linear program data we need.
52  ReducedCosts(const CompactSparseMatrix& matrix_, const DenseRow& objective,
53  const RowToColMapping& basis,
54  const VariablesInfo& variables_info,
55  const BasisFactorization& basis_factorization,
56  absl::BitGenRef random);
57 
58  // If this is true, then the caller must re-factorize the basis before the
59  // next call to GetReducedCosts().
60  bool NeedsBasisRefactorization() const;
61 
62  // Checks the precision of the entering variable choice now that the direction
63  // is computed. Returns its precise version. This will also trigger a
64  // reduced cost recomputation if it was deemed too imprecise.
65  Fractional TestEnteringReducedCostPrecision(ColIndex entering_col,
66  const ScatteredColumn& direction);
67 
68  // Computes the current dual residual and infeasibility. Note that these
69  // functions are not really fast (many scalar products will be computed) and
70  // shouldn't be called at each iteration.
71  //
72  // These function will compute the reduced costs if needed.
73  // ComputeMaximumDualResidual() also needs ComputeBasicObjectiveLeftInverse()
74  // and do not depends on reduced costs.
78 
79  // Same as ComputeMaximumDualInfeasibility() but ignore boxed variables.
80  // Because we can always switch bounds of boxed variables, if this is under
81  // the dual tolerance, then we can easily have a dual feasible solution and do
82  // not need to run a dual phase I algorithm.
84 
85  // Updates any internal data BEFORE the given simplex pivot is applied to B.
86  // Note that no updates are needed in case of a bound flip.
87  // The arguments are in order:
88  // - The index of the entering non-basic column of A.
89  // - The index in B of the leaving basic variable.
90  // - The 'direction', i.e. the right inverse of the entering column.
91  void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row,
92  const ScatteredColumn& direction,
93  UpdateRow* update_row);
94 
95  // Sets the cost of the given non-basic variable to zero and updates its
96  // reduced cost. Note that changing the cost of a non-basic variable only
97  // impacts its reduced cost and not the one of any other variables.
98  // The current_cost pointer must be equal to the address of objective[col]
99  // where objective is the DenseRow passed at construction.
100  void SetNonBasicVariableCostToZero(ColIndex col, Fractional* current_cost);
101 
102  // Sets the pricing parameters. This does not change the pricing rule.
104 
105  // Returns true if the current reduced costs are computed with maximum
106  // precision.
107  bool AreReducedCostsPrecise() { return are_reduced_costs_precise_; }
108 
109  // Returns true if the current reduced costs where just recomputed or will be
110  // on the next call to GetReducedCosts().
112  return recompute_reduced_costs_ || are_reduced_costs_recomputed_;
113  }
114 
115  // Makes sure the next time the reduced cost are needed, they will be
116  // recomputed with maximum precision (i.e. from scratch with a basis
117  // refactorization first).
119 
120  // Randomly perturb the costs. Both Koberstein and Huangfu recommend doing
121  // that before the dual simplex starts in their Phd thesis.
122  //
123  // The perturbation follows what is explained in Huangfu Q (2013) "High
124  // performance simplex solver", Ph.D, dissertation, University of Edinburgh,
125  // section 3.2.3, page 58.
126  void PerturbCosts();
127 
128  // Shifts the cost of the given non-basic column such that its current reduced
129  // cost becomes 0.0. Actually, this shifts the cost a bit more according to
130  // the positive_direction parameter.
131  //
132  // This is explained in Koberstein's thesis (section 6.2.2.3) and helps on
133  // degenerate problems. As of july 2013, this allowed to pass dano3mip and
134  // dbic1 without cycling forever. Note that contrary to what is explained in
135  // the thesis, we do not shift any other variable costs. If any becomes
136  // infeasible, it will be selected and shifted in subsequent iterations.
137  void ShiftCostIfNeeded(bool increasing_rc_is_needed, ColIndex col);
138 
139  // Returns true if ShiftCostIfNeeded() was applied since the last
140  // ClearAndRemoveCostShifts().
141  bool HasCostShift() const { return has_cost_shift_; }
142 
143  // Returns true if this step direction make the given column even more
144  // infeasible. This is just used for reporting stats.
145  bool StepIsDualDegenerate(bool increasing_rc_is_needed, ColIndex col);
146 
147  // Removes any cost shift and cost perturbation. This also lazily forces a
148  // recomputation of all the derived quantities. This effectively resets the
149  // class to its initial state.
151 
152  // Invalidates all internal structure that depends on the objective function.
153  void ResetForNewObjective();
154 
155  // Invalidates the data that depends on the order of the column in basis_.
157 
158  // Returns the current reduced costs. If AreReducedCostsPrecise() is true,
159  // then for basic columns, this gives the error between 'c_B' and 'y.B' and
160  // for non-basic columns, this is the classic reduced cost. If it is false,
161  // then this is defined only for the columns in
162  // variables_info_.GetIsRelevantBitRow().
163  const DenseRow& GetReducedCosts();
164 
165  // Same as GetReducedCosts() but trigger a recomputation if not already done
166  // to have access to the reduced costs on all positions, not just the relevant
167  // one.
168  const DenseRow& GetFullReducedCosts();
169 
170  // Returns the dual values associated to the current basis.
171  const DenseColumn& GetDualValues();
172 
173  // Stats related functions.
174  std::string StatString() const { return stats_.StatString(); }
175 
176  // Returns the current dual feasibility tolerance.
178  return dual_feasibility_tolerance_;
179  }
180 
181  // Does basic checking of an entering candidate.
182  bool IsValidPrimalEnteringCandidate(ColIndex col) const;
183 
184  // Visible for testing.
185  const DenseRow& GetCostPerturbations() const { return cost_perturbations_; }
186 
187  // The deterministic time used by this class.
188  double DeterministicTime() const { return deterministic_time_; }
189 
190  // Registers a boolean that will be set to true each time the reduced costs
191  // are or will be recomputed. This allows anyone that depends on this to know
192  // that it cannot just assume an incremental changes and needs to updates its
193  // data. Important: UpdateBeforeBasisPivot() will not trigger this.
194  void AddRecomputationWatcher(bool* watcher) { watchers_.push_back(watcher); }
195 
196  private:
197  // Statistics about this class.
198  struct Stats : public StatsGroup {
199  Stats()
200  : StatsGroup("ReducedCosts"),
201  basic_objective_left_inverse_density(
202  "basic_objective_left_inverse_density", this),
203  reduced_costs_accuracy("reduced_costs_accuracy", this),
204  cost_shift("cost_shift", this) {}
205  RatioDistribution basic_objective_left_inverse_density;
206  DoubleDistribution reduced_costs_accuracy;
207  DoubleDistribution cost_shift;
208  };
209 
210  // All these Compute() functions fill the corresponding DenseRow using
211  // the current problem data.
212  void ComputeBasicObjective();
213  void ComputeReducedCosts();
214  void ComputeBasicObjectiveLeftInverse();
215 
216  // Updates reduced_costs_ according to the given pivot. This adds a multiple
217  // of the vector equal to 1.0 on the leaving column and given by
218  // ComputeUpdateRow() on the non-basic columns. The multiple is such that the
219  // new leaving reduced cost is zero.
220  void UpdateReducedCosts(ColIndex entering_col, ColIndex leaving_col,
221  RowIndex leaving_row, Fractional pivot,
222  UpdateRow* update_row);
223 
224  // Updates basic_objective_ according to the given pivot.
225  void UpdateBasicObjective(ColIndex entering_col, RowIndex leaving_row);
226 
227  // All places that do 'recompute_reduced_costs_ = true' must go through here.
228  void SetRecomputeReducedCostsAndNotifyWatchers();
229 
230  // Problem data that should be updated from outside.
231  const CompactSparseMatrix& matrix_;
232  const DenseRow& objective_;
233  const RowToColMapping& basis_;
234  const VariablesInfo& variables_info_;
235  const BasisFactorization& basis_factorization_;
236  absl::BitGenRef random_;
237 
238  // Internal data.
239  GlopParameters parameters_;
240  mutable Stats stats_;
241 
242  // Booleans to control what happens on the next ChooseEnteringColumn() call.
243  bool must_refactorize_basis_;
244  bool recompute_basic_objective_left_inverse_;
245  bool recompute_basic_objective_;
246  bool recompute_reduced_costs_;
247 
248  // Indicates if we have computed the reduced costs with a good precision.
249  bool are_reduced_costs_precise_;
250  bool are_reduced_costs_recomputed_;
251 
252  bool has_cost_shift_ = false;
253 
254  // Values of the objective on the columns of the basis. The order is given by
255  // the basis_ mapping. It is usually denoted as 'c_B' in the literature .
256  DenseRow basic_objective_;
257 
258  // Perturbations to the objective function. This may be introduced to
259  // counter degenerecency. It will be removed at the end of the algorithm.
260  DenseRow cost_perturbations_;
261 
262  // Reduced costs of the relevant columns of A.
263  DenseRow reduced_costs_;
264 
265  // Left inverse by B of the basic_objective_. This is known as 'y' or 'pi' in
266  // the literature. Its scalar product with a column 'a' of A gives the value
267  // of the scalar product of the basic objective with the right inverse of 'a'.
268  //
269  // TODO(user): using the unit_row_left_inverse_, we can update the
270  // basic_objective_left_inverse_ at each iteration, this is not needed for the
271  // algorithm, but may gives us a good idea of the current precision of our
272  // estimates. It is also faster to compute the unit_row_left_inverse_ because
273  // of sparsity.
274  ScatteredRow basic_objective_left_inverse_;
275 
276  // This is usually parameters_.dual_feasibility_tolerance() except when the
277  // dual residual error |y.B - c_B| is higher than it and we have to increase
278  // the tolerance.
279  Fractional dual_feasibility_tolerance_;
280 
281  // Boolean(s) to set to false when the reduced cost are changed outside of the
282  // UpdateBeforeBasisPivot() function.
283  std::vector<bool*> watchers_;
284 
285  double deterministic_time_ = 0.0;
286 
287  DISALLOW_COPY_AND_ASSIGN(ReducedCosts);
288 };
289 
290 // Maintains the list of dual infeasible positions and their associated prices.
291 //
292 // TODO(user): Not high priority but should probably be moved to its own file.
294  public:
295  // Takes references to what we need.
296  // TODO(user): Switch to a model based API like in CP-SAT.
297  PrimalPrices(absl::BitGenRef random, const VariablesInfo& variables_info,
298  PrimalEdgeNorms* primal_edge_norms, ReducedCosts* reduced_costs);
299 
300  // Returns the best candidate out of the dual infeasible positions to enter
301  // the basis during a primal simplex iterations.
302  ColIndex GetBestEnteringColumn();
303 
304  // Similar to the other UpdateBeforeBasisPivot() functions.
305  //
306  // Important: Both the primal norms and reduced costs must have been updated
307  // before this is called.
308  void UpdateBeforeBasisPivot(ColIndex entering_col, UpdateRow* update_row);
309 
310  // Triggers a recomputation of the price at the given column only.
311  void RecomputePriceAt(ColIndex col);
312 
313  // Same than RecomputePriceAt() for the case where we know the position is
314  // dual feasible.
316 
317  // If the incremental updates are not properly called for a while, then it is
318  // important to make sure that the prices will be recomputed the next time
319  // GetBestEnteringColumn() is called.
320  void ForceRecomputation() { recompute_ = true; }
321 
322  private:
323  // Recomputes the primal prices but only for the given column indices. If
324  // from_clean_state is true, then we assume that there is currently no
325  // candidates in prices_.
326  template <bool from_clean_state, typename ColumnsToUpdate>
327  void UpdateEnteringCandidates(const ColumnsToUpdate& cols);
328 
329  bool recompute_ = true;
330  DynamicMaximum<ColIndex> prices_;
331 
332  const VariablesInfo& variables_info_;
333  PrimalEdgeNorms* primal_edge_norms_;
334  ReducedCosts* reduced_costs_;
335 };
336 
337 } // namespace glop
338 } // namespace operations_research
339 
340 #endif // OR_TOOLS_GLOP_REDUCED_COSTS_H_
StrictITIVector< RowIndex, ColIndex > RowToColMapping
Definition: lp_types.h:346
Fractional ComputeMaximumDualInfeasibilityOnNonBoxedVariables()
void ShiftCostIfNeeded(bool increasing_rc_is_needed, ColIndex col)
ReducedCosts(const CompactSparseMatrix &matrix_, const DenseRow &objective, const RowToColMapping &basis, const VariablesInfo &variables_info, const BasisFactorization &basis_factorization, absl::BitGenRef random)
void SetNonBasicVariableCostToZero(ColIndex col, Fractional *current_cost)
Fractional GetDualFeasibilityTolerance() const
ColIndex col
Definition: markowitz.cc:183
Fractional TestEnteringReducedCostPrecision(ColIndex entering_col, const ScatteredColumn &direction)
void SetParameters(const GlopParameters &parameters)
PrimalPrices(absl::BitGenRef random, const VariablesInfo &variables_info, PrimalEdgeNorms *primal_edge_norms, ReducedCosts *reduced_costs)
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, UpdateRow *update_row)
void UpdateBeforeBasisPivot(ColIndex entering_col, UpdateRow *update_row)
bool IsValidPrimalEnteringCandidate(ColIndex col) const
Collection of objects used to extend the Constraint Solver library.
SatParameters parameters
StrictITIVector< ColIndex, Fractional > DenseRow
Definition: lp_types.h:303
const DenseRow & GetCostPerturbations() const
void SetAndDebugCheckThatColumnIsDualFeasible(ColIndex col)
bool StepIsDualDegenerate(bool increasing_rc_is_needed, ColIndex col)