24 #include "absl/container/flat_hash_set.h" 25 #include "absl/status/status.h" 26 #include "absl/status/statusor.h" 27 #include "absl/strings/str_cat.h" 28 #include "absl/time/clock.h" 29 #include "absl/time/time.h" 30 #include "absl/types/optional.h" 31 #include "absl/types/span.h" 33 #include "ortools/math_opt/callback.pb.h" 37 #include "ortools/math_opt/solution.pb.h" 39 #include "ortools/math_opt/sparse_containers.pb.h" 52 constexpr
int kNumGurobiEvents = 9;
53 constexpr
int kGrbOk = 0;
54 constexpr
double kInf = std::numeric_limits<double>::infinity();
57 constexpr
int CheckedGuroibWhere() {
58 static_assert(
where >= 0 &&
where < kNumGurobiEvents);
62 inline int GurobiEvent(CallbackEventProto event) {
64 case CALLBACK_EVENT_POLLING:
65 return CheckedGuroibWhere<GRB_CB_POLLING>();
66 case CALLBACK_EVENT_PRESOLVE:
67 return CheckedGuroibWhere<GRB_CB_PRESOLVE>();
68 case CALLBACK_EVENT_SIMPLEX:
69 return CheckedGuroibWhere<GRB_CB_SIMPLEX>();
70 case CALLBACK_EVENT_MIP:
71 return CheckedGuroibWhere<GRB_CB_MIP>();
72 case CALLBACK_EVENT_MIP_SOLUTION:
73 return CheckedGuroibWhere<GRB_CB_MIPSOL>();
74 case CALLBACK_EVENT_MIP_NODE:
75 return CheckedGuroibWhere<GRB_CB_MIPNODE>();
76 case CALLBACK_EVENT_BARRIER:
77 return CheckedGuroibWhere<GRB_CB_BARRIER>();
78 case CALLBACK_EVENT_MESSAGE:
79 return CheckedGuroibWhere<GRB_CB_MESSAGE>();
80 case CALLBACK_EVENT_UNSPECIFIED:
82 LOG(
FATAL) <<
"Unexpected callback event: " << event;
87 if (error_code == kGrbOk) {
88 return absl::OkStatus();
91 return absl::InternalError(
92 absl::StrCat(
"Gurobi error ", error_code,
": ",
GRBgeterrormsg(env)));
95 SparseDoubleVectorProto ApplyFilter(
96 const std::vector<double>& grb_solution,
98 const SparseVectorFilterProto& filter) {
99 SparseVectorFilterPredicate predicate(filter);
100 SparseDoubleVectorProto result;
101 for (
const auto [
id, grb_index] : var_ids) {
102 const double val = grb_solution[grb_index];
103 if (predicate.AcceptsAndUpdate(
id, val)) {
105 result.add_values(val);
111 class GurobiCallbackContext {
114 : model_(
model), cbdata_(cbdata), where_(
where) {}
116 absl::StatusOr<int> get_int(
int what)
const {
122 absl::StatusOr<double> get_double(
int what)
const {
128 absl::StatusOr<int64_t> get_int64(
int what)
const {
131 int64_t result64 = static_cast<int64_t>(result);
132 if (result != static_cast<double>(result64)) {
133 return absl::InternalError(
134 absl::StrCat(
"Error converting double attribute: ", what,
135 "with value: ", result,
" to int64_t exactly."));
140 absl::StatusOr<bool> get_bool(
int what)
const {
143 bool result_bool = static_cast<bool>(result);
144 if (result != static_cast<int>(result_bool)) {
145 return absl::InternalError(
146 absl::StrCat(
"Error converting int attribute: ", what,
147 "with value: ", result,
" to bool exactly."));
152 absl::StatusOr<std::string> get_string(
int what)
const {
160 absl::Status get_doubles(
int what, absl::Span<double> doubles_out)
const {
161 double*
const first = doubles_out.data();
163 return absl::OkStatus();
166 GRBmodel* grb_model()
const {
return model_; }
167 int where()
const {
return where_; }
169 absl::Status AddConstraint(absl::Span<const int> vars,
170 absl::Span<const double> coefs,
char sense,
171 double rhs,
bool is_lazy)
const {
173 return AsStatus((*cut_fn)(cbdata_, vars.size(), vars.begin(), coefs.begin(),
177 absl::StatusOr<double> SuggestSolution(absl::Span<const double> coefs)
const {
180 AsStatus(
GRBcbsolution(cbdata_, coefs.begin(), &obj_value)));
185 absl::Status AsStatus(
int error_code)
const {
186 return GurobiStatus(model_, error_code);
197 #define MO_SET_OR_RET(setter, statusor) \ 199 auto eval_status_or = statusor; \ 200 RETURN_IF_ERROR(eval_status_or.status()) << __FILE__ << ":" << __LINE__; \ 201 setter(*std::move(eval_status_or)); \ 206 absl::Status SetRuntime(
const GurobiCallbackInput& callback_input,
207 CallbackDataProto& callback_data) {
209 callback_data.mutable_runtime());
214 absl::StatusOr<absl::optional<CallbackDataProto>> CreateCallbackDataProto(
215 const GurobiCallbackContext& c,
const GurobiCallbackInput& callback_input,
216 MessageCallbackData& message_callback_data) {
217 CallbackDataProto callback_data;
222 callback_data.set_event(CALLBACK_EVENT_POLLING);
226 callback_data.set_event(CALLBACK_EVENT_PRESOLVE);
227 CallbackDataProto::PresolveStats*
const s =
228 callback_data.mutable_presolve_stats();
236 callback_data.set_event(CALLBACK_EVENT_SIMPLEX);
237 CallbackDataProto::SimplexStats*
const s =
238 callback_data.mutable_simplex_stats();
249 callback_data.set_event(CALLBACK_EVENT_BARRIER);
250 CallbackDataProto::BarrierStats*
const s =
251 callback_data.mutable_barrier_stats();
267 <<
"Error getting message string in callback";
268 absl::optional<CallbackDataProto> message_data =
269 message_callback_data.Parse(*msg);
272 return absl::nullopt;
274 callback_data = std::move(*message_data);
278 callback_data.set_event(CALLBACK_EVENT_MIP);
279 CallbackDataProto::MipStats*
const s = callback_data.mutable_mip_stats();
292 callback_data.set_event(CALLBACK_EVENT_MIP_SOLUTION);
293 CallbackDataProto::MipStats*
const s = callback_data.mutable_mip_stats();
299 std::vector<double> var_values(callback_input.num_gurobi_vars);
302 <<
"Error reading solution at event MIP_SOLUTION";
303 PrimalSolutionProto*
const solution =
304 callback_data.mutable_primal_solution();
305 *solution->mutable_variable_values() =
306 ApplyFilter(var_values, callback_input.variable_ids,
307 callback_input.mip_solution_filter);
313 callback_data.set_event(CALLBACK_EVENT_MIP_NODE);
314 CallbackDataProto::MipStats*
const s = callback_data.mutable_mip_stats();
323 <<
"Error reading solution status at event MIP_NODE";
325 std::vector<double> var_values(callback_input.num_gurobi_vars);
328 <<
"Error reading solution at event MIP_NODE";
329 *callback_data.mutable_primal_solution()->mutable_variable_values() =
330 ApplyFilter(var_values, callback_input.variable_ids,
331 callback_input.mip_node_filter);
337 LOG(
FATAL) <<
"Unknown gurobi callback code " << c.where();
341 <<
"Error encoding runtime at callback event: " << c.where();
343 return callback_data;
348 absl::Status ApplyResult(
const GurobiCallbackContext&
context,
349 const GurobiCallbackInput& callback_input,
350 const CallbackResultProto& result) {
351 for (
const CallbackResultProto::GeneratedLinearConstraint& cut :
353 std::vector<int> gurobi_vars;
354 gurobi_vars.reserve(cut.linear_expression().ids_size());
355 for (
const int64_t
id : cut.linear_expression().ids()) {
356 gurobi_vars.push_back(callback_input.variable_ids.at(
id));
358 std::vector<std::pair<char, double>> sense_bound_pairs;
359 if (cut.lower_bound() == cut.upper_bound()) {
360 sense_bound_pairs.emplace_back(
GRB_EQUAL, cut.upper_bound());
362 if (cut.upper_bound() <
kInf) {
363 sense_bound_pairs.emplace_back(
GRB_LESS_EQUAL, cut.upper_bound());
365 if (cut.lower_bound() > -
kInf) {
369 for (
const auto [sense,
bound] : sense_bound_pairs) {
371 cut.linear_expression().values(),
372 sense,
bound, cut.is_lazy()));
375 for (
const PrimalSolutionProto& solution : result.suggested_solution()) {
378 std::vector<double> gurobi_var_values(callback_input.num_gurobi_vars,
380 for (
const auto [
id,
value] :
MakeView(solution.variable_values())) {
381 gurobi_var_values[callback_input.variable_ids.at(
id)] =
value;
386 if (result.terminate()) {
388 return absl::OkStatus();
390 return absl::OkStatus();
396 const absl::flat_hash_set<CallbackEventProto>& events) {
397 std::vector<bool> result(kNumGurobiEvents);
398 for (
const auto event : events) {
399 result[GurobiEvent(event)] =
true;
408 return absl::OkStatus();
410 const GurobiCallbackContext cb_context(grb_model, cbdata,
where);
412 CreateCallbackDataProto(cb_context, callback_input,
413 message_callback_data));
414 if (!callback_data) {
415 return absl::OkStatus();
417 const absl::StatusOr<CallbackResultProto> result =
418 callback_input.
user_cb(*callback_data);
421 return result.status();
424 return absl::OkStatus();
430 absl::optional<CallbackDataProto> callback_data =
431 message_callback_data.
Flush();
432 if (!callback_data) {
433 return absl::OkStatus();
437 <<
"Error encoding runtime when flushing the remaining callbacks";
441 return callback_input.
user_cb(*callback_data).status();
std::function< const char *(GRBenv *env)> GRBgeterrormsg
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
#define GRB_CB_MIPNODE_SOLCNT
#define GRB_CB_PRE_COECHG
#define GRB_GREATER_EQUAL
#define GRB_CB_MIPNODE_OBJBND
#define GRB_CB_MIPSOL_OBJ
#define GRB_CB_PRE_BNDCHG
#define GRB_CB_MIP_CUTCNT
#define GRB_CB_BARRIER_DUALOBJ
std::function< GRBenv *(GRBmodel *model)> GRBgetenv
#define GRB_CB_MIP_NODCNT
struct _GRBmodel GRBmodel
std::function< int(void *cbdata, int lazylen, const int *lazyind, const double *lazyval, char lazysense, double lazyrhs)> GRBcblazy
#define GRB_CB_SPX_DUALINF
#define GRB_CB_BARRIER_DUALINF
std::vector< bool > EventToGurobiWhere(const absl::flat_hash_set< CallbackEventProto > &events)
#define GRB_CB_MIPNODE_NODCNT
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)
#define GRB_CB_PRE_ROWDEL
#define GRB_CB_MIPSOL_NODCNT
#define GRB_CB_MIP_ITRCNT
#define GRB_CB_SPX_ISPERT
absl::Status GurobiCallbackImplFlush(const GurobiCallbackInput &callback_input, MessageCallbackData &message_callback_data)
#define GRB_CB_BARRIER_COMPL
std::function< int(void *cbdata, const double *solution, double *objvalP)> GRBcbsolution
std::function< int(void *cbdata, int cutlen, const int *cutind, const double *cutval, char cutsense, double cutrhs)> GRBcbcut
#define GRB_CB_MIP_NODLFT
#define GRB_CB_SPX_ITRCNT
#define GRB_CB_MIPSOL_OBJBND
absl::optional< CallbackDataProto > Flush()
std::function< int(void *cbdata, int where, int what, void *resultP)> GRBcbget
std::function< void(GRBmodel *model)> GRBterminate
#define GRB_CB_BARRIER_PRIMINF
#define GRB_CB_MIP_SOLCNT
#define GRB_CB_MSG_STRING
#define GRB_CB_MIP_OBJBST
absl::Status GurobiCallbackImpl(GRBmodel *grb_model, void *cbdata, int where, const GurobiCallbackInput &callback_input, MessageCallbackData &message_callback_data)
#define GRB_CB_PRE_COLDEL
#define GRB_CB_MIPSOL_OBJBST
#define GRB_CB_BARRIER_ITRCNT
#define GRB_CB_MIPNODE_STATUS
#define GRB_CB_MIPSOL_SOL
#define MO_SET_OR_RET(setter, statusor)
#define GRB_CB_MIPNODE_OBJBST
#define GRB_CB_SPX_OBJVAL
Collection of objects used to extend the Constraint Solver library.
#define RETURN_IF_ERROR(expr)
#define GRB_CB_BARRIER_PRIMOBJ
#define GRB_CB_MIPNODE_REL
#define GRB_CB_MIP_OBJBND
GurobiMPCallbackContext * context
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define GRB_CB_MIPSOL_SOLCNT
#define GRB_CB_SPX_PRIMINF