OR-Tools  8.2
linear_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 //
15 
17 
18 #if !defined(_MSC_VER)
19 #include <unistd.h>
20 #endif
21 
22 #include <cmath>
23 #include <cstddef>
24 #include <utility>
25 
26 #include "absl/status/status.h"
27 #include "absl/status/statusor.h"
28 #include "absl/strings/ascii.h"
29 #include "absl/strings/match.h"
30 #include "absl/strings/str_cat.h"
31 #include "absl/strings/str_format.h"
32 #include "absl/strings/str_replace.h"
33 #include "absl/synchronization/mutex.h"
37 #include "ortools/base/logging.h"
38 #include "ortools/base/map_util.h"
40 #include "ortools/base/stl_util.h"
44 #include "ortools/port/file.h"
45 #include "ortools/util/fp_utils.h"
46 
47 ABSL_FLAG(bool, verify_solution, false,
48  "Systematically verify the solution when calling Solve()"
49  ", and change the return value of Solve() to ABNORMAL if"
50  " an error was detected.");
51 ABSL_FLAG(bool, log_verification_errors, true,
52  "If --verify_solution is set: LOG(ERROR) all errors detected"
53  " during the verification of the solution.");
54 ABSL_FLAG(bool, linear_solver_enable_verbose_output, false,
55  "If set, enables verbose output for the solver. Setting this flag"
56  " is the same as calling MPSolver::EnableOutput().");
57 
58 ABSL_FLAG(bool, mpsolver_bypass_model_validation, false,
59  "If set, the user-provided Model won't be verified before Solve()."
60  " Invalid models will typically trigger various error responses"
61  " from the underlying solvers; sometimes crashes.");
62 
63 namespace operations_research {
64 
65 bool SolverTypeIsMip(MPModelRequest::SolverType solver_type) {
66  switch (solver_type) {
67  case MPModelRequest::GLOP_LINEAR_PROGRAMMING:
68  case MPModelRequest::CLP_LINEAR_PROGRAMMING:
69  case MPModelRequest::GLPK_LINEAR_PROGRAMMING:
70  case MPModelRequest::GUROBI_LINEAR_PROGRAMMING:
71  case MPModelRequest::XPRESS_LINEAR_PROGRAMMING:
72  case MPModelRequest::CPLEX_LINEAR_PROGRAMMING:
73  return false;
74 
75  case MPModelRequest::SCIP_MIXED_INTEGER_PROGRAMMING:
76  case MPModelRequest::GLPK_MIXED_INTEGER_PROGRAMMING:
77  case MPModelRequest::CBC_MIXED_INTEGER_PROGRAMMING:
78  case MPModelRequest::GUROBI_MIXED_INTEGER_PROGRAMMING:
79  case MPModelRequest::KNAPSACK_MIXED_INTEGER_PROGRAMMING:
80  case MPModelRequest::BOP_INTEGER_PROGRAMMING:
81  case MPModelRequest::SAT_INTEGER_PROGRAMMING:
82  case MPModelRequest::XPRESS_MIXED_INTEGER_PROGRAMMING:
83  case MPModelRequest::CPLEX_MIXED_INTEGER_PROGRAMMING:
84  return true;
85  }
86  LOG(DFATAL) << "Invalid SolverType: " << solver_type;
87  return false;
88 }
89 
90 double MPConstraint::GetCoefficient(const MPVariable* const var) const {
91  DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var;
92  if (var == nullptr) return 0.0;
93  return gtl::FindWithDefault(coefficients_, var, 0.0);
94 }
95 
96 void MPConstraint::SetCoefficient(const MPVariable* const var, double coeff) {
97  DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var;
98  if (var == nullptr) return;
99  if (coeff == 0.0) {
100  auto it = coefficients_.find(var);
101  // If setting a coefficient to 0 when this coefficient did not
102  // exist or was already 0, do nothing: skip
103  // interface_->SetCoefficient() and do not store a coefficient in
104  // the map. Note that if the coefficient being set to 0 did exist
105  // and was not 0, we do have to keep a 0 in the coefficients_ map,
106  // because the extraction of the constraint might rely on it,
107  // depending on the underlying solver.
108  if (it != coefficients_.end() && it->second != 0.0) {
109  const double old_value = it->second;
110  it->second = 0.0;
111  interface_->SetCoefficient(this, var, 0.0, old_value);
112  }
113  return;
114  }
115  auto insertion_result = coefficients_.insert(std::make_pair(var, coeff));
116  const double old_value =
117  insertion_result.second ? 0.0 : insertion_result.first->second;
118  insertion_result.first->second = coeff;
119  interface_->SetCoefficient(this, var, coeff, old_value);
120 }
121 
123  interface_->ClearConstraint(this);
124  coefficients_.clear();
125 }
126 
127 void MPConstraint::SetBounds(double lb, double ub) {
128  const bool change = lb != lb_ || ub != ub_;
129  lb_ = lb;
130  ub_ = ub;
131  if (change && interface_->constraint_is_extracted(index_)) {
132  interface_->SetConstraintBounds(index_, lb_, ub_);
133  }
134 }
135 
136 double MPConstraint::dual_value() const {
137  if (!interface_->IsContinuous()) {
138  LOG(DFATAL) << "Dual value only available for continuous problems";
139  return 0.0;
140  }
141  if (!interface_->CheckSolutionIsSynchronizedAndExists()) return 0.0;
142  return dual_value_;
143 }
144 
146  if (!interface_->IsContinuous()) {
147  LOG(DFATAL) << "Basis status only available for continuous problems";
148  return MPSolver::FREE;
149  }
150  if (!interface_->CheckSolutionIsSynchronizedAndExists()) {
151  return MPSolver::FREE;
152  }
153  // This is done lazily as this method is expected to be rarely used.
154  return interface_->row_status(index_);
155 }
156 
157 bool MPConstraint::ContainsNewVariables() {
158  const int last_variable_index = interface_->last_variable_index();
159  for (const auto& entry : coefficients_) {
160  const int variable_index = entry.first->index();
161  if (variable_index >= last_variable_index ||
162  !interface_->variable_is_extracted(variable_index)) {
163  return true;
164  }
165  }
166  return false;
167 }
168 
169 // ----- MPObjective -----
170 
171 double MPObjective::GetCoefficient(const MPVariable* const var) const {
172  DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var;
173  if (var == nullptr) return 0.0;
174  return gtl::FindWithDefault(coefficients_, var, 0.0);
175 }
176 
177 void MPObjective::SetCoefficient(const MPVariable* const var, double coeff) {
178  DLOG_IF(DFATAL, !interface_->solver_->OwnsVariable(var)) << var;
179  if (var == nullptr) return;
180  if (coeff == 0.0) {
181  auto it = coefficients_.find(var);
182  // See the discussion on MPConstraint::SetCoefficient() for 0 coefficients,
183  // the same reasoning applies here.
184  if (it == coefficients_.end() || it->second == 0.0) return;
185  it->second = 0.0;
186  } else {
187  coefficients_[var] = coeff;
188  }
189  interface_->SetObjectiveCoefficient(var, coeff);
190 }
191 
193  offset_ = value;
194  interface_->SetObjectiveOffset(offset_);
195 }
196 
197 namespace {
198 void CheckLinearExpr(const MPSolver& solver, const LinearExpr& linear_expr) {
199  for (auto var_value_pair : linear_expr.terms()) {
200  CHECK(solver.OwnsVariable(var_value_pair.first))
201  << "Bad MPVariable* in LinearExpr, did you try adding an integer to an "
202  "MPVariable* directly?";
203  }
204 }
205 } // namespace
206 
208  bool is_maximization) {
209  CheckLinearExpr(*interface_->solver_, linear_expr);
210  interface_->ClearObjective();
211  coefficients_.clear();
212  SetOffset(linear_expr.offset());
213  for (const auto& kv : linear_expr.terms()) {
214  SetCoefficient(kv.first, kv.second);
215  }
216  SetOptimizationDirection(is_maximization);
217 }
218 
219 void MPObjective::AddLinearExpr(const LinearExpr& linear_expr) {
220  CheckLinearExpr(*interface_->solver_, linear_expr);
221  SetOffset(offset_ + linear_expr.offset());
222  for (const auto& kv : linear_expr.terms()) {
223  SetCoefficient(kv.first, GetCoefficient(kv.first) + kv.second);
224  }
225 }
226 
228  interface_->ClearObjective();
229  coefficients_.clear();
230  offset_ = 0.0;
231  SetMinimization();
232 }
233 
235  // Note(user): The maximize_ bool would more naturally belong to the
236  // MPObjective, but it actually has to be a member of MPSolverInterface,
237  // because some implementations (such as GLPK) need that bool for the
238  // MPSolverInterface constructor, i.e at a time when the MPObjective is not
239  // constructed yet (MPSolverInterface is always built before MPObjective
240  // when a new MPSolver is constructed).
241  interface_->maximize_ = maximize;
242  interface_->SetOptimizationDirection(maximize);
243 }
244 
245 bool MPObjective::maximization() const { return interface_->maximize_; }
246 
247 bool MPObjective::minimization() const { return !interface_->maximize_; }
248 
249 double MPObjective::Value() const {
250  // Note(user): implementation-wise, the objective value belongs more
251  // naturally to the MPSolverInterface, since all of its implementations write
252  // to it directly.
253  return interface_->objective_value();
254 }
255 
256 double MPObjective::BestBound() const {
257  // Note(user): the best objective bound belongs to the interface for the
258  // same reasons as the objective value does.
259  return interface_->best_objective_bound();
260 }
261 
262 // ----- MPVariable -----
263 
265  if (!interface_->CheckSolutionIsSynchronizedAndExists()) return 0.0;
266  // If the underlying solver supports integer variables, and this is an integer
267  // variable, we round the solution value (i.e., clients usually expect precise
268  // integer values for integer variables).
269  return (integer_ && interface_->IsMIP()) ? round(solution_value_)
270  : solution_value_;
271 }
272 
274  if (!interface_->CheckSolutionIsSynchronizedAndExists()) return 0.0;
275  return solution_value_;
276 }
277 
278 double MPVariable::reduced_cost() const {
279  if (!interface_->IsContinuous()) {
280  LOG(DFATAL) << "Reduced cost only available for continuous problems";
281  return 0.0;
282  }
283  if (!interface_->CheckSolutionIsSynchronizedAndExists()) return 0.0;
284  return reduced_cost_;
285 }
286 
288  if (!interface_->IsContinuous()) {
289  LOG(DFATAL) << "Basis status only available for continuous problems";
290  return MPSolver::FREE;
291  }
292  if (!interface_->CheckSolutionIsSynchronizedAndExists()) {
293  return MPSolver::FREE;
294  }
295  // This is done lazily as this method is expected to be rarely used.
296  return interface_->column_status(index_);
297 }
298 
299 void MPVariable::SetBounds(double lb, double ub) {
300  const bool change = lb != lb_ || ub != ub_;
301  lb_ = lb;
302  ub_ = ub;
303  if (change && interface_->variable_is_extracted(index_)) {
304  interface_->SetVariableBounds(index_, lb_, ub_);
305  }
306 }
307 
308 void MPVariable::SetInteger(bool integer) {
309  if (integer_ != integer) {
310  integer_ = integer;
311  if (interface_->variable_is_extracted(index_)) {
312  interface_->SetVariableInteger(index_, integer);
313  }
314  }
315 }
316 
318  if (priority == branching_priority_) return;
319  branching_priority_ = priority;
320  interface_->BranchingPriorityChangedForVariable(index_);
321 }
322 
323 // ----- Interface shortcuts -----
324 
325 bool MPSolver::IsMIP() const { return interface_->IsMIP(); }
326 
327 std::string MPSolver::SolverVersion() const {
328  return interface_->SolverVersion();
329 }
330 
331 void* MPSolver::underlying_solver() { return interface_->underlying_solver(); }
332 
333 // ---- Solver-specific parameters ----
334 
335 absl::Status MPSolver::SetNumThreads(int num_threads) {
336  if (num_threads < 1) {
337  return absl::InvalidArgumentError("num_threads must be a positive number.");
338  }
339  const absl::Status status = interface_->SetNumThreads(num_threads);
340  if (status.ok()) {
341  num_threads_ = num_threads;
342  }
343  return status;
344 }
345 
347  const std::string& parameters) {
348  solver_specific_parameter_string_ = parameters;
349  return interface_->SetSolverSpecificParametersAsString(parameters);
350 }
351 
352 // ----- Solver -----
353 
354 #if defined(USE_CLP) || defined(USE_CBC)
355 extern MPSolverInterface* BuildCLPInterface(MPSolver* const solver);
356 #endif
357 #if defined(USE_CBC)
358 extern MPSolverInterface* BuildCBCInterface(MPSolver* const solver);
359 #endif
360 #if defined(USE_GLPK)
361 extern MPSolverInterface* BuildGLPKInterface(bool mip, MPSolver* const solver);
362 #endif
363 extern MPSolverInterface* BuildBopInterface(MPSolver* const solver);
364 extern MPSolverInterface* BuildGLOPInterface(MPSolver* const solver);
365 extern MPSolverInterface* BuildSatInterface(MPSolver* const solver);
366 #if defined(USE_SCIP)
367 extern MPSolverInterface* BuildSCIPInterface(MPSolver* const solver);
368 #endif
369 extern MPSolverInterface* BuildGurobiInterface(bool mip,
370  MPSolver* const solver);
371 #if defined(USE_CPLEX)
372 extern MPSolverInterface* BuildCplexInterface(bool mip, MPSolver* const solver);
373 
374 extern MPSolverInterface* BuildGLOPInterface(MPSolver* const solver);
375 #endif
376 #if defined(USE_XPRESS)
377 extern MPSolverInterface* BuildXpressInterface(bool mip,
378  MPSolver* const solver);
379 #endif
380 
381 namespace {
382 MPSolverInterface* BuildSolverInterface(MPSolver* const solver) {
383  DCHECK(solver != nullptr);
384  switch (solver->ProblemType()) {
386  return BuildBopInterface(solver);
388  return BuildSatInterface(solver);
390  return BuildGLOPInterface(solver);
391 #if defined(USE_GLPK)
393  return BuildGLPKInterface(false, solver);
395  return BuildGLPKInterface(true, solver);
396 #endif
397 #if defined(USE_CLP) || defined(USE_CBC)
399  return BuildCLPInterface(solver);
400 #endif
401 #if defined(USE_CBC)
403  return BuildCBCInterface(solver);
404 #endif
405 #if defined(USE_SCIP)
407  return BuildSCIPInterface(solver);
408 #endif
410  return BuildGurobiInterface(false, solver);
412  return BuildGurobiInterface(true, solver);
413 #if defined(USE_CPLEX)
415  return BuildCplexInterface(false, solver);
417  return BuildCplexInterface(true, solver);
418 #endif
419 #if defined(USE_XPRESS)
421  return BuildXpressInterface(true, solver);
423  return BuildXpressInterface(false, solver);
424 #endif
425  default:
426  // TODO(user): Revert to the best *available* interface.
427  LOG(FATAL) << "Linear solver not recognized.";
428  }
429  return nullptr;
430 }
431 } // namespace
432 
433 namespace {
434 int NumDigits(int n) {
435 // Number of digits needed to write a non-negative integer in base 10.
436 // Note(user): max(1, log(0) + 1) == max(1, -inf) == 1.
437 #if defined(_MSC_VER)
438  return static_cast<int>(std::max(1.0L, log(1.0L * n) / log(10.0L) + 1.0));
439 #else
440  return static_cast<int>(std::max(1.0, log10(static_cast<double>(n)) + 1.0));
441 #endif
442 }
443 } // namespace
444 
445 MPSolver::MPSolver(const std::string& name,
447  : name_(name),
448  problem_type_(problem_type),
449  construction_time_(absl::Now()) {
450  interface_.reset(BuildSolverInterface(this));
451  if (absl::GetFlag(FLAGS_linear_solver_enable_verbose_output)) {
452  EnableOutput();
453  }
454  objective_.reset(new MPObjective(interface_.get()));
455 }
456 
458 
459 // static
461 #ifdef USE_CLP
462  if (problem_type == CLP_LINEAR_PROGRAMMING) return true;
463 #endif
464 #ifdef USE_GLPK
467  return true;
468  }
469 #endif
470  if (problem_type == BOP_INTEGER_PROGRAMMING) return true;
471  if (problem_type == SAT_INTEGER_PROGRAMMING) return true;
472  if (problem_type == GLOP_LINEAR_PROGRAMMING) return true;
475  return MPSolver::GurobiIsCorrectlyInstalled();
476  }
477 #ifdef USE_SCIP
478  if (problem_type == SCIP_MIXED_INTEGER_PROGRAMMING) return true;
479 #endif
480 #ifdef USE_CBC
481  if (problem_type == CBC_MIXED_INTEGER_PROGRAMMING) return true;
482 #endif
483 #ifdef USE_XPRESS
486  return true;
487  }
488 #endif
489 #ifdef USE_CPLEX
492  return true;
493  }
494 #endif
495  return false;
496 }
497 
498 // TODO(user): post c++ 14, instead use
499 // std::pair<MPSolver::OptimizationProblemType, const absl::string_view>
500 // once pair gets a constexpr constructor.
501 namespace {
502 struct NamedOptimizationProblemType {
504  absl::string_view name;
505 };
506 } // namespace
507 
508 #if defined(_MSC_VER)
509 const
510 #else
511 constexpr
512 #endif
513  NamedOptimizationProblemType kOptimizationProblemTypeNames[] = {
529 
530 };
531 // static
532 bool MPSolver::ParseSolverType(absl::string_view solver_id,
534  // Normalize the solver id.
535  const std::string id =
536  absl::StrReplaceAll(absl::AsciiStrToUpper(solver_id), {{"-", "_"}});
537 
538  // Support the full enum name
539  MPModelRequest::SolverType solver_type;
540  if (MPModelRequest::SolverType_Parse(id, &solver_type)) {
541  *type = static_cast<MPSolver::OptimizationProblemType>(solver_type);
542  return true;
543  }
544 
545  // Names are stored in lower case.
546  std::string lower_id = absl::AsciiStrToLower(id);
547 
548  // Remove any "_mip" suffix, since they are optional.
549  if (absl::EndsWith(lower_id, "_mip")) {
550  lower_id = lower_id.substr(0, lower_id.size() - 4);
551  }
552 
553  // Rewrite CP-SAT into SAT.
554  if (lower_id == "cp_sat") {
555  lower_id = "sat";
556  }
557 
558  // Reverse lookup in the kOptimizationProblemTypeNames[] array.
559  for (auto& named_solver : kOptimizationProblemTypeNames) {
560  if (named_solver.name == lower_id) {
561  *type = named_solver.problem_type;
562  return true;
563  }
564  }
565 
566  return false;
567 }
568 
569 const absl::string_view ToString(
570  MPSolver::OptimizationProblemType optimization_problem_type) {
571  for (const auto& named_solver : kOptimizationProblemTypeNames) {
572  if (named_solver.problem_type == optimization_problem_type) {
573  return named_solver.name;
574  }
575  }
576  LOG(FATAL) << "Unrecognized solver type: "
577  << static_cast<int>(optimization_problem_type);
578  return "";
579 }
580 
581 bool AbslParseFlag(const absl::string_view text,
583  std::string* error) {
584  DCHECK(solver_type != nullptr);
585  DCHECK(error != nullptr);
586  const bool result = MPSolver::ParseSolverType(text, solver_type);
587  if (!result) {
588  *error = absl::StrCat("Solver type: ", text, " does not exist.");
589  }
590  return result;
591 }
592 
593 /* static */
595  const std::string& solver_id) {
597  CHECK(MPSolver::ParseSolverType(solver_id, &problem_type)) << solver_id;
598  return problem_type;
599 }
600 
601 /* static */
602 MPSolver* MPSolver::CreateSolver(const std::string& solver_id) {
604  if (!MPSolver::ParseSolverType(solver_id, &problem_type)) {
605  LOG(WARNING) << "Unrecognized solver type: " << solver_id;
606  return nullptr;
607  }
609  LOG(WARNING) << "Support for " << solver_id
610  << " not linked in, or the license was not found.";
611  return nullptr;
612  }
613  return new MPSolver("", problem_type);
614 }
615 
616 MPVariable* MPSolver::LookupVariableOrNull(const std::string& var_name) const {
617  if (!variable_name_to_index_) GenerateVariableNameIndex();
618 
619  absl::flat_hash_map<std::string, int>::const_iterator it =
620  variable_name_to_index_->find(var_name);
621  if (it == variable_name_to_index_->end()) return nullptr;
622  return variables_[it->second];
623 }
624 
626  const std::string& constraint_name) const {
627  if (!constraint_name_to_index_) GenerateConstraintNameIndex();
628 
629  const auto it = constraint_name_to_index_->find(constraint_name);
630  if (it == constraint_name_to_index_->end()) return nullptr;
631  return constraints_[it->second];
632 }
633 
634 // ----- Methods using protocol buffers -----
635 
637  const MPModelProto& input_model, std::string* error_message) {
638  // The variable and constraint names are dropped, because we allow
639  // duplicate names in the proto (they're not considered as 'ids'),
640  // unlike the MPSolver C++ API which crashes if there are duplicate names.
641  // Clearing the names makes the MPSolver generate unique names.
642  return LoadModelFromProtoInternal(input_model, /*clear_names=*/true,
643  /*check_model_validity=*/true,
644  error_message);
645 }
646 
648  const MPModelProto& input_model, std::string* error_message) {
649  // Force variable and constraint name indexing (which CHECKs name uniqueness).
650  GenerateVariableNameIndex();
651  GenerateConstraintNameIndex();
652 
653  return LoadModelFromProtoInternal(input_model, /*clear_names=*/false,
654  /*check_model_validity=*/true,
655  error_message);
656 }
657 
658 MPSolverResponseStatus MPSolver::LoadModelFromProtoInternal(
659  const MPModelProto& input_model, bool clear_names,
660  bool check_model_validity, std::string* error_message) {
661  CHECK(error_message != nullptr);
662  if (check_model_validity) {
663  const std::string error = FindErrorInMPModelProto(input_model);
664  if (!error.empty()) {
665  *error_message = error;
667  << "Invalid model given to LoadModelFromProto(): " << error;
668  if (absl::GetFlag(FLAGS_mpsolver_bypass_model_validation)) {
670  << "Ignoring the model error(s) because of"
671  << " --mpsolver_bypass_model_validation.";
672  } else {
673  return absl::StrContains(error, "Infeasible") ? MPSOLVER_INFEASIBLE
675  }
676  }
677  }
678 
679  if (input_model.has_quadratic_objective()) {
680  *error_message =
681  "Optimizing a quadratic objective is only supported through direct "
682  "proto solves. Please use MPSolver::SolveWithProto, or the solver's "
683  "direct proto solve function.";
684  return MPSOLVER_MODEL_INVALID;
685  }
686 
687  MPObjective* const objective = MutableObjective();
688  // Passing empty names makes the MPSolver generate unique names.
689  const std::string empty;
690  for (int i = 0; i < input_model.variable_size(); ++i) {
691  const MPVariableProto& var_proto = input_model.variable(i);
692  MPVariable* variable =
693  MakeNumVar(var_proto.lower_bound(), var_proto.upper_bound(),
694  clear_names ? empty : var_proto.name());
695  variable->SetInteger(var_proto.is_integer());
696  if (var_proto.branching_priority() != 0) {
697  variable->SetBranchingPriority(var_proto.branching_priority());
698  }
699  objective->SetCoefficient(variable, var_proto.objective_coefficient());
700  }
701 
702  for (const MPConstraintProto& ct_proto : input_model.constraint()) {
703  if (ct_proto.lower_bound() == -infinity() &&
704  ct_proto.upper_bound() == infinity()) {
705  continue;
706  }
707 
708  MPConstraint* const ct =
709  MakeRowConstraint(ct_proto.lower_bound(), ct_proto.upper_bound(),
710  clear_names ? empty : ct_proto.name());
711  ct->set_is_lazy(ct_proto.is_lazy());
712  for (int j = 0; j < ct_proto.var_index_size(); ++j) {
713  ct->SetCoefficient(variables_[ct_proto.var_index(j)],
714  ct_proto.coefficient(j));
715  }
716  }
717 
718  for (const MPGeneralConstraintProto& general_constraint :
719  input_model.general_constraint()) {
720  switch (general_constraint.general_constraint_case()) {
721  case MPGeneralConstraintProto::kIndicatorConstraint: {
722  const auto& proto =
723  general_constraint.indicator_constraint().constraint();
724  if (proto.lower_bound() == -infinity() &&
725  proto.upper_bound() == infinity()) {
726  continue;
727  }
728 
729  const int constraint_index = NumConstraints();
730  MPConstraint* const constraint = new MPConstraint(
731  constraint_index, proto.lower_bound(), proto.upper_bound(),
732  clear_names ? "" : proto.name(), interface_.get());
733  if (constraint_name_to_index_) {
734  gtl::InsertOrDie(&*constraint_name_to_index_, constraint->name(),
735  constraint_index);
736  }
737  constraints_.push_back(constraint);
738  constraint_is_extracted_.push_back(false);
739 
740  constraint->set_is_lazy(proto.is_lazy());
741  for (int j = 0; j < proto.var_index_size(); ++j) {
742  constraint->SetCoefficient(variables_[proto.var_index(j)],
743  proto.coefficient(j));
744  }
745 
746  MPVariable* const variable =
747  variables_[general_constraint.indicator_constraint().var_index()];
748  constraint->indicator_variable_ = variable;
749  constraint->indicator_value_ =
750  general_constraint.indicator_constraint().var_value();
751 
752  if (!interface_->AddIndicatorConstraint(constraint)) {
753  *error_message = "Solver doesn't support indicator constraints";
754  return MPSOLVER_MODEL_INVALID;
755  }
756  break;
757  }
758  default:
759  *error_message = absl::StrFormat(
760  "Optimizing general constraints of type %i is only supported "
761  "through direct proto solves. Please use MPSolver::SolveWithProto, "
762  "or the solver's direct proto solve function.",
763  general_constraint.general_constraint_case());
764  return MPSOLVER_MODEL_INVALID;
765  }
766  }
767 
768  objective->SetOptimizationDirection(input_model.maximize());
769  if (input_model.has_objective_offset()) {
770  objective->SetOffset(input_model.objective_offset());
771  }
772 
773  // Stores any hints about where to start the solve.
774  solution_hint_.clear();
775  for (int i = 0; i < input_model.solution_hint().var_index_size(); ++i) {
776  solution_hint_.push_back(
777  std::make_pair(variables_[input_model.solution_hint().var_index(i)],
778  input_model.solution_hint().var_value(i)));
779  }
781 }
782 
783 namespace {
784 MPSolverResponseStatus ResultStatusToMPSolverResponseStatus(
785  MPSolver::ResultStatus status) {
786  switch (status) {
787  case MPSolver::OPTIMAL:
788  return MPSOLVER_OPTIMAL;
789  case MPSolver::FEASIBLE:
790  return MPSOLVER_FEASIBLE;
792  return MPSOLVER_INFEASIBLE;
793  case MPSolver::UNBOUNDED:
794  return MPSOLVER_UNBOUNDED;
795  case MPSolver::ABNORMAL:
796  return MPSOLVER_ABNORMAL;
798  return MPSOLVER_MODEL_INVALID;
800  return MPSOLVER_NOT_SOLVED;
801  }
803 }
804 } // namespace
805 
806 void MPSolver::FillSolutionResponseProto(MPSolutionResponse* response) const {
807  CHECK(response != nullptr);
808  response->Clear();
809  response->set_status(
810  ResultStatusToMPSolverResponseStatus(interface_->result_status_));
811  if (interface_->result_status_ == MPSolver::OPTIMAL ||
812  interface_->result_status_ == MPSolver::FEASIBLE) {
813  response->set_objective_value(Objective().Value());
814  for (int i = 0; i < variables_.size(); ++i) {
815  response->add_variable_value(variables_[i]->solution_value());
816  }
817 
818  if (interface_->IsMIP()) {
819  response->set_best_objective_bound(interface_->best_objective_bound());
820  } else {
821  // Dual values have no meaning in MIP.
822  for (int j = 0; j < constraints_.size(); ++j) {
823  response->add_dual_value(constraints_[j]->dual_value());
824  }
825  // Reduced cost have no meaning in MIP.
826  for (int i = 0; i < variables_.size(); ++i) {
827  response->add_reduced_cost(variables_[i]->reduced_cost());
828  }
829  }
830  }
831 }
832 
833 // static
834 void MPSolver::SolveWithProto(const MPModelRequest& model_request,
835  MPSolutionResponse* response) {
836  CHECK(response != nullptr);
837  MPSolver solver(model_request.model().name(),
839  model_request.solver_type()));
840  if (model_request.enable_internal_solver_output()) {
841  solver.EnableOutput();
842  }
843 
844  auto optional_response = solver.interface_->DirectlySolveProto(model_request);
845  if (optional_response) {
846  *response = std::move(optional_response).value();
847  return;
848  }
849 
850  const absl::optional<LazyMutableCopy<MPModelProto>> optional_model =
852  if (!optional_model) {
853  LOG_IF(WARNING, model_request.enable_internal_solver_output())
854  << "Failed to extract a valid model from protocol buffer. Status: "
855  << ProtoEnumToString<MPSolverResponseStatus>(response->status()) << " ("
856  << response->status() << "): " << response->status_str();
857  return;
858  }
859  std::string error_message;
860  response->set_status(solver.LoadModelFromProtoInternal(
861  optional_model->get(), /*clear_names=*/true,
862  /*check_model_validity=*/false, &error_message));
863  // Even though we don't re-check model validity here, there can be some
864  // problems found by LoadModelFromProto, eg. unsupported features.
865  if (response->status() != MPSOLVER_MODEL_IS_VALID) {
866  response->set_status_str(error_message);
867  LOG_IF(WARNING, model_request.enable_internal_solver_output())
868  << "LoadModelFromProtoInternal() failed even though the model was "
869  << "valid! Status: "
870  << ProtoEnumToString<MPSolverResponseStatus>(response->status()) << " ("
871  << response->status() << "); Error: " << error_message;
872  return;
873  }
874  if (model_request.has_solver_time_limit_seconds()) {
875  solver.SetTimeLimit(
876  absl::Seconds(model_request.solver_time_limit_seconds()));
877  }
878  std::string warning_message;
879  if (model_request.has_solver_specific_parameters()) {
881  model_request.solver_specific_parameters())) {
882  if (model_request.ignore_solver_specific_parameters_failure()) {
883  // We'll add a warning message in status_str after the solve.
884  warning_message =
885  "Warning: the solver specific parameters were not successfully "
886  "applied";
887  } else {
889  return;
890  }
891  }
892  }
893  solver.Solve();
895  if (!warning_message.empty()) {
896  response->set_status_str(absl::StrCat(
897  response->status_str(), (response->status_str().empty() ? "" : "\n"),
898  warning_message));
899  }
900 }
901 
902 void MPSolver::ExportModelToProto(MPModelProto* output_model) const {
903  DCHECK(output_model != nullptr);
904  output_model->Clear();
905  // Name
906  output_model->set_name(Name());
907  // Variables
908  for (int j = 0; j < variables_.size(); ++j) {
909  const MPVariable* const var = variables_[j];
910  MPVariableProto* const variable_proto = output_model->add_variable();
911  // TODO(user): Add option to avoid filling the var name to avoid overly
912  // large protocol buffers.
913  variable_proto->set_name(var->name());
914  variable_proto->set_lower_bound(var->lb());
915  variable_proto->set_upper_bound(var->ub());
916  variable_proto->set_is_integer(var->integer());
917  if (objective_->GetCoefficient(var) != 0.0) {
918  variable_proto->set_objective_coefficient(
919  objective_->GetCoefficient(var));
920  }
921  if (var->branching_priority() != 0) {
922  variable_proto->set_branching_priority(var->branching_priority());
923  }
924  }
925 
926  // Map the variables to their indices. This is needed to output the
927  // variables in the order they were created, which in turn is needed to have
928  // repeatable results with ExportModelAsLpFormat and ExportModelAsMpsFormat.
929  // This step is needed as long as the variable indices are given by the
930  // underlying solver at the time of model extraction.
931  // TODO(user): remove this step.
932  absl::flat_hash_map<const MPVariable*, int> var_to_index;
933  for (int j = 0; j < variables_.size(); ++j) {
934  var_to_index[variables_[j]] = j;
935  }
936 
937  // Constraints
938  for (int i = 0; i < constraints_.size(); ++i) {
939  MPConstraint* const constraint = constraints_[i];
940  MPConstraintProto* constraint_proto;
941  if (constraint->indicator_variable() != nullptr) {
942  MPGeneralConstraintProto* const general_constraint_proto =
943  output_model->add_general_constraint();
944  general_constraint_proto->set_name(constraint->name());
945  MPIndicatorConstraint* const indicator_constraint_proto =
946  general_constraint_proto->mutable_indicator_constraint();
947  indicator_constraint_proto->set_var_index(
948  constraint->indicator_variable()->index());
949  indicator_constraint_proto->set_var_value(constraint->indicator_value());
950  constraint_proto = indicator_constraint_proto->mutable_constraint();
951  } else {
952  constraint_proto = output_model->add_constraint();
953  }
954  constraint_proto->set_name(constraint->name());
955  constraint_proto->set_lower_bound(constraint->lb());
956  constraint_proto->set_upper_bound(constraint->ub());
957  constraint_proto->set_is_lazy(constraint->is_lazy());
958  // Vector linear_term will contain pairs (variable index, coeff), that will
959  // be sorted by variable index.
960  std::vector<std::pair<int, double>> linear_term;
961  for (const auto& entry : constraint->coefficients_) {
962  const MPVariable* const var = entry.first;
963  const int var_index = gtl::FindWithDefault(var_to_index, var, -1);
964  DCHECK_NE(-1, var_index);
965  const double coeff = entry.second;
966  linear_term.push_back(std::pair<int, double>(var_index, coeff));
967  }
968  // The cost of sort is expected to be low as constraints usually have very
969  // few terms.
970  std::sort(linear_term.begin(), linear_term.end());
971  // Now use linear term.
972  for (const std::pair<int, double>& var_and_coeff : linear_term) {
973  constraint_proto->add_var_index(var_and_coeff.first);
974  constraint_proto->add_coefficient(var_and_coeff.second);
975  }
976  }
977 
978  output_model->set_maximize(Objective().maximization());
979  output_model->set_objective_offset(Objective().offset());
980 
981  if (!solution_hint_.empty()) {
982  PartialVariableAssignment* const hint =
983  output_model->mutable_solution_hint();
984  for (const auto& var_value_pair : solution_hint_) {
985  hint->add_var_index(var_value_pair.first->index());
986  hint->add_var_value(var_value_pair.second);
987  }
988  }
989 }
990 
991 absl::Status MPSolver::LoadSolutionFromProto(const MPSolutionResponse& response,
992  double tolerance) {
993  interface_->result_status_ = static_cast<ResultStatus>(response.status());
994  if (response.status() != MPSOLVER_OPTIMAL &&
995  response.status() != MPSOLVER_FEASIBLE) {
996  return absl::InvalidArgumentError(absl::StrCat(
997  "Cannot load a solution unless its status is OPTIMAL or FEASIBLE"
998  " (status was: ",
999  ProtoEnumToString<MPSolverResponseStatus>(response.status()), ")"));
1000  }
1001  // Before touching the variables, verify that the solution looks legit:
1002  // each variable of the MPSolver must have its value listed exactly once, and
1003  // each listed solution should correspond to a known variable.
1004  if (response.variable_value_size() != variables_.size()) {
1005  return absl::InvalidArgumentError(absl::StrCat(
1006  "Trying to load a solution whose number of variables (",
1007  response.variable_value_size(),
1008  ") does not correspond to the Solver's (", variables_.size(), ")"));
1009  }
1010  interface_->ExtractModel();
1011 
1012  if (tolerance != infinity()) {
1013  // Look further: verify that the variable values are within the bounds.
1014  double largest_error = 0;
1015  int num_vars_out_of_bounds = 0;
1016  int last_offending_var = -1;
1017  for (int i = 0; i < response.variable_value_size(); ++i) {
1018  const double var_value = response.variable_value(i);
1019  MPVariable* var = variables_[i];
1020  // TODO(user): Use parameter when they become available in this class.
1021  const double lb_error = var->lb() - var_value;
1022  const double ub_error = var_value - var->ub();
1023  if (lb_error > tolerance || ub_error > tolerance) {
1024  ++num_vars_out_of_bounds;
1025  largest_error = std::max(largest_error, std::max(lb_error, ub_error));
1026  last_offending_var = i;
1027  }
1028  }
1029  if (num_vars_out_of_bounds > 0) {
1030  return absl::InvalidArgumentError(absl::StrCat(
1031  "Loaded a solution whose variables matched the solver's, but ",
1032  num_vars_out_of_bounds, " of ", variables_.size(),
1033  " variables were out of their bounds, by more than the primal"
1034  " tolerance which is: ",
1035  tolerance, ". Max error: ", largest_error, ", last offender var is #",
1036  last_offending_var, ": '", variables_[last_offending_var]->name(),
1037  "'"));
1038  }
1039  }
1040  // TODO(user): Load the reduced costs too, if available.
1041  for (int i = 0; i < response.variable_value_size(); ++i) {
1042  variables_[i]->set_solution_value(response.variable_value(i));
1043  }
1044  // Set the objective value, if is known.
1045  // NOTE(user): We do not verify the objective, even though we could!
1046  if (response.has_objective_value()) {
1047  interface_->objective_value_ = response.objective_value();
1048  }
1049  if (response.has_best_objective_bound()) {
1050  interface_->best_objective_bound_ = response.best_objective_bound();
1051  }
1052  // Mark the status as SOLUTION_SYNCHRONIZED, so that users may inspect the
1053  // solution normally.
1054  interface_->sync_status_ = MPSolverInterface::SOLUTION_SYNCHRONIZED;
1055  return absl::OkStatus();
1056 }
1057 
1059  MutableObjective()->Clear();
1060  gtl::STLDeleteElements(&variables_);
1061  gtl::STLDeleteElements(&constraints_);
1062  variables_.clear();
1063  if (variable_name_to_index_) {
1064  variable_name_to_index_->clear();
1065  }
1066  variable_is_extracted_.clear();
1067  constraints_.clear();
1068  if (constraint_name_to_index_) {
1069  constraint_name_to_index_->clear();
1070  }
1071  constraint_is_extracted_.clear();
1072  interface_->Reset();
1073  solution_hint_.clear();
1074 }
1075 
1076 void MPSolver::Reset() { interface_->Reset(); }
1077 
1078 bool MPSolver::InterruptSolve() { return interface_->InterruptSolve(); }
1079 
1081  const std::vector<BasisStatus>& variable_statuses,
1082  const std::vector<BasisStatus>& constraint_statuses) {
1083  interface_->SetStartingLpBasis(variable_statuses, constraint_statuses);
1084 }
1085 
1086 MPVariable* MPSolver::MakeVar(double lb, double ub, bool integer,
1087  const std::string& name) {
1088  const int var_index = NumVariables();
1089  MPVariable* v =
1090  new MPVariable(var_index, lb, ub, integer, name, interface_.get());
1091  if (variable_name_to_index_) {
1092  gtl::InsertOrDie(&*variable_name_to_index_, v->name(), var_index);
1093  }
1094  variables_.push_back(v);
1095  variable_is_extracted_.push_back(false);
1096  interface_->AddVariable(v);
1097  return v;
1098 }
1099 
1100 MPVariable* MPSolver::MakeNumVar(double lb, double ub,
1101  const std::string& name) {
1102  return MakeVar(lb, ub, false, name);
1103 }
1104 
1105 MPVariable* MPSolver::MakeIntVar(double lb, double ub,
1106  const std::string& name) {
1107  return MakeVar(lb, ub, true, name);
1108 }
1109 
1110 MPVariable* MPSolver::MakeBoolVar(const std::string& name) {
1111  return MakeVar(0.0, 1.0, true, name);
1112 }
1113 
1114 void MPSolver::MakeVarArray(int nb, double lb, double ub, bool integer,
1115  const std::string& name,
1116  std::vector<MPVariable*>* vars) {
1117  DCHECK_GE(nb, 0);
1118  if (nb <= 0) return;
1119  const int num_digits = NumDigits(nb);
1120  for (int i = 0; i < nb; ++i) {
1121  if (name.empty()) {
1122  vars->push_back(MakeVar(lb, ub, integer, name));
1123  } else {
1124  std::string vname =
1125  absl::StrFormat("%s%0*d", name.c_str(), num_digits, i);
1126  vars->push_back(MakeVar(lb, ub, integer, vname));
1127  }
1128  }
1129 }
1130 
1131 void MPSolver::MakeNumVarArray(int nb, double lb, double ub,
1132  const std::string& name,
1133  std::vector<MPVariable*>* vars) {
1134  MakeVarArray(nb, lb, ub, false, name, vars);
1135 }
1136 
1137 void MPSolver::MakeIntVarArray(int nb, double lb, double ub,
1138  const std::string& name,
1139  std::vector<MPVariable*>* vars) {
1140  MakeVarArray(nb, lb, ub, true, name, vars);
1141 }
1142 
1143 void MPSolver::MakeBoolVarArray(int nb, const std::string& name,
1144  std::vector<MPVariable*>* vars) {
1145  MakeVarArray(nb, 0.0, 1.0, true, name, vars);
1146 }
1147 
1149  return MakeRowConstraint(lb, ub, "");
1150 }
1151 
1153  return MakeRowConstraint(-infinity(), infinity(), "");
1154 }
1155 
1157  const std::string& name) {
1158  const int constraint_index = NumConstraints();
1159  MPConstraint* const constraint =
1160  new MPConstraint(constraint_index, lb, ub, name, interface_.get());
1161  if (constraint_name_to_index_) {
1162  gtl::InsertOrDie(&*constraint_name_to_index_, constraint->name(),
1163  constraint_index);
1164  }
1165  constraints_.push_back(constraint);
1166  constraint_is_extracted_.push_back(false);
1167  interface_->AddRowConstraint(constraint);
1168  return constraint;
1169 }
1170 
1172  return MakeRowConstraint(-infinity(), infinity(), name);
1173 }
1174 
1176  return MakeRowConstraint(range, "");
1177 }
1178 
1180  const std::string& name) {
1181  CheckLinearExpr(*this, range.linear_expr());
1182  MPConstraint* constraint =
1183  MakeRowConstraint(range.lower_bound(), range.upper_bound(), name);
1184  for (const auto& kv : range.linear_expr().terms()) {
1185  constraint->SetCoefficient(kv.first, kv.second);
1186  }
1187  return constraint;
1188 }
1189 
1190 int MPSolver::ComputeMaxConstraintSize(int min_constraint_index,
1191  int max_constraint_index) const {
1192  int max_constraint_size = 0;
1193  DCHECK_GE(min_constraint_index, 0);
1194  DCHECK_LE(max_constraint_index, constraints_.size());
1195  for (int i = min_constraint_index; i < max_constraint_index; ++i) {
1196  MPConstraint* const ct = constraints_[i];
1197  if (ct->coefficients_.size() > max_constraint_size) {
1198  max_constraint_size = ct->coefficients_.size();
1199  }
1200  }
1201  return max_constraint_size;
1202 }
1203 
1204 bool MPSolver::HasInfeasibleConstraints() const {
1205  bool hasInfeasibleConstraints = false;
1206  for (int i = 0; i < constraints_.size(); ++i) {
1207  if (constraints_[i]->lb() > constraints_[i]->ub()) {
1208  LOG(WARNING) << "Constraint " << constraints_[i]->name() << " (" << i
1209  << ") has contradictory bounds:"
1210  << " lower bound = " << constraints_[i]->lb()
1211  << " upper bound = " << constraints_[i]->ub();
1212  hasInfeasibleConstraints = true;
1213  }
1214  }
1215  return hasInfeasibleConstraints;
1216 }
1217 
1218 bool MPSolver::HasIntegerVariables() const {
1219  for (const MPVariable* const variable : variables_) {
1220  if (variable->integer()) return true;
1221  }
1222  return false;
1223 }
1224 
1226  MPSolverParameters default_param;
1227  return Solve(default_param);
1228 }
1229 
1231  // Special case for infeasible constraints so that all solvers have
1232  // the same behavior.
1233  // TODO(user): replace this by model extraction to proto + proto validation
1234  // (the proto has very low overhead compared to the wrapper, both in
1235  // performance and memory, so it's ok).
1236  if (HasInfeasibleConstraints()) {
1237  interface_->result_status_ = MPSolver::INFEASIBLE;
1238  return interface_->result_status_;
1239  }
1240 
1241  MPSolver::ResultStatus status = interface_->Solve(param);
1242  if (absl::GetFlag(FLAGS_verify_solution)) {
1243  if (status != MPSolver::OPTIMAL && status != MPSolver::FEASIBLE) {
1244  VLOG(1) << "--verify_solution enabled, but the solver did not find a"
1245  << " solution: skipping the verification.";
1246  } else if (!VerifySolution(
1248  absl::GetFlag(FLAGS_log_verification_errors))) {
1249  status = MPSolver::ABNORMAL;
1250  interface_->result_status_ = status;
1251  }
1252  }
1253  DCHECK_EQ(interface_->result_status_, status);
1254  return status;
1255 }
1256 
1257 void MPSolver::Write(const std::string& file_name) {
1258  interface_->Write(file_name);
1259 }
1260 
1261 namespace {
1262 std::string PrettyPrintVar(const MPVariable& var) {
1263  const std::string prefix = "Variable '" + var.name() + "': domain = ";
1264  if (var.lb() >= MPSolver::infinity() || var.ub() <= -MPSolver::infinity() ||
1265  var.lb() > var.ub()) {
1266  return prefix + "∅"; // Empty set.
1267  }
1268  // Special case: integer variable with at most two possible values
1269  // (and potentially none).
1270  if (var.integer() && var.ub() - var.lb() <= 1) {
1271  const int64 lb = static_cast<int64>(ceil(var.lb()));
1272  const int64 ub = static_cast<int64>(floor(var.ub()));
1273  if (lb > ub) {
1274  return prefix + "∅";
1275  } else if (lb == ub) {
1276  return absl::StrFormat("%s{ %d }", prefix.c_str(), lb);
1277  } else {
1278  return absl::StrFormat("%s{ %d, %d }", prefix.c_str(), lb, ub);
1279  }
1280  }
1281  // Special case: single (non-infinite) real value.
1282  if (var.lb() == var.ub()) {
1283  return absl::StrFormat("%s{ %f }", prefix.c_str(), var.lb());
1284  }
1285  return prefix + (var.integer() ? "Integer" : "Real") + " in " +
1286  (var.lb() <= -MPSolver::infinity()
1287  ? std::string("]-∞")
1288  : absl::StrFormat("[%f", var.lb())) +
1289  ", " +
1290  (var.ub() >= MPSolver::infinity() ? std::string("+∞[")
1291  : absl::StrFormat("%f]", var.ub()));
1292 }
1293 
1294 std::string PrettyPrintConstraint(const MPConstraint& constraint) {
1295  std::string prefix = "Constraint '" + constraint.name() + "': ";
1296  if (constraint.lb() >= MPSolver::infinity() ||
1297  constraint.ub() <= -MPSolver::infinity() ||
1298  constraint.lb() > constraint.ub()) {
1299  return prefix + "ALWAYS FALSE";
1300  }
1301  if (constraint.lb() <= -MPSolver::infinity() &&
1302  constraint.ub() >= MPSolver::infinity()) {
1303  return prefix + "ALWAYS TRUE";
1304  }
1305  prefix += "<linear expr>";
1306  // Equality.
1307  if (constraint.lb() == constraint.ub()) {
1308  return absl::StrFormat("%s = %f", prefix.c_str(), constraint.lb());
1309  }
1310  // Inequalities.
1311  if (constraint.lb() <= -MPSolver::infinity()) {
1312  return absl::StrFormat("%s ≤ %f", prefix.c_str(), constraint.ub());
1313  }
1314  if (constraint.ub() >= MPSolver::infinity()) {
1315  return absl::StrFormat("%s ≥ %f", prefix.c_str(), constraint.lb());
1316  }
1317  return absl::StrFormat("%s ∈ [%f, %f]", prefix.c_str(), constraint.lb(),
1318  constraint.ub());
1319 }
1320 } // namespace
1321 
1323  interface_->ExtractModel();
1324  for (MPVariable* const variable : variables_) {
1325  const double value = variable->solution_value();
1326  if (std::isnan(value)) {
1327  return absl::InvalidArgumentError(
1328  absl::StrCat("NaN value for ", PrettyPrintVar(*variable)));
1329  }
1330  if (value < variable->lb()) {
1331  variable->set_solution_value(variable->lb());
1332  } else if (value > variable->ub()) {
1333  variable->set_solution_value(variable->ub());
1334  }
1335  }
1336  interface_->sync_status_ = MPSolverInterface::SOLUTION_SYNCHRONIZED;
1337  return absl::OkStatus();
1338 }
1339 
1340 std::vector<double> MPSolver::ComputeConstraintActivities() const {
1341  // TODO(user): test this failure case.
1342  if (!interface_->CheckSolutionIsSynchronizedAndExists()) return {};
1343  std::vector<double> activities(constraints_.size(), 0.0);
1344  for (int i = 0; i < constraints_.size(); ++i) {
1345  const MPConstraint& constraint = *constraints_[i];
1346  AccurateSum<double> sum;
1347  for (const auto& entry : constraint.coefficients_) {
1348  sum.Add(entry.first->solution_value() * entry.second);
1349  }
1350  activities[i] = sum.Value();
1351  }
1352  return activities;
1353 }
1354 
1355 // TODO(user): split.
1356 bool MPSolver::VerifySolution(double tolerance, bool log_errors) const {
1357  double max_observed_error = 0;
1358  if (tolerance < 0) tolerance = infinity();
1359  int num_errors = 0;
1360 
1361  // Verify variables.
1362  for (int i = 0; i < variables_.size(); ++i) {
1363  const MPVariable& var = *variables_[i];
1364  const double value = var.solution_value();
1365  // Check for NaN.
1366  if (std::isnan(value)) {
1367  ++num_errors;
1368  max_observed_error = infinity();
1369  LOG_IF(ERROR, log_errors) << "NaN value for " << PrettyPrintVar(var);
1370  continue;
1371  }
1372  // Check lower bound.
1373  if (var.lb() != -infinity()) {
1374  if (value < var.lb() - tolerance) {
1375  ++num_errors;
1376  max_observed_error = std::max(max_observed_error, var.lb() - value);
1377  LOG_IF(ERROR, log_errors)
1378  << "Value " << value << " too low for " << PrettyPrintVar(var);
1379  }
1380  }
1381  // Check upper bound.
1382  if (var.ub() != infinity()) {
1383  if (value > var.ub() + tolerance) {
1384  ++num_errors;
1385  max_observed_error = std::max(max_observed_error, value - var.ub());
1386  LOG_IF(ERROR, log_errors)
1387  << "Value " << value << " too high for " << PrettyPrintVar(var);
1388  }
1389  }
1390  // Check integrality.
1391  if (IsMIP() && var.integer()) {
1392  if (fabs(value - round(value)) > tolerance) {
1393  ++num_errors;
1394  max_observed_error =
1395  std::max(max_observed_error, fabs(value - round(value)));
1396  LOG_IF(ERROR, log_errors)
1397  << "Non-integer value " << value << " for " << PrettyPrintVar(var);
1398  }
1399  }
1400  }
1401  if (!IsMIP() && HasIntegerVariables()) {
1402  LOG_IF(INFO, log_errors) << "Skipped variable integrality check, because "
1403  << "a continuous relaxation of the model was "
1404  << "solved (i.e., the selected solver does not "
1405  << "support integer variables).";
1406  }
1407 
1408  // Verify constraints.
1409  const std::vector<double> activities = ComputeConstraintActivities();
1410  for (int i = 0; i < constraints_.size(); ++i) {
1411  const MPConstraint& constraint = *constraints_[i];
1412  const double activity = activities[i];
1413  // Re-compute the activity with a inaccurate summing algorithm.
1414  double inaccurate_activity = 0.0;
1415  for (const auto& entry : constraint.coefficients_) {
1416  inaccurate_activity += entry.first->solution_value() * entry.second;
1417  }
1418  // Catch NaNs.
1419  if (std::isnan(activity) || std::isnan(inaccurate_activity)) {
1420  ++num_errors;
1421  max_observed_error = infinity();
1422  LOG_IF(ERROR, log_errors)
1423  << "NaN value for " << PrettyPrintConstraint(constraint);
1424  continue;
1425  }
1426  // Check bounds.
1427  if (constraint.indicator_variable() == nullptr ||
1428  std::round(constraint.indicator_variable()->solution_value()) ==
1429  constraint.indicator_value()) {
1430  if (constraint.lb() != -infinity()) {
1431  if (activity < constraint.lb() - tolerance) {
1432  ++num_errors;
1433  max_observed_error =
1434  std::max(max_observed_error, constraint.lb() - activity);
1435  LOG_IF(ERROR, log_errors)
1436  << "Activity " << activity << " too low for "
1437  << PrettyPrintConstraint(constraint);
1438  } else if (inaccurate_activity < constraint.lb() - tolerance) {
1439  LOG_IF(WARNING, log_errors)
1440  << "Activity " << activity << ", computed with the (inaccurate)"
1441  << " standard sum of its terms, is too low for "
1442  << PrettyPrintConstraint(constraint);
1443  }
1444  }
1445  if (constraint.ub() != infinity()) {
1446  if (activity > constraint.ub() + tolerance) {
1447  ++num_errors;
1448  max_observed_error =
1449  std::max(max_observed_error, activity - constraint.ub());
1450  LOG_IF(ERROR, log_errors)
1451  << "Activity " << activity << " too high for "
1452  << PrettyPrintConstraint(constraint);
1453  } else if (inaccurate_activity > constraint.ub() + tolerance) {
1454  LOG_IF(WARNING, log_errors)
1455  << "Activity " << activity << ", computed with the (inaccurate)"
1456  << " standard sum of its terms, is too high for "
1457  << PrettyPrintConstraint(constraint);
1458  }
1459  }
1460  }
1461  }
1462 
1463  // Verify that the objective value wasn't reported incorrectly.
1464  const MPObjective& objective = Objective();
1465  AccurateSum<double> objective_sum;
1466  objective_sum.Add(objective.offset());
1467  double inaccurate_objective_value = objective.offset();
1468  for (const auto& entry : objective.coefficients_) {
1469  const double term = entry.first->solution_value() * entry.second;
1470  objective_sum.Add(term);
1471  inaccurate_objective_value += term;
1472  }
1473  const double actual_objective_value = objective_sum.Value();
1475  objective.Value(), actual_objective_value, tolerance, tolerance)) {
1476  ++num_errors;
1477  max_observed_error = std::max(
1478  max_observed_error, fabs(actual_objective_value - objective.Value()));
1479  LOG_IF(ERROR, log_errors)
1480  << "Objective value " << objective.Value() << " isn't accurate"
1481  << ", it should be " << actual_objective_value
1482  << " (delta=" << actual_objective_value - objective.Value() << ").";
1483  } else if (!AreWithinAbsoluteOrRelativeTolerances(objective.Value(),
1484  inaccurate_objective_value,
1485  tolerance, tolerance)) {
1486  LOG_IF(WARNING, log_errors)
1487  << "Objective value " << objective.Value() << " doesn't correspond"
1488  << " to the value computed with the standard (and therefore inaccurate)"
1489  << " sum of its terms.";
1490  }
1491  if (num_errors > 0) {
1492  LOG_IF(ERROR, log_errors)
1493  << "There were " << num_errors << " errors above the tolerance ("
1494  << tolerance << "), the largest was " << max_observed_error;
1495  return false;
1496  }
1497  return true;
1498 }
1499 
1500 bool MPSolver::OutputIsEnabled() const { return !interface_->quiet(); }
1501 
1502 void MPSolver::EnableOutput() { interface_->set_quiet(false); }
1503 
1504 void MPSolver::SuppressOutput() { interface_->set_quiet(true); }
1505 
1506 int64 MPSolver::iterations() const { return interface_->iterations(); }
1507 
1508 int64 MPSolver::nodes() const { return interface_->nodes(); }
1509 
1511  return interface_->ComputeExactConditionNumber();
1512 }
1513 
1515  if (var == nullptr) return false;
1516  if (var->index() >= 0 && var->index() < variables_.size()) {
1517  // Then, verify that the variable with this index has the same address.
1518  return variables_[var->index()] == var;
1519  }
1520  return false;
1521 }
1522 
1524  std::string* model_str) const {
1525  MPModelProto proto;
1527  MPModelExportOptions options;
1528  options.obfuscate = obfuscate;
1529  const auto status_or =
1531  *model_str = status_or.value_or("");
1532  return status_or.ok();
1533 }
1534 
1535 bool MPSolver::ExportModelAsMpsFormat(bool fixed_format, bool obfuscate,
1536  std::string* model_str) const {
1537  // if (fixed_format) {
1538  // LOG_EVERY_N_SEC(WARNING, 10)
1539  // << "Fixed format is deprecated. Using free format instead.";
1540  //
1541 
1542  MPModelProto proto;
1544  MPModelExportOptions options;
1545  options.obfuscate = obfuscate;
1546  const auto status_or =
1548  *model_str = status_or.value_or("");
1549  return status_or.ok();
1550 }
1551 
1552 void MPSolver::SetHint(std::vector<std::pair<const MPVariable*, double>> hint) {
1553  for (const auto& var_value_pair : hint) {
1554  CHECK(OwnsVariable(var_value_pair.first))
1555  << "hint variable does not belong to this solver";
1556  }
1557  solution_hint_ = std::move(hint);
1558 }
1559 
1560 void MPSolver::GenerateVariableNameIndex() const {
1561  if (variable_name_to_index_) return;
1562  variable_name_to_index_ = absl::flat_hash_map<std::string, int>();
1563  for (const MPVariable* const var : variables_) {
1564  gtl::InsertOrDie(&*variable_name_to_index_, var->name(), var->index());
1565  }
1566 }
1567 
1568 void MPSolver::GenerateConstraintNameIndex() const {
1569  if (constraint_name_to_index_) return;
1570  constraint_name_to_index_ = absl::flat_hash_map<std::string, int>();
1571  for (const MPConstraint* const cst : constraints_) {
1572  gtl::InsertOrDie(&*constraint_name_to_index_, cst->name(), cst->index());
1573  }
1574 }
1575 
1576 bool MPSolver::NextSolution() { return interface_->NextSolution(); }
1577 
1578 void MPSolver::SetCallback(MPCallback* mp_callback) {
1579  interface_->SetCallback(mp_callback);
1580 }
1581 
1583  return interface_->SupportsCallbacks();
1584 }
1585 
1587  switch (status) {
1588  // Cases that don't yield an RPC error when they happen on the server.
1589  case MPSOLVER_OPTIMAL:
1590  case MPSOLVER_FEASIBLE:
1591  case MPSOLVER_INFEASIBLE:
1592  case MPSOLVER_NOT_SOLVED:
1593  case MPSOLVER_UNBOUNDED:
1594  case MPSOLVER_ABNORMAL:
1596  return false;
1597  // Cases that should never happen with the linear solver server. We prefer
1598  // to consider those as "not RPC errors".
1600  return false;
1601  // Cases that yield an RPC error when they happen on the server.
1606  return true;
1607  }
1608  LOG(DFATAL)
1609  << "MPSolverResponseStatusIsRpcError() called with invalid status "
1610  << "(value: " << status << ")";
1611  return false;
1612 }
1613 
1614 // ---------- MPSolverInterface ----------
1615 
1617 
1618 // TODO(user): Initialize objective value and bound to +/- inf (depending on
1619 // optimization direction).
1621  : solver_(solver),
1622  sync_status_(MODEL_SYNCHRONIZED),
1623  result_status_(MPSolver::NOT_SOLVED),
1624  maximize_(false),
1625  last_constraint_index_(0),
1626  last_variable_index_(0),
1627  objective_value_(0.0),
1628  best_objective_bound_(0.0),
1629  quiet_(true) {}
1630 
1632 
1633 void MPSolverInterface::Write(const std::string& filename) {
1634  LOG(WARNING) << "Writing model not implemented in this solver interface.";
1635 }
1636 
1638  switch (sync_status_) {
1639  case MUST_RELOAD: {
1642  ExtractObjective();
1643 
1644  last_constraint_index_ = solver_->constraints_.size();
1645  last_variable_index_ = solver_->variables_.size();
1647  break;
1648  }
1649  case MODEL_SYNCHRONIZED: {
1650  // Everything has already been extracted.
1651  DCHECK_EQ(last_constraint_index_, solver_->constraints_.size());
1652  DCHECK_EQ(last_variable_index_, solver_->variables_.size());
1653  break;
1654  }
1655  case SOLUTION_SYNCHRONIZED: {
1656  // Nothing has changed since last solve.
1657  DCHECK_EQ(last_constraint_index_, solver_->constraints_.size());
1658  DCHECK_EQ(last_variable_index_, solver_->variables_.size());
1659  break;
1660  }
1661  }
1662 }
1663 
1664 // TODO(user): remove this method.
1669  solver_->variable_is_extracted_.assign(solver_->variables_.size(), false);
1670  solver_->constraint_is_extracted_.assign(solver_->constraints_.size(), false);
1671 }
1672 
1675  LOG(DFATAL)
1676  << "The model has been changed since the solution was last computed."
1677  << " MPSolverInterface::sync_status_ = " << sync_status_;
1678  return false;
1679  }
1680  return true;
1681 }
1682 
1683 // Default version that can be overwritten by a solver-specific
1684 // version to accommodate for the quirks of each solver.
1688  LOG(DFATAL) << "No solution exists. MPSolverInterface::result_status_ = "
1689  << result_status_;
1690  return false;
1691  }
1692  return true;
1693 }
1694 
1696  if (!CheckSolutionIsSynchronizedAndExists()) return 0;
1697  return objective_value_;
1698 }
1699 
1701  const double trivial_worst_bound =
1702  maximize_ ? -std::numeric_limits<double>::infinity()
1703  : std::numeric_limits<double>::infinity();
1704  if (!IsMIP()) {
1705  LOG(DFATAL) << "Best objective bound only available for discrete problems.";
1706  return trivial_worst_bound;
1707  }
1708  if (!CheckSolutionIsSynchronized()) {
1709  return trivial_worst_bound;
1710  }
1711  // Special case for empty model.
1712  if (solver_->variables_.empty() && solver_->constraints_.empty()) {
1713  return solver_->Objective().offset();
1714  }
1715  return best_objective_bound_;
1716 }
1717 
1721  }
1722 }
1723 
1725  // Override this method in interfaces that actually support it.
1726  LOG(DFATAL) << "ComputeExactConditionNumber not implemented for "
1727  << ProtoEnumToString<MPModelRequest::SolverType>(
1728  static_cast<MPModelRequest::SolverType>(
1729  solver_->ProblemType()));
1730  return 0.0;
1731 }
1732 
1734  // TODO(user): Overhaul the code that sets parameters to enable changing
1735  // GLOP parameters without issuing warnings.
1736  // By default, we let GLOP keep its own default tolerance, much more accurate
1737  // than for the rest of the solvers.
1738  //
1743  }
1745  // TODO(user): In the future, we could distinguish between the
1746  // algorithm to solve the root LP and the algorithm to solve node
1747  // LPs. Not sure if underlying solvers support it.
1751  }
1752 }
1753 
1758  }
1759 }
1760 
1763  LOG(WARNING) << "Trying to set an unsupported parameter: " << param << ".";
1764 }
1767  LOG(WARNING) << "Trying to set an unsupported parameter: " << param << ".";
1768 }
1770  MPSolverParameters::DoubleParam param, double value) {
1771  LOG(WARNING) << "Trying to set a supported parameter: " << param
1772  << " to an unsupported value: " << value;
1773 }
1776  LOG(WARNING) << "Trying to set a supported parameter: " << param
1777  << " to an unsupported value: " << value;
1778 }
1779 
1780 absl::Status MPSolverInterface::SetNumThreads(int num_threads) {
1781  return absl::UnimplementedError(
1782  absl::StrFormat("SetNumThreads() not supported by %s.", SolverVersion()));
1783 }
1784 
1786  const std::string& parameters) {
1787  if (parameters.empty()) {
1788  return true;
1789  }
1790 
1791  LOG(WARNING) << "SetSolverSpecificParametersAsString() not supported by "
1792  << SolverVersion();
1793  return false;
1794 }
1795 
1796 // ---------- MPSolverParameters ----------
1797 
1798 const double MPSolverParameters::kDefaultRelativeMipGap = 1e-4;
1799 // For the primal and dual tolerances, choose the same default as CLP and GLPK.
1802 const double MPSolverParameters::kDefaultDualTolerance = 1e-7;
1808 
1813 
1814 // The constructor sets all parameters to their default value.
1816  : relative_mip_gap_value_(kDefaultRelativeMipGap),
1817  primal_tolerance_value_(kDefaultPrimalTolerance),
1818  dual_tolerance_value_(kDefaultDualTolerance),
1819  presolve_value_(kDefaultPresolve),
1820  scaling_value_(kDefaultIntegerParamValue),
1821  lp_algorithm_value_(kDefaultIntegerParamValue),
1822  incrementality_value_(kDefaultIncrementality),
1823  lp_algorithm_is_default_(true) {}
1824 
1826  double value) {
1827  switch (param) {
1828  case RELATIVE_MIP_GAP: {
1829  relative_mip_gap_value_ = value;
1830  break;
1831  }
1832  case PRIMAL_TOLERANCE: {
1833  primal_tolerance_value_ = value;
1834  break;
1835  }
1836  case DUAL_TOLERANCE: {
1837  dual_tolerance_value_ = value;
1838  break;
1839  }
1840  default: {
1841  LOG(ERROR) << "Trying to set an unknown parameter: " << param << ".";
1842  }
1843  }
1844 }
1845 
1847  int value) {
1848  switch (param) {
1849  case PRESOLVE: {
1850  if (value != PRESOLVE_OFF && value != PRESOLVE_ON) {
1851  LOG(ERROR) << "Trying to set a supported parameter: " << param
1852  << " to an unknown value: " << value;
1853  }
1854  presolve_value_ = value;
1855  break;
1856  }
1857  case SCALING: {
1858  if (value != SCALING_OFF && value != SCALING_ON) {
1859  LOG(ERROR) << "Trying to set a supported parameter: " << param
1860  << " to an unknown value: " << value;
1861  }
1862  scaling_value_ = value;
1863  break;
1864  }
1865  case LP_ALGORITHM: {
1866  if (value != DUAL && value != PRIMAL && value != BARRIER) {
1867  LOG(ERROR) << "Trying to set a supported parameter: " << param
1868  << " to an unknown value: " << value;
1869  }
1870  lp_algorithm_value_ = value;
1871  lp_algorithm_is_default_ = false;
1872  break;
1873  }
1874  case INCREMENTALITY: {
1876  LOG(ERROR) << "Trying to set a supported parameter: " << param
1877  << " to an unknown value: " << value;
1878  }
1879  incrementality_value_ = value;
1880  break;
1881  }
1882  default: {
1883  LOG(ERROR) << "Trying to set an unknown parameter: " << param << ".";
1884  }
1885  }
1886 }
1887 
1890  switch (param) {
1891  case RELATIVE_MIP_GAP: {
1892  relative_mip_gap_value_ = kDefaultRelativeMipGap;
1893  break;
1894  }
1895  case PRIMAL_TOLERANCE: {
1896  primal_tolerance_value_ = kDefaultPrimalTolerance;
1897  break;
1898  }
1899  case DUAL_TOLERANCE: {
1900  dual_tolerance_value_ = kDefaultDualTolerance;
1901  break;
1902  }
1903  default: {
1904  LOG(ERROR) << "Trying to reset an unknown parameter: " << param << ".";
1905  }
1906  }
1907 }
1908 
1911  switch (param) {
1912  case PRESOLVE: {
1913  presolve_value_ = kDefaultPresolve;
1914  break;
1915  }
1916  case SCALING: {
1917  scaling_value_ = kDefaultIntegerParamValue;
1918  break;
1919  }
1920  case LP_ALGORITHM: {
1921  lp_algorithm_is_default_ = true;
1922  break;
1923  }
1924  case INCREMENTALITY: {
1925  incrementality_value_ = kDefaultIncrementality;
1926  break;
1927  }
1928  default: {
1929  LOG(ERROR) << "Trying to reset an unknown parameter: " << param << ".";
1930  }
1931  }
1932 }
1933 
1942 }
1943 
1945  MPSolverParameters::DoubleParam param) const {
1946  switch (param) {
1947  case RELATIVE_MIP_GAP: {
1948  return relative_mip_gap_value_;
1949  }
1950  case PRIMAL_TOLERANCE: {
1951  return primal_tolerance_value_;
1952  }
1953  case DUAL_TOLERANCE: {
1954  return dual_tolerance_value_;
1955  }
1956  default: {
1957  LOG(ERROR) << "Trying to get an unknown parameter: " << param << ".";
1958  return kUnknownDoubleParamValue;
1959  }
1960  }
1961 }
1962 
1964  MPSolverParameters::IntegerParam param) const {
1965  switch (param) {
1966  case PRESOLVE: {
1967  return presolve_value_;
1968  }
1969  case LP_ALGORITHM: {
1970  if (lp_algorithm_is_default_) return kDefaultIntegerParamValue;
1971  return lp_algorithm_value_;
1972  }
1973  case INCREMENTALITY: {
1974  return incrementality_value_;
1975  }
1976  case SCALING: {
1977  return scaling_value_;
1978  }
1979  default: {
1980  LOG(ERROR) << "Trying to get an unknown parameter: " << param << ".";
1982  }
1983  }
1984 }
1985 
1986 } // namespace operations_research
int64 max
Definition: alldiff_cst.cc:139
#define DLOG_IF(severity, condition)
Definition: base/logging.h:877
#define LOG_IF(severity, condition)
Definition: base/logging.h:479
#define CHECK(condition)
Definition: base/logging.h:495
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:887
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:886
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:889
#define LOG(severity)
Definition: base/logging.h:420
#define DCHECK(condition)
Definition: base/logging.h:884
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:885
#define VLOG(verboselevel)
Definition: base/logging.h:978
void Add(const FpNumber &value)
Definition: accurate_sum.h:29
LinearExpr models a quantity that is linear in the decision variables (MPVariable) of an optimization...
Definition: linear_expr.h:114
const absl::flat_hash_map< const MPVariable *, double > & terms() const
Definition: linear_expr.h:143
An expression of the form:
Definition: linear_expr.h:192
const LinearExpr & linear_expr() const
Definition: linear_expr.h:206
The class for constraints of a Mathematical Programming (MP) model.
void SetBounds(double lb, double ub)
Sets both the lower and upper bounds.
void SetCoefficient(const MPVariable *const var, double coeff)
Sets the coefficient of the variable on the constraint.
double GetCoefficient(const MPVariable *const var) const
Gets the coefficient of a given variable on the constraint (which is 0 if the variable does not appea...
double ub() const
Returns the upper bound.
const MPVariable * indicator_variable() const
void Clear()
Clears all variables and coefficients. Does not clear the bounds.
bool is_lazy() const
Advanced usage: returns true if the constraint is "lazy" (see below).
double lb() const
Returns the lower bound.
const std::string & name() const
Returns the name of the constraint.
MPSolver::BasisStatus basis_status() const
Advanced usage: returns the basis status of the constraint.
double dual_value() const
Advanced usage: returns the dual value of the constraint in the current solution (only available for ...
A class to express a linear objective.
void SetCoefficient(const MPVariable *const var, double coeff)
Sets the coefficient of the variable in the objective.
double GetCoefficient(const MPVariable *const var) const
Gets the coefficient of a given variable in the objective.
void SetOffset(double value)
Sets the constant term in the objective.
bool maximization() const
Is the optimization direction set to maximize?
void OptimizeLinearExpr(const LinearExpr &linear_expr, bool is_maximization)
Resets the current objective to take the value of linear_expr, and sets the objective direction to ma...
void AddLinearExpr(const LinearExpr &linear_expr)
Adds linear_expr to the current objective, does not change the direction.
double Value() const
Returns the objective value of the best solution found so far.
double offset() const
Gets the constant term in the objective.
double BestBound() const
Returns the best objective bound.
bool minimization() const
Is the optimization direction set to minimize?
void Clear()
Clears the offset, all variables and coefficients, and the optimization direction.
void SetMinimization()
Sets the optimization direction to minimize.
void SetOptimizationDirection(bool maximize)
Sets the optimization direction (maximize: true or minimize: false).
This mathematical programming (MP) solver class is the main class though which users build and solve ...
void FillSolutionResponseProto(MPSolutionResponse *response) const
Encodes the current solution in a solution response protocol buffer.
int NumConstraints() const
Returns the number of constraints.
static OptimizationProblemType ParseSolverTypeOrDie(const std::string &solver_id)
Parses the name of the solver and returns the correct optimization type or dies.
const std::string & Name() const
Returns the name of the model set at construction.
void MakeBoolVarArray(int nb, const std::string &name, std::vector< MPVariable * > *vars)
Creates an array of boolean variables.
MPObjective * MutableObjective()
Returns the mutable objective object.
bool VerifySolution(double tolerance, bool log_errors) const
Advanced usage: Verifies the correctness of the solution.
void Reset()
Advanced usage: resets extracted model to solve from scratch.
MPVariable * LookupVariableOrNull(const std::string &var_name) const
Looks up a variable by name, and returns nullptr if it does not exist.
void SetStartingLpBasis(const std::vector< MPSolver::BasisStatus > &variable_statuses, const std::vector< MPSolver::BasisStatus > &constraint_statuses)
Advanced usage: Incrementality.
static bool SupportsProblemType(OptimizationProblemType problem_type)
Whether the given problem type is supported (this will depend on the targets that you linked).
static MPSolver * CreateSolver(const std::string &solver_id)
Recommended factory method to create a MPSolver instance, especially in non C++ languages.
MPVariable * MakeBoolVar(const std::string &name)
Creates a boolean variable.
void SetHint(std::vector< std::pair< const MPVariable *, double > > hint)
Sets a hint for solution.
double ComputeExactConditionNumber() const
Advanced usage: computes the exact condition number of the current scaled basis: L1norm(B) * L1norm(i...
const MPObjective & Objective() const
Returns the objective object.
int64 nodes() const
Returns the number of branch-and-bound nodes evaluated during the solve.
ResultStatus
The status of solving the problem.
@ FEASIBLE
feasible, or stopped by limit.
@ NOT_SOLVED
not been solved yet.
@ INFEASIBLE
proven infeasible.
@ UNBOUNDED
proven unbounded.
@ ABNORMAL
abnormal, i.e., error of some kind.
@ MODEL_INVALID
the model is trivially invalid (NaN coefficients, etc).
static void SolveWithProto(const MPModelRequest &model_request, MPSolutionResponse *response)
Solves the model encoded by a MPModelRequest protocol buffer and fills the solution encoded as a MPSo...
void MakeNumVarArray(int nb, double lb, double ub, const std::string &name, std::vector< MPVariable * > *vars)
Creates an array of continuous variables.
void MakeVarArray(int nb, double lb, double ub, bool integer, const std::string &name_prefix, std::vector< MPVariable * > *vars)
Creates an array of variables.
void * underlying_solver()
Advanced usage: returns the underlying solver.
OptimizationProblemType
The type of problems (LP or MIP) that will be solved and the underlying solver (GLOP,...
bool SetSolverSpecificParametersAsString(const std::string &parameters)
Advanced usage: pass solver specific parameters in text format.
absl::Status SetNumThreads(int num_threads)
Sets the number of threads to use by the underlying solver.
std::string SolverVersion() const
Returns a string describing the underlying solver and its version.
void ExportModelToProto(MPModelProto *output_model) const
Exports model to protocol buffer.
int64 iterations() const
Returns the number of simplex iterations.
void MakeIntVarArray(int nb, double lb, double ub, const std::string &name, std::vector< MPVariable * > *vars)
Creates an array of integer variables.
std::vector< double > ComputeConstraintActivities() const
Advanced usage: compute the "activities" of all constraints, which are the sums of their linear terms...
static double infinity()
Infinity.
static bool ParseSolverType(absl::string_view solver_id, OptimizationProblemType *type)
Parses the name of the solver.
int NumVariables() const
Returns the number of variables.
absl::Status ClampSolutionWithinBounds()
Resets values of out of bound variables to the corresponding bound and returns an error if any of the...
bool OwnsVariable(const MPVariable *var) const
void Clear()
Clears the objective (including the optimization direction), all variables and constraints.
bool ExportModelAsLpFormat(bool obfuscate, std::string *model_str) const
Shortcuts to the homonymous MPModelProtoExporter methods, via exporting to a MPModelProto with Export...
void Write(const std::string &file_name)
Writes the model using the solver internal write function.
MPConstraint * MakeRowConstraint()
Creates a constraint with -infinity and +infinity bounds.
void SetCallback(MPCallback *mp_callback)
MPSolverResponseStatus LoadModelFromProto(const MPModelProto &input_model, std::string *error_message)
Loads model from protocol buffer.
bool OutputIsEnabled() const
Controls (or queries) the amount of output produced by the underlying solver.
bool ExportModelAsMpsFormat(bool fixed_format, bool obfuscate, std::string *model_str) const
ABSL_MUST_USE_RESULT bool NextSolution()
Some solvers (MIP only, not LP) can produce multiple solutions to the problem.
MPVariable * MakeVar(double lb, double ub, bool integer, const std::string &name)
Creates a variable with the given bounds, integrality requirement and name.
MPConstraint * LookupConstraintOrNull(const std::string &constraint_name) const
Looks up a constraint by name, and returns nullptr if it does not exist.
MPVariable * MakeNumVar(double lb, double ub, const std::string &name)
Creates a continuous variable.
bool InterruptSolve()
Interrupts the Solve() execution to terminate processing if possible.
MPVariable * MakeIntVar(double lb, double ub, const std::string &name)
Creates an integer variable.
MPSolver(const std::string &name, OptimizationProblemType problem_type)
Create a solver with the given name and underlying solver backend.
ResultStatus Solve()
Solves the problem using the default parameter values.
void EnableOutput()
Enables solver logging.
void SuppressOutput()
Suppresses solver logging.
absl::Status LoadSolutionFromProto(const MPSolutionResponse &response, double tolerance=kDefaultPrimalTolerance)
Load a solution encoded in a protocol buffer onto this solver for easy access via the MPSolver interf...
MPSolverResponseStatus LoadModelFromProtoWithUniqueNamesOrDie(const MPModelProto &input_model, std::string *error_message)
Loads model from protocol buffer.
virtual OptimizationProblemType ProblemType() const
Returns the optimization problem type set at construction.
BasisStatus
Advanced usage: possible basis status values for a variable and the slack variable of a linear constr...
void SetTimeLimit(absl::Duration time_limit)
virtual void SetLpAlgorithm(int value)=0
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
void SetUnsupportedDoubleParam(MPSolverParameters::DoubleParam param)
void SetMIPParameters(const MPSolverParameters &param)
virtual bool IsContinuous() const =0
virtual double ComputeExactConditionNumber() const
virtual void Write(const std::string &filename)
MPSolverInterface(MPSolver *const solver)
bool constraint_is_extracted(int ct_index) const
virtual void SetVariableBounds(int index, double lb, double ub)=0
virtual void SetPrimalTolerance(double value)=0
virtual void BranchingPriorityChangedForVariable(int var_index)
virtual void SetRelativeMipGap(double value)=0
virtual void SetOptimizationDirection(bool maximize)=0
virtual bool SetSolverSpecificParametersAsString(const std::string &parameters)
virtual MPSolver::BasisStatus column_status(int variable_index) const =0
virtual MPSolver::BasisStatus row_status(int constraint_index) const =0
virtual std::string SolverVersion() const =0
virtual absl::Status SetNumThreads(int num_threads)
virtual void ClearConstraint(MPConstraint *const constraint)=0
virtual void SetObjectiveOffset(double value)=0
virtual void SetVariableInteger(int index, bool integer)=0
bool variable_is_extracted(int var_index) const
virtual void SetDualTolerance(double value)=0
virtual void SetPresolveMode(int value)=0
virtual void SetUnsupportedIntegerParam(MPSolverParameters::IntegerParam param)
virtual void SetCoefficient(MPConstraint *const constraint, const MPVariable *const variable, double new_value, double old_value)=0
virtual void SetObjectiveCoefficient(const MPVariable *const variable, double coefficient)=0
void SetDoubleParamToUnsupportedValue(MPSolverParameters::DoubleParam param, double value)
virtual void SetConstraintBounds(int index, double lb, double ub)=0
void SetCommonParameters(const MPSolverParameters &param)
This class stores parameter settings for LP and MIP solvers.
void ResetIntegerParam(MPSolverParameters::IntegerParam param)
Sets an integer parameter to its default value (default value defined in MPSolverParameters if it exi...
void SetDoubleParam(MPSolverParameters::DoubleParam param, double value)
Sets a double parameter to a specific value.
IncrementalityValues
Advanced usage: Incrementality options.
@ INCREMENTALITY_OFF
Start solve from scratch.
@ INCREMENTALITY_ON
Reuse results from previous solve as much as the underlying solver allows.
static const IncrementalityValues kDefaultIncrementality
void Reset()
Sets all parameters to their default value.
DoubleParam
Enumeration of parameters that take continuous values.
@ DUAL_TOLERANCE
Advanced usage: tolerance for dual feasibility of basic solutions.
@ PRIMAL_TOLERANCE
Advanced usage: tolerance for primal feasibility of basic solutions.
@ RELATIVE_MIP_GAP
Limit for relative MIP gap.
static const PresolveValues kDefaultPresolve
double GetDoubleParam(MPSolverParameters::DoubleParam param) const
Returns the value of a double parameter.
IntegerParam
Enumeration of parameters that take integer or categorical values.
@ LP_ALGORITHM
Algorithm to solve linear programs.
@ SCALING
Advanced usage: enable or disable matrix scaling.
@ PRESOLVE
Advanced usage: presolve mode.
@ INCREMENTALITY
Advanced usage: incrementality from one solve to the next.
PresolveValues
For each categorical parameter, enumeration of possible values.
void SetIntegerParam(MPSolverParameters::IntegerParam param, int value)
Sets a integer parameter to a specific value.
int GetIntegerParam(MPSolverParameters::IntegerParam param) const
Returns the value of an integer parameter.
MPSolverParameters()
The constructor sets all parameters to their default value.
void ResetDoubleParam(MPSolverParameters::DoubleParam param)
Sets a double parameter to its default value (default value defined in MPSolverParameters if it exist...
The class for variables of a Mathematical Programming (MP) model.
void SetBounds(double lb, double ub)
Sets both the lower and upper bounds.
double unrounded_solution_value() const
Advanced usage: unrounded solution value.
void SetBranchingPriority(int priority)
double ub() const
Returns the upper bound.
double reduced_cost() const
Advanced usage: returns the reduced cost of the variable in the current solution (only available for ...
void SetInteger(bool integer)
Sets the integrality requirement of the variable.
bool integer() const
Returns the integrality requirement of the variable.
int index() const
Returns the index of the variable in the MPSolver::variables_.
double lb() const
Returns the lower bound.
const std::string & name() const
Returns the name of the variable.
double solution_value() const
Returns the value of the variable in the current solution.
MPSolver::BasisStatus basis_status() const
Advanced usage: returns the basis status of the variable in the current solution (only available for ...
virtual std::string name() const
Object naming.
SatParameters parameters
CpModelProto proto
SharedResponseManager * response
const Constraint * ct
int64 value
IntVar * var
Definition: expr_array.cc:1858
int64_t int64
absl::string_view name
ABSL_FLAG(bool, verify_solution, false, "Systematically verify the solution when calling Solve()" ", and change the return value of Solve() to ABNORMAL if" " an error was detected.")
MPSolver::OptimizationProblemType problem_type
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
const int WARNING
Definition: log_severity.h:31
const int INFO
Definition: log_severity.h:31
const int ERROR
Definition: log_severity.h:32
const int FATAL
Definition: log_severity.h:32
Definition: cleanup.h:22
void STLDeleteElements(T *container)
Definition: stl_util.h:372
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
Definition: map_util.h:135
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:26
std::function< int64(const Model &)> Value(IntegerVariable v)
Definition: integer.h:1487
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
MPSolverInterface * BuildGurobiInterface(bool mip, MPSolver *const solver)
MPSolverInterface * BuildSCIPInterface(MPSolver *const solver)
MPSolverInterface * BuildBopInterface(MPSolver *const solver)
constexpr double kDefaultPrimalTolerance
const absl::string_view ToString(MPSolver::OptimizationProblemType optimization_problem_type)
bool SolverTypeIsMip(MPModelRequest::SolverType solver_type)
MPSolverInterface * BuildCBCInterface(MPSolver *const solver)
bool AreWithinAbsoluteOrRelativeTolerances(FloatType x, FloatType y, FloatType relative_tolerance, FloatType absolute_tolerance)
Definition: fp_utils.h:120
absl::StatusOr< std::string > ExportModelAsMpsFormat(const MPModelProto &model, const MPModelExportOptions &options)
Outputs the current model (variables, constraints, objective) as a string encoded in MPS file format,...
bool AbslParseFlag(const absl::string_view text, MPSolver::OptimizationProblemType *solver_type, std::string *error)
absl::optional< LazyMutableCopy< MPModelProto > > ExtractValidMPModelOrPopulateResponseStatus(const MPModelRequest &request, MPSolutionResponse *response)
If the model is valid and non-empty, returns it (possibly after extracting the model_delta).
MPSolverInterface * BuildSatInterface(MPSolver *const solver)
MPSolverInterface * BuildCLPInterface(MPSolver *const solver)
constexpr NamedOptimizationProblemType kOptimizationProblemTypeNames[]
MPSolverInterface * BuildGLOPInterface(MPSolver *const solver)
std::string FindErrorInMPModelProto(const MPModelProto &model, double abs_value_threshold)
Returns an empty string iff the model is valid and not trivially infeasible.
absl::StatusOr< std::string > ExportModelAsLpFormat(const MPModelProto &model, const MPModelExportOptions &options)
Outputs the current model (variables, constraints, objective) as a string encoded in the so-called "C...
bool MPSolverResponseStatusIsRpcError(MPSolverResponseStatus status)
const bool maximize_
Definition: search.cc:2499
bool obfuscate
Obfuscates variable and constraint names.