Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
gurobi_proto_solver.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
15
16#include <algorithm>
17#include <cmath>
18#include <cstdint>
19#include <limits>
20#include <memory>
21#include <numeric>
22#include <optional>
23#include <string>
24#include <vector>
25
26#include "absl/base/attributes.h"
27#include "absl/cleanup/cleanup.h"
28#include "absl/log/check.h"
29#include "absl/status/status.h"
30#include "absl/status/statusor.h"
31#include "absl/strings/str_cat.h"
32#include "absl/strings/str_format.h"
33#include "absl/strings/str_join.h"
34#include "absl/strings/str_split.h"
35#include "absl/strings/string_view.h"
36#include "absl/time/clock.h"
37#include "absl/time/time.h"
38#include "absl/types/optional.h"
41#include "ortools/base/timer.h"
43#include "ortools/linear_solver/linear_solver.pb.h"
46
47namespace operations_research {
48
49namespace {
50constexpr int GRB_OK = 0;
51
52inline absl::Status GurobiCodeToUtilStatus(int error_code,
53 const char* source_file,
54 int source_line,
55 const char* statement,
56 GRBenv* const env) {
57 if (error_code == GRB_OK) return absl::OkStatus();
58 return absl::InvalidArgumentError(absl::StrFormat(
59 "Gurobi error code %d (file '%s', line %d) on '%s': %s", error_code,
60 source_file, source_line, statement, GRBgeterrormsg(env)));
61}
62
63int AddIndicatorConstraint(const MPGeneralConstraintProto& gen_cst,
64 GRBmodel* gurobi_model,
65 std::vector<int>* tmp_variables,
66 std::vector<double>* tmp_coefficients) {
67 CHECK(gurobi_model != nullptr);
68 CHECK(tmp_variables != nullptr);
69 CHECK(tmp_coefficients != nullptr);
70
71 const auto& ind_cst = gen_cst.indicator_constraint();
72 MPConstraintProto cst = ind_cst.constraint();
73 if (cst.lower_bound() > -std::numeric_limits<double>::infinity()) {
74 int status = GRBaddgenconstrIndicator(
75 gurobi_model, gen_cst.name().c_str(), ind_cst.var_index(),
76 ind_cst.var_value(), cst.var_index_size(),
77 cst.mutable_var_index()->mutable_data(),
78 cst.mutable_coefficient()->mutable_data(),
79 cst.upper_bound() == cst.lower_bound() ? GRB_EQUAL : GRB_GREATER_EQUAL,
80 cst.lower_bound());
81 if (status != GRB_OK) return status;
82 }
83 if (cst.upper_bound() < std::numeric_limits<double>::infinity() &&
84 cst.lower_bound() != cst.upper_bound()) {
85 return GRBaddgenconstrIndicator(gurobi_model, gen_cst.name().c_str(),
86 ind_cst.var_index(), ind_cst.var_value(),
87 cst.var_index_size(),
88 cst.mutable_var_index()->mutable_data(),
89 cst.mutable_coefficient()->mutable_data(),
90 GRB_LESS_EQUAL, cst.upper_bound());
91 }
92
93 return GRB_OK;
94}
95
96int AddSosConstraint(const MPSosConstraint& sos_cst, GRBmodel* gurobi_model,
97 std::vector<int>* tmp_variables,
98 std::vector<double>* tmp_weights) {
99 CHECK(gurobi_model != nullptr);
100 CHECK(tmp_variables != nullptr);
101 CHECK(tmp_weights != nullptr);
102
103 tmp_variables->resize(sos_cst.var_index_size(), 0);
104 for (int v = 0; v < sos_cst.var_index_size(); ++v) {
105 (*tmp_variables)[v] = sos_cst.var_index(v);
106 }
107 tmp_weights->resize(sos_cst.var_index_size(), 0);
108 if (sos_cst.weight_size() == sos_cst.var_index_size()) {
109 for (int w = 0; w < sos_cst.weight_size(); ++w) {
110 (*tmp_weights)[w] = sos_cst.weight(w);
111 }
112 } else {
113 DCHECK_EQ(sos_cst.weight_size(), 0);
114 // Gurobi requires variable weights in their SOS constraints.
115 std::iota(tmp_weights->begin(), tmp_weights->end(), 1);
116 }
117
118 std::vector<int> types = {sos_cst.type() == MPSosConstraint::SOS1_DEFAULT
120 : GRB_SOS_TYPE2};
121 std::vector<int> begins = {0};
122 return GRBaddsos(gurobi_model, /*numsos=*/1,
123 /*nummembers=*/sos_cst.var_index_size(),
124 /*types=*/types.data(),
125 /*beg=*/begins.data(), /*ind=*/tmp_variables->data(),
126 /*weight*/ tmp_weights->data());
127}
128
129int AddQuadraticConstraint(const MPGeneralConstraintProto& gen_cst,
130 GRBmodel* gurobi_model) {
131 CHECK(gurobi_model != nullptr);
132 constexpr double kInfinity = std::numeric_limits<double>::infinity();
133
134 CHECK(gen_cst.has_quadratic_constraint());
135 const MPQuadraticConstraint& quad_cst = gen_cst.quadratic_constraint();
136
137 auto addqconstr = [](GRBmodel* gurobi_model, MPQuadraticConstraint quad_cst,
138 char sense, double rhs, const std::string& name) {
139 return GRBaddqconstr(
140 gurobi_model,
141 /*numlnz=*/quad_cst.var_index_size(),
142 /*lind=*/quad_cst.mutable_var_index()->mutable_data(),
143 /*lval=*/quad_cst.mutable_coefficient()->mutable_data(),
144 /*numqnz=*/quad_cst.qvar1_index_size(),
145 /*qrow=*/quad_cst.mutable_qvar1_index()->mutable_data(),
146 /*qcol=*/quad_cst.mutable_qvar2_index()->mutable_data(),
147 /*qval=*/quad_cst.mutable_qcoefficient()->mutable_data(),
148 /*sense=*/sense,
149 /*rhs=*/rhs,
150 /*QCname=*/name.c_str());
151 };
152
153 if (quad_cst.has_lower_bound() && quad_cst.lower_bound() > -kInfinity) {
154 const int grb_status =
155 addqconstr(gurobi_model, gen_cst.quadratic_constraint(),
156 GRB_GREATER_EQUAL, quad_cst.lower_bound(),
157 gen_cst.has_name() ? gen_cst.name() + "_lb" : "");
158 if (grb_status != GRB_OK) return grb_status;
159 }
160 if (quad_cst.has_upper_bound() && quad_cst.upper_bound() < kInfinity) {
161 const int grb_status =
162 addqconstr(gurobi_model, gen_cst.quadratic_constraint(), GRB_LESS_EQUAL,
163 quad_cst.upper_bound(),
164 gen_cst.has_name() ? gen_cst.name() + "_ub" : "");
165 if (grb_status != GRB_OK) return grb_status;
166 }
167
168 return GRB_OK;
169}
170
171int AddAndConstraint(const MPGeneralConstraintProto& gen_cst,
172 GRBmodel* gurobi_model, std::vector<int>* tmp_variables) {
173 CHECK(gurobi_model != nullptr);
174 CHECK(tmp_variables != nullptr);
175
176 auto and_cst = gen_cst.and_constraint();
177 return GRBaddgenconstrAnd(
178 gurobi_model,
179 /*name=*/gen_cst.name().c_str(),
180 /*resvar=*/and_cst.resultant_var_index(),
181 /*nvars=*/and_cst.var_index_size(),
182 /*vars=*/and_cst.mutable_var_index()->mutable_data());
183}
184
185int AddOrConstraint(const MPGeneralConstraintProto& gen_cst,
186 GRBmodel* gurobi_model, std::vector<int>* tmp_variables) {
187 CHECK(gurobi_model != nullptr);
188 CHECK(tmp_variables != nullptr);
189
190 auto or_cst = gen_cst.or_constraint();
191 return GRBaddgenconstrOr(gurobi_model,
192 /*name=*/gen_cst.name().c_str(),
193 /*resvar=*/or_cst.resultant_var_index(),
194 /*nvars=*/or_cst.var_index_size(),
195 /*vars=*/or_cst.mutable_var_index()->mutable_data());
196}
197
198int AddMinConstraint(const MPGeneralConstraintProto& gen_cst,
199 GRBmodel* gurobi_model, std::vector<int>* tmp_variables) {
200 CHECK(gurobi_model != nullptr);
201 CHECK(tmp_variables != nullptr);
202
203 auto min_cst = gen_cst.min_constraint();
204 return GRBaddgenconstrMin(
205 gurobi_model,
206 /*name=*/gen_cst.name().c_str(),
207 /*resvar=*/min_cst.resultant_var_index(),
208 /*nvars=*/min_cst.var_index_size(),
209 /*vars=*/min_cst.mutable_var_index()->mutable_data(),
210 /*constant=*/min_cst.has_constant()
211 ? min_cst.constant()
212 : std::numeric_limits<double>::infinity());
213}
214
215int AddMaxConstraint(const MPGeneralConstraintProto& gen_cst,
216 GRBmodel* gurobi_model, std::vector<int>* tmp_variables) {
217 CHECK(gurobi_model != nullptr);
218 CHECK(tmp_variables != nullptr);
219
220 auto max_cst = gen_cst.max_constraint();
221 return GRBaddgenconstrMax(
222 gurobi_model,
223 /*name=*/gen_cst.name().c_str(),
224 /*resvar=*/max_cst.resultant_var_index(),
225 /*nvars=*/max_cst.var_index_size(),
226 /*vars=*/max_cst.mutable_var_index()->mutable_data(),
227 /*constant=*/max_cst.has_constant()
228 ? max_cst.constant()
229 : -std::numeric_limits<double>::infinity());
230}
231} // namespace
232
233absl::Status SetSolverSpecificParameters(absl::string_view parameters,
234 GRBenv* gurobi) {
235 if (parameters.empty()) return absl::OkStatus();
236 std::vector<std::string> error_messages;
237 for (absl::string_view line : absl::StrSplit(parameters, '\n')) {
238 // Empty lines are simply ignored.
239 if (line.empty()) continue;
240 // Comment tokens end at the next new-line, or the end of the string.
241 // The first character must be '#'
242 if (line[0] == '#') continue;
243 for (absl::string_view token :
244 absl::StrSplit(line, ',', absl::SkipWhitespace())) {
245 if (token.empty()) continue;
246 std::vector<std::string> key_value =
247 absl::StrSplit(token, absl::ByAnyChar(" ="), absl::SkipWhitespace());
248 // If one parameter fails, we keep processing the list of parameters.
249 if (key_value.size() != 2) {
250 const std::string current_message =
251 absl::StrCat("Cannot parse parameter '", token,
252 "'. Expected format is 'ParameterName value' or "
253 "'ParameterName=value'");
254 error_messages.push_back(current_message);
255 continue;
256 }
257 const int gurobi_code =
258 GRBsetparam(gurobi, key_value[0].c_str(), key_value[1].c_str());
259 if (gurobi_code != GRB_OK) {
260 const std::string current_message = absl::StrCat(
261 "Error setting parameter '", key_value[0], "' to value '",
262 key_value[1], "': ", GRBgeterrormsg(gurobi));
263 error_messages.push_back(current_message);
264 continue;
265 }
266 VLOG(2) << absl::StrCat("Set parameter '", key_value[0], "' to value '",
267 key_value[1]);
268 }
269 }
270
271 if (error_messages.empty()) return absl::OkStatus();
272 return absl::InvalidArgumentError(absl::StrJoin(error_messages, "\n"));
273}
274
275absl::StatusOr<MPSolutionResponse> GurobiSolveProto(
276 LazyMutableCopy<MPModelRequest> request, GRBenv* gurobi_env) {
277 MPSolutionResponse response;
278 const std::optional<LazyMutableCopy<MPModelProto>> optional_model =
279 GetMPModelOrPopulateResponse(request, &response);
280 if (!optional_model) return response;
281 const MPModelProto& model = **optional_model;
282
283 // We set `gurobi_env` to point to a new environment if no existing one is
284 // provided. We must make sure that we free this environment when we exit this
285 // function.
286 bool gurobi_env_was_created = false;
287 auto gurobi_env_deleter = absl::MakeCleanup([&]() {
288 if (gurobi_env_was_created && gurobi_env != nullptr) {
289 GRBfreeenv(gurobi_env);
290 }
291 });
292 if (gurobi_env == nullptr) {
293 ASSIGN_OR_RETURN(gurobi_env, GetGurobiEnv());
294 gurobi_env_was_created = true;
295 }
296
297 GRBmodel* gurobi_model = nullptr;
298 auto gurobi_model_deleter = absl::MakeCleanup([&]() {
299 const int error_code = GRBfreemodel(gurobi_model);
300 LOG_IF(DFATAL, error_code != GRB_OK)
301 << "GRBfreemodel failed with error " << error_code << ": "
302 << GRBgeterrormsg(gurobi_env);
303 });
304
305// `gurobi_env` references ther GRBenv argument.
306#define RETURN_IF_GUROBI_ERROR(x) \
307 RETURN_IF_ERROR( \
308 GurobiCodeToUtilStatus(x, __FILE__, __LINE__, #x, gurobi_env));
309
310 RETURN_IF_GUROBI_ERROR(GRBnewmodel(gurobi_env, &gurobi_model,
311 model.name().c_str(),
312 /*numvars=*/0,
313 /*obj=*/nullptr,
314 /*lb=*/nullptr,
315 /*ub=*/nullptr,
316 /*vtype=*/nullptr,
317 /*varnames=*/nullptr));
318 GRBenv* const model_env = GRBgetenv(gurobi_model);
319
322 request->enable_internal_solver_output()));
323 if (request->has_solver_specific_parameters()) {
324 const auto parameters_status = SetSolverSpecificParameters(
325 request->solver_specific_parameters(), model_env);
326 if (!parameters_status.ok()) {
327 response.set_status(MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS);
328 response.set_status_str(
329 std::string(parameters_status.message())); // NOLINT
330 return response;
331 }
332 }
333 if (request->solver_time_limit_seconds() > 0) {
336 request->solver_time_limit_seconds()));
337 }
338
339 const int variable_size = model.variable_size();
340 bool has_integer_variables = false;
341 {
342 std::vector<double> obj_coeffs(variable_size, 0);
343 std::vector<double> lb(variable_size);
344 std::vector<double> ub(variable_size);
345 std::vector<char> ctype(variable_size);
346 std::vector<const char*> varnames(variable_size);
347 for (int v = 0; v < variable_size; ++v) {
348 const MPVariableProto& variable = model.variable(v);
349 obj_coeffs[v] = variable.objective_coefficient();
350 lb[v] = variable.lower_bound();
351 ub[v] = variable.upper_bound();
352 ctype[v] = variable.is_integer() &&
353 request->solver_type() ==
354 MPModelRequest::GUROBI_MIXED_INTEGER_PROGRAMMING
357 if (variable.is_integer()) has_integer_variables = true;
358 if (!variable.name().empty()) varnames[v] = variable.name().c_str();
359 }
360
362 GRBaddvars(gurobi_model, variable_size, 0, nullptr, nullptr, nullptr,
363 /*obj=*/obj_coeffs.data(),
364 /*lb=*/lb.data(), /*ub=*/ub.data(), /*vtype=*/ctype.data(),
365 /*varnames=*/const_cast<char**>(varnames.data())));
366
367 // Set solution hints if any.
368 for (int i = 0; i < model.solution_hint().var_index_size(); ++i) {
370 gurobi_model, GRB_DBL_ATTR_START, model.solution_hint().var_index(i),
371 model.solution_hint().var_value(i)));
372 }
373 }
374
375 {
376 std::vector<int> ct_variables;
377 std::vector<double> ct_coefficients;
378 for (int c = 0; c < model.constraint_size(); ++c) {
379 const MPConstraintProto& constraint = model.constraint(c);
380 const int size = constraint.var_index_size();
381 ct_variables.resize(size, 0);
382 ct_coefficients.resize(size, 0);
383 for (int i = 0; i < size; ++i) {
384 ct_variables[i] = constraint.var_index(i);
385 ct_coefficients[i] = constraint.coefficient(i);
386 }
387 // Using GRBaddrangeconstr for constraints that don't require it adds
388 // a slack which is not always removed by presolve.
389 if (constraint.lower_bound() == constraint.upper_bound()) {
391 gurobi_model, /*numnz=*/size, /*cind=*/ct_variables.data(),
392 /*cval=*/ct_coefficients.data(),
393 /*sense=*/GRB_EQUAL, /*rhs=*/constraint.lower_bound(),
394 /*constrname=*/constraint.name().c_str()));
395 } else if (constraint.lower_bound() ==
396 -std::numeric_limits<double>::infinity()) {
398 gurobi_model, /*numnz=*/size, /*cind=*/ct_variables.data(),
399 /*cval=*/ct_coefficients.data(),
400 /*sense=*/GRB_LESS_EQUAL, /*rhs=*/constraint.upper_bound(),
401 /*constrname=*/constraint.name().c_str()));
402 } else if (constraint.upper_bound() ==
403 std::numeric_limits<double>::infinity()) {
405 gurobi_model, /*numnz=*/size, /*cind=*/ct_variables.data(),
406 /*cval=*/ct_coefficients.data(),
407 /*sense=*/GRB_GREATER_EQUAL, /*rhs=*/constraint.lower_bound(),
408 /*constrname=*/constraint.name().c_str()));
409 } else {
411 gurobi_model, /*numnz=*/size, /*cind=*/ct_variables.data(),
412 /*cval=*/ct_coefficients.data(),
413 /*lower=*/constraint.lower_bound(),
414 /*upper=*/constraint.upper_bound(),
415 /*constrname=*/constraint.name().c_str()));
416 }
417 }
418
419 for (const auto& gen_cst : model.general_constraint()) {
420 switch (gen_cst.general_constraint_case()) {
421 case MPGeneralConstraintProto::kIndicatorConstraint: {
422 RETURN_IF_GUROBI_ERROR(AddIndicatorConstraint(
423 gen_cst, gurobi_model, &ct_variables, &ct_coefficients));
424 break;
425 }
426 case MPGeneralConstraintProto::kSosConstraint: {
427 RETURN_IF_GUROBI_ERROR(AddSosConstraint(gen_cst.sos_constraint(),
428 gurobi_model, &ct_variables,
429 &ct_coefficients));
430 break;
431 }
432 case MPGeneralConstraintProto::kQuadraticConstraint: {
433 RETURN_IF_GUROBI_ERROR(AddQuadraticConstraint(gen_cst, gurobi_model));
434 break;
435 }
436 case MPGeneralConstraintProto::kAbsConstraint: {
438 gurobi_model,
439 /*name=*/gen_cst.name().c_str(),
440 /*resvar=*/gen_cst.abs_constraint().resultant_var_index(),
441 /*argvar=*/gen_cst.abs_constraint().var_index()));
442 break;
443 }
444 case MPGeneralConstraintProto::kAndConstraint: {
446 AddAndConstraint(gen_cst, gurobi_model, &ct_variables));
447 break;
448 }
449 case MPGeneralConstraintProto::kOrConstraint: {
451 AddOrConstraint(gen_cst, gurobi_model, &ct_variables));
452 break;
453 }
454 case MPGeneralConstraintProto::kMinConstraint: {
456 AddMinConstraint(gen_cst, gurobi_model, &ct_variables));
457 break;
458 }
459 case MPGeneralConstraintProto::kMaxConstraint: {
461 AddMaxConstraint(gen_cst, gurobi_model, &ct_variables));
462 break;
463 }
464 default:
465 return absl::UnimplementedError(
466 absl::StrFormat("General constraints of type %i not supported.",
467 gen_cst.general_constraint_case()));
468 }
469 }
470 }
471
473 model.maximize() ? -1 : 1));
475 model.objective_offset()));
476 if (model.has_quadratic_objective()) {
477 MPQuadraticObjective qobj = model.quadratic_objective();
478 if (qobj.coefficient_size() > 0) {
480 GRBaddqpterms(gurobi_model, /*numqnz=*/qobj.coefficient_size(),
481 /*qrow=*/qobj.mutable_qvar1_index()->mutable_data(),
482 /*qcol=*/qobj.mutable_qvar2_index()->mutable_data(),
483 /*qval=*/qobj.mutable_coefficient()->mutable_data()));
484 }
485 }
486
488
489 const absl::Time time_before = absl::Now();
490 UserTimer user_timer;
491 user_timer.Start();
492
493 RETURN_IF_GUROBI_ERROR(GRBoptimize(gurobi_model));
494
495 const absl::Duration solving_duration = absl::Now() - time_before;
496 user_timer.Stop();
497 VLOG(1) << "Finished solving in GurobiSolveProto(), walltime = "
498 << solving_duration << ", usertime = " << user_timer.GetDuration();
499 response.mutable_solve_info()->set_solve_wall_time_seconds(
500 absl::ToDoubleSeconds(solving_duration));
501 response.mutable_solve_info()->set_solve_user_time_seconds(
502 absl::ToDoubleSeconds(user_timer.GetDuration()));
503
504 int optimization_status = 0;
506 GRBgetintattr(gurobi_model, GRB_INT_ATTR_STATUS, &optimization_status));
507 int solution_count = 0;
509 GRBgetintattr(gurobi_model, GRB_INT_ATTR_SOLCOUNT, &solution_count));
510 switch (optimization_status) {
511 case GRB_OPTIMAL:
512 response.set_status(MPSOLVER_OPTIMAL);
513 break;
514 case GRB_INF_OR_UNBD:
515 DLOG(INFO) << "Gurobi solve returned GRB_INF_OR_UNBD, which we treat as "
516 "INFEASIBLE even though it may mean UNBOUNDED.";
517 response.set_status_str(
518 "The model may actually be unbounded: Gurobi returned "
519 "GRB_INF_OR_UNBD");
520 ABSL_FALLTHROUGH_INTENDED;
521 case GRB_INFEASIBLE:
522 response.set_status(MPSOLVER_INFEASIBLE);
523 break;
524 case GRB_UNBOUNDED:
525 response.set_status(MPSOLVER_UNBOUNDED);
526 break;
527 default: {
528 if (solution_count > 0) {
529 response.set_status(MPSOLVER_FEASIBLE);
530 } else {
531 response.set_status(MPSOLVER_NOT_SOLVED);
532 response.set_status_str(
533 absl::StrFormat("Gurobi status code %d", optimization_status));
534 }
535 break;
536 }
537 }
538
539 if (solution_count > 0 && (response.status() == MPSOLVER_FEASIBLE ||
540 response.status() == MPSOLVER_OPTIMAL)) {
541 double objective_value = 0;
543 GRBgetdblattr(gurobi_model, GRB_DBL_ATTR_OBJVAL, &objective_value));
544 response.set_objective_value(objective_value);
545 double best_objective_bound = 0;
546 const int error = GRBgetdblattr(gurobi_model, GRB_DBL_ATTR_OBJBOUND,
547 &best_objective_bound);
548 if (response.status() == MPSOLVER_OPTIMAL &&
550 // If the presolve deletes all variables, there's no best bound.
551 response.set_best_objective_bound(objective_value);
552 } else {
554 response.set_best_objective_bound(best_objective_bound);
555 }
556
557 response.mutable_variable_value()->Resize(variable_size, 0);
559 GRBgetdblattrarray(gurobi_model, GRB_DBL_ATTR_X, 0, variable_size,
560 response.mutable_variable_value()->mutable_data()));
561 // NOTE, GurobiSolveProto() is exposed to external clients via MPSolver API,
562 // which assumes the solution values of integer variables are rounded to
563 // integer values.
564 auto round_values_of_integer_variables_fn =
565 [&](google::protobuf::RepeatedField<double>* values) {
566 for (int v = 0; v < variable_size; ++v) {
567 if (model.variable(v).is_integer()) {
568 (*values)[v] = std::round((*values)[v]);
569 }
570 }
571 };
572 round_values_of_integer_variables_fn(response.mutable_variable_value());
573 if (!has_integer_variables && model.general_constraint_size() == 0) {
574 response.mutable_dual_value()->Resize(model.constraint_size(), 0);
576 gurobi_model, GRB_DBL_ATTR_PI, 0, model.constraint_size(),
577 response.mutable_dual_value()->mutable_data()));
578 }
579 const int additional_solutions = std::min(
580 solution_count, std::min(request->populate_additional_solutions_up_to(),
581 std::numeric_limits<int32_t>::max() - 1) +
582 1);
583 for (int i = 1; i < additional_solutions; ++i) {
586 MPSolution* solution = response.add_additional_solutions();
587 solution->mutable_variable_value()->Resize(variable_size, 0);
588 double objective_value = 0;
590 gurobi_model, GRB_DBL_ATTR_POOLOBJVAL, &objective_value));
591 solution->set_objective_value(objective_value);
593 gurobi_model, GRB_DBL_ATTR_XN, 0, variable_size,
594 solution->mutable_variable_value()->mutable_data()));
595 round_values_of_integer_variables_fn(solution->mutable_variable_value());
596 }
597 }
598#undef RETURN_IF_GUROBI_ERROR
599
600 return response;
601}
602
603} // namespace operations_research
#define ASSIGN_OR_RETURN(lhs, rexpr)
absl::Duration GetDuration() const
Definition timer.h:49
void Start()
When Start() is called multiple times, only the most recent is used.
Definition timer.h:32
void Stop()
Definition timer.h:40
#define GRB_DBL_ATTR_START
#define GRB_ERROR_DATA_NOT_AVAILABLE
Definition environment.h:73
#define GRB_INT_ATTR_MODELSENSE
struct _GRBenv GRBenv
Definition environment.h:32
#define GRB_GREATER_EQUAL
#define GRB_OPTIMAL
#define GRB_INTEGER
#define GRB_DBL_ATTR_PI
#define GRB_DBL_ATTR_OBJVAL
#define GRB_DBL_ATTR_XN
#define GRB_CONTINUOUS
#define GRB_SOS_TYPE1
struct _GRBmodel GRBmodel
Definition environment.h:31
#define GRB_DBL_ATTR_OBJCON
#define GRB_INF_OR_UNBD
#define GRB_DBL_ATTR_X
#define GRB_INFEASIBLE
#define GRB_EQUAL
#define GRB_SOS_TYPE2
#define GRB_UNBOUNDED
#define GRB_INT_ATTR_STATUS
#define GRB_LESS_EQUAL
#define GRB_DBL_ATTR_POOLOBJVAL
#define GRB_INT_PAR_SOLUTIONNUMBER
#define GRB_INT_ATTR_SOLCOUNT
#define GRB_INT_PAR_OUTPUTFLAG
#define GRB_DBL_PAR_TIMELIMIT
#define GRB_DBL_ATTR_OBJBOUND
#define RETURN_IF_GUROBI_ERROR(x)
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 numvars, int numnz, int *vbeg, int *vind, double *vval, double *obj, double *lb, double *ub, char *vtype, char **varnames)> GRBaddvars
static constexpr double kInfinity
std::function< int(GRBmodel *model, int numlnz, int *lind, double *lval, int numqnz, int *qrow, int *qcol, double *qval, char sense, double rhs, const char *QCname)> GRBaddqconstr
std::function< int(GRBmodel *model, const char *attrname, int newvalue)> GRBsetintattr
std::function< int(GRBenv *env, const char *paramname, const char *value)> GRBsetparam
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)> GRBupdatemodel
std::function< GRBenv *(GRBmodel *model)> GRBgetenv
std::function< int(GRBmodel *model)> GRBfreemodel
std::function< int(GRBenv *env, const char *paramname, int value)> GRBsetintparam
std::function< int(GRBmodel *model, const char *name, int resvar, int nvars, const int *vars, double constant)> GRBaddgenconstrMin
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::optional< LazyMutableCopy< MPModelProto > > GetMPModelOrPopulateResponse(LazyMutableCopy< MPModelRequest > &request, MPSolutionResponse *response)
std::function< int(GRBmodel *model)> GRBoptimize
std::function< int(GRBmodel *model, int numqnz, int *qrow, int *qcol, double *qval)> GRBaddqpterms
std::function< int(GRBmodel *model, const char *name, int resvar, int argvar)> GRBaddgenconstrAbs
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 *name, int resvar, int nvars, const int *vars)> GRBaddgenconstrAnd
std::function< int(GRBmodel *model, const char *name, int resvar, int nvars, const int *vars)> GRBaddgenconstrOr
std::function< int(GRBmodel *model, const char *name, int resvar, int nvars, const int *vars, double constant)> GRBaddgenconstrMax
std::function< int(GRBmodel *model, int numsos, int nummembers, int *types, int *beg, int *ind, double *weight)> GRBaddsos
absl::Status SetSolverSpecificParameters(absl::string_view parameters, GRBenv *gurobi)
std::function< int(GRBenv *env, const char *paramname, double value)> GRBsetdblparam
std::function< int(GRBmodel *model, const char *attrname, int element, double newvalue)> GRBsetdblattrelement
trees with all degrees equal w the current value of w
WallTimer UserTimer
Definition timer.h:67