23#include "absl/container/flat_hash_set.h"
24#include "absl/status/status.h"
25#include "absl/status/statusor.h"
26#include "absl/strings/str_cat.h"
27#include "absl/time/clock.h"
28#include "absl/time/time.h"
29#include "absl/types/span.h"
51constexpr int kNumGurobiEvents = 9;
52constexpr double kInf = std::numeric_limits<double>::infinity();
55constexpr int CheckedGuroibWhere() {
56 static_assert(where >= 0 && where < kNumGurobiEvents);
63 return CheckedGuroibWhere<GRB_CB_PRESOLVE>();
65 return CheckedGuroibWhere<GRB_CB_SIMPLEX>();
67 return CheckedGuroibWhere<GRB_CB_MIP>();
69 return CheckedGuroibWhere<GRB_CB_MIPSOL>();
71 return CheckedGuroibWhere<GRB_CB_MIPNODE>();
73 return CheckedGuroibWhere<GRB_CB_BARRIER>();
76 LOG(FATAL) <<
"Unexpected callback event: " << event;
81 absl::Span<const double> grb_solution,
82 const gtl::linked_hash_map<int64_t, int>& var_ids,
86 for (
const auto [
id, grb_index] : var_ids) {
87 const double val = grb_solution[grb_index];
88 if (predicate.AcceptsAndUpdate(
id, val)) {
90 result.add_values(val);
99 int64_t result64 =
static_cast<int64_t
>(result);
100 if (result !=
static_cast<double>(result64)) {
101 return absl::InternalError(
102 absl::StrCat(
"Error converting double attribute: ", what,
103 "with value: ", result,
" to int64_t exactly."));
111 bool result_bool =
static_cast<bool>(result);
112 if (result !=
static_cast<int>(result_bool)) {
113 return absl::InternalError(
114 absl::StrCat(
"Error converting int attribute: ", what,
115 "with value: ", result,
" to bool exactly."));
123#define MO_SET_OR_RET(setter, statusor) \
125 auto eval_status_or = statusor; \
126 RETURN_IF_ERROR(eval_status_or.status()) << __FILE__ << ":" << __LINE__; \
127 setter(*std::move(eval_status_or)); \
132absl::Status SetRuntime(
const GurobiCallbackInput& callback_input,
133 CallbackDataProto& callback_data) {
135 callback_data.mutable_runtime());
140absl::StatusOr<std::optional<CallbackDataProto>> CreateCallbackDataProto(
150 callback_data.mutable_presolve_stats();
160 callback_data.mutable_simplex_stats();
173 callback_data.mutable_barrier_stats();
210 std::vector<double> var_values(callback_input.num_gurobi_vars);
213 <<
"Error reading solution at event MIP_SOLUTION";
214 *callback_data.mutable_primal_solution_vector() =
215 ApplyFilter(var_values, callback_input.variable_ids,
216 callback_input.mip_solution_filter);
231 <<
"Error reading solution status at event MIP_NODE";
233 std::vector<double> var_values(callback_input.num_gurobi_vars);
236 <<
"Error reading solution at event MIP_NODE";
237 *callback_data.mutable_primal_solution_vector() =
238 ApplyFilter(var_values, callback_input.variable_ids,
239 callback_input.mip_node_filter);
245 LOG(FATAL) <<
"Unknown gurobi callback code " <<
c.where();
249 <<
"Error encoding runtime at callback event: " <<
c.where();
251 return callback_data;
259 SolveInterrupter& local_interrupter) {
262 std::vector<int> gurobi_vars;
263 gurobi_vars.reserve(cut.linear_expression().ids_size());
264 for (
const int64_t
id : cut.linear_expression().ids()) {
265 gurobi_vars.push_back(callback_input.variable_ids.at(
id));
267 std::vector<std::pair<char, double>> sense_bound_pairs;
268 if (cut.lower_bound() == cut.upper_bound()) {
269 sense_bound_pairs.emplace_back(
GRB_EQUAL, cut.upper_bound());
271 if (cut.upper_bound() <
kInf) {
272 sense_bound_pairs.emplace_back(
GRB_LESS_EQUAL, cut.upper_bound());
274 if (cut.lower_bound() > -
kInf) {
278 for (
const auto [sense,
bound] : sense_bound_pairs) {
281 gurobi_vars, cut.linear_expression().values(), sense,
bound));
284 gurobi_vars, cut.linear_expression().values(), sense,
bound));
289 result.suggested_solutions()) {
292 std::vector<double> gurobi_var_values(callback_input.num_gurobi_vars,
294 for (
const auto [
id, value] :
MakeView(solution_vector)) {
295 gurobi_var_values[callback_input.variable_ids.at(
id)] = value;
300 if (result.terminate()) {
301 local_interrupter.Interrupt();
302 return absl::OkStatus();
304 return absl::OkStatus();
310 const absl::flat_hash_set<CallbackEventProto>& events) {
311 std::vector<bool> result(kNumGurobiEvents);
312 for (
const auto event : events) {
313 result[GurobiEvent(event)] =
true;
331 if (local_interrupter !=
nullptr && local_interrupter->
IsInterrupted()) {
341 return absl::OkStatus();
345 const absl::StatusOr<std::string> msg = context.
CbGetMessage();
347 <<
"Error getting message string in callback";
348 const std::vector<std::string> lines = message_callback_data.
Parse(*msg);
349 if (!lines.empty()) {
353 return absl::OkStatus();
356 if (callback_input.
user_cb ==
nullptr ||
358 return absl::OkStatus();
362 CHECK(local_interrupter !=
nullptr);
365 const std::optional<CallbackDataProto> callback_data,
366 CreateCallbackDataProto(context, callback_input, message_callback_data));
367 if (!callback_data) {
368 return absl::OkStatus();
370 const absl::StatusOr<CallbackResultProto> result =
371 callback_input.
user_cb(*callback_data);
374 return result.status();
377 ApplyResult(context, callback_input, *result, *local_interrupter));
378 return absl::OkStatus();
383 const std::vector<std::string> lines = message_callback_data.
Flush();
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
bool IsInterrupted() const
CallbackDataProto_SimplexStats SimplexStats
CallbackDataProto_BarrierStats BarrierStats
CallbackDataProto_MipStats MipStats
CallbackDataProto_PresolveStats PresolveStats
CallbackResultProto_GeneratedLinearConstraint GeneratedLinearConstraint
absl::StatusOr< std::string > CbGetMessage() const
std::vector< std::string > Parse(absl::string_view message)
std::vector< std::string > Flush()
#define MO_SET_OR_RET(setter, statusor)
#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
std::vector< bool > EventToGurobiWhere(const absl::flat_hash_set< CallbackEventProto > &events)
@ CALLBACK_EVENT_PRESOLVE
@ CALLBACK_EVENT_UNSPECIFIED
@ CALLBACK_EVENT_MIP_NODE
@ CALLBACK_EVENT_MIP_SOLUTION
void GurobiCallbackImplFlush(const GurobiCallbackInput &callback_input, MessageCallbackData &message_callback_data)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
absl::Status GurobiCallbackImpl(const Gurobi::CallbackContext &context, const GurobiCallbackInput &callback_input, MessageCallbackData &message_callback_data, SolveInterrupter *const local_interrupter)
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)