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