19#include "absl/status/status.h"
20#include "absl/status/statusor.h"
21#include "absl/time/clock.h"
22#include "absl/time/time.h"
28#include "ortools/math_opt/callback.pb.h"
30#include "ortools/math_opt/sparse_containers.pb.h"
32#include "scip/type_var.h"
48GScipConstraintHandlerProperties MakeHandlerProperties() {
50 .name =
"GScipSolverConstraintHandler",
51 .description =
"A single handler for all mathopt callbacks",
52 .enforcement_priority = -9'999'998,
53 .feasibility_check_priority = -9'999'998,
54 .separation_priority = -9'999'998,
61 return absl::OkStatus();
64 return absl::InternalError(
65 "GScipSolverConstraintData::variables must be set when "
66 "GScipSolverConstraintData::user_callback is not null");
69 return absl::InternalError(
70 "GScipSolverConstraintData::variable_node_filter must be set when "
71 "GScipSolverConstraintData::variable_node_filter is not null");
74 return absl::InternalError(
75 "GScipSolverConstraintData::variable_solution_filter must be set when "
76 "GScipSolverConstraintData::user_callback is not null");
79 return absl::InternalError(
80 "GScipSolverConstraintData::interrupter must be set when "
81 "GScipSolverConstraintData::user_callback is not null");
83 return absl::OkStatus();
87 const CallbackRegistrationProto& registration) {
88 for (
const int event_int : registration.request_registration()) {
89 switch (
static_cast<CallbackEventProto
>(event_int)) {
90 case CALLBACK_EVENT_MIP_NODE:
93 case CALLBACK_EVENT_MIP_SOLUTION:
107absl::StatusOr<GScipCallbackResult> GScipSolverConstraintHandler::EnforceLp(
110 bool solution_infeasible) {
117 const CallbackDataProto cb_data,
118 MakeCbData(
context, constraint_data, CALLBACK_EVENT_MIP_SOLUTION));
122 return ApplyCallback(result,
context, constraint_data,
126absl::StatusOr<bool> GScipSolverConstraintHandler::CheckIsFeasible(
127 GScipConstraintHandlerContext
context,
128 const GScipSolverConstraintData& constraint_data,
129 const bool check_integrality,
const bool check_lp_rows,
130 const bool print_reason,
const bool check_completely) {
131 if (check_completely) {
132 return absl::InternalError(
133 "check_completely inside of CONSCHECK not supported. This is called "
134 "only if you have set some SCIP parameters manually, e.g. "
135 "display/allviols=TRUE");
138 if (!constraint_data.run_at_solutions ||
139 constraint_data.user_callback ==
nullptr) {
143 const CallbackDataProto cb_data,
144 MakeCbData(
context, constraint_data, CALLBACK_EVENT_MIP_SOLUTION));
146 constraint_data.user_callback(cb_data));
148 ApplyCallback(result,
context, constraint_data,
153absl::StatusOr<GScipCallbackResult> GScipSolverConstraintHandler::SeparateLp(
154 GScipConstraintHandlerContext
context,
155 const GScipSolverConstraintData& constraint_data) {
157 if (!constraint_data.run_at_nodes ||
158 constraint_data.user_callback ==
nullptr) {
162 const CallbackDataProto cb_data,
163 MakeCbData(
context, constraint_data, CALLBACK_EVENT_MIP_NODE));
165 constraint_data.user_callback(cb_data));
167 ApplyCallback(result,
context, constraint_data,
175absl::StatusOr<GScipCallbackResult>
176GScipSolverConstraintHandler::SeparateSolution(
177 GScipConstraintHandlerContext
context,
178 const GScipSolverConstraintData& constraint_data) {
180 if (!constraint_data.run_at_solutions ||
181 constraint_data.user_callback ==
nullptr) {
185 const CallbackDataProto cb_data,
186 MakeCbData(
context, constraint_data, CALLBACK_EVENT_MIP_SOLUTION));
188 constraint_data.user_callback(cb_data));
190 ApplyCallback(result,
context, constraint_data,
198absl::StatusOr<CallbackDataProto> GScipSolverConstraintHandler::MakeCbData(
199 GScipConstraintHandlerContext&
context,
200 const GScipSolverConstraintData& constraint_data,
201 const CallbackEventProto event) {
202 if (event != CALLBACK_EVENT_MIP_NODE &&
203 event != CALLBACK_EVENT_MIP_SOLUTION) {
205 <<
"Only events MIP_NODE and MIP_SOLUTION are supported, but was "
209 CallbackDataProto cb_data;
210 cb_data.set_event(event);
211 const SparseVectorFilterProto* filter =
212 event == CALLBACK_EVENT_MIP_NODE
213 ? constraint_data.variable_node_filter
214 : constraint_data.variable_solution_filter;
215 auto& var_values = *cb_data.mutable_primal_solution_vector();
216 SparseVectorFilterPredicate predicate(*filter);
217 for (
const auto [var_id, scip_var] : *constraint_data.variables) {
219 if (predicate.AcceptsAndUpdate(var_id,
value)) {
220 var_values.add_ids(var_id);
221 var_values.add_values(
value);
224 const GScipCallbackStats& stats =
context.stats();
225 CallbackDataProto::MipStats& cb_stats = *cb_data.mutable_mip_stats();
226 cb_stats.set_primal_bound(stats.primal_bound);
227 cb_stats.set_dual_bound(stats.dual_bound);
228 cb_stats.set_explored_nodes(stats.num_processed_nodes_total);
229 cb_stats.set_open_nodes(stats.num_nodes_left);
233 cb_stats.set_simplex_iterations(stats.primal_simplex_iterations +
234 stats.dual_simplex_iterations);
235 cb_stats.set_number_of_solutions_found(stats.num_solutions_found);
236 cb_stats.set_cutting_planes_in_lp(stats.num_cuts_in_lp);
237 const absl::Duration elapsed = absl::Now() - constraint_data.solve_start_time;
243absl::StatusOr<GScipCallbackResult> GScipSolverConstraintHandler::ApplyCallback(
244 const CallbackResultProto& result, GScipConstraintHandlerContext&
context,
245 const GScipSolverConstraintData& constraint_data,
247 if (!result.suggested_solutions().empty()) {
248 return absl::UnimplementedError(
249 "suggested solution is not yet implemented for SCIP callbacks in "
253 for (
const CallbackResultProto::GeneratedLinearConstraint& cut :
255 GScipLinearRange scip_constraint{.lower_bound = cut.lower_bound(),
256 .upper_bound = cut.upper_bound()};
257 for (
int i = 0;
i < cut.linear_expression().ids_size(); ++
i) {
258 scip_constraint.variables.push_back(
259 constraint_data.variables->at(cut.linear_expression().ids(i)));
260 scip_constraint.coefficients.push_back(cut.linear_expression().values(i));
268 context.AddCut(scip_constraint,
""));
273 if (result.terminate()) {
276 constraint_data.interrupter->Interrupt();
281std::vector<std::pair<SCIP_VAR*, RoundingLockDirection>>
282GScipSolverConstraintHandler::RoundingLock(
283 GScip* gscip,
const GScipSolverConstraintData& constraint_data,
284 const bool lock_type_is_model) {
288 const bool generates_constraints =
289 constraint_data.adds_cuts || constraint_data.adds_lazy_constraints;
290 if (constraint_data.user_callback ==
nullptr || !generates_constraints) {
293 std::vector<std::pair<SCIP_VAR*, RoundingLockDirection>> result;
294 for (SCIP_VAR*
var : gscip->variables()) {
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
GScipSolverConstraintHandler()
GurobiMPCallbackContext * context
An object oriented wrapper for quadratic constraints in ModelStorage.
ConstraintHandlerCallbackType
std::string ProtoEnumToString(ProtoEnumType enum_value)
GScipCallbackResult
Equivalent to type_result.h in SCIP.
GScipCallbackResult MergeConstraintHandlerResults(const GScipCallbackResult result1, const GScipCallbackResult result2, const ConstraintHandlerCallbackType callback_type)
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)
StatusBuilder InternalErrorBuilder()
SolverInterface::Callback user_callback
absl::Status Validate() const
const SparseVectorFilterProto * variable_solution_filter
bool adds_lazy_constraints
void SetWhenRunAndAdds(const CallbackRegistrationProto ®istration)
GScip::Interrupter * interrupter
const gtl::linked_hash_map< int64_t, SCIP_VAR * > * variables
const SparseVectorFilterProto * variable_node_filter