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