25#include "absl/base/attributes.h"
26#include "absl/cleanup/cleanup.h"
27#include "absl/flags/flag.h"
28#include "absl/status/status.h"
29#include "absl/strings/str_format.h"
30#include "absl/synchronization/mutex.h"
31#include "absl/time/time.h"
44#include "scip/cons_indicator.h"
45#include "scip/cons_linear.h"
47#include "scip/scip_cons.h"
48#include "scip/scip_copy.h"
49#include "scip/scip_general.h"
50#include "scip/scip_message.h"
51#include "scip/scip_numerics.h"
52#include "scip/scip_param.h"
53#include "scip/scip_prob.h"
54#include "scip/scip_sol.h"
55#include "scip/scip_solve.h"
56#include "scip/scip_solvingstats.h"
57#include "scip/scip_var.h"
58#include "scip/scipdefplugins.h"
59#include "scip/type_clock.h"
60#include "scip/type_cons.h"
61#include "scip/type_paramset.h"
62#include "scip/type_prob.h"
63#include "scip/type_retcode.h"
64#include "scip/type_scip.h"
65#include "scip/type_sol.h"
66#include "scip/type_stat.h"
67#include "scip/type_var.h"
70 "When true, emphasize search towards feasibility. This may or "
71 "may not result in speedups in some problems.");
91 std::atomic<bool>* interrupt)
override;
93 void Reset()
override;
105 double new_value,
double old_value)
override;
108 double coefficient)
override;
114 int64_t
nodes()
const override;
116 LOG(DFATAL) <<
"Basis status only available for continuous problems";
120 LOG(DFATAL) <<
"Basis status only available for continuous problems";
125 bool IsLP()
const override {
return false; }
126 bool IsMIP()
const override {
return true; }
133 return absl::StrFormat(
"SCIP %d.%d.%d [LP solver: %s]", SCIPmajorVersion(),
134 SCIPminorVersion(), SCIPtechVersion(),
135 SCIPlpiGetSolverName());
139 const absl::MutexLock lock(hold_interruptions_mutex_);
140 if (scip_ ==
nullptr) {
141 LOG_IF(DFATAL, status_.ok()) <<
"scip_ is null is unexpected here, since "
142 "status_ did not report any error";
145 return SCIPinterruptSolve(scip_) == SCIP_OKAY;
178 void SetRelativeMipGap(
double value)
override;
179 void SetPrimalTolerance(
double value)
override;
180 void SetDualTolerance(
double value)
override;
181 void SetPresolveMode(
int presolve)
override;
182 void SetScalingMode(
int scaling)
override;
183 void SetLpAlgorithm(
int lp_algorithm)
override;
193 absl::Status SetNumThreads(
int num_threads)
override;
195 bool SetSolverSpecificParametersAsString(
196 const std::string& parameters)
override;
198 void SetUnsupportedIntegerParam(
205 void SetSolution(SCIP_SOL*
solution);
207 absl::Status CreateSCIP();
211 SCIP* DeleteSCIP(
bool return_scip =
false);
219 absl::Status status_;
222 std::vector<SCIP_VAR*> scip_variables_;
223 std::vector<SCIP_CONS*> scip_constraints_;
224 int current_solution_index_ = 0;
226 std::unique_ptr<ScipConstraintHandlerForMPCallback> scip_constraint_handler_;
228 EmptyStruct constraint_data_for_handler_;
229 bool branching_priority_reset_ =
false;
230 bool callback_reset_ =
false;
235 mutable absl::Mutex hold_interruptions_mutex_;
252 std::vector<CallbackRangeConstraint> SeparateSolution(
258#define RETURN_IF_ALREADY_IN_ERROR_STATE \
260 if (!status_.ok()) { \
261 VLOG_EVERY_N(1, 10) << "Early abort: SCIP is in error state."; \
266#define RETURN_AND_STORE_IF_SCIP_ERROR(x) \
268 status_ = SCIP_TO_STATUS(x); \
269 if (!status_.ok()) return; \
274 status_ = CreateSCIP();
281 const absl::MutexLock lock(hold_interruptions_mutex_);
284 SCIP* old_scip = DeleteSCIP(
true);
285 const auto scip_deleter = absl::MakeCleanup(
286 [&old_scip]() { CHECK_EQ(SCIPfree(&old_scip), SCIP_OKAY); });
288 scip_constraint_handler_.reset();
292 status_ = CreateSCIP();
306absl::Status SCIPInterface::CreateSCIP() {
311 if (absl::GetFlag(FLAGS_scip_feasibility_emphasis)) {
322 SCIPsetIntParam(scip_,
"timing/clocktype", SCIP_CLOCKTYPE_WALL));
324 nullptr,
nullptr,
nullptr,
nullptr,
327 scip_,
maximize_ ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
328 return absl::OkStatus();
333SCIP* SCIPInterface::DeleteSCIP(
bool return_scip) {
338 CHECK(scip_ !=
nullptr);
339 for (
int i = 0; i < scip_variables_.size(); ++i) {
340 CHECK_EQ(SCIPreleaseVar(scip_, &scip_variables_[i]), SCIP_OKAY);
342 scip_variables_.clear();
343 for (
int j = 0; j < scip_constraints_.size(); ++j) {
344 CHECK_EQ(SCIPreleaseCons(scip_, &scip_constraints_[j]), SCIP_OKAY);
346 scip_constraints_.clear();
348 SCIP* old_scip = scip_;
351 CHECK_EQ(SCIPfree(&old_scip), SCIP_OKAY);
362 scip_, maximize ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
373 SCIPchgVarLb(scip_, scip_variables_[var_index], lb));
375 SCIPchgVarUb(scip_, scip_variables_[var_index], ub));
387#if (SCIP_VERSION >= 210)
388 SCIP_Bool infeasible =
false;
390 scip_, scip_variables_[var_index],
391 integer ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS, &infeasible));
394 scip_, scip_variables_[var_index],
395 integer ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS));
410 SCIPchgLhsLinear(scip_, scip_constraints_[index], lb));
412 SCIPchgRhsLinear(scip_, scip_constraints_[index], ub));
433 scip_, scip_constraints_[constraint->
index()],
434 scip_variables_[variable->
index()], new_value - old_value));
446 const int constraint_index = constraint->
index();
449 for (
const auto& entry : constraint->coefficients_) {
450 const int var_index = entry.first->index();
451 const double old_coef_value = entry.second;
456 SCIPaddCoefLinear(scip_, scip_constraints_[constraint_index],
457 scip_variables_[var_index], -old_coef_value));
477 for (
const auto& entry :
solver_->objective_->coefficients_) {
478 const int var_index = entry.first->index();
484 SCIPchgVarObj(scip_, scip_variables_[var_index], 0.0));
502 branching_priority_reset_ =
true;
519 int total_num_vars =
solver_->variables_.size();
527 SCIP_VAR* scip_var =
nullptr;
529 double tmp_obj_coef = 0.0;
531 scip_, &scip_var, var->
name().c_str(), var->
lb(), var->
ub(),
533 var->
integer() ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS,
true,
534 false,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr));
536 scip_variables_.push_back(scip_var);
538 if (branching_priority != 0) {
539 const int index = var->
index();
541 scip_, scip_variables_[index], branching_priority));
547 for (
const auto& entry : ct->coefficients_) {
548 const int var_index = entry.first->index();
554 SCIPaddCoefLinear(scip_, scip_constraints_[i],
555 scip_variables_[var_index], entry.second));
564 int total_num_rows =
solver_->constraints_.size();
568 int max_row_length = 0;
573 if (ct->coefficients_.size() > max_row_length) {
574 max_row_length = ct->coefficients_.size();
577 std::unique_ptr<SCIP_VAR*[]> vars(
new SCIP_VAR*[max_row_length]);
578 std::unique_ptr<double[]> coeffs(
new double[max_row_length]);
583 const int size = ct->coefficients_.size();
585 for (
const auto& entry : ct->coefficients_) {
586 const int var_index = entry.first->index();
588 vars[j] = scip_variables_[var_index];
589 coeffs[j] = entry.second;
592 SCIP_CONS* scip_constraint =
nullptr;
593 const bool is_lazy = ct->
is_lazy();
597 SCIP_VAR* ind_var = scip_variables_[ind_index];
600 SCIPgetNegatedVar(scip_, scip_variables_[ind_index], &ind_var));
603 if (ct->
ub() < std::numeric_limits<double>::infinity()) {
605 scip_, &scip_constraint, ct->
name().c_str(), ind_var, size,
606 vars.get(), coeffs.get(), ct->
ub(),
617 scip_constraints_.push_back(scip_constraint);
619 if (ct->
lb() > -std::numeric_limits<double>::infinity()) {
620 for (
int i = 0; i < size; ++i) {
624 scip_, &scip_constraint, ct->
name().c_str(), ind_var, size,
625 vars.get(), coeffs.get(), -ct->
lb(),
636 scip_constraints_.push_back(scip_constraint);
643 scip_, &scip_constraint, ct->
name().c_str(), size, vars.get(),
644 coeffs.get(), ct->
lb(), ct->
ub(),
656 scip_constraints_.push_back(scip_constraint);
667 for (
const auto& entry :
solver_->objective_->coefficients_) {
668 const int var_index = entry.first->index();
669 const double obj_coef = entry.second;
671 SCIPchgVarObj(scip_, scip_variables_[var_index], obj_coef));
676 scip_,
solver_->Objective().offset() - SCIPgetOrigObjoffset(scip_)));
679#define RETURN_ABNORMAL_IF_BAD_STATUS \
681 if (!status_.ok()) { \
682 LOG_IF(INFO, solver_->OutputIsEnabled()) \
683 << "Invalid SCIP status: " << status_; \
684 return result_status_ = MPSolver::ABNORMAL; \
688#define RETURN_ABNORMAL_IF_SCIP_ERROR(x) \
690 RETURN_ABNORMAL_IF_BAD_STATUS; \
691 status_ = SCIP_TO_STATUS(x); \
692 RETURN_ABNORMAL_IF_BAD_STATUS; \
709 branching_priority_reset_ || callback_reset_) {
711 branching_priority_reset_ =
false;
712 callback_reset_ =
false;
716 SCIPsetMessagehdlrQuiet(scip_,
quiet_);
719 if (
solver_->variables_.empty() &&
solver_->constraints_.empty()) {
728 VLOG(1) << absl::StrFormat(
"Model built in %s.",
730 if (scip_constraint_handler_ !=
nullptr) {
735 CHECK_EQ(scip_constraint_handler_->mp_callback(), callback_);
736 }
else if (callback_ !=
nullptr) {
737 scip_constraint_handler_ =
738 std::make_unique<ScipConstraintHandlerForMPCallback>(callback_);
742 "mp_solver_callback_constraint_for_scip",
743 &constraint_data_for_handler_,
748 if (
solver_->time_limit() != 0) {
749 VLOG(1) <<
"Setting time limit = " <<
solver_->time_limit() <<
" ms.";
751 SCIPsetRealParam(scip_,
"limits/time",
solver_->time_limit_in_secs()));
761 SetParameters(param);
762 solver_->SetSolverSpecificParametersAsString(
763 solver_->solver_specific_parameter_string_);
766 if (!
solver_->solution_hint_.empty()) {
768 bool is_solution_partial =
false;
769 const int num_vars =
solver_->variables_.size();
770 if (
solver_->solution_hint_.size() != num_vars) {
773 SCIPcreatePartialSol(scip_, &
solution,
nullptr));
774 is_solution_partial =
true;
781 for (
const std::pair<const MPVariable*, double>& p :
784 scip_,
solution, scip_variables_[p.first->index()], p.second));
787 if (!is_solution_partial) {
788 SCIP_Bool is_feasible;
793 VLOG(1) <<
"Solution hint is "
794 << (is_feasible ?
"FEASIBLE" :
"INFEASIBLE");
802 if (!is_solution_partial && SCIPisTransformed(scip_)) {
809 SCIPaddSolFree(scip_, &
solution, &is_stored));
816 ? SCIPsolveConcurrent(scip_)
818 VLOG(1) << absl::StrFormat(
"Solved in %s.",
820 current_solution_index_ = 0;
822 SCIP_SOL*
const solution = SCIPgetBestSol(scip_);
827 VLOG(1) <<
"No feasible solution found.";
831 SCIP_STATUS scip_status = SCIPgetStatus(scip_);
832 switch (scip_status) {
833 case SCIP_STATUS_OPTIMAL:
836 case SCIP_STATUS_GAPLIMIT:
840 case SCIP_STATUS_INFEASIBLE:
843 case SCIP_STATUS_UNBOUNDED:
846 case SCIP_STATUS_INFORUNBD:
854 }
else if (scip_status == SCIP_STATUS_TIMELIMIT ||
855 scip_status == SCIP_STATUS_TOTALNODELIMIT) {
869void SCIPInterface::SetSolution(SCIP_SOL*
solution) {
874 for (
int i = 0; i <
solver_->variables_.size(); ++i) {
876 const int var_index = var->
index();
878 SCIPgetSolVal(scip_,
solution, scip_variables_[var_index]);
880 VLOG(3) << var->
name() <<
"=" << val;
885 std::atomic<bool>* interrupt)
const {
887 if (
solver_->GetNumThreads() > 1)
return false;
890 if (interrupt !=
nullptr)
return false;
897 const bool log_error = request->enable_internal_solver_output();
902int SCIPInterface::SolutionCount() {
return SCIPgetNSols(scip_); }
909 if (current_solution_index_ + 1 >= SolutionCount()) {
912 current_solution_index_++;
913 SCIP_SOL** all_solutions = SCIPgetSols(scip_);
914 SetSolution(all_solutions[current_solution_index_]);
922 return SCIPgetNLPIterations(scip_);
931 return SCIPgetNTotalNodes(scip_);
939void SCIPInterface::SetRelativeMipGap(
double value) {
952 if (status_.ok()) status_ = status;
955void SCIPInterface::SetPrimalTolerance(
double value) {
958 SCIP_TO_STATUS(SCIPsetRealParam(scip_,
"numerics/feastol", value));
959 if (status_.ok()) status_ = status;
962void SCIPInterface::SetDualTolerance(
double value) {
964 SCIP_TO_STATUS(SCIPsetRealParam(scip_,
"numerics/dualfeastol", value));
965 if (status_.ok()) status_ = status;
968void SCIPInterface::SetPresolveMode(
int presolve) {
973 SCIP_TO_STATUS(SCIPsetIntParam(scip_,
"presolving/maxrounds", 0));
974 if (status_.ok()) status_ = status;
979 SCIP_TO_STATUS(SCIPsetIntParam(scip_,
"presolving/maxrounds", -1));
980 if (status_.ok()) status_ = status;
990void SCIPInterface::SetScalingMode(
int) {
997void SCIPInterface::SetLpAlgorithm(
int lp_algorithm) {
999 switch (lp_algorithm) {
1002 SCIP_TO_STATUS(SCIPsetCharParam(scip_,
"lp/initalgorithm",
'd'));
1003 if (status_.ok()) status_ = status;
1008 SCIP_TO_STATUS(SCIPsetCharParam(scip_,
"lp/initalgorithm",
'p'));
1009 if (status_.ok()) status_ = status;
1015 SCIP_TO_STATUS(SCIPsetCharParam(scip_,
"lp/initalgorithm",
'p'));
1016 if (status_.ok()) status_ = status;
1027void SCIPInterface::SetUnsupportedIntegerParam(
1031 status_ = absl::InvalidArgumentError(absl::StrFormat(
1032 "Tried to set unsupported integer parameter %d", param));
1036void SCIPInterface::SetIntegerParamToUnsupportedValue(
1040 status_ = absl::InvalidArgumentError(absl::StrFormat(
1041 "Tried to set integer parameter %d to unsupported value %d", param,
1046absl::Status SCIPInterface::SetNumThreads(
int num_threads) {
1047 if (SetSolverSpecificParametersAsString(
1048 absl::StrFormat(
"parallel/maxnthreads = %d\n", num_threads))) {
1049 return absl::OkStatus();
1051 return absl::InternalError(
1052 "Could not set parallel/maxnthreads, which may "
1053 "indicate that SCIP API has changed.");
1056bool SCIPInterface::SetSolverSpecificParametersAsString(
1057 const std::string& parameters) {
1058 const absl::Status s =
1061 LOG(WARNING) <<
"Failed to set SCIP parameter string: " << parameters
1062 <<
", error is: " << s;
1070 bool at_integer_solution)
1071 : scip_context_(scip_context),
1072 at_integer_solution_(at_integer_solution) {}
1075 if (at_integer_solution_) {
1082 return !scip_context_->is_pseudo_solution();
1087 return scip_context_->VariableValue(variable);
1092 constraint.
is_cut =
true;
1093 constraint.
range = cutting_plane;
1094 constraint.
local =
false;
1095 constraints_added_.push_back(std::move(constraint));
1100 constraint.
is_cut =
false;
1101 constraint.
range = lazy_constraint;
1102 constraint.
local =
false;
1103 constraints_added_.push_back(std::move(constraint));
1107 const absl::flat_hash_map<const MPVariable*, double>&
solution)
override {
1108 LOG(FATAL) <<
"SuggestSolution() not currently supported for SCIP.";
1119 return std::max(int64_t{0}, scip_context_->NumNodesProcessed() - 1);
1123 return constraints_added_;
1128 bool at_integer_solution_;
1130 std::vector<CallbackRangeConstraint> constraints_added_;
1138std::vector<CallbackRangeConstraint>
1141 return SeparateSolution(context,
false);
1144std::vector<CallbackRangeConstraint>
1147 return SeparateSolution(context,
true);
1150std::vector<CallbackRangeConstraint>
1151ScipConstraintHandlerForMPCallback::SeparateSolution(
1153 const bool at_integer_solution) {
1156 return mp_context.constraints_added();
1160 if (callback_ !=
nullptr) {
1161 callback_reset_ =
true;
1163 callback_ = mp_callback;
1169const void*
const kRegisterSCIP ABSL_ATTRIBUTE_UNUSED = [] {
1180#undef RETURN_AND_STORE_IF_SCIP_ERROR
1181#undef RETURN_IF_ALREADY_IN_ERROR_STATE
1182#undef RETURN_ABNORMAL_IF_BAD_STATUS
1183#undef RETURN_ABNORMAL_IF_SCIP_ERROR
absl::Duration GetDuration() const
virtual void RunCallback(MPCallbackContext *callback_context)=0
bool is_lazy() const
Advanced usage: returns true if the constraint is "lazy" (see below).
double lb() const
Returns the lower bound.
double ub() const
Returns the upper bound.
const MPVariable * indicator_variable() const
const std::string & name() const
Returns the name of the constraint.
int index() const
Returns the index of the constraint in the MPSolver::constraints_.
bool indicator_value() const
static MPSolverInterfaceFactoryRepository * GetInstance()
void Register(MPSolverInterfaceFactory factory, MPSolver::OptimizationProblemType problem_type, std::function< bool()> is_runtime_ready={})
void set_variable_as_extracted(int var_index, bool extracted)
bool CheckSolutionIsSynchronized() const
static constexpr int64_t kUnknownNumberOfIterations
friend class MPConstraint
void InvalidateSolutionSynchronization()
void set_constraint_as_extracted(int ct_index, bool extracted)
void ResetExtractionInformation()
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
int last_constraint_index_
virtual void SetUnsupportedIntegerParam(MPSolverParameters::IntegerParam param)
bool variable_is_extracted(int var_index) const
bool constraint_is_extracted(int ct_index) const
static constexpr int64_t kUnknownNumberOfNodes
double best_objective_bound_
void SetMIPParameters(const MPSolverParameters ¶m)
MPSolverInterface(MPSolver *solver)
void SetCommonParameters(const MPSolverParameters ¶m)
bool CheckSolutionIsSynchronizedAndExists() const
MPSolver::ResultStatus result_status_
SynchronizationStatus sync_status_
@ PRESOLVE_OFF
Presolve is off.
@ PRESOLVE_ON
Presolve is on.
@ BARRIER
Barrier algorithm.
IntegerParam
Enumeration of parameters that take integer or categorical values.
@ INCREMENTALITY
Advanced usage: incrementality from one solve to the next.
@ PRESOLVE
Advanced usage: presolve mode.
@ LP_ALGORITHM
Algorithm to solve linear programs.
@ SCALING
Advanced usage: enable or disable matrix scaling.
@ INCREMENTALITY_OFF
Start solve from scratch.
int GetIntegerParam(MPSolverParameters::IntegerParam param) const
Returns the value of an integer parameter.
@ FEASIBLE
feasible, or stopped by limit.
@ NOT_SOLVED
not been solved yet.
@ INFEASIBLE
proven infeasible.
@ UNBOUNDED
proven unbounded.
@ ABNORMAL
abnormal, i.e., error of some kind.
@ SCIP_MIXED_INTEGER_PROGRAMMING
The class for variables of a Mathematical Programming (MP) model.
bool integer() const
Returns the integrality requirement of the variable.
double lb() const
Returns the lower bound.
double ub() const
Returns the upper bound.
const std::string & name() const
Returns the name of the variable.
int branching_priority() const
void set_solution_value(double value)
int index() const
Returns the index of the variable in the MPSolver::variables_.
void ExtractObjective() override
bool InterruptSolve() override
void ExtractNewConstraints() override
void AddVariable(MPVariable *var) override
int64_t nodes() const override
double infinity() override
void SetObjectiveCoefficient(const MPVariable *variable, double coefficient) override
void SetObjectiveOffset(double value) override
void ExtractNewVariables() override
void * underlying_solver() override
bool IsContinuous() const override
~SCIPInterface() override
int64_t iterations() const override
void SetVariableInteger(int var_index, bool integer) override
SCIPInterface(MPSolver *solver)
void ClearConstraint(MPConstraint *constraint) override
void SetCallback(MPCallback *mp_callback) override
void ClearObjective() override
MPSolver::ResultStatus Solve(const MPSolverParameters ¶m) override
void BranchingPriorityChangedForVariable(int var_index) override
void SetVariableBounds(int var_index, double lb, double ub) override
bool IsMIP() const override
void SetCoefficient(MPConstraint *constraint, const MPVariable *variable, double new_value, double old_value) override
void SetConstraintBounds(int row_index, double lb, double ub) override
bool SupportsCallbacks() const override
void AddRowConstraint(MPConstraint *ct) override
std::string SolverVersion() const override
MPSolver::BasisStatus row_status(int) const override
bool IsLP() const override
bool SupportsDirectlySolveProto(std::atomic< bool > *interrupt) const override
bool NextSolution() override
MPSolutionResponse DirectlySolveProto(LazyMutableCopy< MPModelRequest > request, std::atomic< bool > *interrupt) override
void SetOptimizationDirection(bool maximize) override
bool AddIndicatorConstraint(MPConstraint *ct) override
MPSolver::BasisStatus column_status(int) const override
ScipConstraintHandler(const ScipConstraintHandlerDescription &description)
void AddLazyConstraint(const LinearRange &lazy_constraint) override
void AddCut(const LinearRange &cutting_plane) override
int64_t NumExploredNodes() override
MPCallbackEvent Event() override
bool CanQueryVariableValues() override
double VariableValue(const MPVariable *variable) override
ScipMPCallbackContext(const ScipConstraintHandlerContext *scip_context, bool at_integer_solution)
double SuggestSolution(const absl::flat_hash_map< const MPVariable *, double > &solution) override
const std::vector< CallbackRangeConstraint > & constraints_added()
void AddCallbackConstraint(SCIP *scip, ScipConstraintHandler< ConstraintData > *handler, const std::string &constraint_name, const ConstraintData *constraint_data, const ScipCallbackConstraintOptions &options)
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
absl::StatusOr< MPSolutionResponse > ScipSolveProto(LazyMutableCopy< MPModelRequest > request)
void RegisterConstraintHandler(ScipConstraintHandler< Constraint > *handler, SCIP *scip)
MPSolutionResponse ConvertStatusOrMPSolutionResponse(bool log_error, absl::StatusOr< MPSolutionResponse > response)
absl::Status LegacyScipSetSolverSpecificParameters(absl::string_view parameters, SCIP *scip)
#define SCIP_TO_STATUS(x)
#define RETURN_IF_SCIP_ERROR(x)
ABSL_FLAG(bool, scip_feasibility_emphasis, false, "When true, emphasize search towards feasibility. This may or " "may not result in speedups in some problems.")
#define RETURN_IF_ALREADY_IN_ERROR_STATE
#define RETURN_ABNORMAL_IF_SCIP_ERROR(x)
#define RETURN_AND_STORE_IF_SCIP_ERROR(x)
#define RETURN_ABNORMAL_IF_BAD_STATUS