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