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