25#include "absl/container/flat_hash_set.h"
26#include "absl/status/status.h"
27#include "absl/status/statusor.h"
28#include "absl/strings/str_cat.h"
29#include "absl/time/clock.h"
30#include "absl/time/time.h"
31#include "absl/types/span.h"
33#include "ortools/math_opt/callback.pb.h"
38#include "ortools/math_opt/solution.pb.h"
40#include "ortools/math_opt/sparse_containers.pb.h"
53constexpr int kNumGurobiEvents = 9;
54constexpr int kGrbOk = 0;
55constexpr double kInf = std::numeric_limits<double>::infinity();
58constexpr int CheckedGuroibWhere() {
59 static_assert(
where >= 0 &&
where < kNumGurobiEvents);
63inline int GurobiEvent(CallbackEventProto event) {
65 case CALLBACK_EVENT_PRESOLVE:
66 return CheckedGuroibWhere<GRB_CB_PRESOLVE>();
67 case CALLBACK_EVENT_SIMPLEX:
68 return CheckedGuroibWhere<GRB_CB_SIMPLEX>();
69 case CALLBACK_EVENT_MIP:
70 return CheckedGuroibWhere<GRB_CB_MIP>();
71 case CALLBACK_EVENT_MIP_SOLUTION:
72 return CheckedGuroibWhere<GRB_CB_MIPSOL>();
73 case CALLBACK_EVENT_MIP_NODE:
74 return CheckedGuroibWhere<GRB_CB_MIPNODE>();
75 case CALLBACK_EVENT_BARRIER:
76 return CheckedGuroibWhere<GRB_CB_BARRIER>();
77 case CALLBACK_EVENT_UNSPECIFIED:
79 LOG(
FATAL) <<
"Unexpected callback event: " << event;
83SparseDoubleVectorProto ApplyFilter(
84 const std::vector<double>& grb_solution,
86 const SparseVectorFilterProto& filter) {
87 SparseVectorFilterPredicate predicate(filter);
88 SparseDoubleVectorProto result;
89 for (
const auto [
id, grb_index] : var_ids) {
90 const double val = grb_solution[grb_index];
91 if (predicate.AcceptsAndUpdate(
id, val)) {
93 result.add_values(val);
99absl::StatusOr<int64_t> CbGetInt64(
const Gurobi::CallbackContext&
context,
102 int64_t result64 =
static_cast<int64_t
>(result);
103 if (result !=
static_cast<double>(result64)) {
104 return absl::InternalError(
105 absl::StrCat(
"Error converting double attribute: ", what,
106 "with value: ", result,
" to int64_t exactly."));
111absl::StatusOr<bool> CbGetBool(
const Gurobi::CallbackContext&
context,
114 bool result_bool =
static_cast<bool>(result);
115 if (result !=
static_cast<int>(result_bool)) {
116 return absl::InternalError(
117 absl::StrCat(
"Error converting int attribute: ", what,
118 "with value: ", result,
" to bool exactly."));
126#define MO_SET_OR_RET(setter, statusor) \
128 auto eval_status_or = statusor; \
129 RETURN_IF_ERROR(eval_status_or.status()) << __FILE__ << ":" << __LINE__; \
130 setter(*std::move(eval_status_or)); \
135absl::Status SetRuntime(
const GurobiCallbackInput& callback_input,
136 CallbackDataProto& callback_data) {
138 callback_data.mutable_runtime());
143absl::StatusOr<std::optional<CallbackDataProto>> CreateCallbackDataProto(
144 const Gurobi::CallbackContext& c,
const GurobiCallbackInput& callback_input,
145 MessageCallbackData& message_callback_data) {
146 CallbackDataProto callback_data;
151 callback_data.set_event(CALLBACK_EVENT_PRESOLVE);
152 CallbackDataProto::PresolveStats*
const s =
153 callback_data.mutable_presolve_stats();
161 callback_data.set_event(CALLBACK_EVENT_SIMPLEX);
162 CallbackDataProto::SimplexStats*
const s =
163 callback_data.mutable_simplex_stats();
174 callback_data.set_event(CALLBACK_EVENT_BARRIER);
175 CallbackDataProto::BarrierStats*
const s =
176 callback_data.mutable_barrier_stats();
191 callback_data.set_event(CALLBACK_EVENT_MIP);
192 CallbackDataProto::MipStats*
const s = callback_data.mutable_mip_stats();
206 callback_data.set_event(CALLBACK_EVENT_MIP_SOLUTION);
207 CallbackDataProto::MipStats*
const s = callback_data.mutable_mip_stats();
213 std::vector<double> var_values(callback_input.num_gurobi_vars);
216 <<
"Error reading solution at event MIP_SOLUTION";
217 *callback_data.mutable_primal_solution_vector() =
218 ApplyFilter(var_values, callback_input.variable_ids,
219 callback_input.mip_solution_filter);
223 callback_data.set_event(CALLBACK_EVENT_MIP_NODE);
224 CallbackDataProto::MipStats*
const s = callback_data.mutable_mip_stats();
234 <<
"Error reading solution status at event MIP_NODE";
236 std::vector<double> var_values(callback_input.num_gurobi_vars);
239 <<
"Error reading solution at event MIP_NODE";
240 *callback_data.mutable_primal_solution_vector() =
241 ApplyFilter(var_values, callback_input.variable_ids,
242 callback_input.mip_node_filter);
248 LOG(
FATAL) <<
"Unknown gurobi callback code " << c.where();
252 <<
"Error encoding runtime at callback event: " << c.where();
254 return callback_data;
259absl::Status ApplyResult(
const Gurobi::CallbackContext&
context,
260 const GurobiCallbackInput& callback_input,
261 const CallbackResultProto& result,
262 SolveInterrupter& local_interrupter) {
263 for (
const CallbackResultProto::GeneratedLinearConstraint& cut :
265 std::vector<int> gurobi_vars;
266 gurobi_vars.reserve(cut.linear_expression().ids_size());
267 for (
const int64_t
id : cut.linear_expression().ids()) {
268 gurobi_vars.push_back(callback_input.variable_ids.at(
id));
270 std::vector<std::pair<char, double>> sense_bound_pairs;
271 if (cut.lower_bound() == cut.upper_bound()) {
272 sense_bound_pairs.emplace_back(
GRB_EQUAL, cut.upper_bound());
274 if (cut.upper_bound() <
kInf) {
275 sense_bound_pairs.emplace_back(
GRB_LESS_EQUAL, cut.upper_bound());
277 if (cut.lower_bound() > -
kInf) {
281 for (
const auto [sense,
bound] : sense_bound_pairs) {
284 gurobi_vars, cut.linear_expression().values(), sense,
bound));
287 gurobi_vars, cut.linear_expression().values(), sense,
bound));
291 for (
const SparseDoubleVectorProto& solution_vector :
292 result.suggested_solutions()) {
295 std::vector<double> gurobi_var_values(callback_input.num_gurobi_vars,
298 gurobi_var_values[callback_input.variable_ids.at(
id)] =
value;
303 if (result.terminate()) {
304 local_interrupter.Interrupt();
305 return absl::OkStatus();
307 return absl::OkStatus();
313 const absl::flat_hash_set<CallbackEventProto>& events) {
314 std::vector<bool> result(kNumGurobiEvents);
315 for (
const auto event : events) {
316 result[GurobiEvent(event)] =
true;
334 if (local_interrupter !=
nullptr && local_interrupter->
IsInterrupted()) {
344 return absl::OkStatus();
348 const absl::StatusOr<std::string> msg =
context.CbGetMessage();
350 <<
"Error getting message string in callback";
351 const std::vector<std::string> lines = message_callback_data.
Parse(*msg);
352 if (!lines.empty()) {
356 return absl::OkStatus();
359 if (callback_input.
user_cb ==
nullptr ||
361 return absl::OkStatus();
365 CHECK(local_interrupter !=
nullptr);
368 const std::optional<CallbackDataProto> callback_data,
369 CreateCallbackDataProto(
context, callback_input, message_callback_data));
370 if (!callback_data) {
371 return absl::OkStatus();
373 const absl::StatusOr<CallbackResultProto> result =
374 callback_input.
user_cb(*callback_data);
377 return result.status();
380 ApplyResult(
context, callback_input, *result, *local_interrupter));
381 return absl::OkStatus();
386 const std::vector<std::string> lines = message_callback_data.
Flush();
std::vector< std::string > Flush()
std::vector< std::string > Parse(std::string_view message)
bool IsInterrupted() const
#define GRB_CB_MIP_NODLFT
#define GRB_CB_SPX_ISPERT
#define GRB_CB_SPX_OBJVAL
#define GRB_CB_PRE_COECHG
#define GRB_GREATER_EQUAL
#define GRB_CB_MIPSOL_OBJBST
#define GRB_CB_MIP_ITRCNT
#define GRB_CB_MIPSOL_SOLCNT
#define GRB_CB_SPX_PRIMINF
#define GRB_CB_MIPNODE_REL
#define GRB_CB_MIPNODE_OBJBND
#define GRB_CB_MIPNODE_OBJBST
#define GRB_CB_SPX_ITRCNT
#define GRB_CB_BARRIER_COMPL
#define GRB_CB_MIPSOL_NODCNT
#define GRB_CB_MIPNODE_STATUS
#define GRB_CB_PRE_ROWDEL
#define GRB_CB_BARRIER_PRIMOBJ
#define GRB_CB_MIPNODE_SOLCNT
#define GRB_CB_SPX_DUALINF
#define GRB_CB_MIP_OBJBST
#define GRB_CB_BARRIER_DUALINF
#define GRB_CB_BARRIER_ITRCNT
#define GRB_CB_MIP_CUTCNT
#define GRB_CB_BARRIER_DUALOBJ
#define GRB_CB_MIP_SOLCNT
#define GRB_CB_PRE_COLDEL
#define GRB_CB_MIP_NODCNT
#define GRB_CB_MIPSOL_SOL
#define GRB_CB_MIPSOL_OBJBND
#define GRB_CB_MIP_OBJBND
#define GRB_CB_BARRIER_PRIMINF
#define GRB_CB_PRE_BNDCHG
#define GRB_CB_MIPNODE_NODCNT
#define MO_SET_OR_RET(setter, statusor)
GurobiMPCallbackContext * context
void GurobiCallbackImplFlush(const GurobiCallbackInput &callback_input, MessageCallbackData &message_callback_data)
absl::Status GurobiCallbackImpl(const Gurobi::CallbackContext &context, const GurobiCallbackInput &callback_input, MessageCallbackData &message_callback_data, SolveInterrupter *const local_interrupter)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
std::vector< bool > EventToGurobiWhere(const absl::flat_hash_set< CallbackEventProto > &events)
Collection of objects used to extend the Constraint Solver library.
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)