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