23#include "google/protobuf/duration.pb.h"
24#include "absl/status/status.h"
25#include "absl/strings/match.h"
26#include "absl/strings/str_cat.h"
27#include "absl/strings/string_view.h"
28#include "ortools/math_opt/callback.pb.h"
31#include "ortools/math_opt/solution.pb.h"
32#include "ortools/math_opt/sparse_containers.pb.h"
38#include "absl/status/status.h"
45constexpr double kInf = std::numeric_limits<double>::infinity();
47absl::Status IsEventRegistered(
48 const CallbackEventProto event,
49 const CallbackRegistrationProto& callback_registration) {
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(
63 const CallbackResultProto::GeneratedLinearConstraint& linear_constraint,
64 const bool add_cuts,
const bool add_lazy_constraints,
65 const ModelSummary& model_summary) {
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();
100 const CallbackRegistrationProto& callback_registration,
103 callback_registration.mip_solution_filter(), model_summary.
variables))
104 <<
"Invalid CallbackRegistrationProto.mip_solution_filter";
106 callback_registration.mip_node_filter(), model_summary.
variables))
107 <<
"Invalid CallbackRegistrationProto.mip_node_filter";
109 const int num_events = callback_registration.request_registration_size();
110 for (
int k = 0; k < num_events; ++k) {
111 const CallbackEventProto requested_event =
112 callback_registration.request_registration(k);
113 if (requested_event == CALLBACK_EVENT_UNSPECIFIED ||
114 !CallbackEventProto_IsValid(requested_event)) {
115 return absl::InvalidArgumentError(absl::StrCat(
116 "Invalid event ", requested_event,
" can not be registered"));
119 return absl::OkStatus();
123 const CallbackDataProto& cb_data,
124 const CallbackRegistrationProto& callback_registration,
126 const CallbackEventProto
event = cb_data.event();
128 <<
"Invalid CallbackDataProto.event for given CallbackRegistrationProto";
130 if (!cb_data.messages().empty() && event != CALLBACK_EVENT_MESSAGE) {
131 return absl::InvalidArgumentError(
132 absl::StrCat(
"Can't provide message(s) for event ", event,
" (",
136 const bool has_primal_solution = cb_data.has_primal_solution();
137 if (has_primal_solution && event != CALLBACK_EVENT_MIP_SOLUTION &&
138 event != CALLBACK_EVENT_MIP_NODE) {
139 return absl::InvalidArgumentError(
140 absl::StrCat(
"Can't provide primal_solution for event ", event,
" (",
144#ifdef RETURN_IF_SCALAR
145#error Collision in macro definition RETURN_IF_SCALAR
147#define RETURN_IF_SCALAR(stat, value, option) \
149 if (stat.has_##value()) { \
150 RETURN_IF_ERROR(CheckScalar(static_cast<double>(stat.value()), option)) \
151 << "Invalid CallbackDataProto." << #stat << "." << #value; \
156 .allow_negative_infinity =
false};
158 .allow_negative =
false};
160 const auto& presolve_stats = cb_data.presolve_stats();
165 const auto& simplex_stats = cb_data.simplex_stats();
172 const auto& barrier_stats = cb_data.barrier_stats();
181 const auto& mip_stats = cb_data.mip_stats();
192 <<
"Invalid CallbackDataProto.runtime.seconds";
194 <<
"Invalid CallbackDataProto.runtime.nanos";
195#undef RETURN_IF_SCALAR
199 case CALLBACK_EVENT_MIP_NODE:
200 case CALLBACK_EVENT_MIP_SOLUTION: {
201 if (has_primal_solution) {
202 const SparseVectorFilterProto& filter =
203 event == CALLBACK_EVENT_MIP_NODE
204 ? callback_registration.mip_node_filter()
205 : callback_registration.mip_solution_filter();
207 filter, model_summary))
208 <<
"Invalid CallbackDataProto.primal_solution";
209 }
else if (event == CALLBACK_EVENT_MIP_SOLUTION) {
210 return absl::InvalidArgumentError(
211 absl::StrCat(
"Must provide primal_solution for event ", event,
" (",
217 case CALLBACK_EVENT_MESSAGE: {
218 if (!!cb_data.messages().empty()) {
219 return absl::InvalidArgumentError(
220 absl::StrCat(
"Invalid CallbackDataProto.messages, must provide "
221 "message(s) for event ",
224 for (absl::string_view
message : cb_data.messages()) {
227 return absl::InvalidArgumentError(
228 absl::StrCat(
"Invalid CallbackDataProto.messages[], message '",
229 message,
"' contains a new line character '\\n'"));
235 case CALLBACK_EVENT_UNSPECIFIED:
239 <<
"CALLBACK_EVENT_UNSPECIFIED can not be a registered event, this "
240 "points to either an invalid CallbackRegistrationProto (which "
242 "one of the assumptions of this function), or memory corruption";
249 return absl::OkStatus();
253 const CallbackResultProto& callback_result,
254 const CallbackEventProto callback_event,
255 const CallbackRegistrationProto& callback_registration,
259 CHECK_OK(IsEventRegistered(callback_event, callback_registration));
261 if (!callback_result.cuts().empty()) {
262 if (callback_event != CALLBACK_EVENT_MIP_NODE &&
263 callback_event != CALLBACK_EVENT_MIP_SOLUTION) {
264 return absl::InvalidArgumentError(absl::StrCat(
265 "Invalid CallbackResultProto, can't return cuts for callback_event ",
268 for (
const CallbackResultProto::GeneratedLinearConstraint& cut :
269 callback_result.cuts()) {
271 cut, callback_registration.add_cuts(),
272 callback_registration.add_lazy_constraints(), model_summary));
275 if (!callback_result.suggested_solution().empty()) {
276 if (callback_event != CALLBACK_EVENT_MIP_NODE) {
277 return absl::InvalidArgumentError(absl::StrCat(
278 "Invalid CallbackResultProto, can't return suggested solutions for "
282 for (
const PrimalSolutionProto& primal_solution :
283 callback_result.suggested_solution()) {
285 primal_solution, SparseVectorFilterProto(), model_summary))
286 <<
"Invalid CallbackResultProto.suggested_solution";
290 return absl::OkStatus();
#define RETURN_IF_SCALAR(stat, value, option)
absl::Span< const double > coefficients
absl::Status CheckScalar(const double value, const DoubleOptions &options)
absl::Status ValidateCallbackResultProto(const CallbackResultProto &callback_result, const CallbackEventProto callback_event, const CallbackRegistrationProto &callback_registration, const ModelSummary &model_summary)
absl::Status ValidatePrimalSolution(const PrimalSolutionProto &primal_solution, const SparseVectorFilterProto &filter, const ModelSummary &model_summary)
absl::Status ValidateCallbackDataProto(const CallbackDataProto &cb_data, const CallbackRegistrationProto &callback_registration, const ModelSummary &model_summary)
absl::Status ValidateCallbackRegistration(const CallbackRegistrationProto &callback_registration, const ModelSummary &model_summary)
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")
absl::Status ValidateSparseVectorFilter(const SparseVectorFilterProto &v, const IdNameBiMap &valid_ids)
absl::Status CheckIdsSubset(absl::Span< const int64_t > ids, const IdNameBiMap &universe, absl::string_view ids_description, absl::string_view universe_description)
Collection of objects used to extend the Constraint Solver library.
std::string ProtoEnumToString(ProtoEnumType enum_value)
#define RETURN_IF_ERROR(expr)
bool allow_positive_infinity