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