21#include "absl/container/flat_hash_set.h"
22#include "absl/status/status.h"
23#include "absl/strings/str_cat.h"
24#include "absl/strings/str_join.h"
25#include "google/protobuf/duration.pb.h"
45constexpr double kInf = std::numeric_limits<double>::infinity();
47absl::Status IsEventRegistered(
51 const int num_events = callback_registration.request_registration_size();
52 for (
int k = 0; k < num_events; ++k) {
53 if (callback_registration.request_registration(k) == event) {
54 return absl::OkStatus();
57 return absl::InvalidArgumentError(absl::StrCat(
59 " not part of the registered_events in callback_registration"));
62absl::Status ValidateGeneratedLinearConstraint(
64 const bool add_cuts,
const bool add_lazy_constraints,
66 const auto coefficients =
MakeView(linear_constraint.linear_expression());
69 {.allow_positive_infinity =
false, .allow_negative_infinity =
false}))
70 <<
"invalid linear_constraint coefficients";
72 "cut variables",
"model IDs"));
74 {.allow_positive_infinity = false}))
75 <<
"for GeneratedLinearConstraint.lower_bound";
77 {.allow_negative_infinity = false}))
78 <<
"for GeneratedLinearConstraint.upper_bound";
79 if (linear_constraint.lower_bound() == -
kInf &&
80 linear_constraint.upper_bound() ==
kInf) {
81 return absl::InvalidArgumentError(
82 "invalid GeneratedLinearConstraint, bounds [-inf,inf]");
84 if (linear_constraint.is_lazy() && !add_lazy_constraints) {
85 return absl::InvalidArgumentError(
86 "invalid GeneratedLinearConstraint with lazy attribute set to true, "
87 "adding lazy constraints requires "
88 "CallbackRegistrationProto.add_lazy_constraints=true");
90 if (!linear_constraint.is_lazy() && !add_cuts) {
91 return absl::InvalidArgumentError(
92 "invalid GeneratedLinearConstraint with lazy attribute set to false, "
93 "adding cuts requires CallbackRegistrationProto.add_cuts=true");
95 return absl::OkStatus();
99struct ProtoEnumFormatter {
100 void operator()(std::string*
const out,
const T value) {
112 <<
"invalid CallbackRegistrationProto.mip_solution_filter";
115 <<
"invalid CallbackRegistrationProto.mip_node_filter";
118 bool can_add_lazy_constraints =
false;
119 bool can_add_cuts =
false;
120 for (
int k = 0; k < num_events; ++k) {
125 return absl::InvalidArgumentError(absl::StrCat(
126 "invalid event ", requested_event,
" can not be registered"));
129 can_add_lazy_constraints =
true;
133 can_add_lazy_constraints =
true;
136 if (callback_registration.
add_cuts() && !can_add_cuts) {
137 return absl::InvalidArgumentError(
138 "can only add cuts at event CALLBACK_EVENT_MIP_NODE but this event was "
142 !can_add_lazy_constraints) {
143 return absl::InvalidArgumentError(
144 "can only add lazy constraints at events CALLBACK_EVENT_MIP_NODE and "
145 "CALLBACK_EVENT_MIP_SOLUTION but neither of these events were "
149 return absl::OkStatus();
158 <<
"invalid CallbackDataProto.event for given CallbackRegistrationProto";
163 return absl::InvalidArgumentError(
164 absl::StrCat(
"can't provide primal_solution_vector for event ", event,
168#ifdef RETURN_IF_SCALAR
169#error Collision in macro definition RETURN_IF_SCALAR
171#define RETURN_IF_SCALAR(stat, value, option) \
173 if (stat.has_##value()) { \
174 RETURN_IF_ERROR(CheckScalar(static_cast<double>(stat.value()), option)) \
175 << "Invalid CallbackDataProto." << #stat << "." << #value; \
180 .allow_negative_infinity =
false};
181 const DoubleOptions noneg = {.allow_positive_infinity =
false,
182 .allow_negative =
false};
205 const auto& mip_stats = cb_data.
mip_stats();
216 <<
"Invalid CallbackDataProto.runtime.seconds";
218 <<
"Invalid CallbackDataProto.runtime.nanos";
219#undef RETURN_IF_SCALAR
225 if (has_primal_solution) {
232 <<
"invalid CallbackDataProto.primal_solution_vector";
234 return absl::InvalidArgumentError(
235 absl::StrCat(
"must provide primal_solution_vector for event ",
245 <<
"CALLBACK_EVENT_UNSPECIFIED can not be a registered event, this "
246 "points to either an invalid CallbackRegistrationProto (which "
248 "one of the assumptions of this function), or memory corruption";
255 return absl::OkStatus();
265 CHECK_OK(IsEventRegistered(callback_event, callback_registration));
267 if (!callback_result.
cuts().empty()) {
270 return absl::InvalidArgumentError(absl::StrCat(
271 "invalid CallbackResultProto, can't return cuts for callback_event ",
275 callback_result.
cuts()) {
277 cut, callback_registration.
add_cuts(),
283 return absl::InvalidArgumentError(absl::StrCat(
284 "invalid CallbackResultProto, can't return suggested solutions for "
292 <<
"invalid CallbackResultProto.suggested_solutions";
296 return absl::OkStatus();
301 const absl::flat_hash_set<CallbackEventProto>& supported_events) {
302 std::vector<CallbackEventProto> unsupported_events;
304 if (!supported_events.contains(event)) {
305 unsupported_events.push_back(event);
309 if (unsupported_events.empty()) {
310 return absl::OkStatus();
313 std::sort(unsupported_events.begin(), unsupported_events.end());
315 const bool plural = unsupported_events.size() >= 2;
316 return absl::InvalidArgumentError(
317 absl::StrCat(
"event", (plural ?
"s { " :
" "),
318 absl::StrJoin(unsupported_events,
", ",
319 ProtoEnumFormatter<CallbackEventProto>()),
320 (plural ?
" } are" :
" is"),
" not supported"));
#define RETURN_IF_ERROR(expr)
#define RETURN_IF_SCALAR(stat, value, option)
const ::operations_research::math_opt::CallbackDataProto_SimplexStats & simplex_stats() const
::operations_research::math_opt::CallbackEventProto event() const
bool has_primal_solution_vector() const
.operations_research.math_opt.SparseDoubleVectorProto primal_solution_vector = 2;
const ::operations_research::math_opt::CallbackDataProto_MipStats & mip_stats() const
const ::operations_research::math_opt::CallbackDataProto_PresolveStats & presolve_stats() const
const ::google::protobuf::Duration & runtime() const
const ::operations_research::math_opt::SparseDoubleVectorProto & primal_solution_vector() const
const ::operations_research::math_opt::CallbackDataProto_BarrierStats & barrier_stats() const
const ::operations_research::math_opt::SparseVectorFilterProto & mip_solution_filter() const
bool add_lazy_constraints() const
::operations_research::math_opt::CallbackEventProto request_registration(int index) const
const ::operations_research::math_opt::SparseVectorFilterProto & mip_node_filter() const
int request_registration_size() const
repeated .operations_research.math_opt.CallbackEventProto request_registration = 1;
const ::operations_research::math_opt::SparseDoubleVectorProto & suggested_solutions(int index) const
const ::operations_research::math_opt::CallbackResultProto_GeneratedLinearConstraint & cuts(int index) const
CallbackResultProto_GeneratedLinearConstraint GeneratedLinearConstraint
nested types -------------------------------------------------—
An object oriented wrapper for quadratic constraints in ModelStorage.
absl::Status ValidateCallbackResultProto(const CallbackResultProto &callback_result, const CallbackEventProto callback_event, const CallbackRegistrationProto &callback_registration, const ModelSummary &model_summary)
*If primal *dual status is and *dual bound is equal to primal bound *is finite
absl::flat_hash_set< CallbackEventProto > EventSet(const CallbackRegistrationProto &callback_registration)
Returns the callback_registration.request_registration as a set of enums.
@ CALLBACK_EVENT_UNSPECIFIED
@ CALLBACK_EVENT_MIP_NODE
@ CALLBACK_EVENT_MIP_SOLUTION
absl::Status CheckRegisteredCallbackEvents(const CallbackRegistrationProto ®istration, const absl::flat_hash_set< CallbackEventProto > &supported_events)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
absl::Status CheckIdsAndValues(const SparseVectorView< T > &vector_view, absl::string_view value_name="values")
bool CallbackEventProto_IsValid(int value)
absl::Status CheckScalar(const double value, const DoubleOptions &options)
Checks value is not NaN and satisfies the additional conditions in options.
absl::Status ValidatePrimalSolutionVector(const SparseDoubleVectorProto &vector, const SparseVectorFilterProto &filter, const ModelSummary &model_summary)
absl::Status CheckIdsSubset(absl::Span< const int64_t > ids, const IdNameBiMap &universe, std::optional< int64_t > upper_bound)
absl::Status ValidateCallbackDataProto(const CallbackDataProto &cb_data, const CallbackRegistrationProto &callback_registration, const ModelSummary &model_summary)
absl::Status ValidateSparseVectorFilter(const SparseVectorFilterProto &v, const IdNameBiMap &valid_ids)
absl::Status ValidateCallbackRegistration(const CallbackRegistrationProto &callback_registration, const ModelSummary &model_summary)
Checks that CallbackRegistrationProto is valid given a valid model summary.
In SWIG mode, we don't want anything besides these top-level includes.
std::string ProtoEnumToString(ProtoEnumType enum_value)