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