OR-Tools  9.1
gurobi_interface.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 // Gurobi backend to MPSolver.
15 //
16 // Implementation Notes:
17 //
18 // Incrementalism (last updated June 29, 2020): For solving both LPs and MIPs,
19 // Gurobi attempts to reuse information from previous solves, potentially
20 // giving a faster solve time. MPSolver supports this for the following problem
21 // modification types:
22 // * Adding a variable,
23 // * Adding a linear constraint,
24 // * Updating a variable bound,
25 // * Updating an objective coefficient or the objective offset (note that in
26 // Gurobi 7.5 LP solver, there is a bug if you update only the objective
27 // offset and nothing else).
28 // * Updating a coefficient in the constraint matrix.
29 // * Updating the type of variable (integer, continuous)
30 // * Changing the optimization direction.
31 // Updates of the following types will force a resolve from scratch:
32 // * Updating the upper or lower bounds of a linear constraint. Note that in
33 // MPSolver's model, this includes updating the sense (le, ge, eq, range) of
34 // a linear constraint.
35 // * Clearing a constraint
36 // Any model containing indicator constraints is considered "non-incremental"
37 // and will always solve from scratch.
38 //
39 // The above limitations are largely due MPSolver and this file, not Gurobi.
40 //
41 // Warning(rander): the interactions between callbacks and incrementalism are
42 // poorly tested, proceed with caution.
43 //
44 
45 #include <cmath>
46 #include <cstddef>
47 #include <cstdint>
48 #include <limits>
49 #include <memory>
50 #include <stdexcept>
51 #include <string>
52 #include <utility>
53 #include <vector>
54 
55 #include "absl/status/status.h"
56 #include "absl/strings/match.h"
57 #include "absl/strings/str_format.h"
60 #include "ortools/base/logging.h"
61 #include "ortools/base/map_util.h"
62 #include "ortools/base/timer.h"
67 
68 ABSL_FLAG(int, num_gurobi_threads, 4,
69  "Number of threads available for Gurobi.");
70 
71 namespace operations_research {
72 
74  public:
75  // Constructor that takes a name for the underlying GRB solver.
76  explicit GurobiInterface(MPSolver* const solver, bool mip);
77  ~GurobiInterface() override;
78 
79  // Sets the optimization direction (min/max).
80  void SetOptimizationDirection(bool maximize) override;
81 
82  // ----- Solve -----
83  // Solves the problem using the parameter values specified.
84  MPSolver::ResultStatus Solve(const MPSolverParameters& param) override;
85  absl::optional<MPSolutionResponse> DirectlySolveProto(
86  const MPModelRequest& request) override;
87 
88  // Writes the model.
89  void Write(const std::string& filename) override;
90 
91  // ----- Model modifications and extraction -----
92  // Resets extracted model
93  void Reset() override;
94 
95  // Modifies bounds.
96  void SetVariableBounds(int var_index, double lb, double ub) override;
97  void SetVariableInteger(int var_index, bool integer) override;
98  void SetConstraintBounds(int row_index, double lb, double ub) override;
99 
100  // Adds Constraint incrementally.
101  void AddRowConstraint(MPConstraint* const ct) override;
102  bool AddIndicatorConstraint(MPConstraint* const ct) override;
103  // Adds variable incrementally.
104  void AddVariable(MPVariable* const var) override;
105  // Changes a coefficient in a constraint.
106  void SetCoefficient(MPConstraint* const constraint,
107  const MPVariable* const variable, double new_value,
108  double old_value) override;
109  // Clears a constraint from all its terms.
110  void ClearConstraint(MPConstraint* const constraint) override;
111  // Changes a coefficient in the linear objective
112  void SetObjectiveCoefficient(const MPVariable* const variable,
113  double coefficient) override;
114  // Changes the constant term in the linear objective.
115  void SetObjectiveOffset(double value) override;
116  // Clears the objective from all its terms.
117  void ClearObjective() override;
118  void BranchingPriorityChangedForVariable(int var_index) override;
119 
120  // ------ Query statistics on the solution and the solve ------
121  // Number of simplex or interior-point iterations
122  int64_t iterations() const override;
123  // Number of branch-and-bound nodes. Only available for discrete problems.
124  int64_t nodes() const override;
125 
126  // Returns the basis status of a row.
127  MPSolver::BasisStatus row_status(int constraint_index) const override;
128  // Returns the basis status of a column.
129  MPSolver::BasisStatus column_status(int variable_index) const override;
130 
131  // ----- Misc -----
132  // Queries problem type.
133  bool IsContinuous() const override { return IsLP(); }
134  bool IsLP() const override { return !mip_; }
135  bool IsMIP() const override { return mip_; }
136 
137  void ExtractNewVariables() override;
138  void ExtractNewConstraints() override;
139  void ExtractObjective() override;
140 
141  std::string SolverVersion() const override {
142  int major, minor, technical;
143  GRBversion(&major, &minor, &technical);
144  return absl::StrFormat("Gurobi library version %d.%d.%d\n", major, minor,
145  technical);
146  }
147 
148  bool InterruptSolve() override {
149  const absl::MutexLock lock(&hold_interruptions_mutex_);
150  if (model_ != nullptr) GRBterminate(model_);
151  return true;
152  }
153 
154  void* underlying_solver() override { return reinterpret_cast<void*>(model_); }
155 
156  double ComputeExactConditionNumber() const override {
157  if (!IsContinuous()) {
158  LOG(DFATAL) << "ComputeExactConditionNumber not implemented for"
159  << " GUROBI_MIXED_INTEGER_PROGRAMMING";
160  return 0.0;
161  }
162 
163  // TODO(user,user): Not yet working.
164  LOG(DFATAL) << "ComputeExactConditionNumber not implemented for"
165  << " GUROBI_LINEAR_PROGRAMMING";
166  return 0.0;
167 
168  // double cond = 0.0;
169  // const int status = GRBgetdblattr(model_, GRB_DBL_ATTR_KAPPA, &cond);
170  // if (0 == status) {
171  // return cond;
172  // } else {
173  // LOG(DFATAL) << "Condition number only available for "
174  // << "continuous problems";
175  // return 0.0;
176  // }
177  }
178 
179  // Iterates through the solutions in Gurobi's solution pool.
180  bool NextSolution() override;
181 
182  void SetCallback(MPCallback* mp_callback) override;
183  bool SupportsCallbacks() const override { return true; }
184 
185  private:
186  // Sets all parameters in the underlying solver.
187  void SetParameters(const MPSolverParameters& param) override;
188  // Sets solver-specific parameters (avoiding using files). The previous
189  // implementations supported multi-line strings of the form:
190  // parameter_i value_i\n
191  // We extend support for strings of the form:
192  // parameter1=value1,....,parametern=valuen
193  // or for strings of the form:
194  // parameter1 value1, ... ,parametern valuen
195  // which are easier to set in the command line.
196  // This implementations relies on SetSolverSpecificParameters, which has the
197  // extra benefit of unifying the way we handle specific parameters for both
198  // proto-based solves and for MPModel solves.
199  bool SetSolverSpecificParametersAsString(
200  const std::string& parameters) override;
201  // Sets each parameter in the underlying solver.
202  void SetRelativeMipGap(double value) override;
203  void SetPrimalTolerance(double value) override;
204  void SetDualTolerance(double value) override;
205  void SetPresolveMode(int value) override;
206  void SetScalingMode(int value) override;
207  void SetLpAlgorithm(int value) override;
208 
209  MPSolver::BasisStatus TransformGRBVarBasisStatus(
210  int gurobi_basis_status) const;
211  MPSolver::BasisStatus TransformGRBConstraintBasisStatus(
212  int gurobi_basis_status, int constraint_index) const;
213 
214  // See the implementation note at the top of file on incrementalism.
215  bool ModelIsNonincremental() const;
216 
217  void SetIntAttr(const char* name, int value);
218  int GetIntAttr(const char* name) const;
219  void SetDoubleAttr(const char* name, double value);
220  double GetDoubleAttr(const char* name) const;
221  void SetIntAttrElement(const char* name, int index, int value);
222  int GetIntAttrElement(const char* name, int index) const;
223  void SetDoubleAttrElement(const char* name, int index, double value);
224  double GetDoubleAttrElement(const char* name, int index) const;
225  std::vector<double> GetDoubleAttrArray(const char* name, int elements);
226  void SetCharAttrElement(const char* name, int index, char value);
227  char GetCharAttrElement(const char* name, int index) const;
228 
229  void CheckedGurobiCall(int err) const;
230 
231  int SolutionCount() const;
232 
233  GRBmodel* model_;
234  GRBenv* env_;
235  bool mip_;
236  int current_solution_index_;
237  MPCallback* callback_ = nullptr;
238  bool update_branching_priorities_ = false;
239  // Has length equal to the number of MPVariables in
240  // MPSolverInterface::solver_. Values are the index of the corresponding
241  // Gurobi variable. Note that Gurobi may have additional auxiliary variables
242  // not represented by MPVariables, such as those created by two-sided range
243  // constraints.
244  std::vector<int> mp_var_to_gurobi_var_;
245  // Has length equal to the number of MPConstraints in
246  // MPSolverInterface::solver_. Values are the index of the corresponding
247  // linear (or range) constraint in Gurobi, or -1 if no such constraint exists
248  // (e.g. for indicator constraints).
249  std::vector<int> mp_cons_to_gurobi_linear_cons_;
250  // Should match the Gurobi model after it is updated.
251  int num_gurobi_vars_ = 0;
252  // Should match the Gurobi model after it is updated.
253  // NOTE(user): indicator constraints are not counted below.
254  int num_gurobi_linear_cons_ = 0;
255  // See the implementation note at the top of file on incrementalism.
256  bool had_nonincremental_change_ = false;
257 
258  // Mutex is held to prevent InterruptSolve() to call GRBterminate() when
259  // model_ is not completely built. It also prevents model_ to be changed
260  // during the execution of GRBterminate().
261  mutable absl::Mutex hold_interruptions_mutex_;
262 };
263 
264 namespace {
265 
266 void CheckedGurobiCall(int err, GRBenv* const env) {
267  CHECK_EQ(0, err) << "Fatal error with code " << err << ", due to "
268  << GRBgeterrormsg(env);
269 }
270 
271 // For interacting directly with the Gurobi C API for callbacks.
272 struct GurobiInternalCallbackContext {
275  int where;
276 };
277 
278 class GurobiMPCallbackContext : public MPCallbackContext {
279  public:
280  GurobiMPCallbackContext(GRBenv* env,
281  const std::vector<int>* mp_var_to_gurobi_var,
282  int num_gurobi_vars, bool might_add_cuts,
283  bool might_add_lazy_constraints);
284 
285  // Implementation of the interface.
286  MPCallbackEvent Event() override;
287  bool CanQueryVariableValues() override;
288  double VariableValue(const MPVariable* variable) override;
289  void AddCut(const LinearRange& cutting_plane) override;
290  void AddLazyConstraint(const LinearRange& lazy_constraint) override;
291  double SuggestSolution(
292  const absl::flat_hash_map<const MPVariable*, double>& solution) override;
293  int64_t NumExploredNodes() override;
294 
295  // Call this method to update the internal state of the callback context
296  // before passing it to MPCallback::RunCallback().
297  void UpdateFromGurobiState(
298  const GurobiInternalCallbackContext& gurobi_internal_context);
299 
300  private:
301  // Wraps GRBcbget(), used to query the state of the solver. See
302  // http://www.gurobi.com/documentation/8.0/refman/callback_codes.html#sec:CallbackCodes
303  // for callback_code values.
304  template <typename T>
305  T GurobiCallbackGet(
306  const GurobiInternalCallbackContext& gurobi_internal_context,
307  int callback_code);
308  void CheckedGurobiCall(int gurobi_error_code) const;
309 
310  template <typename GRBConstraintFunction>
311  void AddGeneratedConstraint(const LinearRange& linear_range,
312  GRBConstraintFunction grb_constraint_function);
313 
314  GRBenv* const env_;
315  const std::vector<int>* const mp_var_to_gurobi_var_;
316  const int num_gurobi_vars_;
317 
318  const bool might_add_cuts_;
319  const bool might_add_lazy_constraints_;
320 
321  // Stateful, updated before each call to the callback.
322  GurobiInternalCallbackContext current_gurobi_internal_callback_context_;
323  bool variable_values_extracted_ = false;
324  std::vector<double> gurobi_variable_values_;
325 };
326 
327 void GurobiMPCallbackContext::CheckedGurobiCall(int gurobi_error_code) const {
328  ::operations_research::CheckedGurobiCall(gurobi_error_code, env_);
329 }
330 
331 GurobiMPCallbackContext::GurobiMPCallbackContext(
332  GRBenv* env, const std::vector<int>* mp_var_to_gurobi_var,
333  int num_gurobi_vars, bool might_add_cuts, bool might_add_lazy_constraints)
334  : env_(ABSL_DIE_IF_NULL(env)),
335  mp_var_to_gurobi_var_(ABSL_DIE_IF_NULL(mp_var_to_gurobi_var)),
336  num_gurobi_vars_(num_gurobi_vars),
337  might_add_cuts_(might_add_cuts),
338  might_add_lazy_constraints_(might_add_lazy_constraints) {}
339 
340 void GurobiMPCallbackContext::UpdateFromGurobiState(
341  const GurobiInternalCallbackContext& gurobi_internal_context) {
342  current_gurobi_internal_callback_context_ = gurobi_internal_context;
343  variable_values_extracted_ = false;
344 }
345 
346 int64_t GurobiMPCallbackContext::NumExploredNodes() {
347  switch (Event()) {
348  case MPCallbackEvent::kMipNode:
349  return static_cast<int64_t>(GurobiCallbackGet<double>(
350  current_gurobi_internal_callback_context_, GRB_CB_MIPNODE_NODCNT));
351  case MPCallbackEvent::kMipSolution:
352  return static_cast<int64_t>(GurobiCallbackGet<double>(
353  current_gurobi_internal_callback_context_, GRB_CB_MIPSOL_NODCNT));
354  default:
355  LOG(FATAL) << "Node count is supported only for callback events MIP_NODE "
356  "and MIP_SOL, but was requested at: "
357  << ToString(Event());
358  }
359 }
360 
361 template <typename T>
362 T GurobiMPCallbackContext::GurobiCallbackGet(
363  const GurobiInternalCallbackContext& gurobi_internal_context,
364  const int callback_code) {
365  T result = 0;
366  CheckedGurobiCall(
367  GRBcbget(gurobi_internal_context.gurobi_internal_callback_data,
368  gurobi_internal_context.where, callback_code,
369  static_cast<void*>(&result)));
370  return result;
371 }
372 
373 MPCallbackEvent GurobiMPCallbackContext::Event() {
374  switch (current_gurobi_internal_callback_context_.where) {
375  case GRB_CB_POLLING:
376  return MPCallbackEvent::kPolling;
377  case GRB_CB_PRESOLVE:
378  return MPCallbackEvent::kPresolve;
379  case GRB_CB_SIMPLEX:
380  return MPCallbackEvent::kSimplex;
381  case GRB_CB_MIP:
382  return MPCallbackEvent::kMip;
383  case GRB_CB_MIPSOL:
384  return MPCallbackEvent::kMipSolution;
385  case GRB_CB_MIPNODE:
386  return MPCallbackEvent::kMipNode;
387  case GRB_CB_MESSAGE:
388  return MPCallbackEvent::kMessage;
389  case GRB_CB_BARRIER:
390  return MPCallbackEvent::kBarrier;
391  // TODO(b/112427356): in Gurobi 8.0, there is a new callback location.
392  // case GRB_CB_MULTIOBJ:
393  // return MPCallbackEvent::kMultiObj;
394  default:
395  LOG_FIRST_N(ERROR, 1) << "Gurobi callback at unknown where="
396  << current_gurobi_internal_callback_context_.where;
397  return MPCallbackEvent::kUnknown;
398  }
399 }
400 
401 bool GurobiMPCallbackContext::CanQueryVariableValues() {
402  const MPCallbackEvent where = Event();
403  if (where == MPCallbackEvent::kMipSolution) {
404  return true;
405  }
406  if (where == MPCallbackEvent::kMipNode) {
407  const int gurobi_node_status = GurobiCallbackGet<int>(
408  current_gurobi_internal_callback_context_, GRB_CB_MIPNODE_STATUS);
409  return gurobi_node_status == GRB_OPTIMAL;
410  }
411  return false;
412 }
413 
414 double GurobiMPCallbackContext::VariableValue(const MPVariable* variable) {
415  CHECK(variable != nullptr);
416  if (!variable_values_extracted_) {
417  const MPCallbackEvent where = Event();
418  CHECK(where == MPCallbackEvent::kMipSolution ||
419  where == MPCallbackEvent::kMipNode)
420  << "You can only call VariableValue at "
421  << ToString(MPCallbackEvent::kMipSolution) << " or "
422  << ToString(MPCallbackEvent::kMipNode)
423  << " but called from: " << ToString(where);
424  const int gurobi_get_var_param = where == MPCallbackEvent::kMipNode
427 
428  gurobi_variable_values_.resize(num_gurobi_vars_);
429  CheckedGurobiCall(GRBcbget(
430  current_gurobi_internal_callback_context_.gurobi_internal_callback_data,
431  current_gurobi_internal_callback_context_.where, gurobi_get_var_param,
432  static_cast<void*>(gurobi_variable_values_.data())));
433  variable_values_extracted_ = true;
434  }
435  return gurobi_variable_values_[mp_var_to_gurobi_var_->at(variable->index())];
436 }
437 
438 template <typename GRBConstraintFunction>
439 void GurobiMPCallbackContext::AddGeneratedConstraint(
440  const LinearRange& linear_range,
441  GRBConstraintFunction grb_constraint_function) {
442  std::vector<int> variable_indices;
443  std::vector<double> variable_coefficients;
444  const int num_terms = linear_range.linear_expr().terms().size();
445  variable_indices.reserve(num_terms);
446  variable_coefficients.reserve(num_terms);
447  for (const auto& var_coef_pair : linear_range.linear_expr().terms()) {
448  variable_indices.push_back(
449  mp_var_to_gurobi_var_->at(var_coef_pair.first->index()));
450  variable_coefficients.push_back(var_coef_pair.second);
451  }
452  if (std::isfinite(linear_range.upper_bound())) {
453  CheckedGurobiCall(grb_constraint_function(
454  current_gurobi_internal_callback_context_.gurobi_internal_callback_data,
455  variable_indices.size(), variable_indices.data(),
456  variable_coefficients.data(), GRB_LESS_EQUAL,
457  linear_range.upper_bound()));
458  }
459  if (std::isfinite(linear_range.lower_bound())) {
460  CheckedGurobiCall(grb_constraint_function(
461  current_gurobi_internal_callback_context_.gurobi_internal_callback_data,
462  variable_indices.size(), variable_indices.data(),
463  variable_coefficients.data(), GRB_GREATER_EQUAL,
464  linear_range.lower_bound()));
465  }
466 }
467 
468 void GurobiMPCallbackContext::AddCut(const LinearRange& cutting_plane) {
469  CHECK(might_add_cuts_);
470  const MPCallbackEvent where = Event();
471  CHECK(where == MPCallbackEvent::kMipNode)
472  << "Cuts can only be added at MIP_NODE, tried to add cut at: "
473  << ToString(where);
474  AddGeneratedConstraint(cutting_plane, GRBcbcut);
475 }
476 
477 void GurobiMPCallbackContext::AddLazyConstraint(
478  const LinearRange& lazy_constraint) {
479  CHECK(might_add_lazy_constraints_);
480  const MPCallbackEvent where = Event();
481  CHECK(where == MPCallbackEvent::kMipNode ||
482  where == MPCallbackEvent::kMipSolution)
483  << "Lazy constraints can only be added at MIP_NODE or MIP_SOL, tried to "
484  "add lazy constraint at: "
485  << ToString(where);
486  AddGeneratedConstraint(lazy_constraint, GRBcblazy);
487 }
488 
489 double GurobiMPCallbackContext::SuggestSolution(
490  const absl::flat_hash_map<const MPVariable*, double>& solution) {
491  const MPCallbackEvent where = Event();
492  CHECK(where == MPCallbackEvent::kMipNode)
493  << "Feasible solutions can only be added at MIP_NODE, tried to add "
494  "solution at: "
495  << ToString(where);
496 
497  std::vector<double> full_solution(num_gurobi_vars_, GRB_UNDEFINED);
498  for (const auto& variable_value : solution) {
499  const MPVariable* var = variable_value.first;
500  full_solution[mp_var_to_gurobi_var_->at(var->index())] =
501  variable_value.second;
502  }
503 
504  double objval;
505  CheckedGurobiCall(GRBcbsolution(
506  current_gurobi_internal_callback_context_.gurobi_internal_callback_data,
507  full_solution.data(), &objval));
508 
509  return objval;
510 }
511 
512 struct MPCallbackWithGurobiContext {
513  GurobiMPCallbackContext* context;
514  MPCallback* callback;
515 };
516 
517 // NOTE(user): This function must have this exact API, because we are passing
518 // it to Gurobi as a callback.
519 int GUROBI_STDCALL CallbackImpl(GRBmodel* model,
521  void* raw_model_and_callback) {
522  MPCallbackWithGurobiContext* const callback_with_context =
523  static_cast<MPCallbackWithGurobiContext*>(raw_model_and_callback);
524  CHECK(callback_with_context != nullptr);
525  CHECK(callback_with_context->context != nullptr);
526  CHECK(callback_with_context->callback != nullptr);
527  GurobiInternalCallbackContext gurobi_internal_context{
529  callback_with_context->context->UpdateFromGurobiState(
530  gurobi_internal_context);
531  callback_with_context->callback->RunCallback(callback_with_context->context);
532  return 0;
533 }
534 
535 } // namespace
536 
537 void GurobiInterface::CheckedGurobiCall(int err) const {
538  ::operations_research::CheckedGurobiCall(err, env_);
539 }
540 
541 void GurobiInterface::SetIntAttr(const char* name, int value) {
542  CheckedGurobiCall(GRBsetintattr(model_, name, value));
543 }
544 
545 int GurobiInterface::GetIntAttr(const char* name) const {
546  int value;
547  CheckedGurobiCall(GRBgetintattr(model_, name, &value));
548  return value;
549 }
550 
551 void GurobiInterface::SetDoubleAttr(const char* name, double value) {
552  CheckedGurobiCall(GRBsetdblattr(model_, name, value));
553 }
554 
555 double GurobiInterface::GetDoubleAttr(const char* name) const {
556  double value;
557  CheckedGurobiCall(GRBgetdblattr(model_, name, &value));
558  return value;
559 }
560 
561 void GurobiInterface::SetIntAttrElement(const char* name, int index,
562  int value) {
563  CheckedGurobiCall(GRBsetintattrelement(model_, name, index, value));
564 }
565 
566 int GurobiInterface::GetIntAttrElement(const char* name, int index) const {
567  int value;
568  CheckedGurobiCall(GRBgetintattrelement(model_, name, index, &value));
569  return value;
570 }
571 
572 void GurobiInterface::SetDoubleAttrElement(const char* name, int index,
573  double value) {
574  CheckedGurobiCall(GRBsetdblattrelement(model_, name, index, value));
575 }
576 double GurobiInterface::GetDoubleAttrElement(const char* name,
577  int index) const {
578  double value;
579  CheckedGurobiCall(GRBgetdblattrelement(model_, name, index, &value));
580  return value;
581 }
582 
583 std::vector<double> GurobiInterface::GetDoubleAttrArray(const char* name,
584  int elements) {
585  std::vector<double> results(elements);
586  CheckedGurobiCall(
587  GRBgetdblattrarray(model_, name, 0, elements, results.data()));
588  return results;
589 }
590 
591 void GurobiInterface::SetCharAttrElement(const char* name, int index,
592  char value) {
593  CheckedGurobiCall(GRBsetcharattrelement(model_, name, index, value));
594 }
595 char GurobiInterface::GetCharAttrElement(const char* name, int index) const {
596  char value;
597  CheckedGurobiCall(GRBgetcharattrelement(model_, name, index, &value));
598  return value;
599 }
600 
601 // Creates a LP/MIP instance with the specified name and minimization objective.
602 GurobiInterface::GurobiInterface(MPSolver* const solver, bool mip)
603  : MPSolverInterface(solver),
604  model_(nullptr),
605  env_(nullptr),
606  mip_(mip),
607  current_solution_index_(0) {
608  env_ = GetGurobiEnv().value();
609  CheckedGurobiCall(GRBnewmodel(env_, &model_, solver_->name_.c_str(),
610  0, // numvars
611  nullptr, // obj
612  nullptr, // lb
613  nullptr, // ub
614  nullptr, // vtype
615  nullptr)); // varnanes
617  CheckedGurobiCall(GRBsetintparam(env_, GRB_INT_PAR_THREADS,
618  absl::GetFlag(FLAGS_num_gurobi_threads)));
619 }
620 
622  CheckedGurobiCall(GRBfreemodel(model_));
623  GRBfreeenv(env_);
624 }
625 
626 // ------ Model modifications and extraction -----
627 
629  // We hold calls to GRBterminate() until the new model_ is ready.
630  const absl::MutexLock lock(&hold_interruptions_mutex_);
631 
632  GRBmodel* old_model = model_;
633  CheckedGurobiCall(GRBnewmodel(env_, &model_, solver_->name_.c_str(),
634  0, // numvars
635  nullptr, // obj
636  nullptr, // lb
637  nullptr, // ub
638  nullptr, // vtype
639  nullptr)); // varnames
640 
641  // Copy all existing parameters from the previous model to the new one. This
642  // ensures that if a user calls multiple times
643  // SetSolverSpecificParametersAsString() and then Reset() is called, we still
644  // take into account all parameters.
645  //
646  // The current code only reapplies the parameters stored in
647  // solver_specific_parameter_string_ at the start of the solve; other
648  // parameters set by previous calls are only kept in the Gurobi model.
649  CheckedGurobiCall(GRBcopyparams(GRBgetenv(model_), GRBgetenv(old_model)));
650 
651  CheckedGurobiCall(GRBfreemodel(old_model));
652  old_model = nullptr;
653 
655  mp_var_to_gurobi_var_.clear();
656  mp_cons_to_gurobi_linear_cons_.clear();
657  num_gurobi_vars_ = 0;
658  num_gurobi_linear_cons_ = 0;
659  had_nonincremental_change_ = false;
660 }
661 
665 }
666 
667 void GurobiInterface::SetVariableBounds(int var_index, double lb, double ub) {
669  if (!had_nonincremental_change_ && variable_is_extracted(var_index)) {
670  SetDoubleAttrElement(GRB_DBL_ATTR_LB, mp_var_to_gurobi_var_.at(var_index),
671  lb);
672  SetDoubleAttrElement(GRB_DBL_ATTR_UB, mp_var_to_gurobi_var_.at(var_index),
673  ub);
674  } else {
676  }
677 }
678 
681  if (!had_nonincremental_change_ && variable_is_extracted(index)) {
682  char type_var;
683  if (integer) {
684  type_var = GRB_INTEGER;
685  } else {
686  type_var = GRB_CONTINUOUS;
687  }
688  SetCharAttrElement(GRB_CHAR_ATTR_VTYPE, mp_var_to_gurobi_var_.at(index),
689  type_var);
690  } else {
692  }
693 }
694 
695 void GurobiInterface::SetConstraintBounds(int index, double lb, double ub) {
698  had_nonincremental_change_ = true;
699  }
700  // TODO(user): this is nontrivial to make incremental:
701  // 1. Make sure it is a linear constraint (not an indicator or indicator
702  // range constraint).
703  // 2. Check if the sense of the constraint changes. If it was previously a
704  // range constraint, we can do nothing, and if it becomes a range
705  // constraint, we can do nothing. We could support range constraints if
706  // we tracked the auxiliary variable that is added with range
707  // constraints.
708 }
709 
712 }
713 
715  had_nonincremental_change_ = true;
717  return !IsContinuous();
718 }
719 
722 }
723 
725  const MPVariable* const variable,
726  double new_value, double old_value) {
728  if (!had_nonincremental_change_ && variable_is_extracted(variable->index()) &&
729  constraint_is_extracted(constraint->index())) {
730  // Cannot be const, GRBchgcoeffs needs non-const pointer.
731  int grb_var = mp_var_to_gurobi_var_.at(variable->index());
732  int grb_cons = mp_cons_to_gurobi_linear_cons_.at(constraint->index());
733  if (grb_cons < 0) {
734  had_nonincremental_change_ = true;
736  } else {
737  // TODO(user): investigate if this has bad performance.
738  CheckedGurobiCall(
739  GRBchgcoeffs(model_, 1, &grb_cons, &grb_var, &new_value));
740  }
741  } else {
743  }
744 }
745 
747  had_nonincremental_change_ = true;
749  // TODO(user): this is difficult to make incremental, like
750  // SetConstraintBounds(), because of the auxiliary Gurobi variables that
751  // range constraints introduce.
752 }
753 
755  double coefficient) {
757  if (!had_nonincremental_change_ && variable_is_extracted(variable->index())) {
758  SetDoubleAttrElement(GRB_DBL_ATTR_OBJ,
759  mp_var_to_gurobi_var_.at(variable->index()),
760  coefficient);
761  } else {
763  }
764 }
765 
768  if (!had_nonincremental_change_) {
769  SetDoubleAttr(GRB_DBL_ATTR_OBJCON, value);
770  } else {
772  }
773 }
774 
777  if (!had_nonincremental_change_) {
778  SetObjectiveOffset(0.0);
779  for (const auto& entry : solver_->objective_->coefficients_) {
780  SetObjectiveCoefficient(entry.first, 0.0);
781  }
782  } else {
784  }
785 }
786 
788  update_branching_priorities_ = true;
789 }
790 
791 // ------ Query statistics on the solution and the solve ------
792 
794  double iter;
796  CheckedGurobiCall(GRBgetdblattr(model_, GRB_DBL_ATTR_ITERCOUNT, &iter));
797  return static_cast<int64_t>(iter);
798 }
799 
800 int64_t GurobiInterface::nodes() const {
801  if (mip_) {
803  return static_cast<int64_t>(GetDoubleAttr(GRB_DBL_ATTR_NODECOUNT));
804  } else {
805  LOG(DFATAL) << "Number of nodes only available for discrete problems.";
806  return kUnknownNumberOfNodes;
807  }
808 }
809 
810 MPSolver::BasisStatus GurobiInterface::TransformGRBVarBasisStatus(
811  int gurobi_basis_status) const {
812  switch (gurobi_basis_status) {
813  case GRB_BASIC:
814  return MPSolver::BASIC;
815  case GRB_NONBASIC_LOWER:
817  case GRB_NONBASIC_UPPER:
819  case GRB_SUPERBASIC:
820  return MPSolver::FREE;
821  default:
822  LOG(DFATAL) << "Unknown GRB basis status.";
823  return MPSolver::FREE;
824  }
825 }
826 
827 MPSolver::BasisStatus GurobiInterface::TransformGRBConstraintBasisStatus(
828  int gurobi_basis_status, int constraint_index) const {
829  const int grb_index = mp_cons_to_gurobi_linear_cons_.at(constraint_index);
830  if (grb_index < 0) {
831  LOG(DFATAL) << "Basis status not available for nonlinear constraints.";
832  return MPSolver::FREE;
833  }
834  switch (gurobi_basis_status) {
835  case GRB_BASIC:
836  return MPSolver::BASIC;
837  default: {
838  // Non basic.
839  double tolerance = 0.0;
840  CheckedGurobiCall(GRBgetdblparam(GRBgetenv(model_),
841  GRB_DBL_PAR_FEASIBILITYTOL, &tolerance));
842  const double slack = GetDoubleAttrElement(GRB_DBL_ATTR_SLACK, grb_index);
843  const char sense = GetCharAttrElement(GRB_CHAR_ATTR_SENSE, grb_index);
844  VLOG(4) << "constraint " << constraint_index << " , slack = " << slack
845  << " , sense = " << sense;
846  if (fabs(slack) <= tolerance) {
847  switch (sense) {
848  case GRB_EQUAL:
849  case GRB_LESS_EQUAL:
851  case GRB_GREATER_EQUAL:
853  default:
854  return MPSolver::FREE;
855  }
856  } else {
857  return MPSolver::FREE;
858  }
859  }
860  }
861 }
862 
863 // Returns the basis status of a row.
865  const int optim_status = GetIntAttr(GRB_INT_ATTR_STATUS);
866  if (optim_status != GRB_OPTIMAL && optim_status != GRB_SUBOPTIMAL) {
867  LOG(DFATAL) << "Basis status only available after a solution has "
868  << "been found.";
869  return MPSolver::FREE;
870  }
871  if (mip_) {
872  LOG(DFATAL) << "Basis status only available for continuous problems.";
873  return MPSolver::FREE;
874  }
875  const int grb_index = mp_cons_to_gurobi_linear_cons_.at(constraint_index);
876  if (grb_index < 0) {
877  LOG(DFATAL) << "Basis status not available for nonlinear constraints.";
878  return MPSolver::FREE;
879  }
880  const int gurobi_basis_status =
881  GetIntAttrElement(GRB_INT_ATTR_CBASIS, grb_index);
882  return TransformGRBConstraintBasisStatus(gurobi_basis_status,
883  constraint_index);
884 }
885 
886 // Returns the basis status of a column.
888  const int optim_status = GetIntAttr(GRB_INT_ATTR_STATUS);
889  if (optim_status != GRB_OPTIMAL && optim_status != GRB_SUBOPTIMAL) {
890  LOG(DFATAL) << "Basis status only available after a solution has "
891  << "been found.";
892  return MPSolver::FREE;
893  }
894  if (mip_) {
895  LOG(DFATAL) << "Basis status only available for continuous problems.";
896  return MPSolver::FREE;
897  }
898  const int grb_index = mp_var_to_gurobi_var_.at(variable_index);
899  const int gurobi_basis_status =
900  GetIntAttrElement(GRB_INT_ATTR_VBASIS, grb_index);
901  return TransformGRBVarBasisStatus(gurobi_basis_status);
902 }
903 
904 // Extracts new variables.
906  const int total_num_vars = solver_->variables_.size();
907  if (total_num_vars > last_variable_index_) {
908  // Define new variables.
909  for (int j = last_variable_index_; j < total_num_vars; ++j) {
910  const MPVariable* const var = solver_->variables_.at(j);
911  set_variable_as_extracted(var->index(), true);
912  CheckedGurobiCall(GRBaddvar(
913  model_, 0, // numnz
914  nullptr, // vind
915  nullptr, // vval
916  solver_->objective_->GetCoefficient(var), var->lb(), var->ub(),
917  var->integer() && mip_ ? GRB_INTEGER : GRB_CONTINUOUS,
918  var->name().empty() ? nullptr : var->name().c_str()));
919  mp_var_to_gurobi_var_.push_back(num_gurobi_vars_++);
920  }
921  CheckedGurobiCall(GRBupdatemodel(model_));
922  // Add new variables to existing constraints.
923  std::vector<int> grb_cons_ind;
924  std::vector<int> grb_var_ind;
925  std::vector<double> coef;
926  for (int i = 0; i < last_constraint_index_; ++i) {
927  // If there was a nonincremental change/the model is not incremental (e.g.
928  // there is an indicator constraint), we should never enter this loop, as
929  // last_variable_index_ will be reset to zero before ExtractNewVariables()
930  // is called.
931  MPConstraint* const ct = solver_->constraints_[i];
932  const int grb_ct_idx = mp_cons_to_gurobi_linear_cons_.at(ct->index());
933  DCHECK_GE(grb_ct_idx, 0);
934  DCHECK(ct->indicator_variable() == nullptr);
935  for (const auto& entry : ct->coefficients_) {
936  const int var_index = entry.first->index();
937  DCHECK(variable_is_extracted(var_index));
938 
939  if (var_index >= last_variable_index_) {
940  grb_cons_ind.push_back(grb_ct_idx);
941  grb_var_ind.push_back(mp_var_to_gurobi_var_.at(var_index));
942  coef.push_back(entry.second);
943  }
944  }
945  }
946  if (!grb_cons_ind.empty()) {
947  CheckedGurobiCall(GRBchgcoeffs(model_, grb_cons_ind.size(),
948  grb_cons_ind.data(), grb_var_ind.data(),
949  coef.data()));
950  }
951  }
952  CheckedGurobiCall(GRBupdatemodel(model_));
953  DCHECK_EQ(GetIntAttr(GRB_INT_ATTR_NUMVARS), num_gurobi_vars_);
954 }
955 
957  int total_num_rows = solver_->constraints_.size();
958  if (last_constraint_index_ < total_num_rows) {
959  // Add each new constraint.
960  for (int row = last_constraint_index_; row < total_num_rows; ++row) {
961  MPConstraint* const ct = solver_->constraints_[row];
963  const int size = ct->coefficients_.size();
964  std::vector<int> grb_vars;
965  std::vector<double> coefs;
966  grb_vars.reserve(size);
967  coefs.reserve(size);
968  for (const auto& entry : ct->coefficients_) {
969  const int var_index = entry.first->index();
970  CHECK(variable_is_extracted(var_index));
971  grb_vars.push_back(mp_var_to_gurobi_var_.at(var_index));
972  coefs.push_back(entry.second);
973  }
974  char* const name =
975  ct->name().empty() ? nullptr : const_cast<char*>(ct->name().c_str());
976  if (ct->indicator_variable() != nullptr) {
977  const int grb_ind_var =
978  mp_var_to_gurobi_var_.at(ct->indicator_variable()->index());
979  if (ct->lb() > -std::numeric_limits<double>::infinity()) {
980  CheckedGurobiCall(GRBaddgenconstrIndicator(
981  model_, name, grb_ind_var, ct->indicator_value(), size,
982  grb_vars.data(), coefs.data(),
983  ct->ub() == ct->lb() ? GRB_EQUAL : GRB_GREATER_EQUAL, ct->lb()));
984  }
985  if (ct->ub() < std::numeric_limits<double>::infinity() &&
986  ct->lb() != ct->ub()) {
987  CheckedGurobiCall(GRBaddgenconstrIndicator(
988  model_, name, grb_ind_var, ct->indicator_value(), size,
989  grb_vars.data(), coefs.data(), GRB_LESS_EQUAL, ct->ub()));
990  }
991  mp_cons_to_gurobi_linear_cons_.push_back(-1);
992  } else {
993  // Using GRBaddrangeconstr for constraints that don't require it adds
994  // a slack which is not always removed by presolve.
995  if (ct->lb() == ct->ub()) {
996  CheckedGurobiCall(GRBaddconstr(model_, size, grb_vars.data(),
997  coefs.data(), GRB_EQUAL, ct->lb(),
998  name));
999  } else if (ct->lb() == -std::numeric_limits<double>::infinity()) {
1000  CheckedGurobiCall(GRBaddconstr(model_, size, grb_vars.data(),
1001  coefs.data(), GRB_LESS_EQUAL, ct->ub(),
1002  name));
1003  } else if (ct->ub() == std::numeric_limits<double>::infinity()) {
1004  CheckedGurobiCall(GRBaddconstr(model_, size, grb_vars.data(),
1005  coefs.data(), GRB_GREATER_EQUAL,
1006  ct->lb(), name));
1007  } else {
1008  CheckedGurobiCall(GRBaddrangeconstr(model_, size, grb_vars.data(),
1009  coefs.data(), ct->lb(), ct->ub(),
1010  name));
1011  // NOTE(user): range constraints implicitly add an extra variable
1012  // to the model.
1013  num_gurobi_vars_++;
1014  }
1015  mp_cons_to_gurobi_linear_cons_.push_back(num_gurobi_linear_cons_++);
1016  }
1017  }
1018  }
1019  CheckedGurobiCall(GRBupdatemodel(model_));
1020  DCHECK_EQ(GetIntAttr(GRB_INT_ATTR_NUMCONSTRS), num_gurobi_linear_cons_);
1021 }
1022 
1025  SetDoubleAttr(GRB_DBL_ATTR_OBJCON, solver_->Objective().offset());
1026 }
1027 
1028 // ------ Parameters -----
1029 
1030 void GurobiInterface::SetParameters(const MPSolverParameters& param) {
1031  SetCommonParameters(param);
1032  if (mip_) {
1033  SetMIPParameters(param);
1034  }
1035 }
1036 
1037 bool GurobiInterface::SetSolverSpecificParametersAsString(
1038  const std::string& parameters) {
1039  return SetSolverSpecificParameters(parameters, GRBgetenv(model_)).ok();
1040 }
1041 
1042 void GurobiInterface::SetRelativeMipGap(double value) {
1043  if (mip_) {
1044  CheckedGurobiCall(
1046  } else {
1047  LOG(WARNING) << "The relative MIP gap is only available "
1048  << "for discrete problems.";
1049  }
1050 }
1051 
1052 // Gurobi has two different types of primal tolerance (feasibility tolerance):
1053 // constraint and integrality. We need to set them both.
1054 // See:
1055 // http://www.gurobi.com/documentation/6.0/refman/feasibilitytol.html
1056 // and
1057 // http://www.gurobi.com/documentation/6.0/refman/intfeastol.html
1058 void GurobiInterface::SetPrimalTolerance(double value) {
1059  CheckedGurobiCall(
1061  CheckedGurobiCall(
1063 }
1064 
1065 // As opposed to primal (feasibility) tolerance, the dual (optimality) tolerance
1066 // applies only to the reduced costs in the improving direction.
1067 // See:
1068 // http://www.gurobi.com/documentation/6.0/refman/optimalitytol.html
1069 void GurobiInterface::SetDualTolerance(double value) {
1070  CheckedGurobiCall(
1072 }
1073 
1074 void GurobiInterface::SetPresolveMode(int value) {
1075  switch (value) {
1077  CheckedGurobiCall(
1078  GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_PRESOLVE, false));
1079  break;
1080  }
1082  CheckedGurobiCall(
1084  break;
1085  }
1086  default: {
1088  }
1089  }
1090 }
1091 
1092 // Sets the scaling mode.
1093 void GurobiInterface::SetScalingMode(int value) {
1094  switch (value) {
1096  CheckedGurobiCall(
1098  break;
1100  CheckedGurobiCall(
1102  CheckedGurobiCall(
1104  break;
1105  default:
1106  // Leave the parameters untouched.
1107  break;
1108  }
1109 }
1110 
1111 // Sets the LP algorithm : primal, dual or barrier. Note that GRB
1112 // offers automatic selection
1113 void GurobiInterface::SetLpAlgorithm(int value) {
1114  switch (value) {
1116  CheckedGurobiCall(GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_METHOD,
1117  GRB_METHOD_DUAL));
1118  break;
1120  CheckedGurobiCall(GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_METHOD,
1122  break;
1124  CheckedGurobiCall(GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_METHOD,
1126  break;
1127  default:
1129  value);
1130  }
1131 }
1132 
1133 int GurobiInterface::SolutionCount() const {
1134  return GetIntAttr(GRB_INT_ATTR_SOLCOUNT);
1135 }
1136 
1137 bool GurobiInterface::ModelIsNonincremental() const {
1138  for (const MPConstraint* c : solver_->constraints()) {
1139  if (c->indicator_variable() != nullptr) {
1140  return true;
1141  }
1142  }
1143  return false;
1144 }
1145 
1147  WallTimer timer;
1148  timer.Start();
1149 
1152  ModelIsNonincremental() || had_nonincremental_change_) {
1153  Reset();
1154  }
1155 
1156  // Set log level.
1157  CheckedGurobiCall(
1159 
1160  ExtractModel();
1161  // Sync solver.
1162  CheckedGurobiCall(GRBupdatemodel(model_));
1163  VLOG(1) << absl::StrFormat("Model built in %s.",
1164  absl::FormatDuration(timer.GetDuration()));
1165 
1166  // Set solution hints if any.
1167  for (const std::pair<const MPVariable*, double>& p :
1168  solver_->solution_hint_) {
1169  SetDoubleAttrElement(GRB_DBL_ATTR_START,
1170  mp_var_to_gurobi_var_.at(p.first->index()), p.second);
1171  }
1172 
1173  // Pass branching priority annotations if at least one has been updated.
1174  if (update_branching_priorities_) {
1175  for (const MPVariable* var : solver_->variables_) {
1176  SetIntAttrElement(GRB_INT_ATTR_BRANCHPRIORITY,
1177  mp_var_to_gurobi_var_.at(var->index()),
1178  var->branching_priority());
1179  }
1180  update_branching_priorities_ = false;
1181  }
1182 
1183  // Time limit.
1184  if (solver_->time_limit() != 0) {
1185  VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
1186  CheckedGurobiCall(GRBsetdblparam(GRBgetenv(model_), GRB_DBL_PAR_TIMELIMIT,
1188  }
1189 
1190  // We first set our internal MPSolverParameters from 'param' and then set
1191  // any user-specified internal solver parameters via
1192  // solver_specific_parameter_string_.
1193  // Default MPSolverParameters can override custom parameters (for example for
1194  // presolving) and therefore we apply MPSolverParameters first.
1195  SetParameters(param);
1197  solver_->solver_specific_parameter_string_);
1198 
1199  std::unique_ptr<GurobiMPCallbackContext> gurobi_context;
1200  MPCallbackWithGurobiContext mp_callback_with_context;
1201  int gurobi_precrush = 0;
1202  int gurobi_lazy_constraint = 0;
1203  if (callback_ == nullptr) {
1204  CheckedGurobiCall(GRBsetcallbackfunc(model_, nullptr, nullptr));
1205  } else {
1206  gurobi_context = absl::make_unique<GurobiMPCallbackContext>(
1207  env_, &mp_var_to_gurobi_var_, num_gurobi_vars_,
1208  callback_->might_add_cuts(), callback_->might_add_lazy_constraints());
1209  mp_callback_with_context.context = gurobi_context.get();
1210  mp_callback_with_context.callback = callback_;
1211  CheckedGurobiCall(GRBsetcallbackfunc(
1212  model_, CallbackImpl, static_cast<void*>(&mp_callback_with_context)));
1213  gurobi_precrush = callback_->might_add_cuts();
1214  gurobi_lazy_constraint = callback_->might_add_lazy_constraints();
1215  }
1216  CheckedGurobiCall(
1217  GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_PRECRUSH, gurobi_precrush));
1218  CheckedGurobiCall(GRBsetintparam(
1219  GRBgetenv(model_), GRB_INT_PAR_LAZYCONSTRAINTS, gurobi_lazy_constraint));
1220 
1221  // Solve
1222  timer.Restart();
1223  const int status = GRBoptimize(model_);
1224 
1225  if (status) {
1226  VLOG(1) << "Failed to optimize MIP." << GRBgeterrormsg(env_);
1227  } else {
1228  VLOG(1) << absl::StrFormat("Solved in %s.",
1229  absl::FormatDuration(timer.GetDuration()));
1230  }
1231 
1232  // Get the status.
1233  const int optimization_status = GetIntAttr(GRB_INT_ATTR_STATUS);
1234  VLOG(1) << absl::StrFormat("Solution status %d.\n", optimization_status);
1235  const int solution_count = SolutionCount();
1236 
1237  switch (optimization_status) {
1238  case GRB_OPTIMAL:
1240  break;
1241  case GRB_INFEASIBLE:
1243  break;
1244  case GRB_UNBOUNDED:
1246  break;
1247  case GRB_INF_OR_UNBD:
1248  // TODO(user,user): We could introduce our own "infeasible or
1249  // unbounded" status.
1251  break;
1252  default: {
1253  if (solution_count > 0) {
1255  } else {
1257  }
1258  break;
1259  }
1260  }
1261 
1262  if (IsMIP() && (result_status_ != MPSolver::UNBOUNDED &&
1264  const int error =
1266  LOG_IF(WARNING, error != 0)
1267  << "Best objective bound is not available, error=" << error
1268  << ", message=" << GRBgeterrormsg(env_);
1269  VLOG(1) << "best bound = " << best_objective_bound_;
1270  }
1271 
1272  if (solution_count > 0 && (result_status_ == MPSolver::FEASIBLE ||
1274  current_solution_index_ = 0;
1275  // Get the results.
1276  objective_value_ = GetDoubleAttr(GRB_DBL_ATTR_OBJVAL);
1277  VLOG(1) << "objective = " << objective_value_;
1278 
1279  {
1280  const std::vector<double> grb_variable_values =
1281  GetDoubleAttrArray(GRB_DBL_ATTR_X, num_gurobi_vars_);
1282  for (int i = 0; i < solver_->variables_.size(); ++i) {
1283  MPVariable* const var = solver_->variables_[i];
1284  const double val = grb_variable_values.at(mp_var_to_gurobi_var_.at(i));
1285  var->set_solution_value(val);
1286  VLOG(3) << var->name() << ", value = " << val;
1287  }
1288  }
1289  if (!mip_) {
1290  {
1291  const std::vector<double> grb_reduced_costs =
1292  GetDoubleAttrArray(GRB_DBL_ATTR_RC, num_gurobi_vars_);
1293  for (int i = 0; i < solver_->variables_.size(); ++i) {
1294  MPVariable* const var = solver_->variables_[i];
1295  const double rc = grb_reduced_costs.at(mp_var_to_gurobi_var_.at(i));
1296  var->set_reduced_cost(rc);
1297  VLOG(4) << var->name() << ", reduced cost = " << rc;
1298  }
1299  }
1300 
1301  {
1302  std::vector<double> grb_dual_values =
1303  GetDoubleAttrArray(GRB_DBL_ATTR_PI, num_gurobi_linear_cons_);
1304  for (int i = 0; i < solver_->constraints_.size(); ++i) {
1305  MPConstraint* const ct = solver_->constraints_[i];
1306  const double dual_value =
1307  grb_dual_values.at(mp_cons_to_gurobi_linear_cons_.at(i));
1308  ct->set_dual_value(dual_value);
1309  VLOG(4) << "row " << ct->index() << ", dual value = " << dual_value;
1310  }
1311  }
1312  }
1313  }
1314 
1316  GRBresetparams(GRBgetenv(model_));
1317  return result_status_;
1318 }
1319 
1320 absl::optional<MPSolutionResponse> GurobiInterface::DirectlySolveProto(
1321  const MPModelRequest& request) {
1322  // Here we reuse the Gurobi environment to support single-use license that
1323  // forbids creating a second environment if one already exists.
1324  const auto status_or = GurobiSolveProto(request, env_);
1325  if (status_or.ok()) return status_or.value();
1326  // Special case: if something is not implemented yet, fall back to solving
1327  // through MPSolver.
1328  if (absl::IsUnimplemented(status_or.status())) return absl::nullopt;
1329 
1330  if (request.enable_internal_solver_output()) {
1331  LOG(INFO) << "Invalid Gurobi status: " << status_or.status();
1332  }
1334  response.set_status(MPSOLVER_NOT_SOLVED);
1335  response.set_status_str(status_or.status().ToString());
1336  return response;
1337 }
1338 
1340  // Next solution only supported for MIP
1341  if (!mip_) return false;
1342 
1343  // Make sure we have successfully solved the problem and not modified it.
1345  return false;
1346  }
1347  // Check if we are out of solutions.
1348  if (current_solution_index_ + 1 >= SolutionCount()) {
1349  return false;
1350  }
1351  current_solution_index_++;
1352 
1353  CheckedGurobiCall(GRBsetintparam(
1354  GRBgetenv(model_), GRB_INT_PAR_SOLUTIONNUMBER, current_solution_index_));
1355 
1356  objective_value_ = GetDoubleAttr(GRB_DBL_ATTR_POOLOBJVAL);
1357  const std::vector<double> grb_variable_values =
1358  GetDoubleAttrArray(GRB_DBL_ATTR_XN, num_gurobi_vars_);
1359 
1360  for (int i = 0; i < solver_->variables_.size(); ++i) {
1361  MPVariable* const var = solver_->variables_[i];
1362  var->set_solution_value(
1363  grb_variable_values.at(mp_var_to_gurobi_var_.at(i)));
1364  }
1365  // TODO(user,user): This reset may not be necessary, investigate.
1366  GRBresetparams(GRBgetenv(model_));
1367  return true;
1368 }
1369 
1370 void GurobiInterface::Write(const std::string& filename) {
1371  if (sync_status_ == MUST_RELOAD) {
1372  Reset();
1373  }
1374  ExtractModel();
1375  // Sync solver.
1376  CheckedGurobiCall(GRBupdatemodel(model_));
1377  VLOG(1) << "Writing Gurobi model file \"" << filename << "\".";
1378  const int status = GRBwrite(model_, filename.c_str());
1379  if (status) {
1380  LOG(WARNING) << "Failed to write MIP." << GRBgeterrormsg(env_);
1381  }
1382 }
1383 
1385  return new GurobiInterface(solver, mip);
1386 }
1387 
1389  callback_ = mp_callback;
1390 }
1391 
1392 } // namespace operations_research
MPSolverInterface * BuildGurobiInterface(bool mip, MPSolver *const solver)
std::function< const char *(GRBenv *env)> GRBgeterrormsg
Definition: environment.cc:426
#define GRB_CB_MIPSOL
Definition: environment.h:324
#define GRB_INT_PAR_PRECRUSH
Definition: environment.h:566
#define GRB_DBL_ATTR_XN
Definition: environment.h:231
ResultStatus
The status of solving the problem.
#define CHECK(condition)
Definition: base/logging.h:491
std::function< int(GRBmodel *model, const char *attrname, int element, double *valueP)> GRBgetdblattrelement
Definition: environment.cc:100
#define GRB_CB_PRESOLVE
Definition: environment.h:321
#define GRB_METHOD_PRIMAL
Definition: environment.h:607
void set_variable_as_extracted(int var_index, bool extracted)
Advanced usage: incrementality from one solve to the next.
#define GRB_DBL_ATTR_RC
Definition: environment.h:233
void AddVariable(MPVariable *const var) override
#define GRB_INT_PAR_OUTPUTFLAG
Definition: environment.h:565
#define GRB_INT_ATTR_NUMVARS
Definition: environment.h:158
#define GRB_GREATER_EQUAL
Definition: environment.h:97
#define GRB_DBL_ATTR_PI
Definition: environment.h:236
const int FATAL
Definition: log_severity.h:32
const std::vector< MPConstraint * > & constraints() const
Returns the array of constraints handled by the MPSolver.
std::string SolverVersion() const override
#define GRB_CHAR_ATTR_VTYPE
Definition: environment.h:181
void set_constraint_as_extracted(int ct_index, bool extracted)
void BranchingPriorityChangedForVariable(int var_index) override
std::function< int(GRBmodel *model, const char *attrname, int *valueP)> GRBgetintattr
Definition: environment.cc:55
#define GRB_DBL_ATTR_NODECOUNT
Definition: environment.h:227
std::function< int(GRBmodel *model, const char *filename)> GRBwrite
Definition: environment.cc:235
#define GRB_DBL_PAR_INTFEASTOL
Definition: environment.h:485
#define GRB_INT_ATTR_CBASIS
Definition: environment.h:241
std::function< int(GRBenv *env, const char *paramname, double value)> GRBsetdblparam
Definition: environment.cc:381
#define VLOG(verboselevel)
Definition: base/logging.h:979
const std::string name
const MPObjective & Objective() const
Returns the objective object.
const int ERROR
Definition: log_severity.h:32
#define GRB_INT_ATTR_VBASIS
Definition: environment.h:235
#define GRB_DBL_ATTR_LB
Definition: environment.h:178
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
void SetVariableBounds(int var_index, double lb, double ub) override
absl::optional< MPSolutionResponse > DirectlySolveProto(const MPModelRequest &request) override
std::function< GRBenv *(GRBmodel *model)> GRBgetenv
Definition: environment.cc:419
absl::StatusOr< GRBenv * > GetGurobiEnv()
Definition: environment.cc:756
MPCallback * callback
struct _GRBmodel GRBmodel
Definition: environment.h:24
std::function< int(void *cbdata, int lazylen, const int *lazyind, const double *lazyval, char lazysense, double lazyrhs)> GRBcblazy
Definition: environment.cc:158
const absl::string_view ToString(MPSolver::OptimizationProblemType optimization_problem_type)
#define GRB_DBL_PAR_OBJSCALE
Definition: environment.h:493
#define LOG(severity)
Definition: base/logging.h:416
#define GRB_METHOD_DUAL
Definition: environment.h:608
std::function< int(GRBmodel *model, const char *attrname, int element, char newvalue)> GRBsetcharattrelement
Definition: environment.cc:81
GRBmodel * model
void SetObjectiveCoefficient(const MPVariable *const variable, double coefficient) override
std::function< int(GRBenv *env, GRBmodel **modelP, const char *Pname, int numvars, double *obj, double *lb, double *ub, char *vtype, char **varnames)> GRBnewmodel
Definition: environment.cc:242
void AddRowConstraint(MPConstraint *const ct) override
#define GRB_INFEASIBLE
Definition: environment.h:458
#define GRB_MINIMIZE
Definition: environment.h:104
BasisStatus
Advanced usage: possible basis status values for a variable and the slack variable of a linear constr...
std::function< int(GRBenv *env, const char *paramname, int value)> GRBsetintparam
Definition: environment.cc:379
RowIndex row
Definition: markowitz.cc:182
#define GRB_MAXIMIZE
Definition: environment.h:105
int64_t coefficient
void Start()
Definition: timer.h:31
#define GRB_CB_BARRIER
Definition: environment.h:327
#define GRB_CB_MESSAGE
Definition: environment.h:326
#define GRB_CHAR_ATTR_SENSE
Definition: environment.h:192
#define GRB_DBL_PAR_MIPGAP
Definition: environment.h:487
#define GRB_UNBOUNDED
Definition: environment.h:460
void * gurobi_internal_callback_data
int64_t coef
Definition: expr_array.cc:1875
GurobiInterface(MPSolver *const solver, bool mip)
int GetIntegerParam(MPSolverParameters::IntegerParam param) const
Returns the value of an integer parameter.
#define GRB_INF_OR_UNBD
Definition: environment.h:459
std::function< int(GRBmodel *model)> GRBfreemodel
Definition: environment.cc:335
std::function< int(GRBmodel *model, int numnz, int *vind, double *vval, double obj, double lb, double ub, char vtype, const char *varname)> GRBaddvar
Definition: environment.cc:258
#define GRB_CB_MIPNODE_NODCNT
Definition: environment.h:357
#define GRB_CB_MIPSOL_NODCNT
Definition: environment.h:350
The class for variables of a Mathematical Programming (MP) model.
std::function< int(GRBenv *env)> GRBresetparams
Definition: environment.cc:386
#define GRB_INT_ATTR_STATUS
Definition: environment.h:217
#define GRB_DBL_ATTR_SLACK
Definition: environment.h:238
std::function< int(GRBmodel *model, const char *attrname, double *valueP)> GRBgetdblattr
Definition: environment.cc:95
bool SetSolverSpecificParametersAsString(const std::string &parameters)
Advanced usage: pass solver specific parameters in text format.
ABSL_FLAG(int, num_gurobi_threads, 4, "Number of threads available for Gurobi.")
#define GRB_CB_POLLING
Definition: environment.h:320
#define GRB_DBL_PAR_OPTIMALITYTOL
Definition: environment.h:489
std::function< int(void *cbdata, const double *solution, double *objvalP)> GRBcbsolution
Definition: environment.cc:152
#define GRB_INT_PAR_SOLUTIONNUMBER
Definition: environment.h:526
std::function< int(void *cbdata, int cutlen, const int *cutind, const double *cutval, char cutsense, double cutrhs)> GRBcbcut
Definition: environment.cc:155
const int WARNING
Definition: log_severity.h:31
#define GRB_UNDEFINED
Definition: environment.h:109
void SetConstraintBounds(int row_index, double lb, double ub) override
std::function< int(GRBmodel *model, int cnt, int *cind, int *vind, double *val)> GRBchgcoeffs
Definition: environment.cc:327
#define GRB_DBL_ATTR_OBJVAL
Definition: environment.h:218
struct _GRBenv GRBenv
Definition: environment.h:25
std::function< int(GRBmodel *model, const char *attrname, int element, int newvalue)> GRBsetintattrelement
Definition: environment.cc:63
#define GRB_INT_PAR_SCALEFLAG
Definition: environment.h:494
void SetCallback(MPCallback *mp_callback) override
void Restart()
Definition: timer.h:35
std::function< int(void *cbdata, int where, int what, void *resultP)> GRBcbget
Definition: environment.cc:147
void ClearConstraint(MPConstraint *const constraint) override
std::function< void(GRBmodel *model)> GRBterminate
Definition: environment.cc:349
#define GRB_DBL_PAR_TIMELIMIT
Definition: environment.h:481
std::function< int(GRBmodel *lp, const char *name, int binvar, int binval, int nvars, const int *vars, const double *vals, char sense, double rhs)> GRBaddgenconstrIndicator
Definition: environment.cc:309
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
std::function< int(GRBmodel *model, int(GUROBI_STDCALL *cb)(CB_ARGS), void *usrdata)> GRBsetcallbackfunc
Definition: environment.cc:140
#define GRB_DBL_PAR_FEASIBILITYTOL
Definition: environment.h:484
#define GRB_DBL_ATTR_START
Definition: environment.h:182
std::function< int(GRBmodel *model, const char *attrname, int element, double newvalue)> GRBsetdblattrelement
Definition: environment.cc:103
std::function< int(GRBmodel *model)> GRBupdatemodel
Definition: environment.cc:333
int where
#define GRB_CONTINUOUS
Definition: environment.h:99
#define GRB_DBL_ATTR_UB
Definition: environment.h:179
void SetCommonParameters(const MPSolverParameters &param)
static constexpr int64_t kUnknownNumberOfIterations
int index
Definition: pack.cc:509
std::function< int(GRBenv *dest, GRBenv *src)> GRBcopyparams
Definition: environment.cc:387
#define GRB_CB_SIMPLEX
Definition: environment.h:322
The class for constraints of a Mathematical Programming (MP) model.
feasible, or stopped by limit.
std::function< int(GRBmodel *model, const char *attrname, double newvalue)> GRBsetdblattr
Definition: environment.cc:97
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:890
void SetMIPParameters(const MPSolverParameters &param)
SharedResponseManager * response
#define GRB_NONBASIC_UPPER
Definition: environment.h:473
#define GRB_DBL_ATTR_OBJCON
Definition: environment.h:171
#define GRB_INT_PAR_PRESOLVE
Definition: environment.h:571
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
bool constraint_is_extracted(int ct_index) const
std::function< int(GRBmodel *model, const char *attrname, int element, char *valueP)> GRBgetcharattrelement
Definition: environment.cc:78
#define GRB_BASIC
Definition: environment.h:471
#define GRB_INT_PAR_METHOD
Definition: environment.h:491
#define GRB_INT_ATTR_SOLCOUNT
Definition: environment.h:224
int index() const
Returns the index of the constraint in the MPSolver::constraints_.
#define GRB_INT_ATTR_MODELSENSE
Definition: environment.h:170
#define GRB_CB_MIPNODE_STATUS
Definition: environment.h:353
#define GRB_LESS_EQUAL
Definition: environment.h:96
absl::Status SetSolverSpecificParameters(const std::string &parameters, GRBenv *gurobi)
#define LOG_IF(severity, condition)
Definition: base/logging.h:475
#define GRB_DBL_ATTR_OBJBOUND
Definition: environment.h:219
#define DCHECK(condition)
Definition: base/logging.h:885
int index() const
Returns the index of the variable in the MPSolver::variables_.
double ComputeExactConditionNumber() const override
absl::Duration GetDuration() const
Definition: timer.h:48
void SetObjectiveOffset(double value) override
#define GRB_INT_ATTR_BRANCHPRIORITY
Definition: environment.h:184
std::function< int(GRBmodel *model)> GRBoptimize
Definition: environment.cc:207
#define GRB_CB_MIPSOL_SOL
Definition: environment.h:346
#define GRB_SUBOPTIMAL
Definition: environment.h:468
#define GRB_INTEGER
Definition: environment.h:101
void SetOptimizationDirection(bool maximize) override
static constexpr int64_t kUnknownNumberOfNodes
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:886
#define GRB_DBL_ATTR_POOLOBJVAL
Definition: environment.h:222
void Write(const std::string &filename) override
#define GRB_INT_PAR_LAZYCONSTRAINTS
Definition: environment.h:560
#define LOG_FIRST_N(severity, n)
Definition: base/logging.h:850
void SetVariableInteger(int var_index, bool integer) override
std::function< int(GRBmodel *model, const char *attrname, int element, int *valueP)> GRBgetintattrelement
Definition: environment.cc:60
std::function< int(GRBmodel *model, const char *attrname, int newvalue)> GRBsetintattr
Definition: environment.cc:57
std::function< void(GRBenv *env)> GRBfreeenv
Definition: environment.cc:425
#define GUROBI_STDCALL
Definition: environment.h:36
#define GRB_DBL_ATTR_X
Definition: environment.h:230
std::function< int(GRBmodel *model, int numnz, int *cind, double *cval, char sense, double rhs, const char *constrname)> GRBaddconstr
Definition: environment.cc:269
void SetCoefficient(MPConstraint *const constraint, const MPVariable *const variable, double new_value, double old_value) override
#define GRB_EQUAL
Definition: environment.h:98
This mathematical programming (MP) solver class is the main class though which users build and solve ...
Collection of objects used to extend the Constraint Solver library.
#define GRB_METHOD_BARRIER
Definition: environment.h:609
SatParameters parameters
MPSolver::BasisStatus row_status(int constraint_index) const override
#define GRB_INT_PAR_THREADS
Definition: environment.h:580
This class stores parameter settings for LP and MIP solvers.
#define GRB_SUPERBASIC
Definition: environment.h:474
IntVar * var
Definition: expr_array.cc:1874
#define GRB_DBL_ATTR_ITERCOUNT
Definition: environment.h:225
#define ABSL_DIE_IF_NULL
Definition: base/logging.h:41
MPSolver::BasisStatus column_status(int variable_index) const override
#define GRB_CB_MIPNODE_REL
Definition: environment.h:354
#define GRB_DBL_ATTR_OBJ
Definition: environment.h:180
GurobiMPCallbackContext * context
#define GRB_CB_MIPNODE
Definition: environment.h:325
std::function< void(int *majorP, int *minorP, int *technicalP)> GRBversion
Definition: environment.cc:428
MPSolver::ResultStatus Solve(const MPSolverParameters &param) override
bool AddIndicatorConstraint(MPConstraint *const ct) override
std::function< int(GRBenv *env, const char *paramname, double *valueP)> GRBgetdblparam
Definition: environment.cc:365
absl::StatusOr< MPSolutionResponse > GurobiSolveProto(const MPModelRequest &request, GRBenv *gurobi_env)
double offset() const
Gets the constant term in the objective.
#define GRB_INT_ATTR_NUMCONSTRS
Definition: environment.h:157
int64_t value
#define GRB_OPTIMAL
Definition: environment.h:457
#define GRB_CB_MIP
Definition: environment.h:323
const Constraint * ct
bool variable_is_extracted(int var_index) const
#define GRB_NONBASIC_LOWER
Definition: environment.h:472
std::function< int(GRBmodel *model, const char *attrname, int first, int len, double *values)> GRBgetdblattrarray
Definition: environment.cc:106
const int INFO
Definition: log_severity.h:31
std::function< int(GRBmodel *model, int numnz, int *cind, double *cval, double lower, double upper, const char *constrname)> GRBaddrangeconstr
Definition: environment.cc:280