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