Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
gscip_constraint_handler.cc
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <functional>
17#include <memory>
18#include <string>
19#include <utility>
20
21#include "absl/log/check.h"
22#include "absl/status/status.h"
23#include "absl/types/span.h"
26#include "ortools/gscip/gscip.h"
29#include "scip/def.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" // IWYU pragma: keep
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"
50
52 std::unique_ptr<operations_research::internal::UntypedGScipConstraintHandler>
54 operations_research::GScip* gscip = nullptr;
55};
56
58 void* data;
59};
60
63
64namespace operations_research {
65namespace {
66
67// Returns the "do nothing" callback result. Used to handle the edge case where
68// Enfo* callbacks need to return kFeasible instead of kDidNotRun.
69GScipCallbackResult DidNotRunCallbackResult(
70 const ConstraintHandlerCallbackType callback_type) {
71 // TODO(user): Add kEnfoRelax when we support it.
72 if (callback_type == ConstraintHandlerCallbackType::kEnfoLp ||
75 }
77}
78
80
81// Calls the callback function over a span of constraints, returning the highest
82// priority callback result, along with a SCIP return code.
83absl::StatusOr<GScipCallbackResult> ApplyCallback(
84 absl::Span<SCIP_Cons*> constraints,
85 std::function<GScipCallbackResult(void*)> callback_function,
86 const ConstraintHandlerCallbackType callback_type) {
87 if (constraints.empty()) {
88 return DidNotRunCallbackResult(callback_type);
89 }
90 GScipCallbackResult callback_result = kMinPriority;
91 for (SCIP_Cons* cons : constraints) {
92 if (cons == nullptr) {
93 return absl::InternalError("Constraint handler has null constraint");
94 }
95 SCIP_CONSDATA* consdata = SCIPconsGetData(cons);
96 if (consdata == nullptr || consdata->data == nullptr) {
97 return absl::InternalError("Constraint handler has null constraint data");
98 }
99 const GScipCallbackResult cons_result = callback_function(consdata->data);
100 if (ConstraintHandlerResultPriority(cons_result, callback_type) >
101 ConstraintHandlerResultPriority(callback_result, callback_type)) {
102 callback_result = cons_result;
103 }
104 }
105 return callback_result;
106}
107
108// Calls the callback function over all the constraints of a constraint handler,
109// prioritizing the ones SCIP deems more useful. Returns the highest priority
110// callback result, along with a SCIP return code.
111absl::StatusOr<GScipCallbackResult> ApplyCallback(
112 SCIP_Cons** constraints, const int num_useful_constraints,
113 const int total_num_constraints,
114 std::function<GScipCallbackResult(void*)> callback_function,
115 const ConstraintHandlerCallbackType callback_type) {
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);
121 const GScipCallbackResult result,
122 ApplyCallback(useful_constraints, callback_function, callback_type));
123 // The first num_useful_constraints are the ones that are more likely to be
124 // violated. If no violation was found, we consider the remaining
125 // constraints.
126 if (result == GScipCallbackResult::kDidNotFind ||
129 absl::Span<SCIP_Cons*> remaining_constraints =
130 all_constraints.subspan(num_useful_constraints);
132 const GScipCallbackResult remaining_result,
133 ApplyCallback(remaining_constraints, callback_function, callback_type));
134 if (remaining_result != GScipCallbackResult::kDidNotFind &&
135 remaining_result != GScipCallbackResult::kDidNotRun &&
136 remaining_result != GScipCallbackResult::kFeasible) {
137 return remaining_result;
138 }
139 }
140 return result;
141}
142
143GScipCallbackStats GetCallbackStats(GScip* gscip) {
144 SCIP* scip = gscip->scip();
145 const SCIP_STAGE stage = SCIPgetStage(scip);
146 GScipCallbackStats stats;
147 switch (stage) {
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);
162 break;
163 default:
164 break;
165 }
166
167 switch (stage) {
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;
174 break;
175 }
176 default:
177 stats.current_node_id = stats.num_processed_nodes;
178 }
179 switch (stage) {
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));
191 // Note: SCIPgetNLimSolsFound() docs claim it can be called in more
192 // stages, but that appears to be a typo in the docs.
193 stats.num_solutions_found = static_cast<int>(SCIPgetNLimSolsFound(scip));
194
195 break;
196 default:
197 break;
198 }
199
200 switch (stage) {
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);
207 break;
208 default:
209 break;
210 }
211 // SCIP counts the focus node (the current node) as explored, but to be
212 // consistent with gurobi, we want to count it as open instead. In particular,
213 // for callbacks at the root, we want num_processed_nodes=0
214 if (stats.num_processed_nodes > 0) {
215 stats.num_processed_nodes--;
216 stats.num_processed_nodes_total--;
217 stats.num_nodes_left++;
218 }
219 switch (stage) {
220 case SCIP_STAGE_SOLVING:
221 case SCIP_STAGE_SOLVED:
222 case SCIP_STAGE_EXITSOLVE:
223 stats.num_cuts_in_lp = SCIPgetNPoolCuts(scip);
224 break;
225 default:
226 break;
227 }
228
229 return stats;
230}
231
232GScipConstraintOptions CallbackLazyConstraintOptions(const bool local,
233 const bool dynamic) {
234 GScipConstraintOptions result;
235 result.initial = true;
236 result.separate = true;
237 result.enforce = true;
238 result.check = 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;
246 return result;
247}
248
249} // namespace
250
252 const GScipCallbackResult result,
253 const ConstraintHandlerCallbackType callback_type) {
254 // In type_cons.h, callback results are consistently ordered across all
255 // constraint handler callback methods except that SCIP_SOLVELP (kSolveLp)
256 // takes higher priority than SCIP_BRANCHED (kBranched) in CONSENFOLP, and the
257 // reverse is true for CONSENFORELAX and CONSENFOLP.
258 switch (result) {
260 return 14;
262 return 13;
264 return 12;
266 return 11;
268 return 10;
270 return 9;
272 return callback_type == ConstraintHandlerCallbackType::kEnfoLp ? 7 : 8;
274 return callback_type == ConstraintHandlerCallbackType::kEnfoLp ? 8 : 7;
276 return 6;
278 return 5;
280 return 4;
282 return 3;
284 return 2;
286 return 1;
288 return 0;
289 default:
290 // kConstraintChanged, kFoundSolution, and kSuspend are not used in
291 // constraint handlers.
292 return -1;
293 }
294}
295
297 const GScipCallbackResult result1, const GScipCallbackResult result2,
298 const ConstraintHandlerCallbackType callback_type) {
299 const int priority1 = ConstraintHandlerResultPriority(result1, callback_type);
300 const int priority2 = ConstraintHandlerResultPriority(result2, callback_type);
301 if (priority2 > priority1) {
302 return result2;
303 }
304 return result1;
305}
306
307double GScipConstraintHandlerContext::VariableValue(SCIP_VAR* variable) const {
308 return SCIPgetSolVal(gscip_->scip(), current_solution_, variable);
309}
310
311absl::StatusOr<GScipCallbackResult> GScipConstraintHandlerContext::AddCut(
312 const GScipLinearRange& range, const std::string& name,
313 const GScipCutOptions& options) {
314 SCIP* scip = gscip_->scip();
315 SCIP_ROW* row = nullptr;
316 RETURN_IF_SCIP_ERROR(SCIPcreateEmptyRowConshdlr(
317 scip, &row, current_handler_, name.data(), range.lower_bound,
318 range.upper_bound, options.local, options.modifiable, options.removable));
319 RETURN_IF_SCIP_ERROR(SCIPcacheRowExtensions(scip, row));
320 if (range.coefficients.size() != range.variables.size()) {
321 return absl::InternalError(
322 "GScipLinearRange variables and coefficients do not match in size");
323 }
324 for (int i = 0; i < range.variables.size(); ++i) {
325 RETURN_IF_SCIP_ERROR(SCIPaddVarToRow(scip, row, range.variables.at(i),
326 range.coefficients.at(i)));
327 }
328 RETURN_IF_SCIP_ERROR(SCIPflushRowExtensions(scip, row));
329 SCIP_Bool infeasible;
330 RETURN_IF_SCIP_ERROR(SCIPaddRow(scip, row, options.force_cut, &infeasible));
331 RETURN_IF_SCIP_ERROR(SCIPreleaseRow(scip, &row));
332 return infeasible ? GScipCallbackResult::kCutOff
334}
335
337 const GScipLinearRange& range, const std::string& name,
338 const GScipLazyConstraintOptions& options) {
339 return gscip_
340 ->AddLinearConstraint(
341 range, name,
342 CallbackLazyConstraintOptions(options.local, options.dynamic))
343 .status();
344}
345
347 return SCIPvarGetLbLocal(var);
348}
350 return SCIPvarGetUbLocal(var);
351}
353 return SCIPvarGetLbGlobal(var);
354}
356 return SCIPvarGetUbGlobal(var);
357}
358
360 double value) {
361 return SCIP_TO_STATUS(
362 SCIPchgVarLbNode(gscip_->scip(), /*node=*/nullptr, var, value));
363}
365 double value) {
366 return SCIP_TO_STATUS(
367 SCIPchgVarUbNode(gscip_->scip(), /*node=*/nullptr, var, value));
368}
370 double value) {
371 return SCIP_TO_STATUS(SCIPchgVarLbGlobal(gscip_->scip(), var, value));
372}
374 double value) {
375 return SCIP_TO_STATUS(SCIPchgVarUbGlobal(gscip_->scip(), var, value));
376}
377
378} // namespace operations_research
379
380// ///////////////////////////////////////////////////////////////////////////
381// SCIP callback implementation
382// //////////////////////////////////////////////////////////////////////////
383
384extern "C" {
385
386// Destructor of the constraint handler to free user data (called when SCIP is
387// exiting).
388static SCIP_DECL_CONSFREE(ConstraintHandlerFreeC) {
389 if (scip == nullptr) {
390 SCIPerrorMessage("SCIP not found in SCIP_DECL_CONSFREE");
391 return SCIP_ERROR;
392 }
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");
396 return SCIP_ERROR;
397 }
398 delete scip_handler_data;
399 SCIPconshdlrSetData(conshdlr, nullptr);
400 return SCIP_OKAY;
401}
402
403static SCIP_DECL_CONSDELETE(ConstraintDataDeleteC) {
404 if (consdata == nullptr || *consdata == nullptr) {
405 SCIPerrorMessage("SCIP constraint data not found in SCIP_DECL_CONSDELETE");
406 return SCIP_ERROR;
407 }
408 delete *consdata;
409 cons->consdata = nullptr;
410 return SCIP_OKAY;
411}
412
413static SCIP_DECL_CONSENFOLP(EnforceLpC) {
414 SCIP_CONSHDLRDATA* scip_handler_data = SCIPconshdlrGetData(conshdlr);
415 operations_research::GScip* gscip = scip_handler_data->gscip;
417 operations_research::GetCallbackStats(gscip);
419 conshdlr, nullptr);
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) {
423 return gscip_handler->CallEnforceLp(context, constraint_data,
424 solution_known_infeasible);
425 };
426 const absl::StatusOr<operations_research::GScipCallbackResult> gresult =
427 operations_research::ApplyCallback(
428 conss, nusefulconss, nconss, DoEnforceLp,
430 if (!gresult.ok()) {
431 SCIPerrorMessage(gresult.status().ToString().c_str());
432 return SCIP_ERROR;
433 }
435 return SCIP_OKAY;
436}
437
438static SCIP_DECL_CONSENFOPS(EnforcePseudoSolutionC) {
439 SCIP_CONSHDLRDATA* scip_handler_data = SCIPconshdlrGetData(conshdlr);
440 operations_research::GScip* gscip = scip_handler_data->gscip;
442 operations_research::GetCallbackStats(gscip);
444 conshdlr, nullptr);
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) {
450 return gscip_handler->CallEnforcePseudoSolution(
451 context, constraint_data, solution_known_infeasible,
452 solution_infeasible_by_objective);
453 };
454 const absl::StatusOr<operations_research::GScipCallbackResult> gresult =
455 operations_research::ApplyCallback(
456 conss, nusefulconss, nconss, DoEnforcePseudoSolution,
458 if (!gresult.ok()) {
459 SCIPerrorMessage(gresult.status().ToString().c_str());
460 return SCIP_ERROR;
461 }
463 return SCIP_OKAY;
464}
465
466static SCIP_DECL_CONSCHECK(CheckFeasibilityC) {
467 SCIP_CONSHDLRDATA* scip_handler_data = SCIPconshdlrGetData(conshdlr);
468 operations_research::GScip* gscip = scip_handler_data->gscip;
470 operations_research::GetCallbackStats(gscip);
472 conshdlr, sol);
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) {
479 return gscip_handler->CallCheckIsFeasible(context, constraint_data,
480 check_integrality, check_lp_rows,
481 print_reason, complete);
482 };
483 const absl::StatusOr<operations_research::GScipCallbackResult> gresult =
484 operations_research::ApplyCallback(
485 conss, nconss, nconss, DoCheckIsFeasible,
487 if (!gresult.ok()) {
488 SCIPerrorMessage(gresult.status().ToString().c_str());
489 return SCIP_ERROR;
490 }
492 return SCIP_OKAY;
493}
494
495static SCIP_DECL_CONSSEPALP(SeparateLpC) {
496 SCIP_CONSHDLRDATA* scip_handler_data = SCIPconshdlrGetData(conshdlr);
497 operations_research::GScip* gscip = scip_handler_data->gscip;
499 operations_research::GetCallbackStats(gscip);
501 conshdlr, nullptr);
502 GScipHandler* gscip_handler = scip_handler_data->gscip_handler.get();
503 auto DoSeparateLp = [=](void* constraint_data) {
504 return gscip_handler->CallSeparateLp(context, constraint_data);
505 };
506 const absl::StatusOr<operations_research::GScipCallbackResult> gresult =
507 operations_research::ApplyCallback(
508 conss, nusefulconss, nconss, DoSeparateLp,
510 if (!gresult.ok()) {
511 SCIPerrorMessage(gresult.status().ToString().c_str());
512 return SCIP_ERROR;
513 }
515 return SCIP_OKAY;
516}
517
518static SCIP_DECL_CONSSEPASOL(SeparatePrimalSolutionC) {
519 SCIP_CONSHDLRDATA* scip_handler_data = SCIPconshdlrGetData(conshdlr);
520 operations_research::GScip* gscip = scip_handler_data->gscip;
522 operations_research::GetCallbackStats(gscip);
524 conshdlr, sol);
525 GScipHandler* gscip_handler = scip_handler_data->gscip_handler.get();
526 auto DoSeparateSolution = [=](void* constraint_data) {
527 return gscip_handler->CallSeparateSolution(context, constraint_data);
528 };
529 const absl::StatusOr<operations_research::GScipCallbackResult> gresult =
530 operations_research::ApplyCallback(
531 conss, nusefulconss, nconss, DoSeparateSolution,
533 if (!gresult.ok()) {
534 SCIPerrorMessage(gresult.status().ToString().c_str());
535 return SCIP_ERROR;
536 }
538 return SCIP_OKAY;
539}
540
541static SCIP_DECL_CONSLOCK(VariableRoundingLockC) {
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) {
547 SCIPerrorMessage(
548 "consdata or consdata->data was null in SCIP_DECL_CONSLOCK");
549 return SCIP_ERROR;
550 }
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)) {
554 int lock_down;
555 int lock_up;
556 switch (lock_direction) {
558 lock_down = nlocksneg;
559 lock_up = nlockspos;
560 break;
562 lock_down = nlockspos;
563 lock_up = nlocksneg;
564 break;
566 lock_down = nlocksneg + nlockspos;
567 lock_up = nlocksneg + nlockspos;
568 break;
569 }
570 SCIP_CALL(
571 SCIPaddVarLocksType(scip, locked_var, locktype, lock_down, lock_up));
572 }
573 return SCIP_OKAY;
574}
575}
576
577namespace operations_research {
578
579namespace internal {
580
581absl::Status RegisterConstraintHandler(
582 GScip* gscip,
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();
589 const GScipConstraintHandlerProperties& properties =
590 scip_handler_data->gscip_handler->properties();
591
592 RETURN_IF_SCIP_ERROR(SCIPincludeConshdlrBasic(
593 scip, &c_scip_handler, properties.name.c_str(),
594 properties.description.c_str(), properties.enforcement_priority,
595 properties.feasibility_check_priority, properties.eager_frequency,
596 properties.needs_constraints, EnforceLpC, EnforcePseudoSolutionC,
597 CheckFeasibilityC, VariableRoundingLockC, scip_handler_data));
598 if (c_scip_handler == nullptr) {
599 return absl::InternalError("SCIP failed to add constraint handler");
600 }
601 RETURN_IF_SCIP_ERROR(SCIPsetConshdlrSepa(
602 scip, c_scip_handler, SeparateLpC, SeparatePrimalSolutionC,
603 properties.separation_frequency, properties.separation_priority,
604 properties.delay_separation));
606 SCIPsetConshdlrFree(scip, c_scip_handler, ConstraintHandlerFreeC));
608 SCIPsetConshdlrDelete(scip, c_scip_handler, ConstraintDataDeleteC));
609
610 return absl::OkStatus();
611}
612
613absl::StatusOr<SCIP_CONS*> AddCallbackConstraint(
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");
620 }
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.";
628 }
629 SCIP_CONSDATA* consdata = new SCIP_CONSDATA;
630 consdata->data = constraint_data;
631
632 return gscip->AddConstraintForHandler(conshdlr, consdata, constraint_name,
633 options);
634}
635
636} // namespace internal
637
638} // namespace operations_research
#define ASSIGN_OR_RETURN(lhs, rexpr)
absl::Status SetLocalVarLb(SCIP_VAR *var, double value)
absl::StatusOr< GScipCallbackResult > AddCut(const GScipLinearRange &range, const std::string &name, const GScipCutOptions &options=GScipCutOptions())
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)
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.
int64_t value
IntVar * var
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
RowIndex row
Definition markowitz.cc:186
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)
@ 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
Definition statistics.cc:37
std::unique_ptr< operations_research::internal::UntypedGScipConstraintHandler > gscip_handler
operations_research::GScip * gscip
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.