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