OR-Tools  9.6
lp_solver.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 
14 #include "ortools/glop/lp_solver.h"
15 
16 #include <algorithm>
17 #include <cmath>
18 #include <memory>
19 #include <stack>
20 #include <string>
21 #include <vector>
22 
23 #include "absl/strings/str_cat.h"
24 #include "absl/strings/str_format.h"
27 #include "ortools/base/version.h"
29 #include "ortools/glop/status.h"
33 #include "ortools/util/fp_utils.h"
34 
35 // TODO(user): abstract this in some way to the port directory.
36 #ifndef __PORTABLE_PLATFORM__
37 #include "ortools/util/file_util.h"
38 #endif
39 
40 ABSL_FLAG(bool, lp_dump_to_proto_file, false,
41  "Tells whether do dump the problem to a protobuf file.");
42 ABSL_FLAG(bool, lp_dump_compressed_file, true,
43  "Whether the proto dump file is compressed.");
44 ABSL_FLAG(bool, lp_dump_binary_file, false,
45  "Whether the proto dump file is binary.");
46 ABSL_FLAG(int, lp_dump_file_number, -1,
47  "Number for the dump file, in the form name-000048.pb. "
48  "If < 0, the file is automatically numbered from the number of "
49  "calls to LPSolver::Solve().");
50 ABSL_FLAG(std::string, lp_dump_dir, "/tmp",
51  "Directory where dump files are written.");
52 ABSL_FLAG(std::string, lp_dump_file_basename, "",
53  "Base name for dump files. LinearProgram::name_ is used if "
54  "lp_dump_file_basename is empty. If LinearProgram::name_ is "
55  "empty, \"linear_program_dump_file\" is used.");
56 ABSL_FLAG(std::string, glop_params, "",
57  "Override any user parameters with the value of this flag. This is "
58  "interpreted as a GlopParameters proto in text format.");
59 
60 namespace operations_research {
61 namespace glop {
62 namespace {
63 
64 // Writes a LinearProgram to a file if FLAGS_lp_dump_to_proto_file is true. The
65 // integer num is appended to the base name of the file. When this function is
66 // called from LPSolver::Solve(), num is usually the number of times Solve() was
67 // called. For a LinearProgram whose name is "LinPro", and num = 48, the default
68 // output file will be /tmp/LinPro-000048.pb.gz.
69 //
70 // Warning: is a no-op on portable platforms (android, ios, etc).
71 void DumpLinearProgramIfRequiredByFlags(const LinearProgram& linear_program,
72  int num) {
73  if (!absl::GetFlag(FLAGS_lp_dump_to_proto_file)) return;
74 #ifdef __PORTABLE_PLATFORM__
75  LOG(WARNING) << "DumpLinearProgramIfRequiredByFlags(linear_program, num) "
76  "requested for linear_program.name()='"
77  << linear_program.name() << "', num=" << num
78  << " but is not implemented for this platform.";
79 #else
80  std::string filename = absl::GetFlag(FLAGS_lp_dump_file_basename);
81  if (filename.empty()) {
82  if (linear_program.name().empty()) {
83  filename = "linear_program_dump";
84  } else {
85  filename = linear_program.name();
86  }
87  }
88  const int file_num = absl::GetFlag(FLAGS_lp_dump_file_number) >= 0
89  ? absl::GetFlag(FLAGS_lp_dump_file_number)
90  : num;
91  absl::StrAppendFormat(&filename, "-%06d.pb", file_num);
92  const std::string filespec =
93  absl::StrCat(absl::GetFlag(FLAGS_lp_dump_dir), "/", filename);
94  MPModelProto proto;
95  LinearProgramToMPModelProto(linear_program, &proto);
96  const ProtoWriteFormat write_format = absl::GetFlag(FLAGS_lp_dump_binary_file)
99  if (!WriteProtoToFile(filespec, proto, write_format,
100  absl::GetFlag(FLAGS_lp_dump_compressed_file))) {
101  LOG(DFATAL) << "Could not write " << filespec;
102  }
103 #endif
104 }
105 
106 } // anonymous namespace
107 
108 // --------------------------------------------------------
109 // LPSolver
110 // --------------------------------------------------------
111 
112 LPSolver::LPSolver() : num_solves_(0) {}
113 
114 std::string LPSolver::GlopVersion() {
115  return absl::StrCat("Glop solver v", OrToolsVersionString());
116 }
117 
118 void LPSolver::SetParameters(const GlopParameters& parameters) {
119  parameters_ = parameters;
120 #ifndef __PORTABLE_PLATFORM__
121  if (!absl::GetFlag(FLAGS_glop_params).empty()) {
122  GlopParameters flag_params;
123  CHECK(google::protobuf::TextFormat::ParseFromString(
124  absl::GetFlag(FLAGS_glop_params), &flag_params));
125  parameters_.MergeFrom(flag_params);
126  }
127 #endif
128 }
129 
130 const GlopParameters& LPSolver::GetParameters() const { return parameters_; }
131 
132 GlopParameters* LPSolver::GetMutableParameters() { return &parameters_; }
133 
135 
137  std::unique_ptr<TimeLimit> time_limit =
138  TimeLimit::FromParameters(parameters_);
139  return SolveWithTimeLimit(lp, time_limit.get());
140 }
141 
144  if (time_limit == nullptr) {
145  LOG(DFATAL) << "SolveWithTimeLimit() called with a nullptr time_limit.";
147  }
148  ++num_solves_;
149  num_revised_simplex_iterations_ = 0;
150  DumpLinearProgramIfRequiredByFlags(lp, num_solves_);
151 
152  // Display a warning if running in non-opt, unless we're inside a unit test.
153  DLOG(WARNING)
154  << "\n******************************************************************"
155  "\n* WARNING: Glop will be very slow because it will use DCHECKs *"
156  "\n* to verify the results and the precision of the solver. *"
157  "\n* You can gain at least an order of magnitude speedup by *"
158  "\n* compiling with optimizations enabled and by defining NDEBUG. *"
159  "\n******************************************************************";
160 
161  // Setup the logger.
162  logger_.EnableLogging(parameters_.log_search_progress());
163  logger_.SetLogToStdOut(parameters_.log_to_stdout());
164  if (!parameters_.log_search_progress() && VLOG_IS_ON(1)) {
165  logger_.EnableLogging(true);
166  logger_.SetLogToStdOut(false);
167  }
168 
169  // Log some initial info about the input model.
170  if (logger_.LoggingIsEnabled()) {
171  SOLVER_LOG(&logger_, "");
172  SOLVER_LOG(&logger_, "Initial problem: ", lp.GetDimensionString());
173  SOLVER_LOG(&logger_, "Objective stats: ", lp.GetObjectiveStatsString());
174  SOLVER_LOG(&logger_, "Bounds stats: ", lp.GetBoundsStatsString());
175  }
176 
177  // Check some preconditions.
178  if (!lp.IsCleanedUp()) {
179  LOG(DFATAL) << "The columns of the given linear program should be ordered "
180  << "by row and contain no zero coefficients. Call CleanUp() "
181  << "on it before calling Solve().";
182  ResizeSolution(lp.num_constraints(), lp.num_variables());
184  }
185 
186  // TODO(user): Unfortunately we are not really helpful with the error message
187  // here. We could do a better job. However most client should talk to glop via
188  // an input protocol buffer which should have better validation messages.
189  if (!lp.IsValid(parameters_.max_valid_magnitude())) {
190  SOLVER_LOG(&logger_,
191  "The given linear program is invalid. It contains NaNs, "
192  "coefficients too large or invalid bounds specification.");
193  ResizeSolution(lp.num_constraints(), lp.num_variables());
195  }
196 
197  // Make an internal copy of the problem for the preprocessing.
198  current_linear_program_.PopulateFromLinearProgram(lp);
199 
200  // Preprocess.
201  MainLpPreprocessor preprocessor(&parameters_);
202  preprocessor.SetLogger(&logger_);
203  preprocessor.SetTimeLimit(time_limit);
204 
205  const bool postsolve_is_needed = preprocessor.Run(&current_linear_program_);
206 
207  if (logger_.LoggingIsEnabled()) {
208  SOLVER_LOG(&logger_, "");
209  SOLVER_LOG(&logger_, "Presolved problem: ",
210  current_linear_program_.GetDimensionString());
211  SOLVER_LOG(&logger_, "Objective stats: ",
212  current_linear_program_.GetObjectiveStatsString());
213  SOLVER_LOG(&logger_, "Bounds stats: ",
214  current_linear_program_.GetBoundsStatsString());
215  }
216 
217  // At this point, we need to initialize a ProblemSolution with the correct
218  // size and status.
219  ProblemSolution solution(current_linear_program_.num_constraints(),
220  current_linear_program_.num_variables());
221  solution.status = preprocessor.status();
222  // LoadAndVerifySolution() below updates primal_values_, dual_values_,
223  // variable_statuses_ and constraint_statuses_ with the values stored in
224  // solution by RunPrimalDualPathFollowingMethodIfNeeded() and
225  // RunRevisedSimplexIfNeeded(), and hence clears any results stored in them
226  // from a previous run. In contrast, primal_ray_, constraints_dual_ray_, and
227  // variable_bounds_dual_ray_ are modified directly by
228  // RunRevisedSimplexIfNeeded(), so we explicitly clear them from previous run
229  // results.
230  primal_ray_.clear();
231  constraints_dual_ray_.clear();
232  variable_bounds_dual_ray_.clear();
233 
234  // Do not launch the solver if the time limit was already reached. This might
235  // mean that the pre-processors were not all run, and current_linear_program_
236  // might not be in a completely safe state.
237  if (!time_limit->LimitReached()) {
238  RunRevisedSimplexIfNeeded(&solution, time_limit);
239  }
240  if (postsolve_is_needed) preprocessor.DestructiveRecoverSolution(&solution);
241  const ProblemStatus status = LoadAndVerifySolution(lp, solution);
242  // LOG some statistics that can be parsed by our benchmark script.
243  if (logger_.LoggingIsEnabled()) {
244  SOLVER_LOG(&logger_, "status: ", GetProblemStatusString(status));
245  SOLVER_LOG(&logger_, "objective: ", GetObjectiveValue());
246  SOLVER_LOG(&logger_, "iterations: ", GetNumberOfSimplexIterations());
247  SOLVER_LOG(&logger_, "time: ", time_limit->GetElapsedTime());
248  SOLVER_LOG(&logger_, "deterministic_time: ",
249  time_limit->GetElapsedDeterministicTime());
250  SOLVER_LOG(&logger_, "");
251  }
252 
253  return status;
254 }
255 
257  ResizeSolution(RowIndex(0), ColIndex(0));
258  revised_simplex_.reset(nullptr);
259 }
260 
262  const VariableStatusRow& variable_statuses,
263  const ConstraintStatusColumn& constraint_statuses) {
264  // Create the associated basis state.
265  BasisState state;
266  state.statuses = variable_statuses;
268  // Note the change of upper/lower bound between the status of a constraint
269  // and the status of its associated slack variable.
270  switch (status) {
273  break;
276  break;
279  break;
282  break;
285  break;
286  }
287  }
288  if (revised_simplex_ == nullptr) {
289  revised_simplex_ = std::make_unique<RevisedSimplex>();
290  revised_simplex_->SetLogger(&logger_);
291  }
292  revised_simplex_->LoadStateForNextSolve(state);
293  if (parameters_.use_preprocessing()) {
294  LOG(WARNING) << "In GLOP, SetInitialBasis() was called but the parameter "
295  "use_preprocessing is true, this will likely not result in "
296  "what you want.";
297  }
298 }
299 
300 namespace {
301 // Computes the "real" problem objective from the one without offset nor
302 // scaling.
303 Fractional ProblemObjectiveValue(const LinearProgram& lp, Fractional value) {
304  return lp.objective_scaling_factor() * (value + lp.objective_offset());
305 }
306 
307 // Returns the allowed error magnitude for something that should evaluate to
308 // value under the given tolerance.
309 Fractional AllowedError(Fractional tolerance, Fractional value) {
310  return tolerance * std::max(1.0, std::abs(value));
311 }
312 } // namespace
313 
314 // TODO(user): Try to also check the precision of an INFEASIBLE or UNBOUNDED
315 // return status.
317  const ProblemSolution& solution) {
318  SOLVER_LOG(&logger_, "");
319  SOLVER_LOG(&logger_, "Final unscaled solution:");
320 
321  if (!IsProblemSolutionConsistent(lp, solution)) {
322  SOLVER_LOG(&logger_, "Inconsistency detected in the solution.");
323  ResizeSolution(lp.num_constraints(), lp.num_variables());
325  }
326 
327  // Load the solution.
328  primal_values_ = solution.primal_values;
329  dual_values_ = solution.dual_values;
330  variable_statuses_ = solution.variable_statuses;
331  constraint_statuses_ = solution.constraint_statuses;
332 
333  ProblemStatus status = solution.status;
334 
335  // Objective before eventually moving the primal/dual values inside their
336  // bounds.
337  ComputeReducedCosts(lp);
338  const Fractional primal_objective_value = ComputeObjective(lp);
339  const Fractional dual_objective_value = ComputeDualObjective(lp);
340  SOLVER_LOG(&logger_, "Primal objective (before moving primal/dual values) = ",
341  absl::StrFormat(
342  "%.15E", ProblemObjectiveValue(lp, primal_objective_value)));
343  SOLVER_LOG(&logger_, "Dual objective (before moving primal/dual values) = ",
344  absl::StrFormat("%.15E",
345  ProblemObjectiveValue(lp, dual_objective_value)));
346 
347  // Eventually move the primal/dual values inside their bounds.
349  parameters_.provide_strong_optimal_guarantee()) {
350  MovePrimalValuesWithinBounds(lp);
351  MoveDualValuesWithinBounds(lp);
352  }
353 
354  // The reported objective to the user.
355  problem_objective_value_ = ProblemObjectiveValue(lp, ComputeObjective(lp));
356  SOLVER_LOG(&logger_, "Primal objective (after moving primal/dual values) = ",
357  absl::StrFormat("%.15E", problem_objective_value_));
358 
359  ComputeReducedCosts(lp);
360  ComputeConstraintActivities(lp);
361 
362  // These will be set to true if the associated "infeasibility" is too large.
363  //
364  // The tolerance used is the parameter solution_feasibility_tolerance. To be
365  // somewhat independent of the original problem scaling, the thresholds used
366  // depend of the quantity involved and of its coordinates:
367  // - tolerance * max(1.0, abs(cost[col])) when a reduced cost is infeasible.
368  // - tolerance * max(1.0, abs(bound)) when a bound is crossed.
369  // - tolerance for an infeasible dual value (because the limit is always 0.0).
370  bool rhs_perturbation_is_too_large = false;
371  bool cost_perturbation_is_too_large = false;
372  bool primal_infeasibility_is_too_large = false;
373  bool dual_infeasibility_is_too_large = false;
374  bool primal_residual_is_too_large = false;
375  bool dual_residual_is_too_large = false;
376 
377  // Computes all the infeasiblities and update the Booleans above.
378  ComputeMaxRhsPerturbationToEnforceOptimality(lp,
379  &rhs_perturbation_is_too_large);
380  ComputeMaxCostPerturbationToEnforceOptimality(
381  lp, &cost_perturbation_is_too_large);
382  const double primal_infeasibility =
383  ComputePrimalValueInfeasibility(lp, &primal_infeasibility_is_too_large);
384  const double dual_infeasibility =
385  ComputeDualValueInfeasibility(lp, &dual_infeasibility_is_too_large);
386  const double primal_residual =
387  ComputeActivityInfeasibility(lp, &primal_residual_is_too_large);
388  const double dual_residual =
389  ComputeReducedCostInfeasibility(lp, &dual_residual_is_too_large);
390 
391  // TODO(user): the name is not really consistent since in practice those are
392  // the "residual" since the primal/dual infeasibility are zero when
393  // parameters_.provide_strong_optimal_guarantee() is true.
394  max_absolute_primal_infeasibility_ =
395  std::max(primal_infeasibility, primal_residual);
396  max_absolute_dual_infeasibility_ =
397  std::max(dual_infeasibility, dual_residual);
398  SOLVER_LOG(&logger_, "Max. primal infeasibility = ",
399  max_absolute_primal_infeasibility_);
400  SOLVER_LOG(&logger_,
401  "Max. dual infeasibility = ", max_absolute_dual_infeasibility_);
402 
403  // Now that all the relevant quantities are computed, we check the precision
404  // and optimality of the result. See Chvatal pp. 61-62. If any of the tests
405  // fail, we return the IMPRECISE status.
406  const double objective_error_ub = ComputeMaxExpectedObjectiveError(lp);
407  SOLVER_LOG(&logger_, "Objective error <= ", objective_error_ub);
408 
410  parameters_.provide_strong_optimal_guarantee()) {
411  // If the primal/dual values were moved to the bounds, then the primal/dual
412  // infeasibilities should be exactly zero (but not the residuals).
413  if (primal_infeasibility != 0.0 || dual_infeasibility != 0.0) {
414  LOG(ERROR) << "Primal/dual values have been moved to their bounds. "
415  << "Therefore the primal/dual infeasibilities should be "
416  << "exactly zero (but not the residuals). If this message "
417  << "appears, there is probably a bug in "
418  << "MovePrimalValuesWithinBounds() or in "
419  << "MoveDualValuesWithinBounds().";
420  }
421  if (rhs_perturbation_is_too_large) {
422  SOLVER_LOG(&logger_, "The needed rhs perturbation is too large !!");
423  if (parameters_.change_status_to_imprecise()) {
425  }
426  }
427  if (cost_perturbation_is_too_large) {
428  SOLVER_LOG(&logger_, "The needed cost perturbation is too large !!");
429  if (parameters_.change_status_to_imprecise()) {
431  }
432  }
433  }
434 
435  // Note that we compare the values without offset nor scaling. We also need to
436  // compare them before we move the primal/dual values, otherwise we lose some
437  // precision since the values are modified independently of each other.
439  if (std::abs(primal_objective_value - dual_objective_value) >
440  objective_error_ub) {
441  SOLVER_LOG(&logger_,
442  "The objective gap of the final solution is too large.");
443  if (parameters_.change_status_to_imprecise()) {
445  }
446  }
447  }
448  if ((status == ProblemStatus::OPTIMAL ||
450  (primal_residual_is_too_large || primal_infeasibility_is_too_large)) {
451  SOLVER_LOG(&logger_,
452  "The primal infeasibility of the final solution is too large.");
453  if (parameters_.change_status_to_imprecise()) {
455  }
456  }
457  if ((status == ProblemStatus::OPTIMAL ||
459  (dual_residual_is_too_large || dual_infeasibility_is_too_large)) {
460  SOLVER_LOG(&logger_,
461  "The dual infeasibility of the final solution is too large.");
462  if (parameters_.change_status_to_imprecise()) {
464  }
465  }
466 
467  may_have_multiple_solutions_ =
468  (status == ProblemStatus::OPTIMAL) ? IsOptimalSolutionOnFacet(lp) : false;
469  return status;
470 }
471 
472 bool LPSolver::IsOptimalSolutionOnFacet(const LinearProgram& lp) {
473  // Note(user): We use the following same two tolerances for the dual and
474  // primal values.
475  // TODO(user): investigate whether to use the tolerances defined in
476  // parameters.proto.
477  const double kReducedCostTolerance = 1e-9;
478  const double kBoundTolerance = 1e-7;
479  const ColIndex num_cols = lp.num_variables();
480  for (ColIndex col(0); col < num_cols; ++col) {
481  if (variable_statuses_[col] == VariableStatus::FIXED_VALUE) continue;
484  const Fractional value = primal_values_[col];
485  if (AreWithinAbsoluteTolerance(reduced_costs_[col], 0.0,
486  kReducedCostTolerance) &&
487  (AreWithinAbsoluteTolerance(value, lower_bound, kBoundTolerance) ||
488  AreWithinAbsoluteTolerance(value, upper_bound, kBoundTolerance))) {
489  return true;
490  }
491  }
492  const RowIndex num_rows = lp.num_constraints();
493  for (RowIndex row(0); row < num_rows; ++row) {
494  if (constraint_statuses_[row] == ConstraintStatus::FIXED_VALUE) continue;
497  const Fractional activity = constraint_activities_[row];
498  if (AreWithinAbsoluteTolerance(dual_values_[row], 0.0,
499  kReducedCostTolerance) &&
500  (AreWithinAbsoluteTolerance(activity, lower_bound, kBoundTolerance) ||
501  AreWithinAbsoluteTolerance(activity, upper_bound, kBoundTolerance))) {
502  return true;
503  }
504  }
505  return false;
506 }
507 
509  return problem_objective_value_;
510 }
511 
513  return max_absolute_primal_infeasibility_;
514 }
515 
517  return max_absolute_dual_infeasibility_;
518 }
519 
521  return may_have_multiple_solutions_;
522 }
523 
525  return num_revised_simplex_iterations_;
526 }
527 
529  return revised_simplex_ == nullptr ? 0.0
530  : revised_simplex_->DeterministicTime();
531 }
532 
533 void LPSolver::MovePrimalValuesWithinBounds(const LinearProgram& lp) {
534  const ColIndex num_cols = lp.num_variables();
535  DCHECK_EQ(num_cols, primal_values_.size());
536  Fractional error = 0.0;
537  for (ColIndex col(0); col < num_cols; ++col) {
540  DCHECK_LE(lower_bound, upper_bound);
541 
542  error = std::max(error, primal_values_[col] - upper_bound);
543  error = std::max(error, lower_bound - primal_values_[col]);
544  primal_values_[col] = std::min(primal_values_[col], upper_bound);
545  primal_values_[col] = std::max(primal_values_[col], lower_bound);
546  }
547  SOLVER_LOG(&logger_, "Max. primal values move = ", error);
548 }
549 
550 void LPSolver::MoveDualValuesWithinBounds(const LinearProgram& lp) {
551  const RowIndex num_rows = lp.num_constraints();
552  DCHECK_EQ(num_rows, dual_values_.size());
553  const Fractional optimization_sign = lp.IsMaximizationProblem() ? -1.0 : 1.0;
554  Fractional error = 0.0;
555  for (RowIndex row(0); row < num_rows; ++row) {
556  const Fractional lower_bound = lp.constraint_lower_bounds()[row];
557  const Fractional upper_bound = lp.constraint_upper_bounds()[row];
558 
559  // For a minimization problem, we want a lower bound.
560  Fractional minimization_dual_value = optimization_sign * dual_values_[row];
561  if (lower_bound == -kInfinity && minimization_dual_value > 0.0) {
562  error = std::max(error, minimization_dual_value);
563  minimization_dual_value = 0.0;
564  }
565  if (upper_bound == kInfinity && minimization_dual_value < 0.0) {
566  error = std::max(error, -minimization_dual_value);
567  minimization_dual_value = 0.0;
568  }
569  dual_values_[row] = optimization_sign * minimization_dual_value;
570  }
571  SOLVER_LOG(&logger_, "Max. dual values move = ", error);
572 }
573 
574 void LPSolver::ResizeSolution(RowIndex num_rows, ColIndex num_cols) {
575  primal_values_.resize(num_cols, 0.0);
576  reduced_costs_.resize(num_cols, 0.0);
577  variable_statuses_.resize(num_cols, VariableStatus::FREE);
578 
579  dual_values_.resize(num_rows, 0.0);
580  constraint_activities_.resize(num_rows, 0.0);
581  constraint_statuses_.resize(num_rows, ConstraintStatus::FREE);
582 }
583 
584 void LPSolver::RunRevisedSimplexIfNeeded(ProblemSolution* solution,
585  TimeLimit* time_limit) {
586  // Note that the transpose matrix is no longer needed at this point.
587  // This helps reduce the peak memory usage of the solver.
588  //
589  // TODO(user): actually, once the linear_program is loaded into the internal
590  // glop memory, there is no point keeping it around. Add a more complex
591  // Load/Solve API to RevisedSimplex so we can completely reclaim its memory
592  // right away.
593  current_linear_program_.ClearTransposeMatrix();
594  if (solution->status != ProblemStatus::INIT) return;
595  if (revised_simplex_ == nullptr) {
596  revised_simplex_ = std::make_unique<RevisedSimplex>();
597  revised_simplex_->SetLogger(&logger_);
598  }
599  revised_simplex_->SetParameters(parameters_);
600  if (revised_simplex_->Solve(current_linear_program_, time_limit).ok()) {
601  num_revised_simplex_iterations_ = revised_simplex_->GetNumberOfIterations();
602  solution->status = revised_simplex_->GetProblemStatus();
603 
604  // Make sure we do not copy the slacks added by revised_simplex_.
605  const ColIndex num_cols = solution->primal_values.size();
606  DCHECK_LE(num_cols, revised_simplex_->GetProblemNumCols());
607  for (ColIndex col(0); col < num_cols; ++col) {
608  solution->primal_values[col] = revised_simplex_->GetVariableValue(col);
609  solution->variable_statuses[col] =
610  revised_simplex_->GetVariableStatus(col);
611  }
612  const RowIndex num_rows = revised_simplex_->GetProblemNumRows();
613  DCHECK_EQ(solution->dual_values.size(), num_rows);
614  for (RowIndex row(0); row < num_rows; ++row) {
615  solution->dual_values[row] = revised_simplex_->GetDualValue(row);
616  solution->constraint_statuses[row] =
617  revised_simplex_->GetConstraintStatus(row);
618  }
619  if (!parameters_.use_preprocessing() && !parameters_.use_scaling()) {
620  if (solution->status == ProblemStatus::PRIMAL_UNBOUNDED) {
621  primal_ray_ = revised_simplex_->GetPrimalRay();
622  // Make sure we do not copy the slacks added by revised_simplex_.
623  primal_ray_.resize(num_cols);
624  } else if (solution->status == ProblemStatus::DUAL_UNBOUNDED) {
625  constraints_dual_ray_ = revised_simplex_->GetDualRay();
626  variable_bounds_dual_ray_ =
627  revised_simplex_->GetDualRayRowCombination();
628  // Make sure we do not copy the slacks added by revised_simplex_.
629  variable_bounds_dual_ray_.resize(num_cols);
630  // Revised simplex's GetDualRay is always such that GetDualRay.rhs < 0,
631  // which is a cost improving direction for the dual if the primal is a
632  // maximization problem (i.e. when the dual is a minimization problem).
633  // Hence, we change the sign of constraints_dual_ray_ for min problems.
634  //
635  // Revised simplex's GetDualRayRowCombination = A^T GetDualRay and
636  // we must have variable_bounds_dual_ray_ = - A^T constraints_dual_ray_.
637  // Then we need to change the sign of variable_bounds_dual_ray_, but for
638  // min problems this change is implicit because of the sign change of
639  // constraints_dual_ray_ described above.
640  if (current_linear_program_.IsMaximizationProblem()) {
641  ChangeSign(&variable_bounds_dual_ray_);
642  } else {
643  ChangeSign(&constraints_dual_ray_);
644  }
645  }
646  }
647  } else {
648  SOLVER_LOG(&logger_, "Error during the revised simplex algorithm.");
649  solution->status = ProblemStatus::ABNORMAL;
650  }
651 }
652 
653 namespace {
654 
655 void LogVariableStatusError(ColIndex col, Fractional value,
657  Fractional ub) {
658  VLOG(1) << "Variable " << col << " status is "
659  << GetVariableStatusString(status) << " but its value is " << value
660  << " and its bounds are [" << lb << ", " << ub << "].";
661 }
662 
663 void LogConstraintStatusError(RowIndex row, ConstraintStatus status,
664  Fractional lb, Fractional ub) {
665  VLOG(1) << "Constraint " << row << " status is "
666  << GetConstraintStatusString(status) << " but its bounds are [" << lb
667  << ", " << ub << "].";
668 }
669 
670 } // namespace
671 
672 bool LPSolver::IsProblemSolutionConsistent(
673  const LinearProgram& lp, const ProblemSolution& solution) const {
674  const RowIndex num_rows = lp.num_constraints();
675  const ColIndex num_cols = lp.num_variables();
676  if (solution.variable_statuses.size() != num_cols) return false;
677  if (solution.constraint_statuses.size() != num_rows) return false;
678  if (solution.primal_values.size() != num_cols) return false;
679  if (solution.dual_values.size() != num_rows) return false;
680  if (solution.status != ProblemStatus::OPTIMAL &&
681  solution.status != ProblemStatus::PRIMAL_FEASIBLE &&
682  solution.status != ProblemStatus::DUAL_FEASIBLE) {
683  return true;
684  }
685 
686  // This checks that the variable statuses verify the properties described
687  // in the VariableStatus declaration.
688  RowIndex num_basic_variables(0);
689  for (ColIndex col(0); col < num_cols; ++col) {
690  const Fractional value = solution.primal_values[col];
691  const Fractional lb = lp.variable_lower_bounds()[col];
692  const Fractional ub = lp.variable_upper_bounds()[col];
693  const VariableStatus status = solution.variable_statuses[col];
694  switch (solution.variable_statuses[col]) {
696  // TODO(user): Check that the reduced cost of this column is epsilon
697  // close to zero.
698  ++num_basic_variables;
699  break;
701  // TODO(user): Because of scaling, it is possible that a FIXED_VALUE
702  // status (only reserved for the exact lb == ub case) is now set for a
703  // variable where (ub == lb + epsilon). So we do not check here that the
704  // two bounds are exactly equal. The best is probably to remove the
705  // FIXED status from the API completely and report one of AT_LOWER_BOUND
706  // or AT_UPPER_BOUND instead. This also allows to indicate if at
707  // optimality, the objective is limited because of this variable lower
708  // bound or its upper bound. Note that there are other TODOs in the
709  // codebase about removing this FIXED_VALUE status.
710  if (value != ub && value != lb) {
711  LogVariableStatusError(col, value, status, lb, ub);
712  return false;
713  }
714  break;
716  if (value != lb || lb == ub) {
717  LogVariableStatusError(col, value, status, lb, ub);
718  return false;
719  }
720  break;
722  // TODO(user): revert to an exact comparison once the bug causing this
723  // to fail has been fixed.
724  if (!AreWithinAbsoluteTolerance(value, ub, 1e-7) || lb == ub) {
725  LogVariableStatusError(col, value, status, lb, ub);
726  return false;
727  }
728  break;
730  if (lb != -kInfinity || ub != kInfinity || value != 0.0) {
731  LogVariableStatusError(col, value, status, lb, ub);
732  return false;
733  }
734  break;
735  }
736  }
737  for (RowIndex row(0); row < num_rows; ++row) {
738  const Fractional dual_value = solution.dual_values[row];
739  const Fractional lb = lp.constraint_lower_bounds()[row];
740  const Fractional ub = lp.constraint_upper_bounds()[row];
741  const ConstraintStatus status = solution.constraint_statuses[row];
742 
743  // The activity value is not checked since it is imprecise.
744  // TODO(user): Check that the activity is epsilon close to the expected
745  // value.
746  switch (status) {
748  if (dual_value != 0.0) {
749  VLOG(1) << "Constraint " << row << " is BASIC, but its dual value is "
750  << dual_value << " instead of 0.";
751  return false;
752  }
753  ++num_basic_variables;
754  break;
756  // Exactly the same remark as for the VariableStatus::FIXED_VALUE case
757  // above. Because of precision error, this can happen when the
758  // difference between the two bounds is small and not just exactly zero.
759  if (ub - lb > 1e-12) {
760  LogConstraintStatusError(row, status, lb, ub);
761  return false;
762  }
763  break;
765  if (lb == -kInfinity) {
766  LogConstraintStatusError(row, status, lb, ub);
767  return false;
768  }
769  break;
771  if (ub == kInfinity) {
772  LogConstraintStatusError(row, status, lb, ub);
773  return false;
774  }
775  break;
777  if (dual_value != 0.0) {
778  VLOG(1) << "Constraint " << row << " is FREE, but its dual value is "
779  << dual_value << " instead of 0.";
780  return false;
781  }
782  if (lb != -kInfinity || ub != kInfinity) {
783  LogConstraintStatusError(row, status, lb, ub);
784  return false;
785  }
786  break;
787  }
788  }
789 
790  // TODO(user): We could check in debug mode (because it will be costly) that
791  // the basis is actually factorizable.
792  if (num_basic_variables != num_rows) {
793  VLOG(1) << "Wrong number of basic variables: " << num_basic_variables;
794  return false;
795  }
796  return true;
797 }
798 
799 // This computes by how much the objective must be perturbed to enforce the
800 // following complementary slackness conditions:
801 // - Reduced cost is exactly zero for FREE and BASIC variables.
802 // - Reduced cost is of the correct sign for variables at their bounds.
803 Fractional LPSolver::ComputeMaxCostPerturbationToEnforceOptimality(
804  const LinearProgram& lp, bool* is_too_large) {
805  Fractional max_cost_correction = 0.0;
806  const ColIndex num_cols = lp.num_variables();
807  const Fractional optimization_sign = lp.IsMaximizationProblem() ? -1.0 : 1.0;
808  const Fractional tolerance = parameters_.solution_feasibility_tolerance();
809  for (ColIndex col(0); col < num_cols; ++col) {
810  // We correct the reduced cost, so we have a minimization problem and
811  // thus the dual objective value will be a lower bound of the primal
812  // objective.
813  const Fractional reduced_cost = optimization_sign * reduced_costs_[col];
814  const VariableStatus status = variable_statuses_[col];
816  (status == VariableStatus::AT_UPPER_BOUND && reduced_cost > 0.0) ||
817  (status == VariableStatus::AT_LOWER_BOUND && reduced_cost < 0.0)) {
818  max_cost_correction =
819  std::max(max_cost_correction, std::abs(reduced_cost));
820  *is_too_large |=
821  std::abs(reduced_cost) >
822  AllowedError(tolerance, lp.objective_coefficients()[col]);
823  }
824  }
825  SOLVER_LOG(&logger_, "Max. cost perturbation = ", max_cost_correction);
826  return max_cost_correction;
827 }
828 
829 // This computes by how much the rhs must be perturbed to enforce the fact that
830 // the constraint activities exactly reflect their status.
831 Fractional LPSolver::ComputeMaxRhsPerturbationToEnforceOptimality(
832  const LinearProgram& lp, bool* is_too_large) {
833  Fractional max_rhs_correction = 0.0;
834  const RowIndex num_rows = lp.num_constraints();
835  const Fractional tolerance = parameters_.solution_feasibility_tolerance();
836  for (RowIndex row(0); row < num_rows; ++row) {
837  const Fractional lower_bound = lp.constraint_lower_bounds()[row];
838  const Fractional upper_bound = lp.constraint_upper_bounds()[row];
839  const Fractional activity = constraint_activities_[row];
840  const ConstraintStatus status = constraint_statuses_[row];
841 
842  Fractional rhs_error = 0.0;
843  Fractional allowed_error = 0.0;
844  if (status == ConstraintStatus::AT_LOWER_BOUND || activity < lower_bound) {
845  rhs_error = std::abs(activity - lower_bound);
846  allowed_error = AllowedError(tolerance, lower_bound);
848  activity > upper_bound) {
849  rhs_error = std::abs(activity - upper_bound);
850  allowed_error = AllowedError(tolerance, upper_bound);
851  }
852  max_rhs_correction = std::max(max_rhs_correction, rhs_error);
853  *is_too_large |= rhs_error > allowed_error;
854  }
855  SOLVER_LOG(&logger_, "Max. rhs perturbation = ", max_rhs_correction);
856  return max_rhs_correction;
857 }
858 
859 void LPSolver::ComputeConstraintActivities(const LinearProgram& lp) {
860  const RowIndex num_rows = lp.num_constraints();
861  const ColIndex num_cols = lp.num_variables();
862  DCHECK_EQ(num_cols, primal_values_.size());
863  constraint_activities_.assign(num_rows, 0.0);
864  for (ColIndex col(0); col < num_cols; ++col) {
865  lp.GetSparseColumn(col).AddMultipleToDenseVector(primal_values_[col],
866  &constraint_activities_);
867  }
868 }
869 
870 void LPSolver::ComputeReducedCosts(const LinearProgram& lp) {
871  const RowIndex num_rows = lp.num_constraints();
872  const ColIndex num_cols = lp.num_variables();
873  DCHECK_EQ(num_rows, dual_values_.size());
874  reduced_costs_.resize(num_cols, 0.0);
875  for (ColIndex col(0); col < num_cols; ++col) {
876  reduced_costs_[col] = lp.objective_coefficients()[col] -
877  ScalarProduct(dual_values_, lp.GetSparseColumn(col));
878  }
879 }
880 
881 double LPSolver::ComputeObjective(const LinearProgram& lp) {
882  const ColIndex num_cols = lp.num_variables();
883  DCHECK_EQ(num_cols, primal_values_.size());
884  KahanSum sum;
885  for (ColIndex col(0); col < num_cols; ++col) {
886  sum.Add(lp.objective_coefficients()[col] * primal_values_[col]);
887  }
888  return sum.Value();
889 }
890 
891 // By the duality theorem, the dual "objective" is a bound on the primal
892 // objective obtained by taking the linear combinaison of the constraints
893 // given by dual_values_.
894 //
895 // As it is written now, this has no real precise meaning since we ignore
896 // infeasible reduced costs. This is almost the same as computing the objective
897 // to the perturbed problem, but then we don't use the pertubed rhs. It is just
898 // here as an extra "consistency" check.
899 //
900 // Note(user): We could actually compute an EXACT lower bound for the cost of
901 // the non-cost perturbed problem. The idea comes from "Safe bounds in linear
902 // and mixed-integer linear programming", Arnold Neumaier , Oleg Shcherbina,
903 // Math Prog, 2003. Note that this requires having some variable bounds that may
904 // not be in the original problem so that the current dual solution is always
905 // feasible. It also involves changing the rounding mode to obtain exact
906 // confidence intervals on the reduced costs.
907 double LPSolver::ComputeDualObjective(const LinearProgram& lp) {
908  KahanSum dual_objective;
909 
910  // Compute the part coming from the row constraints.
911  const RowIndex num_rows = lp.num_constraints();
912  const Fractional optimization_sign = lp.IsMaximizationProblem() ? -1.0 : 1.0;
913  for (RowIndex row(0); row < num_rows; ++row) {
914  const Fractional lower_bound = lp.constraint_lower_bounds()[row];
915  const Fractional upper_bound = lp.constraint_upper_bounds()[row];
916 
917  // We correct the optimization_sign so we have to compute a lower bound.
918  const Fractional corrected_value = optimization_sign * dual_values_[row];
919  if (corrected_value > 0.0 && lower_bound != -kInfinity) {
920  dual_objective.Add(dual_values_[row] * lower_bound);
921  }
922  if (corrected_value < 0.0 && upper_bound != kInfinity) {
923  dual_objective.Add(dual_values_[row] * upper_bound);
924  }
925  }
926 
927  // For a given column associated to a variable x, we want to find a lower
928  // bound for c.x (where c is the objective coefficient for this column). If we
929  // write a.x the linear combination of the constraints at this column we have:
930  // (c + a - c) * x = a * x, and so
931  // c * x = a * x + (c - a) * x
932  // Now, if we suppose for example that the reduced cost 'c - a' is positive
933  // and that x is lower-bounded by 'lb' then the best bound we can get is
934  // c * x >= a * x + (c - a) * lb.
935  //
936  // Note: when summing over all variables, the left side is the primal
937  // objective and the right side is a lower bound to the objective. In
938  // particular, a necessary and sufficient condition for both objectives to be
939  // the same is that all the single variable inequalities above be equalities.
940  // This is possible only if c == a or if x is at its bound (modulo the
941  // optimization_sign of the reduced cost), or both (this is one side of the
942  // complementary slackness conditions, see Chvatal p. 62).
943  const ColIndex num_cols = lp.num_variables();
944  for (ColIndex col(0); col < num_cols; ++col) {
945  const Fractional lower_bound = lp.variable_lower_bounds()[col];
946  const Fractional upper_bound = lp.variable_upper_bounds()[col];
947 
948  // Correct the reduced cost, so as to have a minimization problem and
949  // thus a dual objective that is a lower bound of the primal objective.
950  const Fractional reduced_cost = optimization_sign * reduced_costs_[col];
951 
952  // We do not do any correction if the reduced cost is 'infeasible', which is
953  // the same as computing the objective of the perturbed problem.
954  Fractional correction = 0.0;
955  if (variable_statuses_[col] == VariableStatus::AT_LOWER_BOUND &&
956  reduced_cost > 0.0) {
957  correction = reduced_cost * lower_bound;
958  } else if (variable_statuses_[col] == VariableStatus::AT_UPPER_BOUND &&
959  reduced_cost < 0.0) {
960  correction = reduced_cost * upper_bound;
961  } else if (variable_statuses_[col] == VariableStatus::FIXED_VALUE) {
962  correction = reduced_cost * upper_bound;
963  }
964  // Now apply the correction in the right direction!
965  dual_objective.Add(optimization_sign * correction);
966  }
967  return dual_objective.Value();
968 }
969 
970 double LPSolver::ComputeMaxExpectedObjectiveError(const LinearProgram& lp) {
971  const ColIndex num_cols = lp.num_variables();
972  DCHECK_EQ(num_cols, primal_values_.size());
973  const Fractional tolerance = parameters_.solution_feasibility_tolerance();
974  Fractional primal_objective_error = 0.0;
975  for (ColIndex col(0); col < num_cols; ++col) {
976  // TODO(user): Be more precise since the non-BASIC variables are exactly at
977  // their bounds, so for them the error bound is just the term magnitude
978  // times std::numeric_limits<double>::epsilon() with KahanSum.
979  primal_objective_error += std::abs(lp.objective_coefficients()[col]) *
980  AllowedError(tolerance, primal_values_[col]);
981  }
982  return primal_objective_error;
983 }
984 
985 double LPSolver::ComputePrimalValueInfeasibility(const LinearProgram& lp,
986  bool* is_too_large) {
987  double infeasibility = 0.0;
988  const Fractional tolerance = parameters_.solution_feasibility_tolerance();
989  const ColIndex num_cols = lp.num_variables();
990  for (ColIndex col(0); col < num_cols; ++col) {
991  const Fractional lower_bound = lp.variable_lower_bounds()[col];
992  const Fractional upper_bound = lp.variable_upper_bounds()[col];
993  DCHECK(IsFinite(primal_values_[col]));
994 
995  if (lower_bound == upper_bound) {
996  const Fractional error = std::abs(primal_values_[col] - upper_bound);
997  infeasibility = std::max(infeasibility, error);
998  *is_too_large |= error > AllowedError(tolerance, upper_bound);
999  continue;
1000  }
1001  if (primal_values_[col] > upper_bound) {
1002  const Fractional error = primal_values_[col] - upper_bound;
1003  infeasibility = std::max(infeasibility, error);
1004  *is_too_large |= error > AllowedError(tolerance, upper_bound);
1005  }
1006  if (primal_values_[col] < lower_bound) {
1007  const Fractional error = lower_bound - primal_values_[col];
1008  infeasibility = std::max(infeasibility, error);
1009  *is_too_large |= error > AllowedError(tolerance, lower_bound);
1010  }
1011  }
1012  return infeasibility;
1013 }
1014 
1015 double LPSolver::ComputeActivityInfeasibility(const LinearProgram& lp,
1016  bool* is_too_large) {
1017  double infeasibility = 0.0;
1018  int num_problematic_rows(0);
1019  const RowIndex num_rows = lp.num_constraints();
1020  const Fractional tolerance = parameters_.solution_feasibility_tolerance();
1021  for (RowIndex row(0); row < num_rows; ++row) {
1022  const Fractional activity = constraint_activities_[row];
1023  const Fractional lower_bound = lp.constraint_lower_bounds()[row];
1024  const Fractional upper_bound = lp.constraint_upper_bounds()[row];
1025  DCHECK(IsFinite(activity));
1026 
1027  if (lower_bound == upper_bound) {
1028  if (std::abs(activity - upper_bound) >
1029  AllowedError(tolerance, upper_bound)) {
1030  VLOG(2) << "Row " << row.value() << " has activity " << activity
1031  << " which is different from " << upper_bound << " by "
1032  << activity - upper_bound;
1033  ++num_problematic_rows;
1034  }
1035  infeasibility = std::max(infeasibility, std::abs(activity - upper_bound));
1036  continue;
1037  }
1038  if (activity > upper_bound) {
1039  const Fractional row_excess = activity - upper_bound;
1040  if (row_excess > AllowedError(tolerance, upper_bound)) {
1041  VLOG(2) << "Row " << row.value() << " has activity " << activity
1042  << ", exceeding its upper bound " << upper_bound << " by "
1043  << row_excess;
1044  ++num_problematic_rows;
1045  }
1046  infeasibility = std::max(infeasibility, row_excess);
1047  }
1048  if (activity < lower_bound) {
1049  const Fractional row_deficit = lower_bound - activity;
1050  if (row_deficit > AllowedError(tolerance, lower_bound)) {
1051  VLOG(2) << "Row " << row.value() << " has activity " << activity
1052  << ", below its lower bound " << lower_bound << " by "
1053  << row_deficit;
1054  ++num_problematic_rows;
1055  }
1056  infeasibility = std::max(infeasibility, row_deficit);
1057  }
1058  }
1059  if (num_problematic_rows > 0) {
1060  *is_too_large = true;
1061  VLOG(1) << "Number of infeasible rows = " << num_problematic_rows;
1062  }
1063  return infeasibility;
1064 }
1065 
1066 double LPSolver::ComputeDualValueInfeasibility(const LinearProgram& lp,
1067  bool* is_too_large) {
1068  const Fractional allowed_error = parameters_.solution_feasibility_tolerance();
1069  const Fractional optimization_sign = lp.IsMaximizationProblem() ? -1.0 : 1.0;
1070  double infeasibility = 0.0;
1071  const RowIndex num_rows = lp.num_constraints();
1072  for (RowIndex row(0); row < num_rows; ++row) {
1073  const Fractional dual_value = dual_values_[row];
1074  const Fractional lower_bound = lp.constraint_lower_bounds()[row];
1075  const Fractional upper_bound = lp.constraint_upper_bounds()[row];
1076  DCHECK(IsFinite(dual_value));
1077  const Fractional minimization_dual_value = optimization_sign * dual_value;
1078  if (lower_bound == -kInfinity) {
1079  *is_too_large |= minimization_dual_value > allowed_error;
1080  infeasibility = std::max(infeasibility, minimization_dual_value);
1081  }
1082  if (upper_bound == kInfinity) {
1083  *is_too_large |= -minimization_dual_value > allowed_error;
1084  infeasibility = std::max(infeasibility, -minimization_dual_value);
1085  }
1086  }
1087  return infeasibility;
1088 }
1089 
1090 double LPSolver::ComputeReducedCostInfeasibility(const LinearProgram& lp,
1091  bool* is_too_large) {
1092  const Fractional optimization_sign = lp.IsMaximizationProblem() ? -1.0 : 1.0;
1093  double infeasibility = 0.0;
1094  const ColIndex num_cols = lp.num_variables();
1095  const Fractional tolerance = parameters_.solution_feasibility_tolerance();
1096  for (ColIndex col(0); col < num_cols; ++col) {
1097  const Fractional reduced_cost = reduced_costs_[col];
1098  const Fractional lower_bound = lp.variable_lower_bounds()[col];
1099  const Fractional upper_bound = lp.variable_upper_bounds()[col];
1100  DCHECK(IsFinite(reduced_cost));
1101  const Fractional minimization_reduced_cost =
1102  optimization_sign * reduced_cost;
1103  const Fractional allowed_error =
1104  AllowedError(tolerance, lp.objective_coefficients()[col]);
1105  if (lower_bound == -kInfinity) {
1106  *is_too_large |= minimization_reduced_cost > allowed_error;
1107  infeasibility = std::max(infeasibility, minimization_reduced_cost);
1108  }
1109  if (upper_bound == kInfinity) {
1110  *is_too_large |= -minimization_reduced_cost > allowed_error;
1111  infeasibility = std::max(infeasibility, -minimization_reduced_cost);
1112  }
1113  }
1114  return infeasibility;
1115 }
1116 
1117 } // namespace glop
1118 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
void push_back(const value_type &x)
void Add(const FpNumber &value)
Definition: accurate_sum.h:29
void SetLogToStdOut(bool enable)
Definition: util/logging.h:45
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
static std::unique_ptr< TimeLimit > FromParameters(const Parameters &parameters)
Creates a time limit object initialized from an object that provides methods max_time_in_seconds() an...
Definition: time_limit.h:159
const GlopParameters & GetParameters() const
Definition: lp_solver.cc:130
static std::string GlopVersion()
Definition: lp_solver.cc:114
void SetInitialBasis(const VariableStatusRow &variable_statuses, const ConstraintStatusColumn &constraint_statuses)
Definition: lp_solver.cc:261
const ConstraintStatusColumn & constraint_statuses() const
Definition: lp_solver.h:121
bool MayHaveMultipleOptimalSolutions() const
Definition: lp_solver.cc:520
const VariableStatusRow & variable_statuses() const
Definition: lp_solver.h:107
GlopParameters * GetMutableParameters()
Definition: lp_solver.cc:132
Fractional GetMaximumDualInfeasibility() const
Definition: lp_solver.cc:516
Fractional GetMaximumPrimalInfeasibility() const
Definition: lp_solver.cc:512
Fractional GetObjectiveValue() const
Definition: lp_solver.cc:508
ProblemStatus LoadAndVerifySolution(const LinearProgram &lp, const ProblemSolution &solution)
Definition: lp_solver.cc:316
ABSL_MUST_USE_RESULT ProblemStatus Solve(const LinearProgram &lp)
Definition: lp_solver.cc:136
ABSL_MUST_USE_RESULT ProblemStatus SolveWithTimeLimit(const LinearProgram &lp, TimeLimit *time_limit)
Definition: lp_solver.cc:142
void SetParameters(const GlopParameters &parameters)
Definition: lp_solver.cc:118
std::string GetObjectiveStatsString() const
Definition: lp_data.cc:453
void PopulateFromLinearProgram(const LinearProgram &linear_program)
Definition: lp_data.cc:863
const DenseRow & variable_lower_bounds() const
Definition: lp_data.h:230
const DenseColumn & constraint_lower_bounds() const
Definition: lp_data.h:216
std::string GetBoundsStatsString() const
Definition: lp_data.cc:466
bool IsValid(Fractional max_valid_magnitude=kInfinity) const
Definition: lp_data.cc:1306
const DenseColumn & constraint_upper_bounds() const
Definition: lp_data.h:219
const DenseRow & variable_upper_bounds() const
Definition: lp_data.h:233
std::string GetDimensionString() const
Definition: lp_data.cc:426
Fractional objective_scaling_factor() const
Definition: lp_data.h:262
void assign(IntType size, const T &v)
Definition: lp_types.h:312
SatParameters parameters
CpModelProto proto
ModelSharedTimeLimit * time_limit
int64_t value
absl::Status status
Definition: g_gurobi.cc:41
ABSL_FLAG(bool, lp_dump_to_proto_file, false, "Tells whether do dump the problem to a protobuf file.")
ColIndex col
Definition: markowitz.cc:186
RowIndex row
Definition: markowitz.cc:185
AccurateSum< Fractional > KahanSum
Fractional ScalarProduct(const DenseRowOrColumn1 &u, const DenseRowOrColumn2 &v)
std::string GetProblemStatusString(ProblemStatus problem_status)
Definition: lp_types.cc:21
constexpr double kInfinity
Definition: lp_types.h:88
std::string GetConstraintStatusString(ConstraintStatus status)
Definition: lp_types.cc:92
void LinearProgramToMPModelProto(const LinearProgram &input, MPModelProto *output)
Definition: proto_utils.cc:20
bool IsFinite(Fractional value)
Definition: lp_types.h:95
void ChangeSign(StrictITIVector< IndexType, Fractional > *data)
std::string GetVariableStatusString(VariableStatus status)
Definition: lp_types.cc:73
Collection of objects used to extend the Constraint Solver library.
bool WriteProtoToFile(absl::string_view filename, const google::protobuf::Message &proto, ProtoWriteFormat proto_write_format, bool gzipped, bool append_extension_to_file_name)
Definition: file_util.cc:112
std::string OrToolsVersionString()
Definition: version.cc:28
bool AreWithinAbsoluteTolerance(FloatType x, FloatType y, FloatType absolute_tolerance)
Definition: fp_utils.h:145
glop::MainLpPreprocessor preprocessor
IntVar * upper_bound
Definition: routing.cc:1087
IntVar * lower_bound
Definition: routing.cc:1086
ConstraintStatusColumn constraint_statuses
Definition: lp_data.h:690
#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