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