21#include "absl/log/check.h"
22#include "absl/status/status.h"
23#include "absl/types/span.h"
30#include "scip/pub_cons.h"
31#include "scip/pub_message.h"
32#include "scip/pub_var.h"
33#include "scip/scip_cons.h"
34#include "scip/scip_cut.h"
35#include "scip/scip_general.h"
36#include "scip/scip_lp.h"
37#include "scip/scip_sol.h"
38#include "scip/scip_solvingstats.h"
39#include "scip/scip_tree.h"
40#include "scip/scip_var.h"
41#include "scip/struct_cons.h"
42#include "scip/struct_tree.h"
43#include "scip/type_cons.h"
44#include "scip/type_lp.h"
45#include "scip/type_retcode.h"
46#include "scip/type_scip.h"
47#include "scip/type_set.h"
48#include "scip/type_tree.h"
49#include "scip/type_var.h"
52 std::unique_ptr<operations_research::internal::UntypedGScipConstraintHandler>
54 operations_research::GScip*
gscip =
nullptr;
83absl::StatusOr<GScipCallbackResult> ApplyCallback(
84 absl::Span<SCIP_Cons*> constraints,
87 if (constraints.empty()) {
88 return DidNotRunCallbackResult(callback_type);
91 for (SCIP_Cons* cons : constraints) {
92 if (cons ==
nullptr) {
93 return absl::InternalError(
"Constraint handler has null constraint");
95 SCIP_CONSDATA* consdata = SCIPconsGetData(cons);
96 if (consdata ==
nullptr || consdata->data ==
nullptr) {
97 return absl::InternalError(
"Constraint handler has null constraint data");
102 callback_result = cons_result;
105 return callback_result;
111absl::StatusOr<GScipCallbackResult> ApplyCallback(
112 SCIP_Cons** constraints,
const int num_useful_constraints,
113 const int total_num_constraints,
116 absl::Span<SCIP_Cons*> all_constraints =
117 absl::MakeSpan(constraints, total_num_constraints);
118 absl::Span<SCIP_Cons*> useful_constraints =
119 all_constraints.subspan(0, num_useful_constraints);
122 ApplyCallback(useful_constraints, callback_function, callback_type));
129 absl::Span<SCIP_Cons*> remaining_constraints =
130 all_constraints.subspan(num_useful_constraints);
133 ApplyCallback(remaining_constraints, callback_function, callback_type));
137 return remaining_result;
143GScipCallbackStats GetCallbackStats(GScip* gscip) {
144 SCIP* scip = gscip->scip();
145 const SCIP_STAGE stage = SCIPgetStage(scip);
146 GScipCallbackStats stats;
148 case SCIP_STAGE_PROBLEM:
149 case SCIP_STAGE_TRANSFORMING:
150 case SCIP_STAGE_TRANSFORMED:
151 case SCIP_STAGE_INITPRESOLVE:
152 case SCIP_STAGE_PRESOLVING:
153 case SCIP_STAGE_EXITPRESOLVE:
154 case SCIP_STAGE_PRESOLVED:
155 case SCIP_STAGE_INITSOLVE:
156 case SCIP_STAGE_SOLVING:
157 case SCIP_STAGE_SOLVED:
158 case SCIP_STAGE_EXITSOLVE:
159 case SCIP_STAGE_FREETRANS:
160 stats.num_processed_nodes = SCIPgetNNodes(scip);
161 stats.num_processed_nodes_total = SCIPgetNTotalNodes(scip);
168 case SCIP_STAGE_INITPRESOLVE:
169 case SCIP_STAGE_PRESOLVING:
170 case SCIP_STAGE_EXITPRESOLVE:
171 case SCIP_STAGE_SOLVING: {
172 SCIP_NODE* node = SCIPgetCurrentNode(scip);
173 stats.current_node_id = node ==
nullptr ? -1 : node->number;
177 stats.current_node_id = stats.num_processed_nodes;
180 case SCIP_STAGE_TRANSFORMED:
181 case SCIP_STAGE_INITPRESOLVE:
182 case SCIP_STAGE_PRESOLVING:
183 case SCIP_STAGE_EXITPRESOLVE:
184 case SCIP_STAGE_PRESOLVED:
185 case SCIP_STAGE_INITSOLVE:
186 case SCIP_STAGE_SOLVING:
187 case SCIP_STAGE_SOLVED:
188 case SCIP_STAGE_EXITSOLVE:
189 stats.primal_bound = gscip->ScipInfUnclamp(SCIPgetPrimalbound(scip));
190 stats.dual_bound = gscip->ScipInfUnclamp(SCIPgetDualbound(scip));
193 stats.num_solutions_found =
static_cast<int>(SCIPgetNLimSolsFound(scip));
201 case SCIP_STAGE_PRESOLVED:
202 case SCIP_STAGE_SOLVING:
203 case SCIP_STAGE_SOLVED:
204 stats.primal_simplex_iterations = SCIPgetNPrimalLPIterations(scip);
205 stats.dual_simplex_iterations = SCIPgetNDualLPIterations(scip);
206 stats.num_nodes_left = SCIPgetNNodesLeft(scip);
214 if (stats.num_processed_nodes > 0) {
215 stats.num_processed_nodes--;
216 stats.num_processed_nodes_total--;
217 stats.num_nodes_left++;
220 case SCIP_STAGE_SOLVING:
221 case SCIP_STAGE_SOLVED:
222 case SCIP_STAGE_EXITSOLVE:
223 stats.num_cuts_in_lp = SCIPgetNPoolCuts(scip);
232GScipConstraintOptions CallbackLazyConstraintOptions(
const bool local,
233 const bool dynamic) {
234 GScipConstraintOptions result;
235 result.initial =
true;
236 result.separate =
true;
237 result.enforce =
true;
239 result.propagate =
true;
240 result.local = local;
241 result.modifiable =
false;
242 result.dynamic = dynamic;
243 result.removable =
true;
244 result.sticking_at_node =
false;
245 result.keep_alive =
false;
301 if (priority2 > priority1) {
308 return SCIPgetSolVal(gscip_->scip(), current_solution_, variable);
312 const GScipLinearRange&
range,
const std::string&
name,
314 SCIP* scip = gscip_->scip();
315 SCIP_ROW*
row =
nullptr;
317 scip, &
row, current_handler_,
name.data(),
range.lower_bound,
320 if (
range.coefficients.size() !=
range.variables.size()) {
321 return absl::InternalError(
322 "GScipLinearRange variables and coefficients do not match in size");
324 for (
int i = 0; i <
range.variables.size(); ++i) {
326 range.coefficients.at(i)));
329 SCIP_Bool infeasible;
337 const GScipLinearRange&
range,
const std::string&
name,
340 ->AddLinearConstraint(
342 CallbackLazyConstraintOptions(options.
local, options.
dynamic))
347 return SCIPvarGetLbLocal(
var);
350 return SCIPvarGetUbLocal(
var);
353 return SCIPvarGetLbGlobal(
var);
356 return SCIPvarGetUbGlobal(
var);
362 SCIPchgVarLbNode(gscip_->scip(),
nullptr,
var,
value));
367 SCIPchgVarUbNode(gscip_->scip(),
nullptr,
var,
value));
389 if (scip ==
nullptr) {
390 SCIPerrorMessage(
"SCIP not found in SCIP_DECL_CONSFREE");
393 SCIP_CONSHDLRDATA* scip_handler_data = SCIPconshdlrGetData(conshdlr);
394 if (scip_handler_data ==
nullptr) {
395 SCIPerrorMessage(
"SCIP handler data not found in SCIP_DECL_CONSFREE");
398 delete scip_handler_data;
399 SCIPconshdlrSetData(conshdlr,
nullptr);
404 if (consdata ==
nullptr || *consdata ==
nullptr) {
405 SCIPerrorMessage(
"SCIP constraint data not found in SCIP_DECL_CONSDELETE");
409 cons->consdata =
nullptr;
414 SCIP_CONSHDLRDATA* scip_handler_data = SCIPconshdlrGetData(conshdlr);
415 operations_research::GScip* gscip = scip_handler_data->gscip;
417 operations_research::GetCallbackStats(gscip);
420 const bool solution_known_infeasible =
static_cast<bool>(solinfeasible);
421 GScipHandler* gscip_handler = scip_handler_data->gscip_handler.get();
422 auto DoEnforceLp = [=](
void* constraint_data) {
424 solution_known_infeasible);
426 const absl::StatusOr<operations_research::GScipCallbackResult> gresult =
427 operations_research::ApplyCallback(
428 conss, nusefulconss, nconss, DoEnforceLp,
431 SCIPerrorMessage(gresult.status().ToString().c_str());
439 SCIP_CONSHDLRDATA* scip_handler_data = SCIPconshdlrGetData(conshdlr);
440 operations_research::GScip* gscip = scip_handler_data->gscip;
442 operations_research::GetCallbackStats(gscip);
445 const bool solution_known_infeasible =
static_cast<bool>(solinfeasible);
446 const bool solution_infeasible_by_objective =
447 static_cast<bool>(objinfeasible);
448 GScipHandler* gscip_handler = scip_handler_data->gscip_handler.get();
449 auto DoEnforcePseudoSolution = [=](
void* constraint_data) {
451 context, constraint_data, solution_known_infeasible,
452 solution_infeasible_by_objective);
454 const absl::StatusOr<operations_research::GScipCallbackResult> gresult =
455 operations_research::ApplyCallback(
456 conss, nusefulconss, nconss, DoEnforcePseudoSolution,
459 SCIPerrorMessage(gresult.status().ToString().c_str());
467 SCIP_CONSHDLRDATA* scip_handler_data = SCIPconshdlrGetData(conshdlr);
468 operations_research::GScip* gscip = scip_handler_data->gscip;
470 operations_research::GetCallbackStats(gscip);
473 const bool check_integrality =
static_cast<bool>(checkintegrality);
474 const bool check_lp_rows =
static_cast<bool>(checklprows);
475 const bool print_reason =
static_cast<bool>(printreason);
476 const bool complete =
static_cast<bool>(completely);
477 GScipHandler* gscip_handler = scip_handler_data->gscip_handler.get();
478 auto DoCheckIsFeasible = [=](
void* constraint_data) {
480 check_integrality, check_lp_rows,
481 print_reason, complete);
483 const absl::StatusOr<operations_research::GScipCallbackResult> gresult =
484 operations_research::ApplyCallback(
485 conss, nconss, nconss, DoCheckIsFeasible,
488 SCIPerrorMessage(gresult.status().ToString().c_str());
496 SCIP_CONSHDLRDATA* scip_handler_data = SCIPconshdlrGetData(conshdlr);
497 operations_research::GScip* gscip = scip_handler_data->gscip;
499 operations_research::GetCallbackStats(gscip);
502 GScipHandler* gscip_handler = scip_handler_data->gscip_handler.get();
503 auto DoSeparateLp = [=](
void* constraint_data) {
506 const absl::StatusOr<operations_research::GScipCallbackResult> gresult =
507 operations_research::ApplyCallback(
508 conss, nusefulconss, nconss, DoSeparateLp,
511 SCIPerrorMessage(gresult.status().ToString().c_str());
519 SCIP_CONSHDLRDATA* scip_handler_data = SCIPconshdlrGetData(conshdlr);
520 operations_research::GScip* gscip = scip_handler_data->gscip;
522 operations_research::GetCallbackStats(gscip);
525 GScipHandler* gscip_handler = scip_handler_data->gscip_handler.get();
526 auto DoSeparateSolution = [=](
void* constraint_data) {
529 const absl::StatusOr<operations_research::GScipCallbackResult> gresult =
530 operations_research::ApplyCallback(
531 conss, nusefulconss, nconss, DoSeparateSolution,
534 SCIPerrorMessage(gresult.status().ToString().c_str());
542 SCIP_CONSHDLRDATA* scip_handler_data = SCIPconshdlrGetData(conshdlr);
543 operations_research::GScip* gscip = scip_handler_data->gscip;
544 GScipHandler* gscip_handler = scip_handler_data->gscip_handler.get();
545 SCIP_CONSDATA* consdata = SCIPconsGetData(cons);
546 if (consdata ==
nullptr || consdata->data ==
nullptr) {
548 "consdata or consdata->data was null in SCIP_DECL_CONSLOCK");
551 const bool lock_type_is_model = locktype == SCIP_LOCKTYPE_MODEL;
552 for (
const auto [locked_var, lock_direction] :
553 gscip_handler->
RoundingLock(gscip, consdata->data, lock_type_is_model)) {
556 switch (lock_direction) {
558 lock_down = nlocksneg;
562 lock_down = nlockspos;
566 lock_down = nlocksneg + nlockspos;
567 lock_up = nlocksneg + nlockspos;
571 SCIPaddVarLocksType(scip, locked_var, locktype, lock_down, lock_up));
583 std::unique_ptr<UntypedGScipConstraintHandler> constraint_handler) {
584 SCIP_CONSHDLR* c_scip_handler;
585 SCIP_CONSHDLRDATA* scip_handler_data =
new SCIP_CONSHDLRDATA;
586 scip_handler_data->gscip_handler = std::move(constraint_handler);
587 scip_handler_data->gscip = gscip;
588 SCIP* scip = gscip->scip();
590 scip_handler_data->gscip_handler->properties();
593 scip, &c_scip_handler, properties.
name.c_str(),
597 CheckFeasibilityC, VariableRoundingLockC, scip_handler_data));
598 if (c_scip_handler ==
nullptr) {
599 return absl::InternalError(
"SCIP failed to add constraint handler");
602 scip, c_scip_handler, SeparateLpC, SeparatePrimalSolutionC,
606 SCIPsetConshdlrFree(scip, c_scip_handler, ConstraintHandlerFreeC));
608 SCIPsetConshdlrDelete(scip, c_scip_handler, ConstraintDataDeleteC));
610 return absl::OkStatus();
614 GScip* gscip,
const std::string& handler_name,
615 const std::string& constraint_name,
void* constraint_data,
616 const GScipConstraintOptions& options) {
617 if (constraint_data ==
nullptr) {
618 return absl::InvalidArgumentError(
619 "Constraint data missing when adding a constraint handler callback");
621 SCIP* scip = gscip->scip();
622 SCIP_CONSHDLR* conshdlr = SCIPfindConshdlr(scip, handler_name.data());
623 if (conshdlr ==
nullptr) {
625 <<
"Constraint handler " << handler_name
626 <<
" not registered with SCIP. Check if you "
627 "registered the constraint handler before adding constraints.";
629 SCIP_CONSDATA* consdata =
new SCIP_CONSDATA;
630 consdata->data = constraint_data;
632 return gscip->AddConstraintForHandler(conshdlr, consdata, constraint_name,
#define ASSIGN_OR_RETURN(lhs, rexpr)
double VariableValue(SCIP_VAR *variable) const
double GlobalVarUb(SCIP_VAR *var) const
absl::Status SetLocalVarLb(SCIP_VAR *var, double value)
absl::StatusOr< GScipCallbackResult > AddCut(const GScipLinearRange &range, const std::string &name, const GScipCutOptions &options=GScipCutOptions())
double GlobalVarLb(SCIP_VAR *var) const
absl::Status SetGlobalVarUb(SCIP_VAR *var, double value)
absl::Status AddLazyLinearConstraint(const GScipLinearRange &range, const std::string &name, const GScipLazyConstraintOptions &options=GScipLazyConstraintOptions())
absl::Status SetLocalVarUb(SCIP_VAR *var, double value)
double LocalVarLb(SCIP_VAR *var) const
double LocalVarUb(SCIP_VAR *var) const
absl::Status SetGlobalVarLb(SCIP_VAR *var, double value)
virtual std::vector< std::pair< SCIP_VAR *, RoundingLockDirection > > RoundingLock(GScip *gscip, const ConstraintData &constraint_data, bool lock_type_is_model)
GScipCallbackResult CallEnforcePseudoSolution(GScipConstraintHandlerContext context, const ConstraintData &constraint_data, bool solution_infeasible, bool objective_infeasible)
GScipCallbackResult CallEnforceLp(GScipConstraintHandlerContext context, const ConstraintData &constraint_data, bool solution_infeasible)
The functions below wrap each callback function to manage status.
GScipCallbackResult CallCheckIsFeasible(GScipConstraintHandlerContext context, const ConstraintData &constraint_data, bool check_integrality, bool check_lp_rows, bool print_reason, bool check_completely)
GScipCallbackResult CallSeparateLp(GScipConstraintHandlerContext context, const ConstraintData &constraint_data)
GScipCallbackResult CallSeparateSolution(GScipConstraintHandlerContext context, const ConstraintData &constraint_data)
const std::string name
A name for logging purposes.
static SCIP_DECL_CONSLOCK(VariableRoundingLockC)
static SCIP_DECL_CONSFREE(ConstraintHandlerFreeC)
static SCIP_DECL_CONSSEPASOL(SeparatePrimalSolutionC)
static SCIP_DECL_CONSENFOPS(EnforcePseudoSolutionC)
static SCIP_DECL_CONSSEPALP(SeparateLpC)
static SCIP_DECL_CONSENFOLP(EnforceLpC)
static SCIP_DECL_CONSDELETE(ConstraintDataDeleteC)
static SCIP_DECL_CONSCHECK(CheckFeasibilityC)
GurobiMPCallbackContext * context
absl::StatusOr< SCIP_CONS * > AddCallbackConstraint(GScip *gscip, const std::string &handler_name, const std::string &constraint_name, void *constraint_data, const GScipConstraintOptions &options)
absl::Status RegisterConstraintHandler(GScip *gscip, std::unique_ptr< UntypedGScipConstraintHandler > constraint_handler)
In SWIG mode, we don't want anything besides these top-level includes.
int ConstraintHandlerResultPriority(const GScipCallbackResult result, const ConstraintHandlerCallbackType callback_type)
ConstraintHandlerCallbackType
@ kEnfoPs
Unsupported: kEnfoRelax, ///< CONSENFORELAX.
SCIP_RESULT ConvertGScipCallbackResult(const GScipCallbackResult result)
GScipCallbackResult
Equivalent to type_result.h in SCIP.
GScipCallbackResult MergeConstraintHandlerResults(const GScipCallbackResult result1, const GScipCallbackResult result2, const ConstraintHandlerCallbackType callback_type)
StatusBuilder InternalErrorBuilder()
#define SCIP_TO_STATUS(x)
#define RETURN_IF_SCIP_ERROR(x)
const std::optional< Range > & range
std::unique_ptr< operations_research::internal::UntypedGScipConstraintHandler > gscip_handler
operations_research::GScip * gscip
int feasibility_check_priority
Options passed to SCIP when adding a cut.
bool local
Cut is only valid for the current subtree.
bool removable
Cut can be removed from the LP due to aging or cleanup.
bool force_cut
Cut is forced to enter the LP.
bool modifiable
Cut is modifiable during node processing (subject to column generation).
Options passed to SCIP when adding a lazy constraint.
bool local
Cut is only valid for the current subtree.
bool dynamic
Constraint is subject to aging.