OR-Tools  8.1
revised_simplex.cc
Go to the documentation of this file.
1 // Copyright 2010-2018 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 
15 
16 #include <algorithm>
17 #include <cmath>
18 #include <functional>
19 #include <map>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
24 #include "absl/strings/str_cat.h"
25 #include "absl/strings/str_format.h"
28 #include "ortools/base/logging.h"
37 #include "ortools/util/fp_utils.h"
38 
39 ABSL_FLAG(bool, simplex_display_numbers_as_fractions, false,
40  "Display numbers as fractions.");
41 ABSL_FLAG(bool, simplex_stop_after_first_basis, false,
42  "Stop after first basis has been computed.");
43 ABSL_FLAG(bool, simplex_stop_after_feasibility, false,
44  "Stop after first phase has been completed.");
45 ABSL_FLAG(bool, simplex_display_stats, false, "Display algorithm statistics.");
46 
47 namespace operations_research {
48 namespace glop {
49 namespace {
50 
51 // Calls the given closure upon destruction. It can be used to ensure that a
52 // closure is executed whenever a function returns.
53 class Cleanup {
54  public:
55  explicit Cleanup(std::function<void()> closure)
56  : closure_(std::move(closure)) {}
57  ~Cleanup() { closure_(); }
58 
59  private:
60  std::function<void()> closure_;
61 };
62 } // namespace
63 
64 #define DCHECK_COL_BOUNDS(col) \
65  { \
66  DCHECK_LE(0, col); \
67  DCHECK_GT(num_cols_, col); \
68  }
69 
70 #define DCHECK_ROW_BOUNDS(row) \
71  { \
72  DCHECK_LE(0, row); \
73  DCHECK_GT(num_rows_, row); \
74  }
75 
76 constexpr const uint64 kDeterministicSeed = 42;
77 
79  : problem_status_(ProblemStatus::INIT),
80  num_rows_(0),
81  num_cols_(0),
82  first_slack_col_(0),
83  objective_(),
84  lower_bound_(),
85  upper_bound_(),
86  basis_(),
87  variable_name_(),
88  direction_(),
89  error_(),
90  basis_factorization_(&compact_matrix_, &basis_),
91  variables_info_(compact_matrix_, lower_bound_, upper_bound_),
92  variable_values_(parameters_, compact_matrix_, basis_, variables_info_,
93  basis_factorization_),
94  dual_edge_norms_(basis_factorization_),
95  primal_edge_norms_(compact_matrix_, variables_info_,
96  basis_factorization_),
97  update_row_(compact_matrix_, transposed_matrix_, variables_info_, basis_,
98  basis_factorization_),
99  reduced_costs_(compact_matrix_, objective_, basis_, variables_info_,
100  basis_factorization_, &random_),
101  entering_variable_(variables_info_, &random_, &reduced_costs_,
102  &primal_edge_norms_),
103  num_iterations_(0),
104  num_feasibility_iterations_(0),
105  num_optimization_iterations_(0),
106  total_time_(0.0),
107  feasibility_time_(0.0),
108  optimization_time_(0.0),
109  last_deterministic_time_update_(0.0),
110  iteration_stats_(),
111  ratio_test_stats_(),
112  function_stats_("SimplexFunctionStats"),
113  parameters_(),
114  test_lu_(),
115  feasibility_phase_(true),
116  random_(kDeterministicSeed) {
117  SetParameters(parameters_);
118 }
119 
121  SCOPED_TIME_STAT(&function_stats_);
122  solution_state_.statuses.clear();
123 }
124 
126  SCOPED_TIME_STAT(&function_stats_);
127  solution_state_ = state;
128  solution_state_has_been_set_externally_ = true;
129 }
130 
132  notify_that_matrix_is_unchanged_ = true;
133 }
134 
136  SCOPED_TIME_STAT(&function_stats_);
137  DCHECK(lp.IsCleanedUp());
139  if (!lp.IsInEquationForm()) {
141  "The problem is not in the equations form.");
142  }
143  Cleanup update_deterministic_time_on_return(
144  [this, time_limit]() { AdvanceDeterministicTime(time_limit); });
145 
146  // Initialization. Note That Initialize() must be called first since it
147  // analyzes the current solver state.
148  const double start_time = time_limit->GetElapsedTime();
149  GLOP_RETURN_IF_ERROR(Initialize(lp));
150 
151  dual_infeasibility_improvement_direction_.clear();
152  update_row_.Invalidate();
153  test_lu_.Clear();
154  problem_status_ = ProblemStatus::INIT;
155  feasibility_phase_ = true;
156  num_iterations_ = 0;
157  num_feasibility_iterations_ = 0;
158  num_optimization_iterations_ = 0;
159  feasibility_time_ = 0.0;
160  optimization_time_ = 0.0;
161  total_time_ = 0.0;
162 
163  // In case we abort because of an error, we cannot assume that the current
164  // solution state will be in sync with all our internal data structure. In
165  // case we abort without resetting it, setting this allow us to still use the
166  // previous state info, but we will double-check everything.
167  solution_state_has_been_set_externally_ = true;
168 
169  if (VLOG_IS_ON(1)) {
170  ComputeNumberOfEmptyRows();
171  ComputeNumberOfEmptyColumns();
172  DisplayBasicVariableStatistics();
173  DisplayProblem();
174  }
175  if (absl::GetFlag(FLAGS_simplex_stop_after_first_basis)) {
176  DisplayAllStats();
177  return Status::OK();
178  }
179 
180  const bool use_dual = parameters_.use_dual_simplex();
181  const bool log_info = parameters_.log_search_progress() || VLOG_IS_ON(1);
182  if (log_info) {
183  LOG(INFO) << "------ " << (use_dual ? "Dual simplex." : "Primal simplex.");
184  LOG(INFO) << "The matrix has " << compact_matrix_.num_rows() << " rows, "
185  << compact_matrix_.num_cols() << " columns, "
186  << compact_matrix_.num_entries() << " entries.";
187  }
188 
189  // TODO(user): Avoid doing the first phase checks when we know from the
190  // incremental solve that the solution is already dual or primal feasible.
191  if (log_info) LOG(INFO) << "------ First phase: feasibility.";
192  entering_variable_.SetPricingRule(parameters_.feasibility_rule());
193  if (use_dual) {
194  if (parameters_.perturb_costs_in_dual_simplex()) {
195  reduced_costs_.PerturbCosts();
196  }
197 
198  variables_info_.MakeBoxedVariableRelevant(false);
199  GLOP_RETURN_IF_ERROR(DualMinimize(time_limit));
200  DisplayIterationInfo();
201 
202  if (problem_status_ != ProblemStatus::DUAL_INFEASIBLE) {
203  // Note(user): In most cases, the matrix will already be refactorized and
204  // both Refactorize() and PermuteBasis() will do nothing. However, if the
205  // time limit is reached during the first phase, this might not be the
206  // case and RecomputeBasicVariableValues() below DCHECKs that the matrix
207  // is refactorized. This is not required, but we currently only want to
208  // recompute values from scratch when the matrix was just refactorized to
209  // maximize precision.
210  GLOP_RETURN_IF_ERROR(basis_factorization_.Refactorize());
211  PermuteBasis();
212 
213  variables_info_.MakeBoxedVariableRelevant(true);
214  reduced_costs_.MakeReducedCostsPrecise();
215 
216  // This is needed to display errors properly.
217  MakeBoxedVariableDualFeasible(variables_info_.GetNonBasicBoxedVariables(),
218  /*update_basic_values=*/false);
219  variable_values_.RecomputeBasicVariableValues();
220  variable_values_.ResetPrimalInfeasibilityInformation();
221  }
222  } else {
223  reduced_costs_.MaintainDualInfeasiblePositions(true);
224  GLOP_RETURN_IF_ERROR(Minimize(time_limit));
225  DisplayIterationInfo();
226 
227  // After the primal phase I, we need to restore the objective.
228  if (problem_status_ != ProblemStatus::PRIMAL_INFEASIBLE) {
229  InitializeObjectiveAndTestIfUnchanged(lp);
230  reduced_costs_.ResetForNewObjective();
231  }
232  }
233 
234  // Reduced costs must be explicitly recomputed because DisplayErrors() is
235  // const.
236  // TODO(user): This API is not really nice.
237  reduced_costs_.GetReducedCosts();
238  DisplayErrors();
239 
240  feasibility_phase_ = false;
241  feasibility_time_ = time_limit->GetElapsedTime() - start_time;
242  entering_variable_.SetPricingRule(parameters_.optimization_rule());
243  num_feasibility_iterations_ = num_iterations_;
244 
245  if (log_info) LOG(INFO) << "------ Second phase: optimization.";
246 
247  // Because of shifts or perturbations, we may need to re-run a dual simplex
248  // after the primal simplex finished, or the opposite.
249  //
250  // We alter between solving with primal and dual Phase II algorithm as long as
251  // time limit permits *and* we did not yet achieve the desired precision.
252  // I.e., we run iteration i if the solution from iteration i-1 was not precise
253  // after we removed the bound and cost shifts and perturbations.
254  //
255  // NOTE(user): We may still hit the limit of max_number_of_reoptimizations()
256  // which means the status returned can be PRIMAL_FEASIBLE or DUAL_FEASIBLE
257  // (i.e., these statuses are not necesserily a consequence of hitting a time
258  // limit).
259  for (int num_optims = 0;
260  // We want to enter the loop when both num_optims and num_iterations_ are
261  // *equal* to the corresponding limits (to return a meaningful status
262  // when the limits are set to 0).
263  num_optims <= parameters_.max_number_of_reoptimizations() &&
264  !objective_limit_reached_ &&
265  (num_iterations_ == 0 ||
266  num_iterations_ < parameters_.max_number_of_iterations()) &&
267  !time_limit->LimitReached() &&
268  !absl::GetFlag(FLAGS_simplex_stop_after_feasibility) &&
269  (problem_status_ == ProblemStatus::PRIMAL_FEASIBLE ||
270  problem_status_ == ProblemStatus::DUAL_FEASIBLE);
271  ++num_optims) {
272  if (problem_status_ == ProblemStatus::PRIMAL_FEASIBLE) {
273  // Run the primal simplex.
274  reduced_costs_.MaintainDualInfeasiblePositions(true);
275  GLOP_RETURN_IF_ERROR(Minimize(time_limit));
276  } else {
277  // Run the dual simplex.
278  reduced_costs_.MaintainDualInfeasiblePositions(false);
279  GLOP_RETURN_IF_ERROR(DualMinimize(time_limit));
280  }
281 
282  // Minimize() or DualMinimize() always double check the result with maximum
283  // precision by refactoring the basis before exiting (except if an
284  // iteration or time limit was reached).
285  DCHECK(problem_status_ == ProblemStatus::PRIMAL_FEASIBLE ||
286  problem_status_ == ProblemStatus::DUAL_FEASIBLE ||
287  basis_factorization_.IsRefactorized());
288 
289  // If SetIntegralityScale() was called, we preform a polish operation.
290  if (!integrality_scale_.empty() &&
291  problem_status_ == ProblemStatus::OPTIMAL) {
292  reduced_costs_.MaintainDualInfeasiblePositions(true);
294  }
295 
296  // Remove the bound and cost shifts (or perturbations).
297  //
298  // Note(user): Currently, we never do both at the same time, so we could
299  // be a bit faster here, but then this is quick anyway.
300  variable_values_.ResetAllNonBasicVariableValues();
301  GLOP_RETURN_IF_ERROR(basis_factorization_.Refactorize());
302  PermuteBasis();
303  variable_values_.RecomputeBasicVariableValues();
304  reduced_costs_.ClearAndRemoveCostShifts();
305 
306  // Reduced costs must be explicitly recomputed because DisplayErrors() is
307  // const.
308  // TODO(user): This API is not really nice.
309  reduced_costs_.GetReducedCosts();
310  DisplayIterationInfo();
311  DisplayErrors();
312 
313  // TODO(user): We should also confirm the PRIMAL_UNBOUNDED or DUAL_UNBOUNDED
314  // status by checking with the other phase I that the problem is really
315  // DUAL_INFEASIBLE or PRIMAL_INFEASIBLE. For instance we currently report
316  // PRIMAL_UNBOUNDED with the primal on the problem l30.mps instead of
317  // OPTIMAL and the dual does not have issues on this problem.
318  if (problem_status_ == ProblemStatus::DUAL_UNBOUNDED) {
319  const Fractional tolerance = parameters_.solution_feasibility_tolerance();
320  if (reduced_costs_.ComputeMaximumDualResidual() > tolerance ||
321  variable_values_.ComputeMaximumPrimalResidual() > tolerance ||
322  reduced_costs_.ComputeMaximumDualInfeasibility() > tolerance) {
323  if (log_info) {
324  LOG(INFO) << "DUAL_UNBOUNDED was reported, but the residual and/or "
325  << "dual infeasibility is above the tolerance";
326  }
327  }
328  break;
329  }
330 
331  // Change the status, if after the shift and perturbation removal the
332  // problem is not OPTIMAL anymore.
333  if (problem_status_ == ProblemStatus::OPTIMAL) {
334  const Fractional solution_tolerance =
335  parameters_.solution_feasibility_tolerance();
336  if (variable_values_.ComputeMaximumPrimalResidual() >
337  solution_tolerance ||
338  reduced_costs_.ComputeMaximumDualResidual() > solution_tolerance) {
339  if (log_info) {
340  LOG(INFO) << "OPTIMAL was reported, yet one of the residuals is "
341  "above the solution feasibility tolerance after the "
342  "shift/perturbation are removed.";
343  }
344  if (parameters_.change_status_to_imprecise()) {
345  problem_status_ = ProblemStatus::IMPRECISE;
346  }
347  } else {
348  // We use the "precise" tolerances here to try to report the best
349  // possible solution.
350  const Fractional primal_tolerance =
351  parameters_.primal_feasibility_tolerance();
352  const Fractional dual_tolerance =
353  parameters_.dual_feasibility_tolerance();
354  const Fractional primal_infeasibility =
355  variable_values_.ComputeMaximumPrimalInfeasibility();
356  const Fractional dual_infeasibility =
357  reduced_costs_.ComputeMaximumDualInfeasibility();
358  if (primal_infeasibility > primal_tolerance &&
359  dual_infeasibility > dual_tolerance) {
360  if (log_info) {
361  LOG(INFO) << "OPTIMAL was reported, yet both of the infeasibility "
362  "are above the tolerance after the "
363  "shift/perturbation are removed.";
364  }
365  if (parameters_.change_status_to_imprecise()) {
366  problem_status_ = ProblemStatus::IMPRECISE;
367  }
368  } else if (primal_infeasibility > primal_tolerance) {
369  if (log_info) LOG(INFO) << "Re-optimizing with dual simplex ... ";
370  problem_status_ = ProblemStatus::DUAL_FEASIBLE;
371  } else if (dual_infeasibility > dual_tolerance) {
372  if (log_info) LOG(INFO) << "Re-optimizing with primal simplex ... ";
373  problem_status_ = ProblemStatus::PRIMAL_FEASIBLE;
374  }
375  }
376  }
377  }
378 
379  // Check that the return status is "precise".
380  //
381  // TODO(user): we curretnly skip the DUAL_INFEASIBLE status because the
382  // quantities are not up to date in this case.
383  if (parameters_.change_status_to_imprecise() &&
384  problem_status_ != ProblemStatus::DUAL_INFEASIBLE) {
385  const Fractional tolerance = parameters_.solution_feasibility_tolerance();
386  if (variable_values_.ComputeMaximumPrimalResidual() > tolerance ||
387  reduced_costs_.ComputeMaximumDualResidual() > tolerance) {
388  problem_status_ = ProblemStatus::IMPRECISE;
389  } else if (problem_status_ == ProblemStatus::DUAL_FEASIBLE ||
390  problem_status_ == ProblemStatus::DUAL_UNBOUNDED ||
391  problem_status_ == ProblemStatus::PRIMAL_INFEASIBLE) {
392  if (reduced_costs_.ComputeMaximumDualInfeasibility() > tolerance) {
393  problem_status_ = ProblemStatus::IMPRECISE;
394  }
395  } else if (problem_status_ == ProblemStatus::PRIMAL_FEASIBLE ||
396  problem_status_ == ProblemStatus::PRIMAL_UNBOUNDED ||
397  problem_status_ == ProblemStatus::DUAL_INFEASIBLE) {
398  if (variable_values_.ComputeMaximumPrimalInfeasibility() > tolerance) {
399  problem_status_ = ProblemStatus::IMPRECISE;
400  }
401  }
402  }
403 
404  // Store the result for the solution getters.
405  SaveState();
406  solution_objective_value_ = ComputeInitialProblemObjectiveValue();
407  solution_dual_values_ = reduced_costs_.GetDualValues();
408  solution_reduced_costs_ = reduced_costs_.GetReducedCosts();
409  if (lp.IsMaximizationProblem()) {
410  ChangeSign(&solution_dual_values_);
411  ChangeSign(&solution_reduced_costs_);
412  }
413 
414  // If the problem is unbounded, set the objective value to +/- infinity.
415  if (problem_status_ == ProblemStatus::DUAL_UNBOUNDED ||
416  problem_status_ == ProblemStatus::PRIMAL_UNBOUNDED) {
417  solution_objective_value_ =
418  (problem_status_ == ProblemStatus::DUAL_UNBOUNDED) ? kInfinity
419  : -kInfinity;
420  if (lp.IsMaximizationProblem()) {
421  solution_objective_value_ = -solution_objective_value_;
422  }
423  }
424 
425  total_time_ = time_limit->GetElapsedTime() - start_time;
426  optimization_time_ = total_time_ - feasibility_time_;
427  num_optimization_iterations_ = num_iterations_ - num_feasibility_iterations_;
428 
429  DisplayAllStats();
430  return Status::OK();
431 }
432 
434  return problem_status_;
435 }
436 
438  return solution_objective_value_;
439 }
440 
441 int64 RevisedSimplex::GetNumberOfIterations() const { return num_iterations_; }
442 
443 RowIndex RevisedSimplex::GetProblemNumRows() const { return num_rows_; }
444 
445 ColIndex RevisedSimplex::GetProblemNumCols() const { return num_cols_; }
446 
448  return variable_values_.Get(col);
449 }
450 
452  return solution_reduced_costs_[col];
453 }
454 
456  return solution_reduced_costs_;
457 }
458 
460  return solution_dual_values_[row];
461 }
462 
464  return variables_info_.GetStatusRow()[col];
465 }
466 
467 const BasisState& RevisedSimplex::GetState() const { return solution_state_; }
468 
470  // Note the negative sign since the slack variable is such that
471  // constraint_activity + slack_value = 0.
472  return -variable_values_.Get(SlackColIndex(row));
473 }
474 
476  // The status of the given constraint is the same as the status of the
477  // associated slack variable with a change of sign.
478  const VariableStatus s = variables_info_.GetStatusRow()[SlackColIndex(row)];
481  }
484  }
485  return VariableToConstraintStatus(s);
486 }
487 
489  DCHECK_EQ(problem_status_, ProblemStatus::PRIMAL_UNBOUNDED);
490  return solution_primal_ray_;
491 }
493  DCHECK_EQ(problem_status_, ProblemStatus::DUAL_UNBOUNDED);
494  return solution_dual_ray_;
495 }
496 
498  DCHECK_EQ(problem_status_, ProblemStatus::DUAL_UNBOUNDED);
499  return solution_dual_ray_row_combination_;
500 }
501 
502 ColIndex RevisedSimplex::GetBasis(RowIndex row) const { return basis_[row]; }
503 
505  DCHECK(basis_factorization_.GetColumnPermutation().empty());
506  return basis_factorization_;
507 }
508 
509 std::string RevisedSimplex::GetPrettySolverStats() const {
510  return absl::StrFormat(
511  "Problem status : %s\n"
512  "Solving time : %-6.4g\n"
513  "Number of iterations : %u\n"
514  "Time for solvability (first phase) : %-6.4g\n"
515  "Number of iterations for solvability : %u\n"
516  "Time for optimization : %-6.4g\n"
517  "Number of iterations for optimization : %u\n"
518  "Stop after first basis : %d\n",
519  GetProblemStatusString(problem_status_), total_time_, num_iterations_,
520  feasibility_time_, num_feasibility_iterations_, optimization_time_,
521  num_optimization_iterations_,
522  absl::GetFlag(FLAGS_simplex_stop_after_first_basis));
523 }
524 
526  // TODO(user): Also take into account the dual edge norms and the reduced cost
527  // updates.
528  return basis_factorization_.DeterministicTime() +
529  update_row_.DeterministicTime() +
530  primal_edge_norms_.DeterministicTime();
531 }
532 
533 void RevisedSimplex::SetVariableNames() {
534  variable_name_.resize(num_cols_, "");
535  for (ColIndex col(0); col < first_slack_col_; ++col) {
536  const ColIndex var_index = col + 1;
537  variable_name_[col] = absl::StrFormat("x%d", ColToIntIndex(var_index));
538  }
539  for (ColIndex col(first_slack_col_); col < num_cols_; ++col) {
540  const ColIndex var_index = col - first_slack_col_ + 1;
541  variable_name_[col] = absl::StrFormat("s%d", ColToIntIndex(var_index));
542  }
543 }
544 
545 VariableStatus RevisedSimplex::ComputeDefaultVariableStatus(
546  ColIndex col) const {
548  if (lower_bound_[col] == upper_bound_[col]) {
550  }
551  if (lower_bound_[col] == -kInfinity && upper_bound_[col] == kInfinity) {
552  return VariableStatus::FREE;
553  }
554 
555  // Returns the bound with the lowest magnitude. Note that it must be finite
556  // because the VariableStatus::FREE case was tested earlier.
557  DCHECK(IsFinite(lower_bound_[col]) || IsFinite(upper_bound_[col]));
558  return std::abs(lower_bound_[col]) <= std::abs(upper_bound_[col])
561 }
562 
563 void RevisedSimplex::SetNonBasicVariableStatusAndDeriveValue(
564  ColIndex col, VariableStatus status) {
565  variables_info_.UpdateToNonBasicStatus(col, status);
566  variable_values_.SetNonBasicVariableValueFromStatus(col);
567 }
568 
569 bool RevisedSimplex::BasisIsConsistent() const {
570  const DenseBitRow& is_basic = variables_info_.GetIsBasicBitRow();
571  const VariableStatusRow& variable_statuses = variables_info_.GetStatusRow();
572  for (RowIndex row(0); row < num_rows_; ++row) {
573  const ColIndex col = basis_[row];
574  if (!is_basic.IsSet(col)) return false;
575  if (variable_statuses[col] != VariableStatus::BASIC) return false;
576  }
577  ColIndex cols_in_basis(0);
578  ColIndex cols_not_in_basis(0);
579  for (ColIndex col(0); col < num_cols_; ++col) {
580  cols_in_basis += is_basic.IsSet(col);
581  cols_not_in_basis += !is_basic.IsSet(col);
582  if (is_basic.IsSet(col) !=
583  (variable_statuses[col] == VariableStatus::BASIC)) {
584  return false;
585  }
586  }
587  if (cols_in_basis != RowToColIndex(num_rows_)) return false;
588  if (cols_not_in_basis != num_cols_ - RowToColIndex(num_rows_)) return false;
589  return true;
590 }
591 
592 // Note(user): The basis factorization is not updated by this function but by
593 // UpdateAndPivot().
594 void RevisedSimplex::UpdateBasis(ColIndex entering_col, RowIndex basis_row,
595  VariableStatus leaving_variable_status) {
596  SCOPED_TIME_STAT(&function_stats_);
597  DCHECK_COL_BOUNDS(entering_col);
598  DCHECK_ROW_BOUNDS(basis_row);
599 
600  // Check that this is not called with an entering_col already in the basis
601  // and that the leaving col is indeed in the basis.
602  DCHECK(!variables_info_.GetIsBasicBitRow().IsSet(entering_col));
603  DCHECK_NE(basis_[basis_row], entering_col);
604  DCHECK_NE(basis_[basis_row], kInvalidCol);
605 
606  const ColIndex leaving_col = basis_[basis_row];
607  DCHECK(variables_info_.GetIsBasicBitRow().IsSet(leaving_col));
608 
609  // Make leaving_col leave the basis and update relevant data.
610  // Note thate the leaving variable value is not necessarily at its exact
611  // bound, which is like a bound shift.
612  variables_info_.Update(leaving_col, leaving_variable_status);
613  DCHECK(leaving_variable_status == VariableStatus::AT_UPPER_BOUND ||
614  leaving_variable_status == VariableStatus::AT_LOWER_BOUND ||
615  leaving_variable_status == VariableStatus::FIXED_VALUE);
616 
617  basis_[basis_row] = entering_col;
618  variables_info_.Update(entering_col, VariableStatus::BASIC);
619  update_row_.Invalidate();
620 }
621 
622 namespace {
623 
624 // Comparator used to sort column indices according to a given value vector.
625 class ColumnComparator {
626  public:
627  explicit ColumnComparator(const DenseRow& value) : value_(value) {}
628  bool operator()(ColIndex col_a, ColIndex col_b) const {
629  return value_[col_a] < value_[col_b];
630  }
631 
632  private:
633  const DenseRow& value_;
634 };
635 
636 } // namespace
637 
638 // To understand better what is going on in this function, let us say that this
639 // algorithm will produce the optimal solution to a problem containing only
640 // singleton columns (provided that the variables start at the minimum possible
641 // cost, see ComputeDefaultVariableStatus()). This is unit tested.
642 //
643 // The error_ must be equal to the constraint activity for the current variable
644 // values before this function is called. If error_[row] is 0.0, that mean this
645 // constraint is currently feasible.
646 void RevisedSimplex::UseSingletonColumnInInitialBasis(RowToColMapping* basis) {
647  SCOPED_TIME_STAT(&function_stats_);
648  // Computes the singleton columns and the cost variation of the corresponding
649  // variables (in the only possible direction, i.e away from its current bound)
650  // for a unit change in the infeasibility of the corresponding row.
651  //
652  // Note that the slack columns will be treated as normal singleton columns.
653  std::vector<ColIndex> singleton_column;
654  DenseRow cost_variation(num_cols_, 0.0);
655  for (ColIndex col(0); col < num_cols_; ++col) {
656  if (compact_matrix_.column(col).num_entries() != 1) continue;
657  if (lower_bound_[col] == upper_bound_[col]) continue;
658  const Fractional slope = compact_matrix_.column(col).GetFirstCoefficient();
659  if (variable_values_.Get(col) == lower_bound_[col]) {
660  cost_variation[col] = objective_[col] / std::abs(slope);
661  } else {
662  cost_variation[col] = -objective_[col] / std::abs(slope);
663  }
664  singleton_column.push_back(col);
665  }
666  if (singleton_column.empty()) return;
667 
668  // Sort the singleton columns for the case where many of them correspond to
669  // the same row (equivalent to a piecewise-linear objective on this variable).
670  // Negative cost_variation first since moving the singleton variable away from
671  // its current bound means the least decrease in the objective function for
672  // the same "error" variation.
673  ColumnComparator comparator(cost_variation);
674  std::sort(singleton_column.begin(), singleton_column.end(), comparator);
675  DCHECK_LE(cost_variation[singleton_column.front()],
676  cost_variation[singleton_column.back()]);
677 
678  // Use a singleton column to "absorb" the error when possible to avoid
679  // introducing unneeded artificial variables. Note that with scaling on, the
680  // only possible coefficient values are 1.0 or -1.0 (or maybe epsilon close to
681  // them) and that the SingletonColumnSignPreprocessor makes them all positive.
682  // However, this code works for any coefficient value.
683  const DenseRow& variable_values = variable_values_.GetDenseRow();
684  for (const ColIndex col : singleton_column) {
685  const RowIndex row = compact_matrix_.column(col).EntryRow(EntryIndex(0));
686 
687  // If no singleton columns have entered the basis for this row, choose the
688  // first one. It will be the one with the least decrease in the objective
689  // function when it leaves the basis.
690  if ((*basis)[row] == kInvalidCol) {
691  (*basis)[row] = col;
692  }
693 
694  // If there is already no error in this row (i.e. it is primal-feasible),
695  // there is nothing to do.
696  if (error_[row] == 0.0) continue;
697 
698  // In this case, all the infeasibility can be "absorbed" and this variable
699  // may not be at one of its bound anymore, so we have to use it in the
700  // basis.
701  const Fractional coeff =
702  compact_matrix_.column(col).EntryCoefficient(EntryIndex(0));
703  const Fractional new_value = variable_values[col] + error_[row] / coeff;
704  if (new_value >= lower_bound_[col] && new_value <= upper_bound_[col]) {
705  error_[row] = 0.0;
706 
707  // Use this variable in the initial basis.
708  (*basis)[row] = col;
709  continue;
710  }
711 
712  // The idea here is that if the singleton column cannot be used to "absorb"
713  // all error_[row], if it is boxed, it can still be used to make the
714  // infeasibility smaller (with a bound flip).
715  const Fractional box_width = variables_info_.GetBoundDifference(col);
716  DCHECK_NE(box_width, 0.0);
717  DCHECK_NE(error_[row], 0.0);
718  const Fractional error_sign = error_[row] / coeff;
719  if (variable_values[col] == lower_bound_[col] && error_sign > 0.0) {
720  DCHECK(IsFinite(box_width));
721  error_[row] -= coeff * box_width;
722  SetNonBasicVariableStatusAndDeriveValue(col,
724  continue;
725  }
726  if (variable_values[col] == upper_bound_[col] && error_sign < 0.0) {
727  DCHECK(IsFinite(box_width));
728  error_[row] += coeff * box_width;
729  SetNonBasicVariableStatusAndDeriveValue(col,
731  continue;
732  }
733  }
734 }
735 
736 bool RevisedSimplex::InitializeMatrixAndTestIfUnchanged(
737  const LinearProgram& lp, bool* only_change_is_new_rows,
738  bool* only_change_is_new_cols, ColIndex* num_new_cols) {
739  SCOPED_TIME_STAT(&function_stats_);
740  DCHECK(only_change_is_new_rows != nullptr);
741  DCHECK(only_change_is_new_cols != nullptr);
742  DCHECK(num_new_cols != nullptr);
743  DCHECK_NE(kInvalidCol, lp.GetFirstSlackVariable());
744  DCHECK_EQ(num_cols_, compact_matrix_.num_cols());
745  DCHECK_EQ(num_rows_, compact_matrix_.num_rows());
746 
747  DCHECK_EQ(lp.num_variables(),
748  lp.GetFirstSlackVariable() + RowToColIndex(lp.num_constraints()));
749  DCHECK(IsRightMostSquareMatrixIdentity(lp.GetSparseMatrix()));
750  const bool old_part_of_matrix_is_unchanged =
752  num_rows_, first_slack_col_, lp.GetSparseMatrix(), compact_matrix_);
753 
754  // Test if the matrix is unchanged, and if yes, just returns true. Note that
755  // this doesn't check the columns corresponding to the slack variables,
756  // because they were checked by lp.IsInEquationForm() when Solve() was called.
757  if (old_part_of_matrix_is_unchanged && lp.num_constraints() == num_rows_ &&
758  lp.num_variables() == num_cols_) {
759  return true;
760  }
761 
762  // Check if the new matrix can be derived from the old one just by adding
763  // new rows (i.e new constraints).
764  *only_change_is_new_rows = old_part_of_matrix_is_unchanged &&
765  lp.num_constraints() > num_rows_ &&
766  lp.GetFirstSlackVariable() == first_slack_col_;
767 
768  // Check if the new matrix can be derived from the old one just by adding
769  // new columns (i.e new variables).
770  *only_change_is_new_cols = old_part_of_matrix_is_unchanged &&
771  lp.num_constraints() == num_rows_ &&
772  lp.GetFirstSlackVariable() > first_slack_col_;
773  *num_new_cols =
774  *only_change_is_new_cols ? lp.num_variables() - num_cols_ : ColIndex(0);
775 
776  // Initialize first_slack_.
777  first_slack_col_ = lp.GetFirstSlackVariable();
778 
779  // Initialize the new dimensions.
780  num_rows_ = lp.num_constraints();
781  num_cols_ = lp.num_variables();
782 
783  // Populate compact_matrix_ and transposed_matrix_ if needed. Note that we
784  // already added all the slack variables at this point, so matrix_ will not
785  // change anymore.
786  // TODO(user): This can be sped up by removing the MatrixView.
787  compact_matrix_.PopulateFromMatrixView(MatrixView(lp.GetSparseMatrix()));
788  if (parameters_.use_transposed_matrix()) {
789  transposed_matrix_.PopulateFromTranspose(compact_matrix_);
790  }
791  return false;
792 }
793 
794 bool RevisedSimplex::OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
795  const LinearProgram& lp, ColIndex num_new_cols) {
796  SCOPED_TIME_STAT(&function_stats_);
797  DCHECK_EQ(lp.num_variables(), num_cols_);
798  DCHECK_LE(num_new_cols, first_slack_col_);
799  const ColIndex first_new_col(first_slack_col_ - num_new_cols);
800 
801  // Check the original variable bounds.
802  for (ColIndex col(0); col < first_new_col; ++col) {
803  if (lower_bound_[col] != lp.variable_lower_bounds()[col] ||
804  upper_bound_[col] != lp.variable_upper_bounds()[col]) {
805  return false;
806  }
807  }
808  // Check that each new variable has a bound of zero.
809  for (ColIndex col(first_new_col); col < first_slack_col_; ++col) {
810  if (lp.variable_lower_bounds()[col] != 0.0 &&
811  lp.variable_upper_bounds()[col] != 0.0) {
812  return false;
813  }
814  }
815  // Check that the slack bounds are unchanged.
816  for (ColIndex col(first_slack_col_); col < num_cols_; ++col) {
817  if (lower_bound_[col - num_new_cols] != lp.variable_lower_bounds()[col] ||
818  upper_bound_[col - num_new_cols] != lp.variable_upper_bounds()[col]) {
819  return false;
820  }
821  }
822  return true;
823 }
824 
825 bool RevisedSimplex::InitializeBoundsAndTestIfUnchanged(
826  const LinearProgram& lp) {
827  SCOPED_TIME_STAT(&function_stats_);
828  lower_bound_.resize(num_cols_, 0.0);
829  upper_bound_.resize(num_cols_, 0.0);
830  bound_perturbation_.AssignToZero(num_cols_);
831 
832  // Variable bounds, for both non-slack and slack variables.
833  bool bounds_are_unchanged = true;
834  DCHECK_EQ(lp.num_variables(), num_cols_);
835  for (ColIndex col(0); col < lp.num_variables(); ++col) {
836  if (lower_bound_[col] != lp.variable_lower_bounds()[col] ||
837  upper_bound_[col] != lp.variable_upper_bounds()[col]) {
838  bounds_are_unchanged = false;
839  break;
840  }
841  }
842  if (!bounds_are_unchanged) {
843  lower_bound_ = lp.variable_lower_bounds();
844  upper_bound_ = lp.variable_upper_bounds();
845  }
846  return bounds_are_unchanged;
847 }
848 
849 bool RevisedSimplex::InitializeObjectiveAndTestIfUnchanged(
850  const LinearProgram& lp) {
851  SCOPED_TIME_STAT(&function_stats_);
852 
853  bool objective_is_unchanged = true;
854  objective_.resize(num_cols_, 0.0);
855  DCHECK_EQ(num_cols_, lp.num_variables());
856  if (lp.IsMaximizationProblem()) {
857  // Note that we use the minimization version of the objective internally.
858  for (ColIndex col(0); col < lp.num_variables(); ++col) {
859  const Fractional coeff = -lp.objective_coefficients()[col];
860  if (objective_[col] != coeff) {
861  objective_is_unchanged = false;
862  }
863  objective_[col] = coeff;
864  }
865  objective_offset_ = -lp.objective_offset();
866  objective_scaling_factor_ = -lp.objective_scaling_factor();
867  } else {
868  for (ColIndex col(0); col < lp.num_variables(); ++col) {
869  if (objective_[col] != lp.objective_coefficients()[col]) {
870  objective_is_unchanged = false;
871  break;
872  }
873  }
874  if (!objective_is_unchanged) {
875  objective_ = lp.objective_coefficients();
876  }
877  objective_offset_ = lp.objective_offset();
878  objective_scaling_factor_ = lp.objective_scaling_factor();
879  }
880  return objective_is_unchanged;
881 }
882 
883 void RevisedSimplex::InitializeObjectiveLimit(const LinearProgram& lp) {
884  objective_limit_reached_ = false;
885  DCHECK(std::isfinite(objective_offset_));
886  DCHECK(std::isfinite(objective_scaling_factor_));
887  DCHECK_NE(0.0, objective_scaling_factor_);
888 
889  // This sets dual_objective_limit_ and then primal_objective_limit_.
890  const Fractional tolerance = parameters_.solution_feasibility_tolerance();
891  for (const bool set_dual : {true, false}) {
892  // NOTE(user): If objective_scaling_factor_ is negative, the optimization
893  // direction was reversed (during preprocessing or inside revised simplex),
894  // i.e., the original problem is maximization. In such case the _meaning_ of
895  // the lower and upper limits is swapped. To this end we must change the
896  // signs of limits, which happens automatically when calculating shifted
897  // limits. We must also use upper (resp. lower) limit in place of lower
898  // (resp. upper) limit when calculating the final objective_limit_.
899  //
900  // Choose lower limit if using the dual simplex and scaling factor is
901  // negative or if using the primal simplex and scaling is nonnegative, upper
902  // limit otherwise.
903  const Fractional limit = (objective_scaling_factor_ >= 0.0) != set_dual
904  ? parameters_.objective_lower_limit()
905  : parameters_.objective_upper_limit();
906  const Fractional shifted_limit =
907  limit / objective_scaling_factor_ - objective_offset_;
908 
909  // The isfinite() test is there to avoid generating NaNs with clang in
910  // fast-math mode on iOS 9.3.i.
911  if (set_dual) {
912  dual_objective_limit_ = std::isfinite(shifted_limit)
913  ? shifted_limit * (1.0 + tolerance)
914  : shifted_limit;
915  } else {
916  primal_objective_limit_ = std::isfinite(shifted_limit)
917  ? shifted_limit * (1.0 - tolerance)
918  : shifted_limit;
919  }
920  }
921 }
922 
923 void RevisedSimplex::InitializeVariableStatusesForWarmStart(
924  const BasisState& state, ColIndex num_new_cols) {
925  variables_info_.InitializeAndComputeType();
926  RowIndex num_basic_variables(0);
927  DCHECK_LE(num_new_cols, first_slack_col_);
928  const ColIndex first_new_col(first_slack_col_ - num_new_cols);
929  // Compute the status for all the columns (note that the slack variables are
930  // already added at the end of the matrix at this stage).
931  for (ColIndex col(0); col < num_cols_; ++col) {
932  const VariableStatus default_status = ComputeDefaultVariableStatus(col);
933 
934  // Start with the given "warm" status from the BasisState if it exists.
935  VariableStatus status = default_status;
936  if (col < first_new_col && col < state.statuses.size()) {
937  status = state.statuses[col];
938  } else if (col >= first_slack_col_ &&
939  col - num_new_cols < state.statuses.size()) {
940  status = state.statuses[col - num_new_cols];
941  }
942 
943  if (status == VariableStatus::BASIC) {
944  // Do not allow more than num_rows_ VariableStatus::BASIC variables.
945  if (num_basic_variables == num_rows_) {
946  VLOG(1) << "Too many basic variables in the warm-start basis."
947  << "Only keeping the first ones as VariableStatus::BASIC.";
948  variables_info_.UpdateToNonBasicStatus(col, default_status);
949  } else {
950  ++num_basic_variables;
951  variables_info_.UpdateToBasicStatus(col);
952  }
953  } else {
954  // Remove incompatibilities between the warm status and the variable
955  // bounds. We use the default status as an indication of the bounds
956  // type.
957  if ((status != default_status) &&
958  ((default_status == VariableStatus::FIXED_VALUE) ||
959  (status == VariableStatus::FREE) ||
960  (status == VariableStatus::FIXED_VALUE) ||
961  (status == VariableStatus::AT_LOWER_BOUND &&
962  lower_bound_[col] == -kInfinity) ||
963  (status == VariableStatus::AT_UPPER_BOUND &&
964  upper_bound_[col] == kInfinity))) {
965  status = default_status;
966  }
967  variables_info_.UpdateToNonBasicStatus(col, status);
968  }
969  }
970 
971  // Initialize the values.
972  variable_values_.ResetAllNonBasicVariableValues();
973 }
974 
975 // This implementation starts with an initial matrix B equal to the identity
976 // matrix (modulo a column permutation). For that it uses either the slack
977 // variables or the singleton columns present in the problem. Afterwards, the
978 // fixed slacks in the basis are exchanged with normal columns of A if possible
979 // by the InitialBasis class.
980 Status RevisedSimplex::CreateInitialBasis() {
981  SCOPED_TIME_STAT(&function_stats_);
982 
983  // Initialize the variable values and statuses.
984  // Note that for the dual algorithm, boxed variables will be made
985  // dual-feasible later by MakeBoxedVariableDualFeasible(), so it doesn't
986  // really matter at which of their two finite bounds they start.
987  int num_free_variables = 0;
988  variables_info_.InitializeAndComputeType();
989  for (ColIndex col(0); col < num_cols_; ++col) {
990  const VariableStatus status = ComputeDefaultVariableStatus(col);
991  SetNonBasicVariableStatusAndDeriveValue(col, status);
992  if (status == VariableStatus::FREE) ++num_free_variables;
993  }
994  VLOG(1) << "Number of free variables in the problem: " << num_free_variables;
995 
996  // Start by using an all-slack basis.
997  RowToColMapping basis(num_rows_, kInvalidCol);
998  for (RowIndex row(0); row < num_rows_; ++row) {
999  basis[row] = SlackColIndex(row);
1000  }
1001 
1002  // If possible, for the primal simplex we replace some slack variables with
1003  // some singleton columns present in the problem.
1004  if (!parameters_.use_dual_simplex() &&
1005  parameters_.initial_basis() != GlopParameters::MAROS &&
1006  parameters_.exploit_singleton_column_in_initial_basis()) {
1007  // For UseSingletonColumnInInitialBasis() to work better, we change
1008  // the value of the boxed singleton column with a non-zero cost to the best
1009  // of their two bounds.
1010  for (ColIndex col(0); col < num_cols_; ++col) {
1011  if (compact_matrix_.column(col).num_entries() != 1) continue;
1012  const VariableStatus status = variables_info_.GetStatusRow()[col];
1013  const Fractional objective = objective_[col];
1014  if (objective > 0 && IsFinite(lower_bound_[col]) &&
1015  status == VariableStatus::AT_UPPER_BOUND) {
1016  SetNonBasicVariableStatusAndDeriveValue(col,
1018  } else if (objective < 0 && IsFinite(upper_bound_[col]) &&
1019  status == VariableStatus::AT_LOWER_BOUND) {
1020  SetNonBasicVariableStatusAndDeriveValue(col,
1022  }
1023  }
1024 
1025  // Compute the primal infeasibility of the initial variable values in
1026  // error_.
1027  ComputeVariableValuesError();
1028 
1029  // TODO(user): A better but slightly more complex algorithm would be to:
1030  // - Ignore all singleton columns except the slacks during phase I.
1031  // - For this, change the slack variable bounds accordingly.
1032  // - At the end of phase I, restore the slack variable bounds and perform
1033  // the same algorithm to start with feasible and "optimal" values of the
1034  // singleton columns.
1035  basis.assign(num_rows_, kInvalidCol);
1036  UseSingletonColumnInInitialBasis(&basis);
1037 
1038  // Eventually complete the basis with fixed slack columns.
1039  for (RowIndex row(0); row < num_rows_; ++row) {
1040  if (basis[row] == kInvalidCol) {
1041  basis[row] = SlackColIndex(row);
1042  }
1043  }
1044  }
1045 
1046  // Use an advanced initial basis to remove the fixed variables from the basis.
1047  if (parameters_.initial_basis() == GlopParameters::NONE) {
1048  return InitializeFirstBasis(basis);
1049  }
1050  if (parameters_.initial_basis() == GlopParameters::MAROS) {
1051  InitialBasis initial_basis(compact_matrix_, objective_, lower_bound_,
1052  upper_bound_, variables_info_.GetTypeRow());
1053  if (parameters_.use_dual_simplex()) {
1054  // This dual version only uses zero-cost columns to complete the
1055  // basis.
1056  initial_basis.GetDualMarosBasis(num_cols_, &basis);
1057  } else {
1058  initial_basis.GetPrimalMarosBasis(num_cols_, &basis);
1059  }
1060  int number_changed = 0;
1061  for (RowIndex row(0); row < num_rows_; ++row) {
1062  if (basis[row] != SlackColIndex(row)) {
1063  number_changed++;
1064  }
1065  }
1066  VLOG(1) << "Number of Maros basis changes: " << number_changed;
1067  } else if (parameters_.initial_basis() == GlopParameters::BIXBY ||
1068  parameters_.initial_basis() == GlopParameters::TRIANGULAR) {
1069  // First unassign the fixed variables from basis.
1070  int num_fixed_variables = 0;
1071  for (RowIndex row(0); row < basis.size(); ++row) {
1072  const ColIndex col = basis[row];
1073  if (lower_bound_[col] == upper_bound_[col]) {
1074  basis[row] = kInvalidCol;
1075  ++num_fixed_variables;
1076  }
1077  }
1078 
1079  if (num_fixed_variables == 0) {
1080  VLOG(1) << "Crash is set to " << parameters_.initial_basis()
1081  << " but there is no equality rows to remove from initial all "
1082  "slack basis.";
1083  } else {
1084  // Then complete the basis with an advanced initial basis algorithm.
1085  VLOG(1) << "Trying to remove " << num_fixed_variables
1086  << " fixed variables from the initial basis.";
1087  InitialBasis initial_basis(compact_matrix_, objective_, lower_bound_,
1088  upper_bound_, variables_info_.GetTypeRow());
1089 
1090  if (parameters_.initial_basis() == GlopParameters::BIXBY) {
1091  if (parameters_.use_scaling()) {
1092  initial_basis.CompleteBixbyBasis(first_slack_col_, &basis);
1093  } else {
1094  VLOG(1) << "Bixby initial basis algorithm requires the problem "
1095  << "to be scaled. Skipping Bixby's algorithm.";
1096  }
1097  } else if (parameters_.initial_basis() == GlopParameters::TRIANGULAR) {
1098  // Note the use of num_cols_ here because this algorithm
1099  // benefits from treating fixed slack columns like any other column.
1100  if (parameters_.use_dual_simplex()) {
1101  // This dual version only uses zero-cost columns to complete the
1102  // basis.
1103  initial_basis.CompleteTriangularDualBasis(num_cols_, &basis);
1104  } else {
1105  initial_basis.CompleteTriangularPrimalBasis(num_cols_, &basis);
1106  }
1107 
1108  const Status status = InitializeFirstBasis(basis);
1109  if (status.ok()) {
1110  return status;
1111  } else {
1112  VLOG(1) << "Reverting to all slack basis.";
1113 
1114  for (RowIndex row(0); row < num_rows_; ++row) {
1115  basis[row] = SlackColIndex(row);
1116  }
1117  }
1118  }
1119  }
1120  } else {
1121  LOG(WARNING) << "Unsupported initial_basis parameters: "
1122  << parameters_.initial_basis();
1123  }
1124 
1125  return InitializeFirstBasis(basis);
1126 }
1127 
1128 Status RevisedSimplex::InitializeFirstBasis(const RowToColMapping& basis) {
1129  basis_ = basis;
1130 
1131  // For each row which does not have a basic column, assign it to the
1132  // corresponding slack column.
1133  basis_.resize(num_rows_, kInvalidCol);
1134  for (RowIndex row(0); row < num_rows_; ++row) {
1135  if (basis_[row] == kInvalidCol) {
1136  basis_[row] = SlackColIndex(row);
1137  }
1138  }
1139 
1140  GLOP_RETURN_IF_ERROR(basis_factorization_.Initialize());
1141  PermuteBasis();
1142 
1143  // Test that the upper bound on the condition number of basis is not too high.
1144  // The number was not computed by any rigorous analysis, we just prefer to
1145  // revert to the all slack basis if the condition number of our heuristic
1146  // first basis seems bad. See for instance on cond11.mps, where we get an
1147  // infinity upper bound.
1148  const Fractional condition_number_ub =
1149  basis_factorization_.ComputeInfinityNormConditionNumberUpperBound();
1150  if (condition_number_ub > parameters_.initial_condition_number_threshold()) {
1151  const std::string error_message =
1152  absl::StrCat("The matrix condition number upper bound is too high: ",
1153  condition_number_ub);
1154  VLOG(1) << error_message;
1155  return Status(Status::ERROR_LU, error_message);
1156  }
1157 
1158  // Everything is okay, finish the initialization.
1159  for (RowIndex row(0); row < num_rows_; ++row) {
1160  variables_info_.Update(basis_[row], VariableStatus::BASIC);
1161  }
1162  DCHECK(BasisIsConsistent());
1163 
1164  // TODO(user): Maybe return an error status if this is too high. Note however
1165  // that if we want to do that, we need to reset variables_info_ to a
1166  // consistent state.
1167  variable_values_.RecomputeBasicVariableValues();
1168  if (VLOG_IS_ON(1)) {
1169  const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1170  if (variable_values_.ComputeMaximumPrimalResidual() > tolerance) {
1171  VLOG(1) << absl::StrCat(
1172  "The primal residual of the initial basis is above the tolerance, ",
1173  variable_values_.ComputeMaximumPrimalResidual(), " vs. ", tolerance);
1174  }
1175  }
1176  return Status::OK();
1177 }
1178 
1179 Status RevisedSimplex::Initialize(const LinearProgram& lp) {
1180  parameters_ = initial_parameters_;
1181  PropagateParameters();
1182 
1183  // Calling InitializeMatrixAndTestIfUnchanged() first is important because
1184  // this is where num_rows_ and num_cols_ are computed.
1185  //
1186  // Note that these functions can't depend on use_dual_simplex() since we may
1187  // change it below.
1188  ColIndex num_new_cols(0);
1189  bool only_change_is_new_rows = false;
1190  bool only_change_is_new_cols = false;
1191  bool matrix_is_unchanged = true;
1192  bool only_new_bounds = false;
1193  if (solution_state_.IsEmpty() || !notify_that_matrix_is_unchanged_) {
1194  matrix_is_unchanged = InitializeMatrixAndTestIfUnchanged(
1195  lp, &only_change_is_new_rows, &only_change_is_new_cols, &num_new_cols);
1196  only_new_bounds = only_change_is_new_cols && num_new_cols > 0 &&
1197  OldBoundsAreUnchangedAndNewVariablesHaveOneBoundAtZero(
1198  lp, num_new_cols);
1199  } else if (DEBUG_MODE) {
1200  CHECK(InitializeMatrixAndTestIfUnchanged(
1201  lp, &only_change_is_new_rows, &only_change_is_new_cols, &num_new_cols));
1202  }
1203  notify_that_matrix_is_unchanged_ = false;
1204  const bool objective_is_unchanged = InitializeObjectiveAndTestIfUnchanged(lp);
1205  const bool bounds_are_unchanged = InitializeBoundsAndTestIfUnchanged(lp);
1206 
1207  // If parameters_.allow_simplex_algorithm_change() is true and we already have
1208  // a primal (resp. dual) feasible solution, then we use the primal (resp.
1209  // dual) algorithm since there is a good chance that it will be faster.
1210  if (matrix_is_unchanged && parameters_.allow_simplex_algorithm_change()) {
1211  if (objective_is_unchanged && !bounds_are_unchanged) {
1212  parameters_.set_use_dual_simplex(true);
1213  PropagateParameters();
1214  }
1215  if (bounds_are_unchanged && !objective_is_unchanged) {
1216  parameters_.set_use_dual_simplex(false);
1217  PropagateParameters();
1218  }
1219  }
1220 
1221  InitializeObjectiveLimit(lp);
1222 
1223  // Computes the variable name as soon as possible for logging.
1224  // TODO(user): do we really need to store them? we could just compute them
1225  // on the fly since we do not need the speed.
1226  if (VLOG_IS_ON(1)) {
1227  SetVariableNames();
1228  }
1229 
1230  // Warm-start? This is supported only if the solution_state_ is non empty,
1231  // i.e., this revised simplex i) was already used to solve a problem, or
1232  // ii) the solution state was provided externally. Note that the
1233  // solution_state_ may have nothing to do with the current problem, e.g.,
1234  // objective, matrix, and/or bounds had changed. So we support several
1235  // scenarios of warm-start depending on how did the problem change and which
1236  // simplex algorithm is used (primal or dual).
1237  bool solve_from_scratch = true;
1238 
1239  // Try to perform a "quick" warm-start with no matrix factorization involved.
1240  if (!solution_state_.IsEmpty() && !solution_state_has_been_set_externally_) {
1241  if (!parameters_.use_dual_simplex()) {
1242  // With primal simplex, always clear dual norms and dual pricing.
1243  // Incrementality is supported only if only change to the matrix and
1244  // bounds is adding new columns (objective may change), and that all
1245  // new columns have a bound equal to zero.
1246  dual_edge_norms_.Clear();
1247  dual_pricing_vector_.clear();
1248  if (matrix_is_unchanged && bounds_are_unchanged) {
1249  // TODO(user): Do not do that if objective_is_unchanged. Currently
1250  // this seems to break something. Investigate.
1251  reduced_costs_.ClearAndRemoveCostShifts();
1252  solve_from_scratch = false;
1253  } else if (only_change_is_new_cols && only_new_bounds) {
1254  InitializeVariableStatusesForWarmStart(solution_state_, num_new_cols);
1255  const ColIndex first_new_col(first_slack_col_ - num_new_cols);
1256  for (ColIndex& col_ref : basis_) {
1257  if (col_ref >= first_new_col) {
1258  col_ref += num_new_cols;
1259  }
1260  }
1261 
1262  // Make sure the primal edge norm are recomputed from scratch.
1263  // TODO(user): only the norms of the new columns actually need to be
1264  // computed.
1265  primal_edge_norms_.Clear();
1266  reduced_costs_.ClearAndRemoveCostShifts();
1267  solve_from_scratch = false;
1268  }
1269  } else {
1270  // With dual simplex, always clear primal norms. Incrementality is
1271  // supported only if the objective remains the same (the matrix may
1272  // contain new rows and the bounds may change).
1273  primal_edge_norms_.Clear();
1274  if (objective_is_unchanged) {
1275  if (matrix_is_unchanged) {
1276  if (!bounds_are_unchanged) {
1277  InitializeVariableStatusesForWarmStart(solution_state_,
1278  ColIndex(0));
1279  variable_values_.RecomputeBasicVariableValues();
1280  }
1281  solve_from_scratch = false;
1282  } else if (only_change_is_new_rows) {
1283  // For the dual-simplex, we also perform a warm start if a couple of
1284  // new rows where added.
1285  InitializeVariableStatusesForWarmStart(solution_state_, ColIndex(0));
1286  dual_edge_norms_.ResizeOnNewRows(num_rows_);
1287 
1288  // TODO(user): The reduced costs do not really need to be recomputed.
1289  // We just need to initialize the ones of the new slack variables to
1290  // 0.
1291  reduced_costs_.ClearAndRemoveCostShifts();
1292  dual_pricing_vector_.clear();
1293 
1294  // Note that this needs to be done after the Clear() calls above.
1295  if (InitializeFirstBasis(basis_).ok()) {
1296  solve_from_scratch = false;
1297  }
1298  }
1299  }
1300  }
1301  }
1302 
1303  // If we couldn't perform a "quick" warm start above, we can at least try to
1304  // reuse the variable statuses.
1305  const bool log_info = parameters_.log_search_progress() || VLOG_IS_ON(1);
1306  if (solve_from_scratch && !solution_state_.IsEmpty()) {
1307  // If an external basis has been provided or if the matrix changed, we need
1308  // to perform more work, e.g., factorize the proposed basis and validate it.
1309  InitializeVariableStatusesForWarmStart(solution_state_, ColIndex(0));
1310  basis_.assign(num_rows_, kInvalidCol);
1311  RowIndex row(0);
1312  for (ColIndex col : variables_info_.GetIsBasicBitRow()) {
1313  basis_[row] = col;
1314  ++row;
1315  }
1316 
1317  basis_factorization_.Clear();
1318  reduced_costs_.ClearAndRemoveCostShifts();
1319  primal_edge_norms_.Clear();
1320  dual_edge_norms_.Clear();
1321  dual_pricing_vector_.clear();
1322 
1323  // TODO(user): If the basis is incomplete, we could complete it with
1324  // better slack variables than is done by InitializeFirstBasis() by
1325  // using a partial LU decomposition (see markowitz.h).
1326  if (InitializeFirstBasis(basis_).ok()) {
1327  solve_from_scratch = false;
1328  } else {
1329  if (log_info) {
1330  LOG(INFO) << "RevisedSimplex is not using the warm start "
1331  "basis because it is not factorizable.";
1332  }
1333  }
1334  }
1335 
1336  if (solve_from_scratch) {
1337  if (log_info) LOG(INFO) << "Solve from scratch.";
1338  basis_factorization_.Clear();
1339  reduced_costs_.ClearAndRemoveCostShifts();
1340  primal_edge_norms_.Clear();
1341  dual_edge_norms_.Clear();
1342  dual_pricing_vector_.clear();
1343  GLOP_RETURN_IF_ERROR(CreateInitialBasis());
1344  } else {
1345  if (log_info) LOG(INFO) << "Incremental solve.";
1346  }
1347  DCHECK(BasisIsConsistent());
1348  return Status::OK();
1349 }
1350 
1351 void RevisedSimplex::DisplayBasicVariableStatistics() {
1352  SCOPED_TIME_STAT(&function_stats_);
1353 
1354  int num_fixed_variables = 0;
1355  int num_free_variables = 0;
1356  int num_variables_at_bound = 0;
1357  int num_slack_variables = 0;
1358  int num_infeasible_variables = 0;
1359 
1360  const DenseRow& variable_values = variable_values_.GetDenseRow();
1361  const VariableTypeRow& variable_types = variables_info_.GetTypeRow();
1362  const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1363  for (RowIndex row(0); row < num_rows_; ++row) {
1364  const ColIndex col = basis_[row];
1365  const Fractional value = variable_values[col];
1366  if (variable_types[col] == VariableType::UNCONSTRAINED) {
1367  ++num_free_variables;
1368  }
1369  if (value > upper_bound_[col] + tolerance ||
1370  value < lower_bound_[col] - tolerance) {
1371  ++num_infeasible_variables;
1372  }
1373  if (col >= first_slack_col_) {
1374  ++num_slack_variables;
1375  }
1376  if (lower_bound_[col] == upper_bound_[col]) {
1377  ++num_fixed_variables;
1378  } else if (variable_values[col] == lower_bound_[col] ||
1379  variable_values[col] == upper_bound_[col]) {
1380  ++num_variables_at_bound;
1381  }
1382  }
1383 
1384  VLOG(1) << "Basis size: " << num_rows_;
1385  VLOG(1) << "Number of basic infeasible variables: "
1386  << num_infeasible_variables;
1387  VLOG(1) << "Number of basic slack variables: " << num_slack_variables;
1388  VLOG(1) << "Number of basic variables at bound: " << num_variables_at_bound;
1389  VLOG(1) << "Number of basic fixed variables: " << num_fixed_variables;
1390  VLOG(1) << "Number of basic free variables: " << num_free_variables;
1391 }
1392 
1393 void RevisedSimplex::SaveState() {
1394  DCHECK_EQ(num_cols_, variables_info_.GetStatusRow().size());
1395  solution_state_.statuses = variables_info_.GetStatusRow();
1396  solution_state_has_been_set_externally_ = false;
1397 }
1398 
1399 RowIndex RevisedSimplex::ComputeNumberOfEmptyRows() {
1400  DenseBooleanColumn contains_data(num_rows_, false);
1401  for (ColIndex col(0); col < num_cols_; ++col) {
1402  for (const SparseColumn::Entry e : compact_matrix_.column(col)) {
1403  contains_data[e.row()] = true;
1404  }
1405  }
1406  RowIndex num_empty_rows(0);
1407  for (RowIndex row(0); row < num_rows_; ++row) {
1408  if (!contains_data[row]) {
1409  ++num_empty_rows;
1410  VLOG(1) << "Row " << row << " is empty.";
1411  }
1412  }
1413  return num_empty_rows;
1414 }
1415 
1416 ColIndex RevisedSimplex::ComputeNumberOfEmptyColumns() {
1417  ColIndex num_empty_cols(0);
1418  for (ColIndex col(0); col < num_cols_; ++col) {
1419  if (compact_matrix_.column(col).IsEmpty()) {
1420  ++num_empty_cols;
1421  VLOG(1) << "Column " << col << " is empty.";
1422  }
1423  }
1424  return num_empty_cols;
1425 }
1426 
1427 void RevisedSimplex::CorrectErrorsOnVariableValues() {
1428  SCOPED_TIME_STAT(&function_stats_);
1429  DCHECK(basis_factorization_.IsRefactorized());
1430 
1431  // TODO(user): The primal residual error does not change if we take degenerate
1432  // steps or if we do not change the variable values. No need to recompute it
1433  // in this case.
1434  const Fractional primal_residual =
1435  variable_values_.ComputeMaximumPrimalResidual();
1436 
1437  // If the primal_residual is within the tolerance, no need to recompute
1438  // the basic variable values with a better precision.
1439  if (primal_residual >= parameters_.harris_tolerance_ratio() *
1440  parameters_.primal_feasibility_tolerance()) {
1441  variable_values_.RecomputeBasicVariableValues();
1442  VLOG(1) << "Primal infeasibility (bounds error) = "
1443  << variable_values_.ComputeMaximumPrimalInfeasibility()
1444  << ", Primal residual |A.x - b| = "
1445  << variable_values_.ComputeMaximumPrimalResidual();
1446  }
1447 
1448  // If we are doing too many degenerate iterations, we try to perturb the
1449  // problem by extending each basic variable bound with a random value. See how
1450  // bound_perturbation_ is used in ComputeHarrisRatioAndLeavingCandidates().
1451  //
1452  // Note that the perturbation is currently only reset to zero at the end of
1453  // the algorithm.
1454  //
1455  // TODO(user): This is currently disabled because the improvement is unclear.
1456  if (/* DISABLES CODE */ false &&
1457  (!feasibility_phase_ && num_consecutive_degenerate_iterations_ >= 100)) {
1458  VLOG(1) << "Perturbing the problem.";
1459  const Fractional tolerance = parameters_.harris_tolerance_ratio() *
1460  parameters_.primal_feasibility_tolerance();
1461  std::uniform_real_distribution<double> dist(0, tolerance);
1462  for (ColIndex col(0); col < num_cols_; ++col) {
1463  bound_perturbation_[col] += dist(random_);
1464  }
1465  }
1466 }
1467 
1468 void RevisedSimplex::ComputeVariableValuesError() {
1469  SCOPED_TIME_STAT(&function_stats_);
1470  error_.AssignToZero(num_rows_);
1471  const DenseRow& variable_values = variable_values_.GetDenseRow();
1472  for (ColIndex col(0); col < num_cols_; ++col) {
1473  const Fractional value = variable_values[col];
1474  compact_matrix_.ColumnAddMultipleToDenseColumn(col, -value, &error_);
1475  }
1476 }
1477 
1478 void RevisedSimplex::ComputeDirection(ColIndex col) {
1479  SCOPED_TIME_STAT(&function_stats_);
1481  basis_factorization_.RightSolveForProblemColumn(col, &direction_);
1482  direction_infinity_norm_ = 0.0;
1483  if (direction_.non_zeros.empty()) {
1484  // We still compute the direction non-zeros because our code relies on it.
1485  for (RowIndex row(0); row < num_rows_; ++row) {
1486  const Fractional value = direction_[row];
1487  if (value != 0.0) {
1488  direction_.non_zeros.push_back(row);
1489  direction_infinity_norm_ =
1490  std::max(direction_infinity_norm_, std::abs(value));
1491  }
1492  }
1493  } else {
1494  for (const auto e : direction_) {
1495  direction_infinity_norm_ =
1496  std::max(direction_infinity_norm_, std::abs(e.coefficient()));
1497  }
1498  }
1499  IF_STATS_ENABLED(ratio_test_stats_.direction_density.Add(
1500  num_rows_ == 0 ? 0.0
1501  : static_cast<double>(direction_.non_zeros.size()) /
1502  static_cast<double>(num_rows_.value())));
1503 }
1504 
1505 Fractional RevisedSimplex::ComputeDirectionError(ColIndex col) {
1506  SCOPED_TIME_STAT(&function_stats_);
1507  compact_matrix_.ColumnCopyToDenseColumn(col, &error_);
1508  for (const auto e : direction_) {
1509  compact_matrix_.ColumnAddMultipleToDenseColumn(col, -e.coefficient(),
1510  &error_);
1511  }
1512  return InfinityNorm(error_);
1513 }
1514 
1515 template <bool is_entering_reduced_cost_positive>
1516 Fractional RevisedSimplex::GetRatio(RowIndex row) const {
1517  const ColIndex col = basis_[row];
1518  const Fractional direction = direction_[row];
1519  const Fractional value = variable_values_.Get(col);
1520  DCHECK(variables_info_.GetIsBasicBitRow().IsSet(col));
1521  DCHECK_NE(direction, 0.0);
1522  if (is_entering_reduced_cost_positive) {
1523  if (direction > 0.0) {
1524  return (upper_bound_[col] - value) / direction;
1525  } else {
1526  return (lower_bound_[col] - value) / direction;
1527  }
1528  } else {
1529  if (direction > 0.0) {
1530  return (value - lower_bound_[col]) / direction;
1531  } else {
1532  return (value - upper_bound_[col]) / direction;
1533  }
1534  }
1535 }
1536 
1537 template <bool is_entering_reduced_cost_positive>
1538 Fractional RevisedSimplex::ComputeHarrisRatioAndLeavingCandidates(
1539  Fractional bound_flip_ratio, SparseColumn* leaving_candidates) const {
1540  SCOPED_TIME_STAT(&function_stats_);
1541  const Fractional harris_tolerance =
1542  parameters_.harris_tolerance_ratio() *
1543  parameters_.primal_feasibility_tolerance();
1544  const Fractional minimum_delta = parameters_.degenerate_ministep_factor() *
1545  parameters_.primal_feasibility_tolerance();
1546 
1547  // Initially, we can skip any variable with a ratio greater than
1548  // bound_flip_ratio since it seems to be always better to choose the
1549  // bound-flip over such leaving variable.
1550  Fractional harris_ratio = bound_flip_ratio;
1551  leaving_candidates->Clear();
1552 
1553  // If the basis is refactorized, then we should have everything with a good
1554  // precision, so we only consider "acceptable" pivots. Otherwise we consider
1555  // all the entries, and if the algorithm return a pivot that is too small, we
1556  // will refactorize and recompute the relevant quantities.
1557  const Fractional threshold = basis_factorization_.IsRefactorized()
1558  ? parameters_.minimum_acceptable_pivot()
1559  : parameters_.ratio_test_zero_threshold();
1560 
1561  for (const auto e : direction_) {
1562  const Fractional magnitude = std::abs(e.coefficient());
1563  if (magnitude <= threshold) continue;
1564  Fractional ratio = GetRatio<is_entering_reduced_cost_positive>(e.row());
1565  // TODO(user): The perturbation is currently disabled, so no need to test
1566  // anything here.
1567  if (false && ratio < 0.0) {
1568  // If the variable is already pass its bound, we use the perturbed version
1569  // of the bound (if bound_perturbation_[basis_[row]] is not zero).
1570  ratio += std::abs(bound_perturbation_[basis_[e.row()]] / e.coefficient());
1571  }
1572  if (ratio <= harris_ratio) {
1573  leaving_candidates->SetCoefficient(e.row(), ratio);
1574 
1575  // The second max() makes sure harris_ratio is lower bounded by a small
1576  // positive value. The more classical approach is to bound it by 0.0 but
1577  // since we will always perform a small positive step, we allow any
1578  // variable to go a bit more out of bound (even if it is past the harris
1579  // tolerance). This increase the number of candidates and allows us to
1580  // choose a more numerically stable pivot.
1581  //
1582  // Note that at least lower bounding it by 0.0 is really important on
1583  // numerically difficult problems because its helps in the choice of a
1584  // stable pivot.
1585  harris_ratio = std::min(harris_ratio,
1586  std::max(minimum_delta / magnitude,
1587  ratio + harris_tolerance / magnitude));
1588  }
1589  }
1590  return harris_ratio;
1591 }
1592 
1593 namespace {
1594 
1595 // Returns true if the candidate ratio is supposed to be more stable than the
1596 // current ratio (or if the two are equal).
1597 // The idea here is to take, by order of preference:
1598 // - the minimum positive ratio in order to intoduce a primal infeasibility
1599 // which is as small as possible.
1600 // - or the least negative one in order to have the smallest bound shift
1601 // possible on the leaving variable.
1602 bool IsRatioMoreOrEquallyStable(Fractional candidate, Fractional current) {
1603  if (current >= 0.0) {
1604  return candidate >= 0.0 && candidate <= current;
1605  } else {
1606  return candidate >= current;
1607  }
1608 }
1609 
1610 } // namespace
1611 
1612 // Ratio-test or Quotient-test. Choose the row of the leaving variable.
1613 // Known as CHUZR or CHUZRO in FORTRAN codes.
1614 Status RevisedSimplex::ChooseLeavingVariableRow(
1615  ColIndex entering_col, Fractional reduced_cost, bool* refactorize,
1616  RowIndex* leaving_row, Fractional* step_length, Fractional* target_bound) {
1617  SCOPED_TIME_STAT(&function_stats_);
1618  GLOP_RETURN_ERROR_IF_NULL(refactorize);
1619  GLOP_RETURN_ERROR_IF_NULL(leaving_row);
1620  GLOP_RETURN_ERROR_IF_NULL(step_length);
1621  DCHECK_COL_BOUNDS(entering_col);
1622  DCHECK_NE(0.0, reduced_cost);
1623 
1624  // A few cases will cause the test to be recomputed from the beginning.
1625  int stats_num_leaving_choices = 0;
1626  equivalent_leaving_choices_.clear();
1627  while (true) {
1628  stats_num_leaving_choices = 0;
1629 
1630  // We initialize current_ratio with the maximum step the entering variable
1631  // can take (bound-flip). Note that we do not use tolerance here.
1632  const Fractional entering_value = variable_values_.Get(entering_col);
1633  Fractional current_ratio =
1634  (reduced_cost > 0.0) ? entering_value - lower_bound_[entering_col]
1635  : upper_bound_[entering_col] - entering_value;
1636  DCHECK_GT(current_ratio, 0.0);
1637 
1638  // First pass of the Harris ratio test. If 'harris_tolerance' is zero, this
1639  // actually computes the minimum leaving ratio of all the variables. This is
1640  // the same as the 'classic' ratio test.
1641  const Fractional harris_ratio =
1642  (reduced_cost > 0.0) ? ComputeHarrisRatioAndLeavingCandidates<true>(
1643  current_ratio, &leaving_candidates_)
1644  : ComputeHarrisRatioAndLeavingCandidates<false>(
1645  current_ratio, &leaving_candidates_);
1646 
1647  // If the bound-flip is a viable solution (i.e. it doesn't move the basic
1648  // variable too much out of bounds), we take it as it is always stable and
1649  // fast.
1650  if (current_ratio <= harris_ratio) {
1651  *leaving_row = kInvalidRow;
1652  *step_length = current_ratio;
1653  break;
1654  }
1655 
1656  // Second pass of the Harris ratio test. Amongst the variables with 'ratio
1657  // <= harris_ratio', we choose the leaving row with the largest coefficient.
1658  //
1659  // This has a big impact, because picking a leaving variable with a small
1660  // direction_[row] is the main source of Abnormal LU errors.
1661  Fractional pivot_magnitude = 0.0;
1662  stats_num_leaving_choices = 0;
1663  *leaving_row = kInvalidRow;
1664  equivalent_leaving_choices_.clear();
1665  for (const SparseColumn::Entry e : leaving_candidates_) {
1666  const Fractional ratio = e.coefficient();
1667  if (ratio > harris_ratio) continue;
1668  ++stats_num_leaving_choices;
1669  const RowIndex row = e.row();
1670 
1671  // If the magnitudes are the same, we choose the leaving variable with
1672  // what is probably the more stable ratio, see
1673  // IsRatioMoreOrEquallyStable().
1674  const Fractional candidate_magnitude = std::abs(direction_[row]);
1675  if (candidate_magnitude < pivot_magnitude) continue;
1676  if (candidate_magnitude == pivot_magnitude) {
1677  if (!IsRatioMoreOrEquallyStable(ratio, current_ratio)) continue;
1678  if (ratio == current_ratio) {
1679  DCHECK_NE(kInvalidRow, *leaving_row);
1680  equivalent_leaving_choices_.push_back(row);
1681  continue;
1682  }
1683  }
1684  equivalent_leaving_choices_.clear();
1685  current_ratio = ratio;
1686  pivot_magnitude = candidate_magnitude;
1687  *leaving_row = row;
1688  }
1689 
1690  // Break the ties randomly.
1691  if (!equivalent_leaving_choices_.empty()) {
1692  equivalent_leaving_choices_.push_back(*leaving_row);
1693  *leaving_row =
1694  equivalent_leaving_choices_[std::uniform_int_distribution<int>(
1695  0, equivalent_leaving_choices_.size() - 1)(random_)];
1696  }
1697 
1698  // Since we took care of the bound-flip at the beginning, at this point
1699  // we have a valid leaving row.
1700  DCHECK_NE(kInvalidRow, *leaving_row);
1701 
1702  // A variable already outside one of its bounds +/- tolerance is considered
1703  // at its bound and its ratio is zero. Not doing this may lead to a step
1704  // that moves the objective in the wrong direction. We may want to allow
1705  // such steps, but then we will need to check that it doesn't break the
1706  // bounds of the other variables.
1707  if (current_ratio <= 0.0) {
1708  // Instead of doing a zero step, we do a small positive step. This
1709  // helps on degenerate problems.
1710  const Fractional minimum_delta =
1711  parameters_.degenerate_ministep_factor() *
1712  parameters_.primal_feasibility_tolerance();
1713  *step_length = minimum_delta / pivot_magnitude;
1714  } else {
1715  *step_length = current_ratio;
1716  }
1717 
1718  // Note(user): Testing the pivot at each iteration is useful for debugging
1719  // an LU factorization problem. Remove the false if you need to investigate
1720  // this, it makes sure that this will be compiled away.
1721  if (/* DISABLES CODE */ (false)) {
1722  TestPivot(entering_col, *leaving_row);
1723  }
1724 
1725  // We try various "heuristics" to avoid a small pivot.
1726  //
1727  // The smaller 'direction_[*leaving_row]', the less precise
1728  // it is. So we want to avoid pivoting by such a row. Small pivots lead to
1729  // ill-conditioned bases or even to matrices that are not a basis at all if
1730  // the actual (infinite-precision) coefficient is zero.
1731  //
1732  // TODO(user): We may have to choose another entering column if
1733  // we cannot prevent pivoting by a small pivot.
1734  // (Chvatal, p.115, about epsilon2.)
1735  if (pivot_magnitude <
1736  parameters_.small_pivot_threshold() * direction_infinity_norm_) {
1737  // The first countermeasure is to recompute everything to the best
1738  // precision we can in the hope of avoiding such a choice. Note that this
1739  // helps a lot on the Netlib problems.
1740  if (!basis_factorization_.IsRefactorized()) {
1741  VLOG(1) << "Refactorizing to avoid pivoting by "
1742  << direction_[*leaving_row]
1743  << " direction_infinity_norm_ = " << direction_infinity_norm_
1744  << " reduced cost = " << reduced_cost;
1745  *refactorize = true;
1746  return Status::OK();
1747  }
1748 
1749  // Because of the "threshold" in ComputeHarrisRatioAndLeavingCandidates()
1750  // we kwnow that this pivot will still have an acceptable magnitude.
1751  //
1752  // TODO(user): An issue left to fix is that if there is no such pivot at
1753  // all, then we will report unbounded even if this is not really the case.
1754  // As of 2018/07/18, this happens on l30.mps.
1755  VLOG(1) << "Couldn't avoid pivoting by " << direction_[*leaving_row]
1756  << " direction_infinity_norm_ = " << direction_infinity_norm_
1757  << " reduced cost = " << reduced_cost;
1758  DCHECK_GE(std::abs(direction_[*leaving_row]),
1759  parameters_.minimum_acceptable_pivot());
1760  IF_STATS_ENABLED(ratio_test_stats_.abs_tested_pivot.Add(pivot_magnitude));
1761  }
1762  break;
1763  }
1764 
1765  // Update the target bound.
1766  if (*leaving_row != kInvalidRow) {
1767  const bool is_reduced_cost_positive = (reduced_cost > 0.0);
1768  const bool is_leaving_coeff_positive = (direction_[*leaving_row] > 0.0);
1769  *target_bound = (is_reduced_cost_positive == is_leaving_coeff_positive)
1770  ? upper_bound_[basis_[*leaving_row]]
1771  : lower_bound_[basis_[*leaving_row]];
1772  }
1773 
1774  // Stats.
1776  ratio_test_stats_.leaving_choices.Add(stats_num_leaving_choices);
1777  if (!equivalent_leaving_choices_.empty()) {
1778  ratio_test_stats_.num_perfect_ties.Add(
1779  equivalent_leaving_choices_.size());
1780  }
1781  if (*leaving_row != kInvalidRow) {
1782  ratio_test_stats_.abs_used_pivot.Add(std::abs(direction_[*leaving_row]));
1783  }
1784  });
1785  return Status::OK();
1786 }
1787 
1788 namespace {
1789 
1790 // Store a row with its ratio, coefficient magnitude and target bound. This is
1791 // used by PrimalPhaseIChooseLeavingVariableRow(), see this function for more
1792 // details.
1793 struct BreakPoint {
1794  BreakPoint(RowIndex _row, Fractional _ratio, Fractional _coeff_magnitude,
1795  Fractional _target_bound)
1796  : row(_row),
1797  ratio(_ratio),
1798  coeff_magnitude(_coeff_magnitude),
1799  target_bound(_target_bound) {}
1800 
1801  // We want to process the breakpoints by increasing ratio and decreasing
1802  // coefficient magnitude (if the ratios are the same). Returns false if "this"
1803  // is before "other" in a priority queue.
1804  bool operator<(const BreakPoint& other) const {
1805  if (ratio == other.ratio) {
1806  if (coeff_magnitude == other.coeff_magnitude) {
1807  return row > other.row;
1808  }
1809  return coeff_magnitude < other.coeff_magnitude;
1810  }
1811  return ratio > other.ratio;
1812  }
1813 
1814  RowIndex row;
1818 };
1819 
1820 } // namespace
1821 
1822 void RevisedSimplex::PrimalPhaseIChooseLeavingVariableRow(
1823  ColIndex entering_col, Fractional reduced_cost, bool* refactorize,
1824  RowIndex* leaving_row, Fractional* step_length,
1825  Fractional* target_bound) const {
1826  SCOPED_TIME_STAT(&function_stats_);
1827  RETURN_IF_NULL(refactorize);
1828  RETURN_IF_NULL(leaving_row);
1829  RETURN_IF_NULL(step_length);
1830  DCHECK_COL_BOUNDS(entering_col);
1831  DCHECK_NE(0.0, reduced_cost);
1832 
1833  // We initialize current_ratio with the maximum step the entering variable
1834  // can take (bound-flip). Note that we do not use tolerance here.
1835  const Fractional entering_value = variable_values_.Get(entering_col);
1836  Fractional current_ratio = (reduced_cost > 0.0)
1837  ? entering_value - lower_bound_[entering_col]
1838  : upper_bound_[entering_col] - entering_value;
1839  DCHECK_GT(current_ratio, 0.0);
1840 
1841  std::vector<BreakPoint> breakpoints;
1842  const Fractional tolerance = parameters_.primal_feasibility_tolerance();
1843  for (const auto e : direction_) {
1844  const Fractional direction =
1845  reduced_cost > 0.0 ? e.coefficient() : -e.coefficient();
1846  const Fractional magnitude = std::abs(direction);
1847  if (magnitude < tolerance) continue;
1848 
1849  // Computes by how much we can add 'direction' to the basic variable value
1850  // with index 'row' until it changes of primal feasibility status. That is
1851  // from infeasible to feasible or from feasible to infeasible. Note that the
1852  // transition infeasible->feasible->infeasible is possible. We use
1853  // tolerances here, but when the step will be performed, it will move the
1854  // variable to the target bound (possibly taking a small negative step).
1855  //
1856  // Note(user): The negative step will only happen when the leaving variable
1857  // was slightly infeasible (less than tolerance). Moreover, the overall
1858  // infeasibility will not necessarily increase since it doesn't take into
1859  // account all the variables with an infeasibility smaller than the
1860  // tolerance, and here we will at least improve the one of the leaving
1861  // variable.
1862  const ColIndex col = basis_[e.row()];
1863  DCHECK(variables_info_.GetIsBasicBitRow().IsSet(col));
1864 
1865  const Fractional value = variable_values_.Get(col);
1866  const Fractional lower_bound = lower_bound_[col];
1867  const Fractional upper_bound = upper_bound_[col];
1868  const Fractional to_lower = (lower_bound - tolerance - value) / direction;
1869  const Fractional to_upper = (upper_bound + tolerance - value) / direction;
1870 
1871  // Enqueue the possible transitions. Note that the second tests exclude the
1872  // case where to_lower or to_upper are infinite.
1873  if (to_lower >= 0.0 && to_lower < current_ratio) {
1874  breakpoints.push_back(
1875  BreakPoint(e.row(), to_lower, magnitude, lower_bound));
1876  }
1877  if (to_upper >= 0.0 && to_upper < current_ratio) {
1878  breakpoints.push_back(
1879  BreakPoint(e.row(), to_upper, magnitude, upper_bound));
1880  }
1881  }
1882 
1883  // Order the breakpoints by increasing ratio and decreasing coefficient
1884  // magnitude (if the ratios are the same).
1885  std::make_heap(breakpoints.begin(), breakpoints.end());
1886 
1887  // Select the last breakpoint that still improves the infeasibility and has
1888  // the largest coefficient magnitude.
1889  Fractional improvement = std::abs(reduced_cost);
1890  Fractional best_magnitude = 0.0;
1891  *leaving_row = kInvalidRow;
1892  while (!breakpoints.empty()) {
1893  const BreakPoint top = breakpoints.front();
1894  // TODO(user): consider using >= here. That will lead to bigger ratio and
1895  // hence a better impact on the infeasibility. The drawback is that more
1896  // effort may be needed to update the reduced costs.
1897  //
1898  // TODO(user): Use a random tie breaking strategy for BreakPoint with
1899  // same ratio and same coefficient magnitude? Koberstein explains in his PhD
1900  // that it helped on the dual-simplex.
1901  if (top.coeff_magnitude > best_magnitude) {
1902  *leaving_row = top.row;
1903  current_ratio = top.ratio;
1904  best_magnitude = top.coeff_magnitude;
1905  *target_bound = top.target_bound;
1906  }
1907 
1908  // As long as the sum of primal infeasibilities is decreasing, we look for
1909  // pivots that are numerically more stable.
1910  improvement -= top.coeff_magnitude;
1911  if (improvement <= 0.0) break;
1912  std::pop_heap(breakpoints.begin(), breakpoints.end());
1913  breakpoints.pop_back();
1914  }
1915 
1916  // Try to avoid a small pivot by refactorizing.
1917  if (*leaving_row != kInvalidRow) {
1918  const Fractional threshold =
1919  parameters_.small_pivot_threshold() * direction_infinity_norm_;
1920  if (best_magnitude < threshold && !basis_factorization_.IsRefactorized()) {
1921  *refactorize = true;
1922  return;
1923  }
1924  }
1925  *step_length = current_ratio;
1926 }
1927 
1928 // This implements the pricing step for the dual simplex.
1929 Status RevisedSimplex::DualChooseLeavingVariableRow(RowIndex* leaving_row,
1930  Fractional* cost_variation,
1932  GLOP_RETURN_ERROR_IF_NULL(leaving_row);
1933  GLOP_RETURN_ERROR_IF_NULL(cost_variation);
1934 
1935  // TODO(user): Reuse parameters_.optimization_rule() to decide if we use
1936  // steepest edge or the normal Dantzig pricing.
1937  const DenseColumn& squared_norm = dual_edge_norms_.GetEdgeSquaredNorms();
1938  SCOPED_TIME_STAT(&function_stats_);
1939 
1940  *leaving_row = kInvalidRow;
1941  Fractional best_price(0.0);
1942  const DenseColumn& squared_infeasibilities =
1943  variable_values_.GetPrimalSquaredInfeasibilities();
1944  equivalent_leaving_choices_.clear();
1945  for (const RowIndex row : variable_values_.GetPrimalInfeasiblePositions()) {
1946  const Fractional scaled_best_price = best_price * squared_norm[row];
1947  if (squared_infeasibilities[row] >= scaled_best_price) {
1948  if (squared_infeasibilities[row] == scaled_best_price) {
1949  DCHECK_NE(*leaving_row, kInvalidRow);
1950  equivalent_leaving_choices_.push_back(row);
1951  continue;
1952  }
1953  equivalent_leaving_choices_.clear();
1954  best_price = squared_infeasibilities[row] / squared_norm[row];
1955  *leaving_row = row;
1956  }
1957  }
1958 
1959  // Break the ties randomly.
1960  if (!equivalent_leaving_choices_.empty()) {
1961  equivalent_leaving_choices_.push_back(*leaving_row);
1962  *leaving_row =
1963  equivalent_leaving_choices_[std::uniform_int_distribution<int>(
1964  0, equivalent_leaving_choices_.size() - 1)(random_)];
1965  }
1966 
1967  // Return right away if there is no leaving variable.
1968  // Fill cost_variation and target_bound otherwise.
1969  if (*leaving_row == kInvalidRow) return Status::OK();
1970  const ColIndex leaving_col = basis_[*leaving_row];
1971  const Fractional value = variable_values_.Get(leaving_col);
1972  if (value < lower_bound_[leaving_col]) {
1973  *cost_variation = lower_bound_[leaving_col] - value;
1974  *target_bound = lower_bound_[leaving_col];
1975  DCHECK_GT(*cost_variation, 0.0);
1976  } else {
1977  *cost_variation = upper_bound_[leaving_col] - value;
1978  *target_bound = upper_bound_[leaving_col];
1979  DCHECK_LT(*cost_variation, 0.0);
1980  }
1981  return Status::OK();
1982 }
1983 
1984 namespace {
1985 
1986 // Returns true if a basic variable with given cost and type is to be considered
1987 // as a leaving candidate for the dual phase I. This utility function is used
1988 // to keep is_dual_entering_candidate_ up to date.
1989 bool IsDualPhaseILeavingCandidate(Fractional cost, VariableType type,
1990  Fractional threshold) {
1991  if (cost == 0.0) return false;
1992  return type == VariableType::UPPER_AND_LOWER_BOUNDED ||
1993  type == VariableType::FIXED_VARIABLE ||
1994  (type == VariableType::UPPER_BOUNDED && cost < -threshold) ||
1995  (type == VariableType::LOWER_BOUNDED && cost > threshold);
1996 }
1997 
1998 } // namespace
1999 
2000 void RevisedSimplex::DualPhaseIUpdatePrice(RowIndex leaving_row,
2001  ColIndex entering_col) {
2002  SCOPED_TIME_STAT(&function_stats_);
2003  const VariableTypeRow& variable_type = variables_info_.GetTypeRow();
2004  const Fractional threshold = parameters_.ratio_test_zero_threshold();
2005 
2006  // Convert the dual_pricing_vector_ from the old basis into the new one (which
2007  // is the same as multiplying it by an Eta matrix corresponding to the
2008  // direction).
2009  const Fractional step =
2010  dual_pricing_vector_[leaving_row] / direction_[leaving_row];
2011  for (const auto e : direction_) {
2012  dual_pricing_vector_[e.row()] -= e.coefficient() * step;
2013  is_dual_entering_candidate_.Set(
2014  e.row(), IsDualPhaseILeavingCandidate(dual_pricing_vector_[e.row()],
2015  variable_type[basis_[e.row()]],
2016  threshold));
2017  }
2018  dual_pricing_vector_[leaving_row] = step;
2019 
2020  // The entering_col which was dual-infeasible is now dual-feasible, so we
2021  // have to remove it from the infeasibility sum.
2022  dual_pricing_vector_[leaving_row] -=
2023  dual_infeasibility_improvement_direction_[entering_col];
2024  if (dual_infeasibility_improvement_direction_[entering_col] != 0.0) {
2025  --num_dual_infeasible_positions_;
2026  }
2027  dual_infeasibility_improvement_direction_[entering_col] = 0.0;
2028 
2029  // The leaving variable will also be dual-feasible.
2030  dual_infeasibility_improvement_direction_[basis_[leaving_row]] = 0.0;
2031 
2032  // Update the leaving row entering candidate status.
2033  is_dual_entering_candidate_.Set(
2034  leaving_row,
2035  IsDualPhaseILeavingCandidate(dual_pricing_vector_[leaving_row],
2036  variable_type[entering_col], threshold));
2037 }
2038 
2039 template <typename Cols>
2040 void RevisedSimplex::DualPhaseIUpdatePriceOnReducedCostChange(
2041  const Cols& cols) {
2042  SCOPED_TIME_STAT(&function_stats_);
2043  bool something_to_do = false;
2044  const DenseBitRow& can_decrease = variables_info_.GetCanDecreaseBitRow();
2045  const DenseBitRow& can_increase = variables_info_.GetCanIncreaseBitRow();
2046  const DenseRow& reduced_costs = reduced_costs_.GetReducedCosts();
2047  const Fractional tolerance = reduced_costs_.GetDualFeasibilityTolerance();
2048  for (ColIndex col : cols) {
2049  const Fractional reduced_cost = reduced_costs[col];
2050  const Fractional sign =
2051  (can_increase.IsSet(col) && reduced_cost < -tolerance) ? 1.0
2052  : (can_decrease.IsSet(col) && reduced_cost > tolerance) ? -1.0
2053  : 0.0;
2054  if (sign != dual_infeasibility_improvement_direction_[col]) {
2055  if (sign == 0.0) {
2056  --num_dual_infeasible_positions_;
2057  } else if (dual_infeasibility_improvement_direction_[col] == 0.0) {
2058  ++num_dual_infeasible_positions_;
2059  }
2060  if (!something_to_do) {
2061  initially_all_zero_scratchpad_.values.resize(num_rows_, 0.0);
2062  initially_all_zero_scratchpad_.ClearSparseMask();
2063  initially_all_zero_scratchpad_.non_zeros.clear();
2064  something_to_do = true;
2065  }
2067  col, sign - dual_infeasibility_improvement_direction_[col],
2068  &initially_all_zero_scratchpad_);
2069  dual_infeasibility_improvement_direction_[col] = sign;
2070  }
2071  }
2072  if (something_to_do) {
2073  initially_all_zero_scratchpad_.ClearNonZerosIfTooDense();
2074  initially_all_zero_scratchpad_.ClearSparseMask();
2075 
2076  const VariableTypeRow& variable_type = variables_info_.GetTypeRow();
2077  const Fractional threshold = parameters_.ratio_test_zero_threshold();
2078  basis_factorization_.RightSolve(&initially_all_zero_scratchpad_);
2079  if (initially_all_zero_scratchpad_.non_zeros.empty()) {
2080  for (RowIndex row(0); row < num_rows_; ++row) {
2081  if (initially_all_zero_scratchpad_[row] == 0.0) continue;
2082  dual_pricing_vector_[row] += initially_all_zero_scratchpad_[row];
2083  is_dual_entering_candidate_.Set(
2084  row, IsDualPhaseILeavingCandidate(dual_pricing_vector_[row],
2085  variable_type[basis_[row]],
2086  threshold));
2087  }
2088  initially_all_zero_scratchpad_.values.AssignToZero(num_rows_);
2089  } else {
2090  for (const auto e : initially_all_zero_scratchpad_) {
2091  dual_pricing_vector_[e.row()] += e.coefficient();
2092  initially_all_zero_scratchpad_[e.row()] = 0.0;
2093  is_dual_entering_candidate_.Set(
2094  e.row(), IsDualPhaseILeavingCandidate(
2095  dual_pricing_vector_[e.row()],
2096  variable_type[basis_[e.row()]], threshold));
2097  }
2098  }
2099  initially_all_zero_scratchpad_.non_zeros.clear();
2100  }
2101 }
2102 
2103 Status RevisedSimplex::DualPhaseIChooseLeavingVariableRow(
2104  RowIndex* leaving_row, Fractional* cost_variation,
2106  SCOPED_TIME_STAT(&function_stats_);
2107  GLOP_RETURN_ERROR_IF_NULL(leaving_row);
2108  GLOP_RETURN_ERROR_IF_NULL(cost_variation);
2109 
2110  // dual_infeasibility_improvement_direction_ is zero for dual-feasible
2111  // positions and contains the sign in which the reduced cost of this column
2112  // needs to move to improve the feasibility otherwise (+1 or -1).
2113  //
2114  // Its current value was the one used to compute dual_pricing_vector_ and
2115  // was updated accordingly by DualPhaseIUpdatePrice().
2116  //
2117  // If more variables changed of dual-feasibility status during the last
2118  // iteration, we need to call DualPhaseIUpdatePriceOnReducedCostChange() to
2119  // take them into account.
2120  if (reduced_costs_.AreReducedCostsRecomputed() ||
2121  dual_pricing_vector_.empty()) {
2122  // Recompute everything from scratch.
2123  num_dual_infeasible_positions_ = 0;
2124  dual_pricing_vector_.AssignToZero(num_rows_);
2125  is_dual_entering_candidate_.ClearAndResize(num_rows_);
2126  dual_infeasibility_improvement_direction_.AssignToZero(num_cols_);
2127  DualPhaseIUpdatePriceOnReducedCostChange(
2128  variables_info_.GetIsRelevantBitRow());
2129  } else {
2130  // Update row is still equal to the row used during the last iteration
2131  // to update the reduced costs.
2132  DualPhaseIUpdatePriceOnReducedCostChange(update_row_.GetNonZeroPositions());
2133  }
2134 
2135  // If there is no dual-infeasible position, we are done.
2136  *leaving_row = kInvalidRow;
2137  if (num_dual_infeasible_positions_ == 0) return Status::OK();
2138 
2139  // TODO(user): Reuse parameters_.optimization_rule() to decide if we use
2140  // steepest edge or the normal Dantzig pricing.
2141  const DenseColumn& squared_norm = dual_edge_norms_.GetEdgeSquaredNorms();
2142 
2143  // Now take a leaving variable that maximizes the infeasibility variation and
2144  // can leave the basis while being dual-feasible.
2145  Fractional best_price(0.0);
2146  equivalent_leaving_choices_.clear();
2147  for (const RowIndex row : is_dual_entering_candidate_) {
2148  const Fractional squared_cost = Square(dual_pricing_vector_[row]);
2149  const Fractional scaled_best_price = best_price * squared_norm[row];
2150  if (squared_cost >= scaled_best_price) {
2151  if (squared_cost == scaled_best_price) {
2152  DCHECK_NE(*leaving_row, kInvalidRow);
2153  equivalent_leaving_choices_.push_back(row);
2154  continue;
2155  }
2156  equivalent_leaving_choices_.clear();
2157  best_price = squared_cost / squared_norm[row];
2158  *leaving_row = row;
2159  }
2160  }
2161 
2162  // Break the ties randomly.
2163  if (!equivalent_leaving_choices_.empty()) {
2164  equivalent_leaving_choices_.push_back(*leaving_row);
2165  *leaving_row =
2166  equivalent_leaving_choices_[std::uniform_int_distribution<int>(
2167  0, equivalent_leaving_choices_.size() - 1)(random_)];
2168  }
2169 
2170  // Returns right away if there is no leaving variable or fill the other
2171  // return values otherwise.
2172  if (*leaving_row == kInvalidRow) return Status::OK();
2173  *cost_variation = dual_pricing_vector_[*leaving_row];
2174  const ColIndex leaving_col = basis_[*leaving_row];
2175  if (*cost_variation < 0.0) {
2176  *target_bound = upper_bound_[leaving_col];
2177  } else {
2178  *target_bound = lower_bound_[leaving_col];
2179  }
2181  return Status::OK();
2182 }
2183 
2184 template <typename BoxedVariableCols>
2185 void RevisedSimplex::MakeBoxedVariableDualFeasible(
2186  const BoxedVariableCols& cols, bool update_basic_values) {
2187  SCOPED_TIME_STAT(&function_stats_);
2188  std::vector<ColIndex> changed_cols;
2189 
2190  // It is important to flip bounds within a tolerance because of precision
2191  // errors. Otherwise, this leads to cycling on many of the Netlib problems
2192  // since this is called at each iteration (because of the bound-flipping ratio
2193  // test).
2194  const DenseRow& variable_values = variable_values_.GetDenseRow();
2195  const DenseRow& reduced_costs = reduced_costs_.GetReducedCosts();
2196  const Fractional dual_feasibility_tolerance =
2197  reduced_costs_.GetDualFeasibilityTolerance();
2198  const VariableStatusRow& variable_status = variables_info_.GetStatusRow();
2199  for (const ColIndex col : cols) {
2200  const Fractional reduced_cost = reduced_costs[col];
2201  const VariableStatus status = variable_status[col];
2202  DCHECK(variables_info_.GetTypeRow()[col] ==
2203  VariableType::UPPER_AND_LOWER_BOUNDED);
2204  // TODO(user): refactor this as DCHECK(IsVariableBasicOrExactlyAtBound())?
2205  DCHECK(variable_values[col] == lower_bound_[col] ||
2206  variable_values[col] == upper_bound_[col] ||
2207  status == VariableStatus::BASIC);
2208  if (reduced_cost > dual_feasibility_tolerance &&
2209  status == VariableStatus::AT_UPPER_BOUND) {
2210  variables_info_.Update(col, VariableStatus::AT_LOWER_BOUND);
2211  changed_cols.push_back(col);
2212  } else if (reduced_cost < -dual_feasibility_tolerance &&
2213  status == VariableStatus::AT_LOWER_BOUND) {
2214  variables_info_.Update(col, VariableStatus::AT_UPPER_BOUND);
2215  changed_cols.push_back(col);
2216  }
2217  }
2218 
2219  if (!changed_cols.empty()) {
2220  variable_values_.UpdateGivenNonBasicVariables(changed_cols,
2221  update_basic_values);
2222  }
2223 }
2224 
2225 Fractional RevisedSimplex::ComputeStepToMoveBasicVariableToBound(
2226  RowIndex leaving_row, Fractional target_bound) {
2227  SCOPED_TIME_STAT(&function_stats_);
2228 
2229  // We just want the leaving variable to go to its target_bound.
2230  const ColIndex leaving_col = basis_[leaving_row];
2231  const Fractional leaving_variable_value = variable_values_.Get(leaving_col);
2232  Fractional unscaled_step = leaving_variable_value - target_bound;
2233 
2234  // In Chvatal p 157 update_[entering_col] is used instead of
2235  // direction_[leaving_row], but the two quantities are actually the
2236  // same. This is because update_[col] is the value at leaving_row of
2237  // the right inverse of col and direction_ is the right inverse of the
2238  // entering_col. Note that direction_[leaving_row] is probably more
2239  // precise.
2240  // TODO(user): use this to check precision and trigger recomputation.
2241  return unscaled_step / direction_[leaving_row];
2242 }
2243 
2244 bool RevisedSimplex::TestPivot(ColIndex entering_col, RowIndex leaving_row) {
2245  VLOG(1) << "Test pivot.";
2246  SCOPED_TIME_STAT(&function_stats_);
2247  const ColIndex leaving_col = basis_[leaving_row];
2248  basis_[leaving_row] = entering_col;
2249 
2250  // TODO(user): If 'is_ok' is true, we could use the computed lu in
2251  // basis_factorization_ rather than recompute it during UpdateAndPivot().
2252  CompactSparseMatrixView basis_matrix(&compact_matrix_, &basis_);
2253  const bool is_ok = test_lu_.ComputeFactorization(basis_matrix).ok();
2254  basis_[leaving_row] = leaving_col;
2255  return is_ok;
2256 }
2257 
2258 // Note that this function is an optimization and that if it was doing nothing
2259 // the algorithm will still be correct and work. Using it does change the pivot
2260 // taken during the simplex method though.
2261 void RevisedSimplex::PermuteBasis() {
2262  SCOPED_TIME_STAT(&function_stats_);
2263 
2264  // Fetch the current basis column permutation and return if it is empty which
2265  // means the permutation is the identity.
2266  const ColumnPermutation& col_perm =
2267  basis_factorization_.GetColumnPermutation();
2268  if (col_perm.empty()) return;
2269 
2270  // Permute basis_.
2271  ApplyColumnPermutationToRowIndexedVector(col_perm, &basis_);
2272 
2273  // Permute dual_pricing_vector_ if needed.
2274  if (!dual_pricing_vector_.empty()) {
2275  // TODO(user): We need to permute is_dual_entering_candidate_ too. Right
2276  // now, we recompute both the dual_pricing_vector_ and
2277  // is_dual_entering_candidate_ on each refactorization, so this don't
2278  // matter.
2279  ApplyColumnPermutationToRowIndexedVector(col_perm, &dual_pricing_vector_);
2280  }
2281 
2282  // Notify the other classes.
2283  reduced_costs_.UpdateDataOnBasisPermutation();
2284  dual_edge_norms_.UpdateDataOnBasisPermutation(col_perm);
2285 
2286  // Finally, remove the column permutation from all subsequent solves since
2287  // it has been taken into account in basis_.
2288  basis_factorization_.SetColumnPermutationToIdentity();
2289 }
2290 
2291 Status RevisedSimplex::UpdateAndPivot(ColIndex entering_col,
2292  RowIndex leaving_row,
2294  SCOPED_TIME_STAT(&function_stats_);
2295  const ColIndex leaving_col = basis_[leaving_row];
2296  const VariableStatus leaving_variable_status =
2297  lower_bound_[leaving_col] == upper_bound_[leaving_col]
2299  : target_bound == lower_bound_[leaving_col]
2302  if (variable_values_.Get(leaving_col) != target_bound) {
2303  ratio_test_stats_.bound_shift.Add(variable_values_.Get(leaving_col) -
2304  target_bound);
2305  }
2306  UpdateBasis(entering_col, leaving_row, leaving_variable_status);
2307 
2308  const Fractional pivot_from_direction = direction_[leaving_row];
2309  const Fractional pivot_from_update_row =
2310  update_row_.GetCoefficient(entering_col);
2311  const Fractional diff =
2312  std::abs(pivot_from_update_row - pivot_from_direction);
2313  if (diff > parameters_.refactorization_threshold() *
2314  (1 + std::abs(pivot_from_direction))) {
2315  VLOG(1) << "Refactorizing: imprecise pivot " << pivot_from_direction
2316  << " diff = " << diff;
2317  GLOP_RETURN_IF_ERROR(basis_factorization_.ForceRefactorization());
2318  } else {
2320  basis_factorization_.Update(entering_col, leaving_row, direction_));
2321  }
2322  if (basis_factorization_.IsRefactorized()) {
2323  PermuteBasis();
2324  }
2325  return Status::OK();
2326 }
2327 
2328 bool RevisedSimplex::NeedsBasisRefactorization(bool refactorize) {
2329  if (basis_factorization_.IsRefactorized()) return false;
2330  if (reduced_costs_.NeedsBasisRefactorization()) return true;
2331  const GlopParameters::PricingRule pricing_rule =
2332  feasibility_phase_ ? parameters_.feasibility_rule()
2333  : parameters_.optimization_rule();
2334  if (parameters_.use_dual_simplex()) {
2335  // TODO(user): Currently the dual is always using STEEPEST_EDGE.
2336  DCHECK_EQ(pricing_rule, GlopParameters::STEEPEST_EDGE);
2337  if (dual_edge_norms_.NeedsBasisRefactorization()) return true;
2338  } else {
2339  if (pricing_rule == GlopParameters::STEEPEST_EDGE &&
2340  primal_edge_norms_.NeedsBasisRefactorization()) {
2341  return true;
2342  }
2343  }
2344  return refactorize;
2345 }
2346 
2347 Status RevisedSimplex::RefactorizeBasisIfNeeded(bool* refactorize) {
2348  SCOPED_TIME_STAT(&function_stats_);
2349  if (NeedsBasisRefactorization(*refactorize)) {
2350  GLOP_RETURN_IF_ERROR(basis_factorization_.Refactorize());
2351  update_row_.Invalidate();
2352  PermuteBasis();
2353  }
2354  *refactorize = false;
2355  return Status::OK();
2356 }
2357 
2359  if (col >= integrality_scale_.size()) {
2360  integrality_scale_.resize(col + 1, 0.0);
2361  }
2362  integrality_scale_[col] = scale;
2363 }
2364 
2365 Status RevisedSimplex::Polish(TimeLimit* time_limit) {
2367  Cleanup update_deterministic_time_on_return(
2368  [this, time_limit]() { AdvanceDeterministicTime(time_limit); });
2369 
2370  // Get all non-basic variables with a reduced costs close to zero.
2371  // Note that because we only choose entering candidate with a cost of zero,
2372  // this set will not change (modulo epsilons).
2373  const DenseRow& rc = reduced_costs_.GetReducedCosts();
2374  std::vector<ColIndex> candidates;
2375  for (const ColIndex col : variables_info_.GetNotBasicBitRow()) {
2376  if (!variables_info_.GetIsRelevantBitRow()[col]) continue;
2377  if (std::abs(rc[col]) < 1e-9) candidates.push_back(col);
2378  }
2379 
2380  bool refactorize = false;
2381  int num_pivots = 0;
2382  Fractional total_gain = 0.0;
2383  for (int i = 0; i < 10; ++i) {
2384  AdvanceDeterministicTime(time_limit);
2385  if (time_limit->LimitReached()) break;
2386  if (num_pivots >= 5) break;
2387  if (candidates.empty()) break;
2388 
2389  // Pick a random one and remove it from the list.
2390  const int index =
2391  std::uniform_int_distribution<int>(0, candidates.size() - 1)(random_);
2392  const ColIndex entering_col = candidates[index];
2393  std::swap(candidates[index], candidates.back());
2394  candidates.pop_back();
2395 
2396  // We need the entering variable to move in the correct direction.
2397  Fractional fake_rc = 1.0;
2398  if (!variables_info_.GetCanDecreaseBitRow()[entering_col]) {
2399  CHECK(variables_info_.GetCanIncreaseBitRow()[entering_col]);
2400  fake_rc = -1.0;
2401  }
2402 
2403  // Compute the direction and by how much we can move along it.
2404  GLOP_RETURN_IF_ERROR(RefactorizeBasisIfNeeded(&refactorize));
2405  ComputeDirection(entering_col);
2406  Fractional step_length;
2407  RowIndex leaving_row;
2409  bool local_refactorize = false;
2411  ChooseLeavingVariableRow(entering_col, fake_rc, &local_refactorize,
2412  &leaving_row, &step_length, &target_bound));
2413 
2414  if (local_refactorize) continue;
2415  if (step_length == kInfinity || step_length == -kInfinity) continue;
2416  if (std::abs(step_length) <= 1e-6) continue;
2417  if (leaving_row != kInvalidRow && std::abs(direction_[leaving_row]) < 0.1) {
2418  continue;
2419  }
2420  const Fractional step = (fake_rc > 0.0) ? -step_length : step_length;
2421 
2422  // Evaluate if pivot reduce the fractionality of the basis.
2423  //
2424  // TODO(user): Count with more weight variable with a small domain, i.e.
2425  // binary variable, compared to a variable in [0, 1k] ?
2426  const auto get_diff = [this](ColIndex col, Fractional old_value,
2427  Fractional new_value) {
2428  if (col >= integrality_scale_.size() || integrality_scale_[col] == 0.0) {
2429  return 0.0;
2430  }
2431  const Fractional s = integrality_scale_[col];
2432  return (std::abs(new_value * s - std::round(new_value * s)) -
2433  std::abs(old_value * s - std::round(old_value * s)));
2434  };
2435  Fractional diff = get_diff(entering_col, variable_values_.Get(entering_col),
2436  variable_values_.Get(entering_col) + step);
2437  for (const auto e : direction_) {
2438  const ColIndex col = basis_[e.row()];
2439  const Fractional old_value = variable_values_.Get(col);
2440  const Fractional new_value = old_value - e.coefficient() * step;
2441  diff += get_diff(col, old_value, new_value);
2442  }
2443 
2444  // Ignore low decrease in integrality.
2445  if (diff > -1e-2) continue;
2446  total_gain -= diff;
2447 
2448  // We perform the change.
2449  num_pivots++;
2450  variable_values_.UpdateOnPivoting(direction_, entering_col, step);
2451 
2452  // This is a bound flip of the entering column.
2453  if (leaving_row == kInvalidRow) {
2454  if (step > 0.0) {
2455  SetNonBasicVariableStatusAndDeriveValue(entering_col,
2457  } else if (step < 0.0) {
2458  SetNonBasicVariableStatusAndDeriveValue(entering_col,
2460  }
2461  reduced_costs_.SetAndDebugCheckThatColumnIsDualFeasible(entering_col);
2462  continue;
2463  }
2464 
2465  // Perform the pivot.
2466  const ColIndex leaving_col = basis_[leaving_row];
2467  update_row_.ComputeUpdateRow(leaving_row);
2468  primal_edge_norms_.UpdateBeforeBasisPivot(
2469  entering_col, leaving_col, leaving_row, direction_, &update_row_);
2470  reduced_costs_.UpdateBeforeBasisPivot(entering_col, leaving_row, direction_,
2471  &update_row_);
2472 
2473  const Fractional dir = -direction_[leaving_row] * step;
2474  const bool is_degenerate =
2475  (dir == 0.0) ||
2476  (dir > 0.0 && variable_values_.Get(leaving_col) >= target_bound) ||
2477  (dir < 0.0 && variable_values_.Get(leaving_col) <= target_bound);
2478  if (!is_degenerate) {
2479  variable_values_.Set(leaving_col, target_bound);
2480  }
2482  UpdateAndPivot(entering_col, leaving_row, target_bound));
2483  }
2484 
2485  VLOG(1) << "Polish num_pivots: " << num_pivots << " gain:" << total_gain;
2486  return Status::OK();
2487 }
2488 
2489 // Minimizes c.x subject to A.x = 0 where A is an mxn-matrix, c an n-vector, and
2490 // x an n-vector.
2491 //
2492 // x is split in two parts x_B and x_N (B standing for basis).
2493 // In the same way, A is split in A_B (also known as B) and A_N, and
2494 // c is split into c_B and c_N.
2495 //
2496 // The goal is to minimize c_B.x_B + c_N.x_N
2497 // subject to B.x_B + A_N.x_N = 0
2498 // and x_lower <= x <= x_upper.
2499 //
2500 // To minimize c.x, at each iteration a variable from x_N is selected to
2501 // enter the basis, and a variable from x_B is selected to leave the basis.
2502 // To avoid explicit inversion of B, the algorithm solves two sub-systems:
2503 // y.B = c_B and B.d = a (a being the entering column).
2504 Status RevisedSimplex::Minimize(TimeLimit* time_limit) {
2506  Cleanup update_deterministic_time_on_return(
2507  [this, time_limit]() { AdvanceDeterministicTime(time_limit); });
2508  num_consecutive_degenerate_iterations_ = 0;
2509  DisplayIterationInfo();
2510  bool refactorize = false;
2511 
2512  if (feasibility_phase_) {
2513  // Initialize the primal phase-I objective.
2514  // Note that this temporarily erases the problem objective.
2515  objective_.AssignToZero(num_cols_);
2516  variable_values_.UpdatePrimalPhaseICosts(
2517  util::IntegerRange<RowIndex>(RowIndex(0), num_rows_), &objective_);
2518  reduced_costs_.ResetForNewObjective();
2519  }
2520 
2521  while (true) {
2522  // TODO(user): we may loop a bit more than the actual number of iteration.
2523  // fix.
2525  ScopedTimeDistributionUpdater timer(&iteration_stats_.total));
2526  GLOP_RETURN_IF_ERROR(RefactorizeBasisIfNeeded(&refactorize));
2527  if (basis_factorization_.IsRefactorized()) {
2528  CorrectErrorsOnVariableValues();
2529  DisplayIterationInfo();
2530 
2531  if (feasibility_phase_) {
2532  // Since the variable values may have been recomputed, we need to
2533  // recompute the primal infeasible variables and update their costs.
2534  if (variable_values_.UpdatePrimalPhaseICosts(
2535  util::IntegerRange<RowIndex>(RowIndex(0), num_rows_),
2536  &objective_)) {
2537  reduced_costs_.ResetForNewObjective();
2538  }
2539  }
2540 
2541  // Computing the objective at each iteration takes time, so we just
2542  // check the limit when the basis is refactorized.
2543  if (!feasibility_phase_ &&
2544  ComputeObjectiveValue() < primal_objective_limit_) {
2545  VLOG(1) << "Stopping the primal simplex because"
2546  << " the objective limit " << primal_objective_limit_
2547  << " has been reached.";
2548  problem_status_ = ProblemStatus::PRIMAL_FEASIBLE;
2549  objective_limit_reached_ = true;
2550  return Status::OK();
2551  }
2552  } else if (feasibility_phase_) {
2553  // Note that direction_.non_zeros contains the positions of the basic
2554  // variables whose values were updated during the last iteration.
2555  if (variable_values_.UpdatePrimalPhaseICosts(direction_.non_zeros,
2556  &objective_)) {
2557  reduced_costs_.ResetForNewObjective();
2558  }
2559  }
2560 
2561  Fractional reduced_cost = 0.0;
2562  ColIndex entering_col = kInvalidCol;
2564  entering_variable_.PrimalChooseEnteringColumn(&entering_col));
2565  if (entering_col == kInvalidCol) {
2566  if (reduced_costs_.AreReducedCostsPrecise() &&
2567  basis_factorization_.IsRefactorized()) {
2568  if (feasibility_phase_) {
2569  const Fractional primal_infeasibility =
2570  variable_values_.ComputeMaximumPrimalInfeasibility();
2571  if (primal_infeasibility <
2572  parameters_.primal_feasibility_tolerance()) {
2573  problem_status_ = ProblemStatus::PRIMAL_FEASIBLE;
2574  } else {
2575  VLOG(1) << "Infeasible problem! infeasibility = "
2576  << primal_infeasibility;
2577  problem_status_ = ProblemStatus::PRIMAL_INFEASIBLE;
2578  }
2579  } else {
2580  problem_status_ = ProblemStatus::OPTIMAL;
2581  }
2582  break;
2583  } else {
2584  VLOG(1) << "Optimal reached, double checking...";
2585  reduced_costs_.MakeReducedCostsPrecise();
2586  refactorize = true;
2587  continue;
2588  }
2589  } else {
2590  reduced_cost = reduced_costs_.GetReducedCosts()[entering_col];
2591  DCHECK(reduced_costs_.IsValidPrimalEnteringCandidate(entering_col));
2592 
2593  // Solve the system B.d = a with a the entering column.
2594  ComputeDirection(entering_col);
2595  primal_edge_norms_.TestEnteringEdgeNormPrecision(entering_col,
2596  direction_);
2597  if (!reduced_costs_.TestEnteringReducedCostPrecision(
2598  entering_col, direction_, &reduced_cost)) {
2599  VLOG(1) << "Skipping col #" << entering_col << " whose reduced cost is "
2600  << reduced_cost;
2601  continue;
2602  }
2603  }
2604 
2605  // This test takes place after the check for optimality/feasibility because
2606  // when running with 0 iterations, we still want to report
2607  // ProblemStatus::OPTIMAL or ProblemStatus::PRIMAL_FEASIBLE if it is the
2608  // case at the beginning of the algorithm.
2609  AdvanceDeterministicTime(time_limit);
2610  if (num_iterations_ == parameters_.max_number_of_iterations() ||
2611  time_limit->LimitReached()) {
2612  break;
2613  }
2614 
2615  Fractional step_length;
2616  RowIndex leaving_row;
2618  if (feasibility_phase_) {
2619  PrimalPhaseIChooseLeavingVariableRow(entering_col, reduced_cost,
2620  &refactorize, &leaving_row,
2621  &step_length, &target_bound);
2622  } else {
2624  ChooseLeavingVariableRow(entering_col, reduced_cost, &refactorize,
2625  &leaving_row, &step_length, &target_bound));
2626  }
2627  if (refactorize) continue;
2628 
2629  if (step_length == kInfinity || step_length == -kInfinity) {
2630  if (!basis_factorization_.IsRefactorized() ||
2631  !reduced_costs_.AreReducedCostsPrecise()) {
2632  VLOG(1) << "Infinite step length, double checking...";
2633  reduced_costs_.MakeReducedCostsPrecise();
2634  continue;
2635  }
2636  if (feasibility_phase_) {
2637  // This shouldn't happen by construction.
2638  VLOG(1) << "Unbounded feasibility problem !?";
2639  problem_status_ = ProblemStatus::ABNORMAL;
2640  } else {
2641  VLOG(1) << "Unbounded problem.";
2642  problem_status_ = ProblemStatus::PRIMAL_UNBOUNDED;
2643  solution_primal_ray_.AssignToZero(num_cols_);
2644  for (RowIndex row(0); row < num_rows_; ++row) {
2645  const ColIndex col = basis_[row];
2646  solution_primal_ray_[col] = -direction_[row];
2647  }
2648  solution_primal_ray_[entering_col] = 1.0;
2649  if (step_length == -kInfinity) {
2650  ChangeSign(&solution_primal_ray_);
2651  }
2652  }
2653  break;
2654  }
2655 
2656  Fractional step = (reduced_cost > 0.0) ? -step_length : step_length;
2657  if (feasibility_phase_ && leaving_row != kInvalidRow) {
2658  // For phase-I we currently always set the leaving variable to its exact
2659  // bound even if by doing so we may take a small step in the wrong
2660  // direction and may increase the overall infeasibility.
2661  //
2662  // TODO(user): Investigate alternatives even if this seems to work well in
2663  // practice. Note that the final returned solution will have the property
2664  // that all non-basic variables are at their exact bound, so it is nice
2665  // that we do not report ProblemStatus::PRIMAL_FEASIBLE if a solution with
2666  // this property cannot be found.
2667  step = ComputeStepToMoveBasicVariableToBound(leaving_row, target_bound);
2668  }
2669 
2670  // Store the leaving_col before basis_ change.
2671  const ColIndex leaving_col =
2672  (leaving_row == kInvalidRow) ? kInvalidCol : basis_[leaving_row];
2673 
2674  // An iteration is called 'degenerate' if the leaving variable is already
2675  // primal-infeasible and we make it even more infeasible or if we do a zero
2676  // step.
2677  bool is_degenerate = false;
2678  if (leaving_row != kInvalidRow) {
2679  Fractional dir = -direction_[leaving_row] * step;
2680  is_degenerate =
2681  (dir == 0.0) ||
2682  (dir > 0.0 && variable_values_.Get(leaving_col) >= target_bound) ||
2683  (dir < 0.0 && variable_values_.Get(leaving_col) <= target_bound);
2684 
2685  // If the iteration is not degenerate, the leaving variable should go to
2686  // its exact target bound (it is how the step is computed).
2687  if (!is_degenerate) {
2688  DCHECK_EQ(step, ComputeStepToMoveBasicVariableToBound(leaving_row,
2689  target_bound));
2690  }
2691  }
2692 
2693  variable_values_.UpdateOnPivoting(direction_, entering_col, step);
2694  if (leaving_row != kInvalidRow) {
2695  primal_edge_norms_.UpdateBeforeBasisPivot(
2696  entering_col, basis_[leaving_row], leaving_row, direction_,
2697  &update_row_);
2698  reduced_costs_.UpdateBeforeBasisPivot(entering_col, leaving_row,
2699  direction_, &update_row_);
2700  if (!is_degenerate) {
2701  // On a non-degenerate iteration, the leaving variable should be at its
2702  // exact bound. This corrects an eventual small numerical error since
2703  // 'value + direction * step' where step is
2704  // '(target_bound - value) / direction'
2705  // may be slighlty different from target_bound.
2706  variable_values_.Set(leaving_col, target_bound);
2707  }
2709  UpdateAndPivot(entering_col, leaving_row, target_bound));
2711  if (is_degenerate) {
2712  timer.AlsoUpdate(&iteration_stats_.degenerate);
2713  } else {
2714  timer.AlsoUpdate(&iteration_stats_.normal);
2715  }
2716  });
2717  } else {
2718  // Bound flip. This makes sure that the flipping variable is at its bound
2719  // and has the correct status.
2720  DCHECK_EQ(VariableType::UPPER_AND_LOWER_BOUNDED,
2721  variables_info_.GetTypeRow()[entering_col]);
2722  if (step > 0.0) {
2723  SetNonBasicVariableStatusAndDeriveValue(entering_col,
2725  } else if (step < 0.0) {
2726  SetNonBasicVariableStatusAndDeriveValue(entering_col,
2728  }
2729  reduced_costs_.SetAndDebugCheckThatColumnIsDualFeasible(entering_col);
2730  IF_STATS_ENABLED(timer.AlsoUpdate(&iteration_stats_.bound_flip));
2731  }
2732 
2733  if (feasibility_phase_ && leaving_row != kInvalidRow) {
2734  // Set the leaving variable to its exact bound.
2735  variable_values_.SetNonBasicVariableValueFromStatus(leaving_col);
2736  reduced_costs_.SetNonBasicVariableCostToZero(leaving_col,
2737  &objective_[leaving_col]);
2738  }
2739 
2740  // Stats about consecutive degenerate iterations.
2741  if (step_length == 0.0) {
2742  num_consecutive_degenerate_iterations_++;
2743  } else {
2744  if (num_consecutive_degenerate_iterations_ > 0) {
2745  iteration_stats_.degenerate_run_size.Add(
2746  num_consecutive_degenerate_iterations_);
2747  num_consecutive_degenerate_iterations_ = 0;
2748  }
2749  }
2750  ++num_iterations_;
2751  }
2752  if (num_consecutive_degenerate_iterations_ > 0) {
2753  iteration_stats_.degenerate_run_size.Add(
2754  num_consecutive_degenerate_iterations_);
2755  }
2756  return Status::OK();
2757 }
2758 
2759 // TODO(user): Two other approaches for the phase I described in Koberstein's
2760 // PhD thesis seem worth trying at some point:
2761 // - The subproblem approach, which enables one to use a normal phase II dual,
2762 // but requires an efficient bound-flipping ratio test since the new problem
2763 // has all its variables boxed.
2764 // - Pan's method, which is really fast but have no theoretical guarantee of
2765 // terminating and thus needs to use one of the other methods as a fallback if
2766 // it fails to make progress.
2767 //
2768 // Note that the returned status applies to the primal problem!
2769 Status RevisedSimplex::DualMinimize(TimeLimit* time_limit) {
2770  Cleanup update_deterministic_time_on_return(
2771  [this, time_limit]() { AdvanceDeterministicTime(time_limit); });
2772  num_consecutive_degenerate_iterations_ = 0;
2773  bool refactorize = false;
2774 
2775  bound_flip_candidates_.clear();
2776  pair_to_ignore_.clear();
2777 
2778  // Leaving variable.
2779  RowIndex leaving_row;
2780  Fractional cost_variation;
2782 
2783  // Entering variable.
2784  ColIndex entering_col;
2785  Fractional ratio;
2786 
2787  while (true) {
2788  // TODO(user): we may loop a bit more than the actual number of iteration.
2789  // fix.
2791  ScopedTimeDistributionUpdater timer(&iteration_stats_.total));
2792 
2793  const bool old_refactorize_value = refactorize;
2794  GLOP_RETURN_IF_ERROR(RefactorizeBasisIfNeeded(&refactorize));
2795 
2796  // If the basis is refactorized, we recompute all the values in order to
2797  // have a good precision.
2798  if (basis_factorization_.IsRefactorized()) {
2799  // We do not want to recompute the reduced costs too often, this is
2800  // because that may break the overall direction taken by the last steps
2801  // and may lead to less improvement on degenerate problems.
2802  //
2803  // During phase-I, we do want the reduced costs to be as precise as
2804  // possible. TODO(user): Investigate why and fix the TODO in
2805  // PermuteBasis().
2806  //
2807  // Reduced costs are needed by MakeBoxedVariableDualFeasible(), so if we
2808  // do recompute them, it is better to do that first.
2809  if (!feasibility_phase_ && !reduced_costs_.AreReducedCostsRecomputed() &&
2810  !old_refactorize_value) {
2811  const Fractional dual_residual_error =
2812  reduced_costs_.ComputeMaximumDualResidual();
2813  if (dual_residual_error >
2814  reduced_costs_.GetDualFeasibilityTolerance()) {
2815  VLOG(1) << "Recomputing reduced costs. Dual residual = "
2816  << dual_residual_error;
2817  reduced_costs_.MakeReducedCostsPrecise();
2818  }
2819  } else {
2820  reduced_costs_.MakeReducedCostsPrecise();
2821  }
2822 
2823  // TODO(user): Make RecomputeBasicVariableValues() do nothing
2824  // if it was already recomputed on a refactorized basis. This is the
2825  // same behavior as MakeReducedCostsPrecise().
2826  //
2827  // TODO(user): Do not recompute the variable values each time we
2828  // refactorize the matrix, like for the reduced costs? That may lead to
2829  // a worse behavior than keeping the "imprecise" version and only
2830  // recomputing it when its precision is above a threshold.
2831  if (!feasibility_phase_) {
2832  MakeBoxedVariableDualFeasible(
2833  variables_info_.GetNonBasicBoxedVariables(),
2834  /*update_basic_values=*/false);
2835  variable_values_.RecomputeBasicVariableValues();
2836  variable_values_.ResetPrimalInfeasibilityInformation();
2837 
2838  // Computing the objective at each iteration takes time, so we just
2839  // check the limit when the basis is refactorized.
2840  if (ComputeObjectiveValue() > dual_objective_limit_) {
2841  VLOG(1) << "Stopping the dual simplex because"
2842  << " the objective limit " << dual_objective_limit_
2843  << " has been reached.";
2844  problem_status_ = ProblemStatus::DUAL_FEASIBLE;
2845  objective_limit_reached_ = true;
2846  return Status::OK();
2847  }
2848  }
2849 
2850  reduced_costs_.GetReducedCosts();
2851  DisplayIterationInfo();
2852  } else {
2853  // Updates from the previous iteration that can be skipped if we
2854  // recompute everything (see other case above).
2855  if (!feasibility_phase_) {
2856  // Make sure the boxed variables are dual-feasible before choosing the
2857  // leaving variable row.
2858  MakeBoxedVariableDualFeasible(bound_flip_candidates_,
2859  /*update_basic_values=*/true);
2860  bound_flip_candidates_.clear();
2861 
2862  // The direction_.non_zeros contains the positions for which the basic
2863  // variable value was changed during the previous iterations.
2864  variable_values_.UpdatePrimalInfeasibilityInformation(
2865  direction_.non_zeros);
2866  }
2867  }
2868 
2869  if (feasibility_phase_) {
2870  GLOP_RETURN_IF_ERROR(DualPhaseIChooseLeavingVariableRow(
2871  &leaving_row, &cost_variation, &target_bound));
2872  } else {
2873  GLOP_RETURN_IF_ERROR(DualChooseLeavingVariableRow(
2874  &leaving_row, &cost_variation, &target_bound));
2875  }
2876  if (leaving_row == kInvalidRow) {
2877  if (!basis_factorization_.IsRefactorized()) {
2878  VLOG(1) << "Optimal reached, double checking.";
2879  refactorize = true;
2880  continue;
2881  }
2882  if (feasibility_phase_) {
2883  // Note that since the basis is refactorized, the variable values
2884  // will be recomputed at the beginning of the second phase. The boxed
2885  // variable values will also be corrected by
2886  // MakeBoxedVariableDualFeasible().
2887  if (num_dual_infeasible_positions_ == 0) {
2888  problem_status_ = ProblemStatus::DUAL_FEASIBLE;
2889  } else {
2890  problem_status_ = ProblemStatus::DUAL_INFEASIBLE;
2891  }
2892  } else {
2893  problem_status_ = ProblemStatus::OPTIMAL;
2894  }
2895  return Status::OK();
2896  }
2897 
2898  update_row_.ComputeUpdateRow(leaving_row);
2899  for (std::pair<RowIndex, ColIndex> pair : pair_to_ignore_) {
2900  if (pair.first == leaving_row) {
2901  update_row_.IgnoreUpdatePosition(pair.second);
2902  }
2903  }
2904  if (feasibility_phase_) {
2906  update_row_, cost_variation, &entering_col, &ratio));
2907  } else {
2909  update_row_, cost_variation, &bound_flip_candidates_, &entering_col,
2910  &ratio));
2911  }
2912 
2913  // No entering_col: Unbounded problem / Infeasible problem.
2914  if (entering_col == kInvalidCol) {
2915  if (!reduced_costs_.AreReducedCostsPrecise()) {
2916  VLOG(1) << "No entering column. Double checking...";
2917  refactorize = true;
2918  continue;
2919  }
2920  DCHECK(basis_factorization_.IsRefactorized());
2921  if (feasibility_phase_) {
2922  // This shouldn't happen by construction.
2923  VLOG(1) << "Unbounded dual feasibility problem !?";
2924  problem_status_ = ProblemStatus::ABNORMAL;
2925  } else {
2926  problem_status_ = ProblemStatus::DUAL_UNBOUNDED;
2927  solution_dual_ray_ =
2928  Transpose(update_row_.GetUnitRowLeftInverse().values);
2929  update_row_.RecomputeFullUpdateRow(leaving_row);
2930  solution_dual_ray_row_combination_.AssignToZero(num_cols_);
2931  for (const ColIndex col : update_row_.GetNonZeroPositions()) {
2932  solution_dual_ray_row_combination_[col] =
2933  update_row_.GetCoefficient(col);
2934  }
2935  if (cost_variation < 0) {
2936  ChangeSign(&solution_dual_ray_);
2937  ChangeSign(&solution_dual_ray_row_combination_);
2938  }
2939  }
2940  return Status::OK();
2941  }
2942 
2943  // If the coefficient is too small, we recompute the reduced costs.
2944  const Fractional entering_coeff = update_row_.GetCoefficient(entering_col);
2945  if (std::abs(entering_coeff) < parameters_.dual_small_pivot_threshold() &&
2946  !reduced_costs_.AreReducedCostsPrecise()) {
2947  VLOG(1) << "Trying not to pivot by " << entering_coeff;
2948  refactorize = true;
2949  continue;
2950  }
2951 
2952  // If the reduced cost is already precise, we check with the direction_.
2953  // This is at least needed to avoid corner cases where
2954  // direction_[leaving_row] is actually 0 which causes a floating
2955  // point exception below.
2956  ComputeDirection(entering_col);
2957  if (std::abs(direction_[leaving_row]) <
2958  parameters_.minimum_acceptable_pivot()) {
2959  VLOG(1) << "Do not pivot by " << entering_coeff
2960  << " because the direction is " << direction_[leaving_row];
2961  refactorize = true;
2962  pair_to_ignore_.push_back({leaving_row, entering_col});
2963  continue;
2964  }
2965  pair_to_ignore_.clear();
2966 
2967  // This test takes place after the check for optimality/feasibility because
2968  // when running with 0 iterations, we still want to report
2969  // ProblemStatus::OPTIMAL or ProblemStatus::PRIMAL_FEASIBLE if it is the
2970  // case at the beginning of the algorithm.
2971  AdvanceDeterministicTime(time_limit);
2972  if (num_iterations_ == parameters_.max_number_of_iterations() ||
2973  time_limit->LimitReached()) {
2974  return Status::OK();
2975  }
2976 
2978  if (ratio == 0.0) {
2979  timer.AlsoUpdate(&iteration_stats_.degenerate);
2980  } else {
2981  timer.AlsoUpdate(&iteration_stats_.normal);
2982  }
2983  });
2984 
2985  // Update basis. Note that direction_ is already computed.
2986  //
2987  // TODO(user): this is pretty much the same in the primal or dual code.
2988  // We just need to know to what bound the leaving variable will be set to.
2989  // Factorize more common code?
2990  //
2991  // During phase I, we do not need the basic variable values at all.
2992  Fractional primal_step = 0.0;
2993  if (feasibility_phase_) {
2994  DualPhaseIUpdatePrice(leaving_row, entering_col);
2995  } else {
2996  primal_step =
2997  ComputeStepToMoveBasicVariableToBound(leaving_row, target_bound);
2998  variable_values_.UpdateOnPivoting(direction_, entering_col, primal_step);
2999  }
3000 
3001  reduced_costs_.UpdateBeforeBasisPivot(entering_col, leaving_row, direction_,
3002  &update_row_);
3003  dual_edge_norms_.UpdateBeforeBasisPivot(
3004  entering_col, leaving_row, direction_,
3005  update_row_.GetUnitRowLeftInverse());
3006 
3007  // It is important to do the actual pivot after the update above!
3008  const ColIndex leaving_col = basis_[leaving_row];
3010  UpdateAndPivot(entering_col, leaving_row, target_bound));
3011 
3012  // This makes sure the leaving variable is at its exact bound. Tests
3013  // indicate that this makes everything more stable. Note also that during
3014  // the feasibility phase, the variable values are not used, but that the
3015  // correct non-basic variable value are needed at the end.
3016  variable_values_.SetNonBasicVariableValueFromStatus(leaving_col);
3017 
3018  // This is slow, but otherwise we have a really bad precision on the
3019  // variable values ...
3020  if (std::abs(primal_step) * parameters_.primal_feasibility_tolerance() >
3021  1.0) {
3022  refactorize = true;
3023  }
3024  ++num_iterations_;
3025  }
3026  return Status::OK();
3027 }
3028 
3029 ColIndex RevisedSimplex::SlackColIndex(RowIndex row) const {
3030  // TODO(user): Remove this function.
3032  return first_slack_col_ + RowToColIndex(row);
3033 }
3034 
3036  std::string result;
3037  result.append(iteration_stats_.StatString());
3038  result.append(ratio_test_stats_.StatString());
3039  result.append(entering_variable_.StatString());
3040  result.append(reduced_costs_.StatString());
3041  result.append(variable_values_.StatString());
3042  result.append(primal_edge_norms_.StatString());
3043  result.append(dual_edge_norms_.StatString());
3044  result.append(update_row_.StatString());
3045  result.append(basis_factorization_.StatString());
3046  result.append(function_stats_.StatString());
3047  return result;
3048 }
3049 
3050 void RevisedSimplex::DisplayAllStats() {
3051  if (absl::GetFlag(FLAGS_simplex_display_stats)) {
3052  absl::FPrintF(stderr, "%s", StatString());
3053  absl::FPrintF(stderr, "%s", GetPrettySolverStats());
3054  }
3055 }
3056 
3057 Fractional RevisedSimplex::ComputeObjectiveValue() const {
3058  SCOPED_TIME_STAT(&function_stats_);
3059  return PreciseScalarProduct(objective_,
3060  Transpose(variable_values_.GetDenseRow()));
3061 }
3062 
3063 Fractional RevisedSimplex::ComputeInitialProblemObjectiveValue() const {
3064  SCOPED_TIME_STAT(&function_stats_);
3065  const Fractional sum = PreciseScalarProduct(
3066  objective_, Transpose(variable_values_.GetDenseRow()));
3067  return objective_scaling_factor_ * (sum + objective_offset_);
3068 }
3069 
3070 void RevisedSimplex::SetParameters(const GlopParameters& parameters) {
3071  SCOPED_TIME_STAT(&function_stats_);
3072  random_.seed(parameters.random_seed());
3073  initial_parameters_ = parameters;
3074  parameters_ = parameters;
3075  PropagateParameters();
3076 }
3077 
3078 void RevisedSimplex::PropagateParameters() {
3079  SCOPED_TIME_STAT(&function_stats_);
3080  basis_factorization_.SetParameters(parameters_);
3081  entering_variable_.SetParameters(parameters_);
3082  reduced_costs_.SetParameters(parameters_);
3083  dual_edge_norms_.SetParameters(parameters_);
3084  primal_edge_norms_.SetParameters(parameters_);
3085  update_row_.SetParameters(parameters_);
3086 }
3087 
3088 void RevisedSimplex::DisplayIterationInfo() const {
3089  if (parameters_.log_search_progress() || VLOG_IS_ON(1)) {
3090  const int iter = feasibility_phase_
3091  ? num_iterations_
3092  : num_iterations_ - num_feasibility_iterations_;
3093  // Note that in the dual phase II, ComputeObjectiveValue() is also computing
3094  // the dual objective even if it uses the variable values. This is because
3095  // if we modify the bounds to make the problem primal-feasible, we are at
3096  // the optimal and hence the two objectives are the same.
3097  const Fractional objective =
3098  !feasibility_phase_
3099  ? ComputeInitialProblemObjectiveValue()
3100  : (parameters_.use_dual_simplex()
3101  ? reduced_costs_.ComputeSumOfDualInfeasibilities()
3102  : variable_values_.ComputeSumOfPrimalInfeasibilities());
3103  LOG(INFO) << (feasibility_phase_ ? "Feasibility" : "Optimization")
3104  << " phase, iteration # " << iter
3105  << ", objective = " << absl::StrFormat("%.15E", objective);
3106  }
3107 }
3108 
3109 void RevisedSimplex::DisplayErrors() const {
3110  if (parameters_.log_search_progress() || VLOG_IS_ON(1)) {
3111  LOG(INFO) << "Primal infeasibility (bounds) = "
3112  << variable_values_.ComputeMaximumPrimalInfeasibility();
3113  LOG(INFO) << "Primal residual |A.x - b| = "
3114  << variable_values_.ComputeMaximumPrimalResidual();
3115  LOG(INFO) << "Dual infeasibility (reduced costs) = "
3116  << reduced_costs_.ComputeMaximumDualInfeasibility();
3117  LOG(INFO) << "Dual residual |c_B - y.B| = "
3118  << reduced_costs_.ComputeMaximumDualResidual();
3119  }
3120 }
3121 
3122 namespace {
3123 
3124 std::string StringifyMonomialWithFlags(const Fractional a,
3125  const std::string& x) {
3126  return StringifyMonomial(
3127  a, x, absl::GetFlag(FLAGS_simplex_display_numbers_as_fractions));
3128 }
3129 
3130 // Returns a string representing the rational approximation of x or a decimal
3131 // approximation of x according to
3132 // absl::GetFlag(FLAGS_simplex_display_numbers_as_fractions).
3133 std::string StringifyWithFlags(const Fractional x) {
3134  return Stringify(x,
3135  absl::GetFlag(FLAGS_simplex_display_numbers_as_fractions));
3136 }
3137 
3138 } // namespace
3139 
3140 std::string RevisedSimplex::SimpleVariableInfo(ColIndex col) const {
3141  std::string output;
3142  VariableType variable_type = variables_info_.GetTypeRow()[col];
3143  VariableStatus variable_status = variables_info_.GetStatusRow()[col];
3144  absl::StrAppendFormat(&output, "%d (%s) = %s, %s, %s, [%s,%s]", col.value(),
3145  variable_name_[col],
3146  StringifyWithFlags(variable_values_.Get(col)),
3147  GetVariableStatusString(variable_status),
3148  GetVariableTypeString(variable_type),
3149  StringifyWithFlags(lower_bound_[col]),
3150  StringifyWithFlags(upper_bound_[col]));
3151  return output;
3152 }
3153 
3154 void RevisedSimplex::DisplayInfoOnVariables() const {
3155  if (VLOG_IS_ON(3)) {
3156  for (ColIndex col(0); col < num_cols_; ++col) {
3157  const Fractional variable_value = variable_values_.Get(col);
3158  const Fractional objective_coefficient = objective_[col];
3159  const Fractional objective_contribution =
3160  objective_coefficient * variable_value;
3161  VLOG(3) << SimpleVariableInfo(col) << ". " << variable_name_[col] << " = "
3162  << StringifyWithFlags(variable_value) << " * "
3163  << StringifyWithFlags(objective_coefficient)
3164  << "(obj) = " << StringifyWithFlags(objective_contribution);
3165  }
3166  VLOG(3) << "------";
3167  }
3168 }
3169 
3170 void RevisedSimplex::DisplayVariableBounds() {
3171  if (VLOG_IS_ON(3)) {
3172  const VariableTypeRow& variable_type = variables_info_.GetTypeRow();
3173  for (ColIndex col(0); col < num_cols_; ++col) {
3174  switch (variable_type[col]) {
3175  case VariableType::UNCONSTRAINED:
3176  break;
3177  case VariableType::LOWER_BOUNDED:
3178  VLOG(3) << variable_name_[col]
3179  << " >= " << StringifyWithFlags(lower_bound_[col]) << ";";
3180  break;
3181  case VariableType::UPPER_BOUNDED:
3182  VLOG(3) << variable_name_[col]
3183  << " <= " << StringifyWithFlags(upper_bound_[col]) << ";";
3184  break;
3185  case VariableType::UPPER_AND_LOWER_BOUNDED:
3186  VLOG(3) << StringifyWithFlags(lower_bound_[col])
3187  << " <= " << variable_name_[col]
3188  << " <= " << StringifyWithFlags(upper_bound_[col]) << ";";
3189  break;
3191  VLOG(3) << variable_name_[col] << " = "
3192  << StringifyWithFlags(lower_bound_[col]) << ";";
3193  break;
3194  default: // This should never happen.
3195  LOG(DFATAL) << "Column " << col << " has no meaningful status.";
3196  break;
3197  }
3198  }
3199  }
3200 }
3201 
3203  const DenseRow* column_scales) {
3204  absl::StrongVector<RowIndex, SparseRow> dictionary(num_rows_.value());
3205  for (ColIndex col(0); col < num_cols_; ++col) {
3206  ComputeDirection(col);
3207  for (const auto e : direction_) {
3208  if (column_scales == nullptr) {
3209  dictionary[e.row()].SetCoefficient(col, e.coefficient());
3210  continue;
3211  }
3212  const Fractional numerator =
3213  col < column_scales->size() ? (*column_scales)[col] : 1.0;
3214  const Fractional denominator = GetBasis(e.row()) < column_scales->size()
3215  ? (*column_scales)[GetBasis(e.row())]
3216  : 1.0;
3217  dictionary[e.row()].SetCoefficient(
3218  col, direction_[e.row()] * (numerator / denominator));
3219  }
3220  }
3221  return dictionary;
3222 }
3223 
3225  const LinearProgram& linear_program, const BasisState& state) {
3226  LoadStateForNextSolve(state);
3227  Status status = Initialize(linear_program);
3228  if (status.ok()) {
3229  variable_values_.RecomputeBasicVariableValues();
3230  variable_values_.ResetPrimalInfeasibilityInformation();
3231  solution_objective_value_ = ComputeInitialProblemObjectiveValue();
3232  }
3233 }
3234 
3235 void RevisedSimplex::DisplayRevisedSimplexDebugInfo() {
3236  if (VLOG_IS_ON(3)) {
3237  // This function has a complexity in O(num_non_zeros_in_matrix).
3238  DisplayInfoOnVariables();
3239 
3240  std::string output = "z = " + StringifyWithFlags(ComputeObjectiveValue());
3241  const DenseRow& reduced_costs = reduced_costs_.GetReducedCosts();
3242  for (const ColIndex col : variables_info_.GetNotBasicBitRow()) {
3243  absl::StrAppend(&output, StringifyMonomialWithFlags(reduced_costs[col],
3244  variable_name_[col]));
3245  }
3246  VLOG(3) << output << ";";
3247 
3248  const RevisedSimplexDictionary dictionary(nullptr, this);
3249  RowIndex r(0);
3250  for (const SparseRow& row : dictionary) {
3251  output.clear();
3252  ColIndex basic_col = basis_[r];
3253  absl::StrAppend(&output, variable_name_[basic_col], " = ",
3254  StringifyWithFlags(variable_values_.Get(basic_col)));
3255  for (const SparseRowEntry e : row) {
3256  if (e.col() != basic_col) {
3257  absl::StrAppend(&output,
3258  StringifyMonomialWithFlags(e.coefficient(),
3259  variable_name_[e.col()]));
3260  }
3261  }
3262  VLOG(3) << output << ";";
3263  }
3264  VLOG(3) << "------";
3265  DisplayVariableBounds();
3266  ++r;
3267  }
3268 }
3269 
3270 void RevisedSimplex::DisplayProblem() const {
3271  // This function has a complexity in O(num_rows * num_cols *
3272  // num_non_zeros_in_row).
3273  if (VLOG_IS_ON(3)) {
3274  DisplayInfoOnVariables();
3275  std::string output = "min: ";
3276  bool has_objective = false;
3277  for (ColIndex col(0); col < num_cols_; ++col) {
3278  const Fractional coeff = objective_[col];
3279  has_objective |= (coeff != 0.0);
3280  absl::StrAppend(&output,
3281  StringifyMonomialWithFlags(coeff, variable_name_[col]));
3282  }
3283  if (!has_objective) {
3284  absl::StrAppend(&output, " 0");
3285  }
3286  VLOG(3) << output << ";";
3287  for (RowIndex row(0); row < num_rows_; ++row) {
3288  output = "";
3289  for (ColIndex col(0); col < num_cols_; ++col) {
3290  absl::StrAppend(&output,
3291  StringifyMonomialWithFlags(
3292  compact_matrix_.column(col).LookUpCoefficient(row),
3293  variable_name_[col]));
3294  }
3295  VLOG(3) << output << " = 0;";
3296  }
3297  VLOG(3) << "------";
3298  }
3299 }
3300 
3301 void RevisedSimplex::AdvanceDeterministicTime(TimeLimit* time_limit) {
3302  DCHECK(time_limit != nullptr);
3303  const double current_deterministic_time = DeterministicTime();
3304  const double deterministic_time_delta =
3305  current_deterministic_time - last_deterministic_time_update_;
3306  time_limit->AdvanceDeterministicTime(deterministic_time_delta);
3307  last_deterministic_time_update_ = current_deterministic_time;
3308 }
3309 
3310 #undef DCHECK_COL_BOUNDS
3311 #undef DCHECK_ROW_BOUNDS
3312 
3313 } // namespace glop
3314 } // namespace operations_research
operations_research::glop::ColumnView::LookUpCoefficient
Fractional LookUpCoefficient(RowIndex index) const
Definition: sparse_column.h:100
operations_research::glop::ReducedCosts::ClearAndRemoveCostShifts
void ClearAndRemoveCostShifts()
Definition: reduced_costs.cc:302
INFO
const int INFO
Definition: log_severity.h:31
operations_research::glop::RevisedSimplex::GetProblemStatus
ProblemStatus GetProblemStatus() const
Definition: revised_simplex.cc:433
operations_research::glop::ScatteredVector::ClearSparseMask
void ClearSparseMask()
Definition: scattered_vector.h:132
operations_research::glop::ReducedCosts::PerturbCosts
void PerturbCosts()
Definition: reduced_costs.cc:240
util::IntegerRange
Definition: iterators.h:146
operations_research::glop::VariableStatus::AT_UPPER_BOUND
@ AT_UPPER_BOUND
operations_research::glop::RevisedSimplex::GetBasisFactorization
const BasisFactorization & GetBasisFactorization() const
Definition: revised_simplex.cc:504
operations_research::glop::StrictITIVector::resize
void resize(IntType size)
Definition: lp_types.h:269
if
if(!yyg->yy_init)
Definition: parser.yy.cc:965
min
int64 min
Definition: alldiff_cst.cc:138
integral_types.h
operations_research::glop::DenseRow
StrictITIVector< ColIndex, Fractional > DenseRow
Definition: lp_types.h:299
DCHECK_LT
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:888
operations_research::glop::VariableStatus::BASIC
@ BASIC
VLOG
#define VLOG(verboselevel)
Definition: base/logging.h:978
operations_research::Bitset64::IsSet
bool IsSet(IndexType i) const
Definition: bitset.h:483
operations_research::glop::VariablesInfo::GetNotBasicBitRow
const DenseBitRow & GetNotBasicBitRow() const
Definition: variables_info.cc:119
operations_research::glop::ReducedCosts::UpdateDataOnBasisPermutation
void UpdateDataOnBasisPermutation()
Definition: reduced_costs.cc:226
operations_research::glop::ApplyColumnPermutationToRowIndexedVector
void ApplyColumnPermutationToRowIndexedVector(const Permutation< ColIndex > &col_perm, RowIndexedVector *v)
Definition: lp_data/permutation.h:116
operations_research::glop::VariableValues::RecomputeBasicVariableValues
void RecomputeBasicVariableValues()
Definition: variable_values.cc:92
operations_research::glop::RevisedSimplex::StatString
std::string StatString()
Definition: revised_simplex.cc:3035
max
int64 max
Definition: alldiff_cst.cc:139
operations_research::glop::RevisedSimplex::DeterministicTime
double DeterministicTime() const
Definition: revised_simplex.cc:525
operations_research::glop::ReducedCosts::MakeReducedCostsPrecise
void MakeReducedCostsPrecise()
Definition: reduced_costs.cc:232
operations_research::glop::CompactSparseMatrix::ColumnCopyToDenseColumn
void ColumnCopyToDenseColumn(ColIndex col, DenseColumn *dense_column) const
Definition: sparse.h:418
IF_STATS_ENABLED
#define IF_STATS_ENABLED(instructions)
Definition: stats.h:435
operations_research::glop::ProblemStatus::ABNORMAL
@ ABNORMAL
operations_research::glop::DualEdgeNorms::SetParameters
void SetParameters(const GlopParameters &parameters)
Definition: dual_edge_norms.h:87
operations_research::glop::VariableValues::ResetPrimalInfeasibilityInformation
void ResetPrimalInfeasibilityInformation()
Definition: variable_values.cc:226
operations_research::glop::UpdateRow::StatString
std::string StatString() const
Definition: update_row.h:81
LOG
#define LOG(severity)
Definition: base/logging.h:420
operations_research::glop::kInvalidCol
const ColIndex kInvalidCol(-1)
operations_research::glop::BasisFactorization::StatString
std::string StatString() const
Definition: basis_representation.h:275
lp_data.h
operations_research::glop::VariableValues::UpdateGivenNonBasicVariables
void UpdateGivenNonBasicVariables(const std::vector< ColIndex > &cols_to_update, bool update_basic_variables)
Definition: variable_values.cc:167
operations_research::glop::CompactSparseMatrix::column
ColumnView column(ColIndex col) const
Definition: sparse.h:364
operations_research::glop::Status::ERROR_INVALID_PROBLEM
@ ERROR_INVALID_PROBLEM
Definition: status.h:41
operations_research::glop::DualEdgeNorms::NeedsBasisRefactorization
bool NeedsBasisRefactorization()
Definition: dual_edge_norms.cc:25
operations_research::glop::ReducedCosts::AreReducedCostsRecomputed
bool AreReducedCostsRecomputed()
Definition: reduced_costs.h:112
operations_research::glop::UpdateRow::IgnoreUpdatePosition
void IgnoreUpdatePosition(ColIndex col)
Definition: update_row.cc:45
operations_research::glop::CompactSparseMatrix::ColumnAddMultipleToDenseColumn
void ColumnAddMultipleToDenseColumn(ColIndex col, Fractional multiplier, DenseColumn *dense_column) const
Definition: sparse.h:393
operations_research::glop::BasisFactorization::Clear
void Clear()
Definition: basis_representation.cc:193
operations_research::glop::EnteringVariable::DualPhaseIChooseEnteringColumn
ABSL_MUST_USE_RESULT Status DualPhaseIChooseEnteringColumn(const UpdateRow &update_row, Fractional cost_variation, ColIndex *entering_col, Fractional *step)
Definition: entering_variable.cc:268
operations_research::glop::RevisedSimplex::LoadStateForNextSolve
void LoadStateForNextSolve(const BasisState &state)
Definition: revised_simplex.cc:125
operations_research::glop::VariablesInfo::UpdateToBasicStatus
void UpdateToBasicStatus(ColIndex col)
Definition: variables_info.cc:68
operations_research::glop::BasisFactorization::Update
ABSL_MUST_USE_RESULT Status Update(ColIndex entering_col, RowIndex leaving_variable_row, const ScatteredColumn &direction)
Definition: basis_representation.cc:284
operations_research::glop::VariablesInfo::GetBoundDifference
Fractional GetBoundDifference(ColIndex col) const
Definition: variables_info.h:76
operations_research::glop::ColToIntIndex
Index ColToIntIndex(ColIndex col)
Definition: lp_types.h:54
logging.h
operations_research::glop::PrimalEdgeNorms::SetParameters
void SetParameters(const GlopParameters &parameters)
Definition: primal_edge_norms.h:110
operations_research::glop::CompactSparseMatrix::PopulateFromMatrixView
void PopulateFromMatrixView(const MatrixView &input)
Definition: sparse.cc:437
operations_research::glop::StringifyMonomial
std::string StringifyMonomial(const Fractional a, const std::string &x, bool fraction)
Definition: lp_print_utils.cc:53
operations_research::glop::ColumnView::IsEmpty
bool IsEmpty() const
Definition: sparse_column.h:114
operations_research::glop::ScatteredVector::non_zeros
std::vector< Index > non_zeros
Definition: scattered_vector.h:62
operations_research::glop::UpdateRow::GetCoefficient
const Fractional GetCoefficient(ColIndex col) const
Definition: update_row.h:66
operations_research::glop::VariablesInfo::MakeBoxedVariableRelevant
void MakeBoxedVariableRelevant(bool value)
Definition: variables_info.cc:46
operations_research::glop::VariableValues::UpdatePrimalInfeasibilityInformation
void UpdatePrimalInfeasibilityInformation(const std::vector< RowIndex > &rows)
Definition: variable_values.cc:244
operations_research::glop::ReducedCosts::ComputeMaximumDualInfeasibility
Fractional ComputeMaximumDualInfeasibility() const
Definition: reduced_costs.cc:141
DCHECK_GT
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:890
value
int64 value
Definition: demon_profiler.cc:43
operations_research::glop::CompactSparseMatrix::num_rows
RowIndex num_rows() const
Definition: sparse.h:344
operations_research::glop::DenseBooleanColumn
StrictITIVector< RowIndex, bool > DenseBooleanColumn
Definition: lp_types.h:331
operations_research::glop::ReducedCosts::NeedsBasisRefactorization
bool NeedsBasisRefactorization() const
Definition: reduced_costs.cc:54
coeff_magnitude
Fractional coeff_magnitude
Definition: revised_simplex.cc:1816
operations_research::glop::Status
Definition: status.h:24
lp_utils.h
operations_research::glop::ReducedCosts::GetDualValues
const DenseColumn & GetDualValues()
Definition: reduced_costs.cc:324
operations_research::glop::RowToColMapping
StrictITIVector< RowIndex, ColIndex > RowToColMapping
Definition: lp_types.h:342
operations_research::glop::InfinityNorm
Fractional InfinityNorm(const DenseColumn &v)
Definition: lp_data/lp_utils.cc:81
operations_research::glop::VariableValues::ResetAllNonBasicVariableValues
void ResetAllNonBasicVariableValues()
Definition: variable_values.cc:67
operations_research::glop::StrictITIVector::size
IntType size() const
Definition: lp_types.h:276
operations_research
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
Definition: dense_doubly_linked_list.h:21
operations_research::glop::DualEdgeNorms::GetEdgeSquaredNorms
const DenseColumn & GetEdgeSquaredNorms()
Definition: dual_edge_norms.cc:35
operations_research::glop::ConstraintStatus
ConstraintStatus
Definition: lp_types.h:227
operations_research::glop::CompactSparseMatrix::num_cols
ColIndex num_cols() const
Definition: sparse.h:345
WARNING
const int WARNING
Definition: log_severity.h:31
operations_research::glop::UpdateRow::DeterministicTime
double DeterministicTime() const
Definition: update_row.h:92
operations_research::glop::CompactSparseMatrix::num_entries
EntryIndex num_entries() const
Definition: sparse.h:340
operations_research::glop::Status::OK
static const Status OK()
Definition: status.h:54
operations_research::glop::BasisFactorization::IsRefactorized
bool IsRefactorized() const
Definition: basis_representation.cc:214
int64
int64_t int64
Definition: integral_types.h:34
operations_research::glop::Stringify
std::string Stringify(const Fractional x, bool fraction)
Definition: lp_print_utils.cc:45
operations_research::glop::RevisedSimplex::SetIntegralityScale
void SetIntegralityScale(ColIndex col, Fractional scale)
Definition: revised_simplex.cc:2358
operations_research::glop::PrimalEdgeNorms::NeedsBasisRefactorization
bool NeedsBasisRefactorization() const
Definition: primal_edge_norms.cc:43
operations_research::glop::UpdateRow::Invalidate
void Invalidate()
Definition: update_row.cc:40
operations_research::TimeLimit
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:105
operations_research::glop::RevisedSimplex::ComputeDictionary
RowMajorSparseMatrix ComputeDictionary(const DenseRow *column_scales)
Definition: revised_simplex.cc:3202
RETURN_IF_NULL
#define RETURN_IF_NULL(x)
Definition: return_macros.h:20
operations_research::glop::VariablesInfo::GetStatusRow
const VariableStatusRow & GetStatusRow() const
Definition: variables_info.cc:101
index
int index
Definition: pack.cc:508
operations_research::glop::ReducedCosts::AreReducedCostsPrecise
bool AreReducedCostsPrecise()
Definition: reduced_costs.h:108
operations_research::glop::kDeterministicSeed
constexpr const uint64 kDeterministicSeed
Definition: revised_simplex.cc:76
operations_research::glop::VariableTypeRow
StrictITIVector< ColIndex, VariableType > VariableTypeRow
Definition: lp_types.h:317
matrix_utils.h
operations_research::glop::RevisedSimplex::GetObjectiveValue
Fractional GetObjectiveValue() const
Definition: revised_simplex.cc:437
revised_simplex.h
operations_research::glop::PrimalEdgeNorms::UpdateBeforeBasisPivot
void UpdateBeforeBasisPivot(ColIndex entering_col, ColIndex leaving_col, RowIndex leaving_row, const ScatteredColumn &direction, UpdateRow *update_row)
Definition: primal_edge_norms.cc:86
operations_research::glop::VariableToConstraintStatus
ConstraintStatus VariableToConstraintStatus(VariableStatus status)
Definition: lp_types.cc:109
operations_research::glop::Fractional
double Fractional
Definition: lp_types.h:77
operations_research::glop::ConstraintStatus::AT_UPPER_BOUND
@ AT_UPPER_BOUND
operations_research::glop::UpdateRow::RecomputeFullUpdateRow
void RecomputeFullUpdateRow(RowIndex leaving_row)
Definition: update_row.cc:244
operations_research::glop::ReducedCosts::SetAndDebugCheckThatColumnIsDualFeasible
void SetAndDebugCheckThatColumnIsDualFeasible(ColIndex col)
Definition: reduced_costs.cc:200
operations_research::glop::RevisedSimplex::Solve
ABSL_MUST_USE_RESULT Status Solve(const LinearProgram &lp, TimeLimit *time_limit)
Definition: revised_simplex.cc:135
DCHECK_NE
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:886
operations_research::glop::VariableStatus::FIXED_VALUE
@ FIXED_VALUE
operations_research::glop::RevisedSimplex::GetPrimalRay
const DenseRow & GetPrimalRay() const
Definition: revised_simplex.cc:488
operations_research::glop::ColumnView::GetFirstCoefficient
Fractional GetFirstCoefficient() const
Definition: sparse_column.h:86
ratio
Fractional ratio
Definition: revised_simplex.cc:1815
SCOPED_TIME_STAT
#define SCOPED_TIME_STAT(stats)
Definition: stats.h:436
operations_research::glop::BasisFactorization::Refactorize
ABSL_MUST_USE_RESULT Status Refactorize()
Definition: basis_representation.cc:216
operations_research::glop::BasisFactorization::Initialize
ABSL_MUST_USE_RESULT Status Initialize()
Definition: basis_representation.cc:206
operations_research::glop::kInfinity
const double kInfinity
Definition: lp_types.h:83
operations_research::glop::RowToColIndex
ColIndex RowToColIndex(RowIndex row)
Definition: lp_types.h:48
cost
int64 cost
Definition: routing_flow.cc:130
operations_research::glop::Status::ERROR_LU
@ ERROR_LU
Definition: status.h:32
operations_research::glop::VariablesInfo::GetCanIncreaseBitRow
const DenseBitRow & GetCanIncreaseBitRow() const
Definition: variables_info.cc:105
DEBUG_MODE
const bool DEBUG_MODE
Definition: macros.h:24
a
int64 a
Definition: constraint_solver/table.cc:42
operations_research::glop::DualEdgeNorms::UpdateBeforeBasisPivot
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, const ScatteredRow &unit_row_left_inverse)
Definition: dual_edge_norms.cc:46
operations_research::glop::DenseBitRow
Bitset64< ColIndex > DenseBitRow
Definition: lp_types.h:323
operations_research::glop::GetVariableTypeString
std::string GetVariableTypeString(VariableType variable_type)
Definition: lp_types.cc:52
operations_research::glop::RevisedSimplex::GetDualRayRowCombination
const DenseRow & GetDualRayRowCombination() const
Definition: revised_simplex.cc:497
operations_research::glop::PrimalEdgeNorms::TestEnteringEdgeNormPrecision
void TestEnteringEdgeNormPrecision(ColIndex entering_col, const ScatteredColumn &direction)
Definition: primal_edge_norms.cc:62
target_bound
Fractional target_bound
Definition: revised_simplex.cc:1817
operations_research::glop::BasisFactorization::DeterministicTime
double DeterministicTime() const
Definition: basis_representation.cc:572
operations_research::glop::BasisState
Definition: revised_simplex.h:132
operations_research::glop::UpdateRow::ComputeUpdateRow
void ComputeUpdateRow(RowIndex leaving_row)
Definition: update_row.cc:78
time_limit
SharedTimeLimit * time_limit
Definition: cp_model_solver.cc:2101
operations_research::glop::IsFinite
bool IsFinite(Fractional value)
Definition: lp_types.h:90
row
RowIndex row
Definition: revised_simplex.cc:1814
operations_research::glop::BasisFactorization
Definition: basis_representation.h:151
ABSL_FLAG
ABSL_FLAG(bool, simplex_display_numbers_as_fractions, false, "Display numbers as fractions.")
DCHECK_COL_BOUNDS
#define DCHECK_COL_BOUNDS(col)
Definition: revised_simplex.cc:64
operations_research::glop::GetVariableStatusString
std::string GetVariableStatusString(VariableStatus status)
Definition: lp_types.cc:71
operations_research::glop::RevisedSimplex::GetConstraintStatus
ConstraintStatus GetConstraintStatus(RowIndex row) const
Definition: revised_simplex.cc:475
operations_research::glop::ReducedCosts::MaintainDualInfeasiblePositions
void MaintainDualInfeasiblePositions(bool maintain)
Definition: reduced_costs.cc:311
operations_research::glop::RevisedSimplex::GetState
const BasisState & GetState() const
Definition: revised_simplex.cc:467
operations_research::glop::VariableValues::ComputeMaximumPrimalResidual
Fractional ComputeMaximumPrimalResidual() const
Definition: variable_values.cc:108
fp_utils.h
operations_research::glop::LuFactorization::Clear
void Clear()
Definition: lu_factorization.cc:31
operations_research::glop::BasisFactorization::ForceRefactorization
ABSL_MUST_USE_RESULT Status ForceRefactorization()
Definition: basis_representation.cc:221
operations_research::glop::VariablesInfo::InitializeAndComputeType
void InitializeAndComputeType()
Definition: variables_info.cc:27
operations_research::glop::ReducedCosts::SetNonBasicVariableCostToZero
void SetNonBasicVariableCostToZero(ColIndex col, Fractional *current_cost)
Definition: reduced_costs.cc:206
operations_research::glop::DualEdgeNorms::StatString
std::string StatString() const
Definition: dual_edge_norms.h:92
DCHECK_ROW_BOUNDS
#define DCHECK_ROW_BOUNDS(row)
Definition: revised_simplex.cc:70
operations_research::glop::PrimalEdgeNorms::StatString
std::string StatString() const
Definition: primal_edge_norms.h:115
operations_research::glop::BasisFactorization::SetColumnPermutationToIdentity
void SetColumnPermutationToIdentity()
Definition: basis_representation.h:176
operations_research::glop::StrictITIVector< ColIndex, Fractional >
operations_research::glop::RevisedSimplex::ComputeBasicVariablesForState
void ComputeBasicVariablesForState(const LinearProgram &linear_program, const BasisState &state)
Definition: revised_simplex.cc:3224
operations_research::glop::VariableValues::Get
const Fractional Get(ColIndex col) const
Definition: variable_values.h:49
operations_research::glop::LinearProgram::IsInEquationForm
bool IsInEquationForm() const
Definition: lp_data.cc:1469
operations_research::glop::EnteringVariable::PrimalChooseEnteringColumn
ABSL_MUST_USE_RESULT Status PrimalChooseEnteringColumn(ColIndex *entering_col)
Definition: entering_variable.cc:37
operations_research::glop::VariableType::FIXED_VARIABLE
@ FIXED_VARIABLE
operations_research::glop::VariableStatusRow
StrictITIVector< ColIndex, VariableStatus > VariableStatusRow
Definition: lp_types.h:320
operations_research::glop::UpdateRow::SetParameters
void SetParameters(const GlopParameters &parameters)
Definition: update_row.cc:174
operations_research::glop::EnteringVariable::SetParameters
void SetParameters(const GlopParameters &parameters)
Definition: entering_variable.cc:372
objective_
IntVar *const objective_
Definition: search.cc:2951
operations_research::glop::StrictITIVector::AssignToZero
void AssignToZero(IntType size)
Definition: lp_types.h:290
operations_research::glop::RevisedSimplex::GetNumberOfIterations
int64 GetNumberOfIterations() const
Definition: revised_simplex.cc:441
operations_research::glop::VariableValues::SetNonBasicVariableValueFromStatus
void SetNonBasicVariableValueFromStatus(ColIndex col)
Definition: variable_values.cc:34
operations_research::glop::VariableValues::Set
void Set(ColIndex col, Fractional value)
Definition: variable_values.h:115
operations_research::glop::ReducedCosts::GetReducedCosts
const DenseRow & GetReducedCosts()
Definition: reduced_costs.cc:318
operations_research::glop::VariablesInfo::GetCanDecreaseBitRow
const DenseBitRow & GetCanDecreaseBitRow() const
Definition: variables_info.cc:109
uint64
uint64_t uint64
Definition: integral_types.h:39
operations_research::glop::RevisedSimplex::GetConstraintActivity
Fractional GetConstraintActivity(RowIndex row) const
Definition: revised_simplex.cc:469
operations_research::glop::GetProblemStatusString
std::string GetProblemStatusString(ProblemStatus problem_status)
Definition: lp_types.cc:19
operations_research::glop::BasisFactorization::RightSolve
void RightSolve(ScatteredColumn *d) const
Definition: basis_representation.cc:322
operations_research::glop::ScatteredVector::ClearNonZerosIfTooDense
void ClearNonZerosIfTooDense(double ratio_for_using_dense_representation)
Definition: scattered_vector.h:152
operations_research::glop::RevisedSimplex::GetDualValue
Fractional GetDualValue(RowIndex row) const
Definition: revised_simplex.cc:459
DCHECK
#define DCHECK(condition)
Definition: base/logging.h:884
operations_research::glop::ReducedCosts::StatString
std::string StatString() const
Definition: reduced_costs.h:173
operations_research::glop::RevisedSimplex::GetVariableStatus
VariableStatus GetVariableStatus(ColIndex col) const
Definition: revised_simplex.cc:463
GLOP_RETURN_ERROR_IF_NULL
#define GLOP_RETURN_ERROR_IF_NULL(arg)
Definition: status.h:85
operations_research::glop::VariablesInfo::GetIsBasicBitRow
const DenseBitRow & GetIsBasicBitRow() const
Definition: variables_info.cc:117
operations_research::glop::VariableValues::StatString
std::string StatString() const
Definition: variable_values.h:118
operations_research::glop::Transpose
const DenseRow & Transpose(const DenseColumn &col)
Definition: lp_data/lp_utils.h:192
operations_research::glop::RevisedSimplex::SetParameters
void SetParameters(const GlopParameters &parameters)
Definition: revised_simplex.cc:3070
operations_research::glop::DualEdgeNorms::ResizeOnNewRows
void ResizeOnNewRows(RowIndex new_size)
Definition: dual_edge_norms.cc:31
operations_research::glop::VariableValues::UpdateOnPivoting
void UpdateOnPivoting(const ScatteredColumn &direction, ColIndex entering_col, Fractional step)
Definition: variable_values.cc:144
operations_research::glop::VariablesInfo::GetIsRelevantBitRow
const DenseBitRow & GetIsRelevantBitRow() const
Definition: variables_info.cc:113
operations_research::Bitset64::Set
void Set(IndexType i)
Definition: bitset.h:493
operations_research::glop::ColumnView::EntryRow
RowIndex EntryRow(EntryIndex i) const
Definition: sparse_column.h:89
operations_research::glop::BasisState::IsEmpty
bool IsEmpty() const
Definition: revised_simplex.h:143
operations_research::glop::RevisedSimplex::NotifyThatMatrixIsUnchangedForNextSolve
void NotifyThatMatrixIsUnchangedForNextSolve()
Definition: revised_simplex.cc:131
operations_research::glop::CompactSparseMatrix::PopulateFromTranspose
void PopulateFromTranspose(const CompactSparseMatrix &input)
Definition: sparse.cc:456
operations_research::glop::EnteringVariable::StatString
std::string StatString() const
Definition: entering_variable.h:92
operations_research::glop::AreFirstColumnsAndRowsExactlyEquals
bool AreFirstColumnsAndRowsExactlyEquals(RowIndex num_rows, ColIndex num_cols, const SparseMatrix &matrix_a, const CompactSparseMatrix &matrix_b)
Definition: matrix_utils.cc:190
operations_research::glop::ColumnPermutation
Permutation< ColIndex > ColumnPermutation
Definition: lp_data/permutation.h:95
operations_research::ScopedTimeDistributionUpdater
DisabledScopedTimeDistributionUpdater ScopedTimeDistributionUpdater
Definition: stats.h:432
operations_research::glop::SparseVector< RowIndex, SparseColumnIterator >::Entry
typename Iterator::Entry Entry
Definition: sparse_vector.h:91
operations_research::glop::ReducedCosts::SetParameters
void SetParameters(const GlopParameters &parameters)
Definition: reduced_costs.cc:214
operations_research::glop::ReducedCosts::TestEnteringReducedCostPrecision
bool TestEnteringReducedCostPrecision(ColIndex entering_col, const ScatteredColumn &direction, Fractional *reduced_cost)
Definition: reduced_costs.cc:58
DCHECK_GE
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:889
operations_research::glop::ColumnView::num_entries
EntryIndex num_entries() const
Definition: sparse_column.h:82
absl::StrongVector::clear
void clear()
Definition: strong_vector.h:170
operations_research::glop::UpdateRow::GetUnitRowLeftInverse
const ScatteredRow & GetUnitRowLeftInverse() const
Definition: update_row.cc:51
operations_research::glop::VariableValues::GetPrimalInfeasiblePositions
const DenseBitColumn & GetPrimalInfeasiblePositions() const
Definition: variable_values.cc:222
operations_research::glop::UpdateRow::GetNonZeroPositions
const ColIndexVector & GetNonZeroPositions() const
Definition: update_row.cc:170
lp_print_utils.h
absl::StrongVector
Definition: strong_vector.h:76
operations_research::glop::ReducedCosts::ResetForNewObjective
void ResetForNewObjective()
Definition: reduced_costs.cc:218
operations_research::glop::ProblemStatus::OPTIMAL
@ OPTIMAL
operations_research::glop::ChangeSign
void ChangeSign(StrictITIVector< IndexType, Fractional > *data)
Definition: lp_data/lp_utils.h:300
col
ColIndex col
Definition: markowitz.cc:176
operations_research::glop::LuFactorization::ComputeFactorization
ABSL_MUST_USE_RESULT Status ComputeFactorization(const CompactSparseMatrixView &compact_matrix)
Definition: lu_factorization.cc:44
operations_research::glop::EnteringVariable::SetPricingRule
void SetPricingRule(GlopParameters::PricingRule rule)
Definition: entering_variable.cc:376
initial_basis.h
operations_research::glop::ProblemStatus
ProblemStatus
Definition: lp_types.h:101
operations_research::glop::LinearProgram::IsMaximizationProblem
bool IsMaximizationProblem() const
Definition: lp_data.h:170
operations_research::glop::PrimalEdgeNorms::Clear
void Clear()
Definition: primal_edge_norms.cc:37
operations_research::glop::LinearProgram
Definition: lp_data.h:54
operations_research::glop::Square
Fractional Square(Fractional f)
Definition: lp_data/lp_utils.h:36
operations_research::glop::VariablesInfo::UpdateToNonBasicStatus
void UpdateToNonBasicStatus(ColIndex col, VariableStatus status)
Definition: variables_info.cc:78
operations_research::glop::DenseColumn
StrictITIVector< RowIndex, Fractional > DenseColumn
Definition: lp_types.h:328
operations_research::glop::ConstraintStatus::AT_LOWER_BOUND
@ AT_LOWER_BOUND
operations_research::glop::RevisedSimplex::ClearStateForNextSolve
void ClearStateForNextSolve()
Definition: revised_simplex.cc:120
operations_research::glop::LinearProgram::IsCleanedUp
bool IsCleanedUp() const
Definition: lp_data.cc:353
operations_research::glop::CompactSparseMatrix::ColumnAddMultipleToSparseScatteredColumn
void ColumnAddMultipleToSparseScatteredColumn(ColIndex col, Fractional multiplier, ScatteredColumn *column) const
Definition: sparse.h:405
DCHECK_EQ
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:885
operations_research::glop::VariablesInfo::GetNonBasicBoxedVariables
const DenseBitRow & GetNonBasicBoxedVariables() const
Definition: variables_info.cc:123
operations_research::glop::ProblemStatus::INIT
@ INIT
operations_research::glop::VariablesInfo::GetTypeRow
const VariableTypeRow & GetTypeRow() const
Definition: variables_info.cc:97
operations_research::glop::VariableValues::GetPrimalSquaredInfeasibilities
const DenseColumn & GetPrimalSquaredInfeasibilities() const
Definition: variable_values.cc:218
operations_research::glop::ReducedCosts::GetDualFeasibilityTolerance
Fractional GetDualFeasibilityTolerance() const
Definition: reduced_costs.h:176
operations_research::glop::VariableType
VariableType
Definition: lp_types.h:174
strong_vector.h
operations_research::glop::RevisedSimplex::GetProblemNumRows
RowIndex GetProblemNumRows() const
Definition: revised_simplex.cc:443
VLOG_IS_ON
#define VLOG_IS_ON(verboselevel)
Definition: vlog_is_on.h:41
operations_research::glop::VariableValues::GetDenseRow
const DenseRow & GetDenseRow() const
Definition: variable_values.h:50
operations_research::glop::RevisedSimplex::RevisedSimplex
RevisedSimplex()
Definition: revised_simplex.cc:78
operations_research::glop::ReducedCosts::ComputeMaximumDualResidual
Fractional ComputeMaximumDualResidual() const
Definition: reduced_costs.cc:113
operations_research::glop::VariableValues::ComputeMaximumPrimalInfeasibility
Fractional ComputeMaximumPrimalInfeasibility() const
Definition: variable_values.cc:120
operations_research::glop::DualEdgeNorms::UpdateDataOnBasisPermutation
void UpdateDataOnBasisPermutation(const ColumnPermutation &col_perm)
Definition: dual_edge_norms.cc:40
operations_research::glop::ReducedCosts::IsValidPrimalEnteringCandidate
bool IsValidPrimalEnteringCandidate(ColIndex col) const
Definition: reduced_costs.cc:516
operations_research::glop::Permutation::empty
bool empty() const
Definition: lp_data/permutation.h:51
operations_research::glop::RevisedSimplex::GetVariableValue
Fractional GetVariableValue(ColIndex col) const
Definition: revised_simplex.cc:447
operations_research::glop::VariableValues::UpdatePrimalPhaseICosts
bool UpdatePrimalPhaseICosts(const Rows &rows, DenseRow *objective)
Definition: variable_values.h:157
operations_research::glop::RevisedSimplex::GetDualRay
const DenseColumn & GetDualRay() const
Definition: revised_simplex.cc:492
permutation.h
operations_research::glop::BasisFactorization::ComputeInfinityNormConditionNumberUpperBound
Fractional ComputeInfinityNormConditionNumberUpperBound() const
Definition: basis_representation.cc:564
operations_research::glop::BasisFactorization::SetParameters
void SetParameters(const GlopParameters &parameters)
Definition: basis_representation.h:158
operations_research::glop::VariableStatus
VariableStatus
Definition: lp_types.h:196
operations_research::glop::VariableStatus::FREE
@ FREE
operations_research::glop::RevisedSimplex::GetBasis
ColIndex GetBasis(RowIndex row) const
Definition: revised_simplex.cc:502
operations_research::glop::PreciseScalarProduct
Fractional PreciseScalarProduct(const DenseRowOrColumn &u, const DenseRowOrColumn2 &v)
Definition: lp_data/lp_utils.h:92
operations_research::StatsGroup::StatString
std::string StatString() const
Definition: stats.cc:71
operations_research::glop::kInvalidRow
const RowIndex kInvalidRow(-1)
operations_research::glop::BasisFactorization::GetColumnPermutation
const ColumnPermutation & GetColumnPermutation() const
Definition: basis_representation.h:168
DCHECK_LE
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:887
operations_research::glop::RevisedSimplex::GetReducedCosts
const DenseRow & GetReducedCosts() const
Definition: revised_simplex.cc:455
absl::StrongVector::empty
bool empty() const
Definition: strong_vector.h:156
operations_research::Bitset64::ClearAndResize
void ClearAndResize(IndexType size)
Definition: bitset.h:438
operations_research::glop::BasisState::statuses
VariableStatusRow statuses
Definition: revised_simplex.h:140
operations_research::glop::RevisedSimplex::GetReducedCost
Fractional GetReducedCost(ColIndex col) const
Definition: revised_simplex.cc:451
operations_research::glop::ReducedCosts::UpdateBeforeBasisPivot
void UpdateBeforeBasisPivot(ColIndex entering_col, RowIndex leaving_row, const ScatteredColumn &direction, UpdateRow *update_row)
Definition: reduced_costs.cc:176
operations_research::glop::BasisFactorization::RightSolveForProblemColumn
void RightSolveForProblemColumn(ColIndex col, ScatteredColumn *d) const
Definition: basis_representation.cc:428
operations_research::glop::PrimalEdgeNorms::DeterministicTime
double DeterministicTime() const
Definition: primal_edge_norms.h:118
CHECK
#define CHECK(condition)
Definition: base/logging.h:495
commandlineflags.h
parameters
SatParameters parameters
Definition: cp_model_fz_solver.cc:108
GLOP_RETURN_IF_ERROR
#define GLOP_RETURN_IF_ERROR(function_call)
Definition: status.h:70
operations_research::glop::Status::ok
bool ok() const
Definition: status.h:59
operations_research::glop::ScatteredVector::values
StrictITIVector< Index, Fractional > values
Definition: scattered_vector.h:57
operations_research::glop::DualEdgeNorms::Clear
void Clear()
Definition: dual_edge_norms.cc:29
parameters.pb.h
operations_research::glop::RevisedSimplex::GetProblemNumCols
ColIndex GetProblemNumCols() const
Definition: revised_simplex.cc:445
operations_research::glop::EnteringVariable::DualChooseEnteringColumn
ABSL_MUST_USE_RESULT Status DualChooseEnteringColumn(const UpdateRow &update_row, Fractional cost_variation, std::vector< ColIndex > *bound_flip_candidates, ColIndex *entering_col, Fractional *step)
Definition: entering_variable.cc:89
operations_research::glop::VariablesInfo::Update
void Update(ColIndex col, VariableStatus status)
Definition: variables_info.cc:60
operations_research::glop::VariableStatus::AT_LOWER_BOUND
@ AT_LOWER_BOUND
operations_research::glop::IsRightMostSquareMatrixIdentity
bool IsRightMostSquareMatrixIdentity(const SparseMatrix &matrix)
Definition: matrix_utils.cc:231
operations_research::glop::ColumnView::EntryCoefficient
Fractional EntryCoefficient(EntryIndex i) const
Definition: sparse_column.h:83