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