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