Google OR-Tools v9.11
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-2024 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#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;
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.
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 {
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.
546int GUROBI_STDCALL CallbackImpl(GRBmodel* model,
548 void* raw_model_and_callback) {
549 MPCallbackWithGurobiContext* const callback_with_context =
550 static_cast<MPCallbackWithGurobiContext*>(raw_model_and_callback);
551 CHECK(callback_with_context != nullptr);
552 CHECK(callback_with_context->context != nullptr);
553 CHECK(callback_with_context->callback != nullptr);
554 GurobiInternalCallbackContext gurobi_internal_context{
556 callback_with_context->context->UpdateFromGurobiState(
557 gurobi_internal_context);
558 callback_with_context->callback->RunCallback(callback_with_context->context);
559 return 0;
560}
561
562} // namespace
563
564void GurobiInterface::CheckedGurobiCall(int err) const {
565 ::operations_research::CheckedGurobiCall(err, global_env_);
566}
567
568void GurobiInterface::SetIntAttr(const char* name, int value) {
569 CheckedGurobiCall(GRBsetintattr(model_, name, value));
570}
571
572int GurobiInterface::GetIntAttr(const char* name) const {
573 int value;
574 CheckedGurobiCall(GRBgetintattr(model_, name, &value));
575 return value;
576}
577
578void GurobiInterface::SetDoubleAttr(const char* name, double value) {
579 CheckedGurobiCall(GRBsetdblattr(model_, name, value));
580}
581
582double GurobiInterface::GetDoubleAttr(const char* name) const {
583 double value;
584 CheckedGurobiCall(GRBgetdblattr(model_, name, &value));
585 return value;
586}
587
588void GurobiInterface::SetIntAttrElement(const char* name, int index,
589 int value) {
590 CheckedGurobiCall(GRBsetintattrelement(model_, name, index, value));
591}
592
593int GurobiInterface::GetIntAttrElement(const char* name, int index) const {
594 int value;
595 CheckedGurobiCall(GRBgetintattrelement(model_, name, index, &value));
596 return value;
597}
598
599void GurobiInterface::SetDoubleAttrElement(const char* name, int index,
600 double value) {
601 CheckedGurobiCall(GRBsetdblattrelement(model_, name, index, value));
602}
603double GurobiInterface::GetDoubleAttrElement(const char* name,
604 int index) const {
605 double value;
606 CheckedGurobiCall(GRBgetdblattrelement(model_, name, index, &value));
607 return value;
608}
609
610std::vector<double> GurobiInterface::GetDoubleAttrArray(const char* name,
611 int elements) {
612 std::vector<double> results(elements);
613 CheckedGurobiCall(
614 GRBgetdblattrarray(model_, name, 0, elements, results.data()));
615 return results;
616}
617
618void GurobiInterface::SetCharAttrElement(const char* name, int index,
619 char value) {
620 CheckedGurobiCall(GRBsetcharattrelement(model_, name, index, value));
621}
622char GurobiInterface::GetCharAttrElement(const char* name, int index) const {
623 char value;
624 CheckedGurobiCall(GRBgetcharattrelement(model_, name, index, &value));
625 return value;
626}
627
628// Creates a LP/MIP instance with the specified name and minimization objective.
629GurobiInterface::GurobiInterface(MPSolver* const solver, bool mip)
630 : MPSolverInterface(solver),
631 model_(nullptr),
632 global_env_(nullptr),
633 mip_(mip),
634 current_solution_index_(0) {
635 global_env_ = GetGurobiEnv().value();
636 CheckedGurobiCall(GRBnewmodel(global_env_, &model_, solver_->name_.c_str(),
637 0, // numvars
638 nullptr, // obj
639 nullptr, // lb
640 nullptr, // ub
641 nullptr, // vtype
642 nullptr)); // varnanes
644 CheckedGurobiCall(
646 CheckedGurobiCall(GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_THREADS,
647 absl::GetFlag(FLAGS_num_gurobi_threads)));
648}
649
651 CheckedGurobiCall(GRBfreemodel(model_));
652 GRBfreeenv(global_env_);
653}
654
655// ------ Model modifications and extraction -----
656
658 // We hold calls to GRBterminate() until the new model_ is ready.
659 const absl::MutexLock lock(&hold_interruptions_mutex_);
660
661 GRBmodel* old_model = model_;
662 CheckedGurobiCall(GRBnewmodel(global_env_, &model_, solver_->name_.c_str(),
663 0, // numvars
664 nullptr, // obj
665 nullptr, // lb
666 nullptr, // ub
667 nullptr, // vtype
668 nullptr)); // varnames
669
670 // Copy all existing parameters from the previous model to the new one. This
671 // ensures that if a user calls multiple times
672 // SetSolverSpecificParametersAsString() and then Reset() is called, we still
673 // take into account all parameters.
674 //
675 // The current code only reapplies the parameters stored in
676 // solver_specific_parameter_string_ at the start of the solve; other
677 // parameters set by previous calls are only kept in the Gurobi model.
678 //
679 // TODO - b/328604189: Fix logging issue upstream, switch to a different API
680 // for copying parameters, or avoid calling Reset() in more places.
681 CheckedGurobiCall(GRBcopyparams(GRBgetenv(model_), GRBgetenv(old_model)));
682
683 CheckedGurobiCall(GRBfreemodel(old_model));
684 old_model = nullptr;
685
687 mp_var_to_gurobi_var_.clear();
688 mp_cons_to_gurobi_linear_cons_.clear();
689 num_gurobi_vars_ = 0;
690 num_gurobi_linear_cons_ = 0;
691 had_nonincremental_change_ = false;
692}
693
698
699void GurobiInterface::SetVariableBounds(int var_index, double lb, double ub) {
701 if (!had_nonincremental_change_ && variable_is_extracted(var_index)) {
702 SetDoubleAttrElement(GRB_DBL_ATTR_LB, mp_var_to_gurobi_var_.at(var_index),
703 lb);
704 SetDoubleAttrElement(GRB_DBL_ATTR_UB, mp_var_to_gurobi_var_.at(var_index),
705 ub);
706 } else {
708 }
709}
710
713 if (!had_nonincremental_change_ && variable_is_extracted(index)) {
714 char type_var;
715 if (integer) {
716 type_var = GRB_INTEGER;
717 } else {
718 type_var = GRB_CONTINUOUS;
719 }
720 SetCharAttrElement(GRB_CHAR_ATTR_VTYPE, mp_var_to_gurobi_var_.at(index),
721 type_var);
722 } else {
724 }
725}
726
727void GurobiInterface::SetConstraintBounds(int index, double lb, double ub) {
730 had_nonincremental_change_ = true;
731 }
732 // TODO(user): this is nontrivial to make incremental:
733 // 1. Make sure it is a linear constraint (not an indicator or indicator
734 // range constraint).
735 // 2. Check if the sense of the constraint changes. If it was previously a
736 // range constraint, we can do nothing, and if it becomes a range
737 // constraint, we can do nothing. We could support range constraints if
738 // we tracked the auxiliary variable that is added with range
739 // constraints.
740}
741
745
747 had_nonincremental_change_ = true;
749 return !IsContinuous();
750}
751
755
757 const MPVariable* const variable,
758 double new_value, double old_value) {
760 if (!had_nonincremental_change_ && variable_is_extracted(variable->index()) &&
761 constraint_is_extracted(constraint->index())) {
762 // Cannot be const, GRBchgcoeffs needs non-const pointer.
763 int grb_var = mp_var_to_gurobi_var_.at(variable->index());
764 int grb_cons = mp_cons_to_gurobi_linear_cons_.at(constraint->index());
765 if (grb_cons < 0) {
766 had_nonincremental_change_ = true;
768 } else {
769 // TODO(user): investigate if this has bad performance.
770 CheckedGurobiCall(
771 GRBchgcoeffs(model_, 1, &grb_cons, &grb_var, &new_value));
772 }
773 } else {
775 }
776}
777
779 had_nonincremental_change_ = true;
781 // TODO(user): this is difficult to make incremental, like
782 // SetConstraintBounds(), because of the auxiliary Gurobi variables that
783 // range constraints introduce.
784}
785
787 double coefficient) {
789 if (!had_nonincremental_change_ && variable_is_extracted(variable->index())) {
790 SetDoubleAttrElement(GRB_DBL_ATTR_OBJ,
791 mp_var_to_gurobi_var_.at(variable->index()),
793 } else {
795 }
796}
797
800 if (!had_nonincremental_change_) {
801 SetDoubleAttr(GRB_DBL_ATTR_OBJCON, value);
802 } else {
804 }
805}
806
809 if (!had_nonincremental_change_) {
811 for (const auto& entry : solver_->objective_->coefficients_) {
812 SetObjectiveCoefficient(entry.first, 0.0);
813 }
814 } else {
816 }
817}
818
820 update_branching_priorities_ = true;
821}
822
823// ------ Query statistics on the solution and the solve ------
824
826 double iter;
828 CheckedGurobiCall(GRBgetdblattr(model_, GRB_DBL_ATTR_ITERCOUNT, &iter));
829 return static_cast<int64_t>(iter);
830}
831
832int64_t GurobiInterface::nodes() const {
833 if (mip_) {
835 return static_cast<int64_t>(GetDoubleAttr(GRB_DBL_ATTR_NODECOUNT));
836 } else {
837 LOG(DFATAL) << "Number of nodes only available for discrete problems.";
839 }
840}
841
842MPSolver::BasisStatus GurobiInterface::TransformGRBVarBasisStatus(
843 int gurobi_basis_status) const {
844 switch (gurobi_basis_status) {
845 case GRB_BASIC:
846 return MPSolver::BASIC;
851 case GRB_SUPERBASIC:
852 return MPSolver::FREE;
853 default:
854 LOG(DFATAL) << "Unknown GRB basis status.";
855 return MPSolver::FREE;
856 }
857}
858
859MPSolver::BasisStatus GurobiInterface::TransformGRBConstraintBasisStatus(
860 int gurobi_basis_status, int constraint_index) const {
861 const int grb_index = mp_cons_to_gurobi_linear_cons_.at(constraint_index);
862 if (grb_index < 0) {
863 LOG(DFATAL) << "Basis status not available for nonlinear constraints.";
864 return MPSolver::FREE;
865 }
866 switch (gurobi_basis_status) {
867 case GRB_BASIC:
868 return MPSolver::BASIC;
869 default: {
870 // Non basic.
871 double tolerance = 0.0;
872 CheckedGurobiCall(GRBgetdblparam(GRBgetenv(model_),
873 GRB_DBL_PAR_FEASIBILITYTOL, &tolerance));
874 const double slack = GetDoubleAttrElement(GRB_DBL_ATTR_SLACK, grb_index);
875 const char sense = GetCharAttrElement(GRB_CHAR_ATTR_SENSE, grb_index);
876 VLOG(4) << "constraint " << constraint_index << " , slack = " << slack
877 << " , sense = " << sense;
878 if (fabs(slack) <= tolerance) {
879 switch (sense) {
880 case GRB_EQUAL:
881 case GRB_LESS_EQUAL:
885 default:
886 return MPSolver::FREE;
887 }
888 } else {
889 return MPSolver::FREE;
890 }
891 }
892 }
893}
894
895// Returns the basis status of a row.
897 const int optim_status = GetIntAttr(GRB_INT_ATTR_STATUS);
898 if (optim_status != GRB_OPTIMAL && optim_status != GRB_SUBOPTIMAL) {
899 LOG(DFATAL) << "Basis status only available after a solution has "
900 << "been found.";
901 return MPSolver::FREE;
902 }
903 if (mip_) {
904 LOG(DFATAL) << "Basis status only available for continuous problems.";
905 return MPSolver::FREE;
906 }
907 const int grb_index = mp_cons_to_gurobi_linear_cons_.at(constraint_index);
908 if (grb_index < 0) {
909 LOG(DFATAL) << "Basis status not available for nonlinear constraints.";
910 return MPSolver::FREE;
911 }
912 const int gurobi_basis_status =
913 GetIntAttrElement(GRB_INT_ATTR_CBASIS, grb_index);
914 return TransformGRBConstraintBasisStatus(gurobi_basis_status,
916}
917
918// Returns the basis status of a column.
920 const int optim_status = GetIntAttr(GRB_INT_ATTR_STATUS);
921 if (optim_status != GRB_OPTIMAL && optim_status != GRB_SUBOPTIMAL) {
922 LOG(DFATAL) << "Basis status only available after a solution has "
923 << "been found.";
924 return MPSolver::FREE;
925 }
926 if (mip_) {
927 LOG(DFATAL) << "Basis status only available for continuous problems.";
928 return MPSolver::FREE;
929 }
930 const int grb_index = mp_var_to_gurobi_var_.at(variable_index);
931 const int gurobi_basis_status =
932 GetIntAttrElement(GRB_INT_ATTR_VBASIS, grb_index);
933 return TransformGRBVarBasisStatus(gurobi_basis_status);
934}
935
936// Extracts new variables.
938 const int total_num_vars = solver_->variables_.size();
939 if (total_num_vars > last_variable_index_) {
940 // Define new variables.
941 for (int j = last_variable_index_; j < total_num_vars; ++j) {
942 const MPVariable* const var = solver_->variables_.at(j);
943 set_variable_as_extracted(var->index(), true);
944 CheckedGurobiCall(GRBaddvar(
945 model_, 0, // numnz
946 nullptr, // vind
947 nullptr, // vval
948 solver_->objective_->GetCoefficient(var), var->lb(), var->ub(),
949 var->integer() && mip_ ? GRB_INTEGER : GRB_CONTINUOUS,
950 var->name().empty() ? nullptr : var->name().c_str()));
951 mp_var_to_gurobi_var_.push_back(num_gurobi_vars_++);
952 }
953 CheckedGurobiCall(GRBupdatemodel(model_));
954 // Add new variables to existing constraints.
955 std::vector<int> grb_cons_ind;
956 std::vector<int> grb_var_ind;
957 std::vector<double> coef;
958 for (int i = 0; i < last_constraint_index_; ++i) {
959 // If there was a nonincremental change/the model is not incremental (e.g.
960 // there is an indicator constraint), we should never enter this loop, as
961 // last_variable_index_ will be reset to zero before ExtractNewVariables()
962 // is called.
963 MPConstraint* const ct = solver_->constraints_[i];
964 const int grb_ct_idx = mp_cons_to_gurobi_linear_cons_.at(ct->index());
965 DCHECK_GE(grb_ct_idx, 0);
966 DCHECK(ct->indicator_variable() == nullptr);
967 for (const auto& entry : ct->coefficients_) {
968 const int var_index = entry.first->index();
970
972 grb_cons_ind.push_back(grb_ct_idx);
973 grb_var_ind.push_back(mp_var_to_gurobi_var_.at(var_index));
974 coef.push_back(entry.second);
975 }
976 }
977 }
978 if (!grb_cons_ind.empty()) {
979 CheckedGurobiCall(GRBchgcoeffs(model_, grb_cons_ind.size(),
980 grb_cons_ind.data(), grb_var_ind.data(),
981 coef.data()));
982 }
983 }
984 CheckedGurobiCall(GRBupdatemodel(model_));
985 DCHECK_EQ(GetIntAttr(GRB_INT_ATTR_NUMVARS), num_gurobi_vars_);
986}
987
989 int total_num_rows = solver_->constraints_.size();
990 if (last_constraint_index_ < total_num_rows) {
991 // Add each new constraint.
992 for (int row = last_constraint_index_; row < total_num_rows; ++row) {
993 MPConstraint* const ct = solver_->constraints_[row];
995 const int size = ct->coefficients_.size();
996 std::vector<int> grb_vars;
997 std::vector<double> coefs;
998 grb_vars.reserve(size);
999 coefs.reserve(size);
1000 for (const auto& entry : ct->coefficients_) {
1001 const int var_index = entry.first->index();
1003 grb_vars.push_back(mp_var_to_gurobi_var_.at(var_index));
1004 coefs.push_back(entry.second);
1005 }
1006 char* const name =
1007 ct->name().empty() ? nullptr : const_cast<char*>(ct->name().c_str());
1008 if (ct->indicator_variable() != nullptr) {
1009 const int grb_ind_var =
1010 mp_var_to_gurobi_var_.at(ct->indicator_variable()->index());
1011 if (ct->lb() > -std::numeric_limits<double>::infinity()) {
1012 CheckedGurobiCall(GRBaddgenconstrIndicator(
1013 model_, name, grb_ind_var, ct->indicator_value(), size,
1014 grb_vars.data(), coefs.data(),
1015 ct->ub() == ct->lb() ? GRB_EQUAL : GRB_GREATER_EQUAL, ct->lb()));
1016 }
1017 if (ct->ub() < std::numeric_limits<double>::infinity() &&
1018 ct->lb() != ct->ub()) {
1019 CheckedGurobiCall(GRBaddgenconstrIndicator(
1020 model_, name, grb_ind_var, ct->indicator_value(), size,
1021 grb_vars.data(), coefs.data(), GRB_LESS_EQUAL, ct->ub()));
1022 }
1023 mp_cons_to_gurobi_linear_cons_.push_back(-1);
1024 } else {
1025 // Using GRBaddrangeconstr for constraints that don't require it adds
1026 // a slack which is not always removed by presolve.
1027 if (ct->lb() == ct->ub()) {
1028 CheckedGurobiCall(GRBaddconstr(model_, size, grb_vars.data(),
1029 coefs.data(), GRB_EQUAL, ct->lb(),
1030 name));
1031 } else if (ct->lb() == -std::numeric_limits<double>::infinity()) {
1032 CheckedGurobiCall(GRBaddconstr(model_, size, grb_vars.data(),
1033 coefs.data(), GRB_LESS_EQUAL, ct->ub(),
1034 name));
1035 } else if (ct->ub() == std::numeric_limits<double>::infinity()) {
1036 CheckedGurobiCall(GRBaddconstr(model_, size, grb_vars.data(),
1037 coefs.data(), GRB_GREATER_EQUAL,
1038 ct->lb(), name));
1039 } else {
1040 CheckedGurobiCall(GRBaddrangeconstr(model_, size, grb_vars.data(),
1041 coefs.data(), ct->lb(), ct->ub(),
1042 name));
1043 // NOTE(user): range constraints implicitly add an extra variable
1044 // to the model.
1045 num_gurobi_vars_++;
1046 }
1047 mp_cons_to_gurobi_linear_cons_.push_back(num_gurobi_linear_cons_++);
1048 }
1049 }
1050 }
1051 CheckedGurobiCall(GRBupdatemodel(model_));
1052 DCHECK_EQ(GetIntAttr(GRB_INT_ATTR_NUMCONSTRS), num_gurobi_linear_cons_);
1053}
1054
1059
1060// ------ Parameters -----
1061
1062void GurobiInterface::SetParameters(const MPSolverParameters& param) {
1063 SetCommonParameters(param);
1064 if (mip_) {
1065 SetMIPParameters(param);
1066 }
1067}
1068
1069bool GurobiInterface::SetSolverSpecificParametersAsString(
1070 const std::string& parameters) {
1071 return SetSolverSpecificParameters(parameters, GRBgetenv(model_)).ok();
1072}
1073
1074void GurobiInterface::SetRelativeMipGap(double value) {
1075 if (mip_) {
1076 CheckedGurobiCall(
1078 } else {
1079 LOG(WARNING) << "The relative MIP gap is only available "
1080 << "for discrete problems.";
1081 }
1082}
1083
1084// Gurobi has two different types of primal tolerance (feasibility tolerance):
1085// constraint and integrality. We need to set them both.
1086// See:
1087// http://www.gurobi.com/documentation/6.0/refman/feasibilitytol.html
1088// and
1089// http://www.gurobi.com/documentation/6.0/refman/intfeastol.html
1090void GurobiInterface::SetPrimalTolerance(double value) {
1091 CheckedGurobiCall(
1093 CheckedGurobiCall(
1095}
1096
1097// As opposed to primal (feasibility) tolerance, the dual (optimality) tolerance
1098// applies only to the reduced costs in the improving direction.
1099// See:
1100// http://www.gurobi.com/documentation/6.0/refman/optimalitytol.html
1101void GurobiInterface::SetDualTolerance(double value) {
1102 CheckedGurobiCall(
1104}
1105
1106void GurobiInterface::SetPresolveMode(int value) {
1107 switch (value) {
1109 CheckedGurobiCall(
1111 break;
1112 }
1114 CheckedGurobiCall(
1116 break;
1117 }
1118 default: {
1120 }
1121 }
1122}
1123
1124// Sets the scaling mode.
1125void GurobiInterface::SetScalingMode(int value) {
1126 switch (value) {
1128 CheckedGurobiCall(
1130 break;
1132 CheckedGurobiCall(
1134 CheckedGurobiCall(
1136 break;
1137 default:
1138 // Leave the parameters untouched.
1139 break;
1140 }
1141}
1142
1143// Sets the LP algorithm : primal, dual or barrier. Note that GRB
1144// offers automatic selection
1145void GurobiInterface::SetLpAlgorithm(int value) {
1146 switch (value) {
1148 CheckedGurobiCall(GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_METHOD,
1150 break;
1152 CheckedGurobiCall(GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_METHOD,
1154 break;
1156 CheckedGurobiCall(GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_METHOD,
1158 break;
1159 default:
1161 value);
1162 }
1163}
1164
1165int GurobiInterface::SolutionCount() const {
1166 return GetIntAttr(GRB_INT_ATTR_SOLCOUNT);
1167}
1168
1169bool GurobiInterface::ModelIsNonincremental() const {
1170 for (const MPConstraint* c : solver_->constraints()) {
1171 if (c->indicator_variable() != nullptr) {
1172 return true;
1173 }
1174 }
1175 return false;
1176}
1177
1179 WallTimer timer;
1180 timer.Start();
1181
1184 ModelIsNonincremental() || had_nonincremental_change_) {
1185 Reset();
1186 }
1187
1188 // Set log level.
1189 CheckedGurobiCall(
1191
1192 ExtractModel();
1193 // Sync solver.
1194 CheckedGurobiCall(GRBupdatemodel(model_));
1195 VLOG(1) << absl::StrFormat("Model built in %s.",
1196 absl::FormatDuration(timer.GetDuration()));
1197
1198 // Set solution hints if any.
1199 for (const std::pair<const MPVariable*, double>& p :
1200 solver_->solution_hint_) {
1201 SetDoubleAttrElement(GRB_DBL_ATTR_START,
1202 mp_var_to_gurobi_var_.at(p.first->index()), p.second);
1203 }
1204
1205 // Pass branching priority annotations if at least one has been updated.
1206 if (update_branching_priorities_) {
1207 for (const MPVariable* var : solver_->variables_) {
1208 SetIntAttrElement(GRB_INT_ATTR_BRANCHPRIORITY,
1209 mp_var_to_gurobi_var_.at(var->index()),
1210 var->branching_priority());
1211 }
1212 update_branching_priorities_ = false;
1213 }
1214
1215 // Time limit.
1216 if (solver_->time_limit() != 0) {
1217 VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
1218 CheckedGurobiCall(GRBsetdblparam(GRBgetenv(model_), GRB_DBL_PAR_TIMELIMIT,
1220 }
1221
1222 // We first set our internal MPSolverParameters from 'param' and then set
1223 // any user-specified internal solver parameters via
1224 // solver_specific_parameter_string_.
1225 // Default MPSolverParameters can override custom parameters (for example for
1226 // presolving) and therefore we apply MPSolverParameters first.
1227 SetParameters(param);
1229 solver_->solver_specific_parameter_string_);
1230
1231 std::unique_ptr<GurobiMPCallbackContext> gurobi_context;
1232 MPCallbackWithGurobiContext mp_callback_with_context;
1233 int gurobi_precrush = 0;
1234 int gurobi_lazy_constraint = 0;
1235 if (callback_ == nullptr) {
1236 CheckedGurobiCall(GRBsetcallbackfunc(model_, nullptr, nullptr));
1237 } else {
1238 gurobi_context = std::make_unique<GurobiMPCallbackContext>(
1239 global_env_, &mp_var_to_gurobi_var_, num_gurobi_vars_,
1240 callback_->might_add_cuts(), callback_->might_add_lazy_constraints());
1241 mp_callback_with_context.context = gurobi_context.get();
1242 mp_callback_with_context.callback = callback_;
1243 CheckedGurobiCall(GRBsetcallbackfunc(
1244 model_, CallbackImpl, static_cast<void*>(&mp_callback_with_context)));
1245 gurobi_precrush = callback_->might_add_cuts();
1246 gurobi_lazy_constraint = callback_->might_add_lazy_constraints();
1247 }
1248 CheckedGurobiCall(
1249 GRBsetintparam(GRBgetenv(model_), GRB_INT_PAR_PRECRUSH, gurobi_precrush));
1250 CheckedGurobiCall(GRBsetintparam(
1251 GRBgetenv(model_), GRB_INT_PAR_LAZYCONSTRAINTS, gurobi_lazy_constraint));
1252
1253 // Logs all parameters not at default values in the model environment.
1254 if (!quiet()) {
1255 std::cout << GurobiParamInfoForLogging(GRBgetenv(model_),
1256 /*one_liner_output=*/true);
1257 }
1258
1259 // Solve
1260 timer.Restart();
1261 const int status = GRBoptimize(model_);
1262
1263 if (status) {
1264 VLOG(1) << "Failed to optimize MIP." << GRBgeterrormsg(global_env_);
1265 } else {
1266 VLOG(1) << absl::StrFormat("Solved in %s.",
1267 absl::FormatDuration(timer.GetDuration()));
1268 }
1269
1270 // Get the status.
1271 const int optimization_status = GetIntAttr(GRB_INT_ATTR_STATUS);
1272 VLOG(1) << absl::StrFormat("Solution status %d.\n", optimization_status);
1273 const int solution_count = SolutionCount();
1274
1275 switch (optimization_status) {
1276 case GRB_OPTIMAL:
1278 break;
1279 case GRB_INFEASIBLE:
1281 break;
1282 case GRB_UNBOUNDED:
1284 break;
1285 case GRB_INF_OR_UNBD:
1286 // TODO(user): We could introduce our own "infeasible or
1287 // unbounded" status.
1289 break;
1290 default: {
1291 if (solution_count > 0) {
1293 } else {
1295 }
1296 break;
1297 }
1298 }
1299
1302 const int error =
1304 LOG_IF(WARNING, error != 0)
1305 << "Best objective bound is not available, error=" << error
1306 << ", message=" << GRBgeterrormsg(global_env_);
1307 VLOG(1) << "best bound = " << best_objective_bound_;
1308 }
1309
1310 if (solution_count > 0 && (result_status_ == MPSolver::FEASIBLE ||
1312 current_solution_index_ = 0;
1313 // Get the results.
1314 objective_value_ = GetDoubleAttr(GRB_DBL_ATTR_OBJVAL);
1315 VLOG(1) << "objective = " << objective_value_;
1316
1317 {
1318 const std::vector<double> grb_variable_values =
1319 GetDoubleAttrArray(GRB_DBL_ATTR_X, num_gurobi_vars_);
1320 for (int i = 0; i < solver_->variables_.size(); ++i) {
1321 MPVariable* const var = solver_->variables_[i];
1322 const double val = grb_variable_values.at(mp_var_to_gurobi_var_.at(i));
1323 var->set_solution_value(val);
1324 VLOG(3) << var->name() << ", value = " << val;
1325 }
1326 }
1327 if (!mip_) {
1328 {
1329 const std::vector<double> grb_reduced_costs =
1330 GetDoubleAttrArray(GRB_DBL_ATTR_RC, num_gurobi_vars_);
1331 for (int i = 0; i < solver_->variables_.size(); ++i) {
1332 MPVariable* const var = solver_->variables_[i];
1333 const double rc = grb_reduced_costs.at(mp_var_to_gurobi_var_.at(i));
1334 var->set_reduced_cost(rc);
1335 VLOG(4) << var->name() << ", reduced cost = " << rc;
1336 }
1337 }
1338
1339 {
1340 std::vector<double> grb_dual_values =
1341 GetDoubleAttrArray(GRB_DBL_ATTR_PI, num_gurobi_linear_cons_);
1342 for (int i = 0; i < solver_->constraints_.size(); ++i) {
1343 MPConstraint* const ct = solver_->constraints_[i];
1344 const double dual_value =
1345 grb_dual_values.at(mp_cons_to_gurobi_linear_cons_.at(i));
1346 ct->set_dual_value(dual_value);
1347 VLOG(4) << "row " << ct->index() << ", dual value = " << dual_value;
1348 }
1349 }
1350 }
1351 }
1352
1354 GRBresetparams(GRBgetenv(model_));
1355 return result_status_;
1356}
1357
1359 // Next solution only supported for MIP
1360 if (!mip_) return false;
1361
1362 // Make sure we have successfully solved the problem and not modified it.
1364 return false;
1365 }
1366 // Check if we are out of solutions.
1367 if (current_solution_index_ + 1 >= SolutionCount()) {
1368 return false;
1369 }
1370 current_solution_index_++;
1371
1372 CheckedGurobiCall(GRBsetintparam(
1373 GRBgetenv(model_), GRB_INT_PAR_SOLUTIONNUMBER, current_solution_index_));
1374
1376 const std::vector<double> grb_variable_values =
1377 GetDoubleAttrArray(GRB_DBL_ATTR_XN, num_gurobi_vars_);
1378
1379 for (int i = 0; i < solver_->variables_.size(); ++i) {
1380 MPVariable* const var = solver_->variables_[i];
1381 var->set_solution_value(
1382 grb_variable_values.at(mp_var_to_gurobi_var_.at(i)));
1383 }
1384 // TODO(user): This reset may not be necessary, investigate.
1385 GRBresetparams(GRBgetenv(model_));
1386 return true;
1387}
1388
1389void GurobiInterface::Write(const std::string& filename) {
1390 if (sync_status_ == MUST_RELOAD) {
1391 Reset();
1392 }
1393 ExtractModel();
1394 // Sync solver.
1395 CheckedGurobiCall(GRBupdatemodel(model_));
1396 VLOG(1) << "Writing Gurobi model file \"" << filename << "\".";
1397 const int status = GRBwrite(model_, filename.c_str());
1398 if (status) {
1399 LOG(WARNING) << "Failed to write MIP." << GRBgeterrormsg(global_env_);
1400 }
1401}
1402
1404 return new GurobiInterface(solver, mip);
1405}
1406
1408 callback_ = mp_callback;
1409}
1410
1411} // namespace operations_research
IntegerValue size
absl::Duration GetDuration() const
Definition timer.h:49
void Restart()
Definition timer.h:36
void Start()
When Start() is called multiple times, only the most recent is used.
Definition timer.h:32
bool IsMIP() const override
Returns true if the problem is discrete and linear.
void Write(const std::string &filename) override
Writes the model.
void ExtractObjective() override
Extracts the objective.
void * underlying_solver() override
Returns the underlying solver.
void SetCoefficient(MPConstraint *constraint, const MPVariable *variable, double new_value, double old_value) override
Changes a coefficient in a constraint.
GurobiInterface(MPSolver *solver, bool mip)
Constructor that takes a name for the underlying GRB solver.
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
Modifies bounds.
void ClearConstraint(MPConstraint *constraint) override
Clears a constraint from all its terms.
void SetCallback(MPCallback *mp_callback) override
See MPSolver::SetCallback() for details.
bool SupportsDirectlySolveProto(std::atomic< bool > *interrupt) const override
--— Directly solve proto is supported without interrupt —
void ClearObjective() override
Clears the objective from all its terms.
void SetObjectiveOffset(double value) override
Changes the constant term in the linear objective.
int64_t nodes() const override
Number of branch-and-bound nodes. Only available for discrete problems.
void ExtractNewConstraints() override
Extracts the constraints that have not been extracted yet.
void Reset() override
---— Model modifications and extraction --—
double ComputeExactConditionNumber() const override
void SetObjectiveCoefficient(const MPVariable *variable, double coefficient) override
Changes a coefficient in the linear objective.
void SetConstraintBounds(int row_index, double lb, double ub) override
Modify bounds of an extracted variable.
MPSolver::BasisStatus column_status(int variable_index) const override
Returns the basis status of a column.
void SetVariableInteger(int var_index, bool integer) override
Modifies integrality of an extracted variable.
std::string SolverVersion() const override
Returns a string describing the underlying solver and its version.
void BranchingPriorityChangedForVariable(int var_index) override
void ExtractNewVariables() override
Extracts new variables.
bool NextSolution() override
Iterates through the solutions in Gurobi's solution pool.
bool AddIndicatorConstraint(MPConstraint *ct) override
MPSolver::BasisStatus row_status(int constraint_index) const override
Returns the basis status of a row.
int64_t iterations() const override
---— Query statistics on the solution and the solve ---—
void SetOptimizationDirection(bool maximize) override
Sets the optimization direction (min/max).
bool IsLP() const override
Returns true if the problem is continuous and linear.
void AddVariable(MPVariable *var) override
Adds variable incrementally.
void AddRowConstraint(MPConstraint *ct) override
Adds Constraint incrementally.
int index() const
Returns the index of the constraint in the MPSolver::constraints_.
double offset() const
Gets the constant term in the objective.
void set_variable_as_extracted(int var_index, bool extracted)
static constexpr int64_t kUnknownNumberOfIterations
friend class MPConstraint
To access the maximize_ bool and the MPSolver.
void set_constraint_as_extracted(int ct_index, bool extracted)
void ResetExtractionInformation()
Resets the extraction information.
int last_variable_index_
Index in MPSolver::constraints_ of last variable extracted.
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
Sets a supported integer parameter to an unsupported value.
int last_constraint_index_
Index in MPSolver::variables_ of last constraint extracted.
bool variable_is_extracted(int var_index) const
bool quiet() const
Returns the boolean indicating the verbosity of the solver output.
bool constraint_is_extracted(int ct_index) const
static constexpr int64_t kUnknownNumberOfNodes
void ExtractModel()
Extracts model stored in MPSolver.
double objective_value_
The value of the objective function.
double best_objective_bound_
The value of the best objective bound. Used only for MIP solvers.
bool maximize_
Optimization direction.
void SetMIPParameters(const MPSolverParameters &param)
Sets MIP specific parameters in the underlying solver.
bool quiet_
Boolean indicator for the verbosity of the solver output.
void SetCommonParameters(const MPSolverParameters &param)
Sets parameters common to LP and MIP in the underlying solver.
bool CheckSolutionIsSynchronizedAndExists() const
Handy shortcut to do both checks above (it is often used).
SynchronizationStatus sync_status_
Indicates whether the model and the solution are synchronized.
@ 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.
const MPObjective & Objective() const
bool SetSolverSpecificParametersAsString(const std::string &parameters)
const std::vector< MPConstraint * > & constraints() const
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
const std::string name
A name for logging purposes.
const Constraint * ct
int64_t value
IntVar * var
int64_t coef
absl::Status status
Definition g_gurobi.cc:44
#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
#define GUROBI_STDCALL
Definition environment.h:27
struct _GRBenv GRBenv
Definition environment.h:32
#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
Definition environment.h:31
#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
GRBmodel * model
ABSL_FLAG(int, num_gurobi_threads, 0, "Number of threads available for Gurobi.")
void * gurobi_internal_callback_data
GurobiMPCallbackContext * context
MPCallback * callback
int where
int constraint_index
int index
RowIndex row
Definition markowitz.cc:186
double solution
In SWIG mode, we don't want anything besides these top-level includes.
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
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
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:41
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
MPSolverInterface * BuildGurobiInterface(bool mip, MPSolver *const solver)
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
int64_t coefficient
int var_index
Definition search.cc:3268