25#include "absl/base/attributes.h"
26#include "absl/status/status.h"
27#include "absl/strings/str_format.h"
28#include "absl/types/optional.h"
43#include "scip/cons_indicator.h"
45#include "scip/scip_copy.h"
46#include "scip/scip_param.h"
47#include "scip/scip_prob.h"
48#include "scip/scipdefplugins.h"
51 "When true, emphasize search towards feasibility. This may or "
52 "may not result in speedups in some problems.");
60class ScipConstraintHandlerForMPCallback;
70 const MPModelRequest& request, std::atomic<bool>* interrupt)
override;
71 void Reset()
override;
81 double new_value,
double old_value)
override;
90 int64_t
nodes()
const override;
92 LOG(DFATAL) <<
"Basis status only available for continuous problems";
96 LOG(DFATAL) <<
"Basis status only available for continuous problems";
101 bool IsLP()
const override {
return false; }
102 bool IsMIP()
const override {
return true; }
109 return absl::StrFormat(
"SCIP %d.%d.%d [LP solver: %s]", SCIPmajorVersion(),
110 SCIPminorVersion(), SCIPtechVersion(),
115 const absl::MutexLock lock(&hold_interruptions_mutex_);
116 if (scip_ ==
nullptr) {
117 LOG_IF(DFATAL, status_.ok()) <<
"scip_ is null is unexpected here, since "
118 "status_ did not report any error";
121 return SCIPinterruptSolve(scip_) == SCIP_OKAY;
155 void SetRelativeMipGap(
double value)
override;
156 void SetPrimalTolerance(
double value)
override;
157 void SetDualTolerance(
double value)
override;
158 void SetPresolveMode(
int presolve)
override;
159 void SetScalingMode(
int scaling)
override;
160 void SetLpAlgorithm(
int lp_algorithm)
override;
170 absl::Status SetNumThreads(
int num_threads)
override;
172 bool SetSolverSpecificParametersAsString(
175 void SetUnsupportedIntegerParam(
182 void SetSolution(SCIP_SOL* solution);
184 absl::Status CreateSCIP();
188 SCIP* DeleteSCIP(
bool return_scip =
false);
196 absl::Status status_;
199 std::vector<SCIP_VAR*> scip_variables_;
200 std::vector<SCIP_CONS*> scip_constraints_;
201 int current_solution_index_ = 0;
203 std::unique_ptr<ScipConstraintHandlerForMPCallback> scip_constraint_handler_;
205 EmptyStruct constraint_data_for_handler_;
206 bool branching_priority_reset_ =
false;
207 bool callback_reset_ =
false;
212 mutable absl::Mutex hold_interruptions_mutex_;
229 std::vector<CallbackRangeConstraint> SeparateSolution(
231 const bool at_integer_solution);
236#define RETURN_IF_ALREADY_IN_ERROR_STATE \
238 if (!status_.ok()) { \
239 VLOG_EVERY_N(1, 10) << "Early abort: SCIP is in error state."; \
244#define RETURN_AND_STORE_IF_SCIP_ERROR(x) \
246 status_ = SCIP_TO_STATUS(x); \
247 if (!status_.ok()) return; \
252 status_ = CreateSCIP();
259 const absl::MutexLock lock(&hold_interruptions_mutex_);
262 SCIP* old_scip = DeleteSCIP(
true);
264 [&old_scip]() {
CHECK_EQ(SCIPfree(&old_scip), SCIP_OKAY); });
266 scip_constraint_handler_.reset();
270 status_ = CreateSCIP();
284absl::Status SCIPInterface::CreateSCIP() {
289 if (absl::GetFlag(FLAGS_scip_feasibility_emphasis)) {
300 SCIPsetIntParam(scip_,
"timing/clocktype", SCIP_CLOCKTYPE_WALL));
302 nullptr,
nullptr,
nullptr,
nullptr,
305 scip_,
maximize_ ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
306 return absl::OkStatus();
309SCIP* SCIPInterface::DeleteSCIP(
bool return_scip) {
314 CHECK(scip_ !=
nullptr);
315 for (
int i = 0; i < scip_variables_.size(); ++i) {
316 CHECK_EQ(SCIPreleaseVar(scip_, &scip_variables_[i]), SCIP_OKAY);
318 scip_variables_.clear();
319 for (
int j = 0; j < scip_constraints_.size(); ++j) {
320 CHECK_EQ(SCIPreleaseCons(scip_, &scip_constraints_[j]), SCIP_OKAY);
322 scip_constraints_.clear();
324 SCIP* old_scip = scip_;
327 CHECK_EQ(SCIPfree(&old_scip), SCIP_OKAY);
338 scip_, maximize ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
349 SCIPchgVarLb(scip_, scip_variables_[var_index], lb));
351 SCIPchgVarUb(scip_, scip_variables_[var_index], ub));
363#if (SCIP_VERSION >= 210)
364 SCIP_Bool infeasible =
false;
366 scip_, scip_variables_[var_index],
367 integer ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS, &infeasible));
370 scip_, scip_variables_[var_index],
371 integer ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS));
386 SCIPchgLhsLinear(scip_, scip_constraints_[
index], lb));
388 SCIPchgRhsLinear(scip_, scip_constraints_[
index], ub));
409 scip_, scip_constraints_[constraint->
index()],
410 scip_variables_[variable->
index()], new_value - old_value));
422 const int constraint_index = constraint->
index();
425 for (
const auto& entry : constraint->coefficients_) {
426 const int var_index = entry.first->index();
427 const double old_coef_value = entry.second;
432 SCIPaddCoefLinear(scip_, scip_constraints_[constraint_index],
433 scip_variables_[var_index], -old_coef_value));
456 for (
const auto& entry :
solver_->objective_->coefficients_) {
457 const int var_index = entry.first->index();
463 SCIPchgVarObj(scip_, scip_variables_[var_index], 0.0));
481 branching_priority_reset_ =
true;
498 int total_num_vars =
solver_->variables_.size();
506 SCIP_VAR* scip_var =
nullptr;
508 double tmp_obj_coef = 0.0;
510 scip_, &scip_var,
var->name().c_str(),
var->lb(),
var->ub(),
512 var->integer() ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS,
true,
513 false,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr));
515 scip_variables_.push_back(scip_var);
516 const int branching_priority =
var->branching_priority();
517 if (branching_priority != 0) {
520 scip_, scip_variables_[
index], branching_priority));
526 for (
const auto& entry :
ct->coefficients_) {
527 const int var_index = entry.first->index();
533 SCIPaddCoefLinear(scip_, scip_constraints_[i],
534 scip_variables_[var_index], entry.second));
543 int total_num_rows =
solver_->constraints_.size();
547 int max_row_length = 0;
552 if (
ct->coefficients_.size() > max_row_length) {
553 max_row_length =
ct->coefficients_.size();
556 std::unique_ptr<SCIP_VAR*[]> vars(
new SCIP_VAR*[max_row_length]);
557 std::unique_ptr<double[]> coeffs(
new double[max_row_length]);
562 const int size =
ct->coefficients_.size();
564 for (
const auto& entry :
ct->coefficients_) {
565 const int var_index = entry.first->index();
567 vars[j] = scip_variables_[var_index];
568 coeffs[j] = entry.second;
571 SCIP_CONS* scip_constraint =
nullptr;
572 const bool is_lazy =
ct->is_lazy();
573 if (
ct->indicator_variable() !=
nullptr) {
574 const int ind_index =
ct->indicator_variable()->index();
576 SCIP_VAR* ind_var = scip_variables_[ind_index];
577 if (
ct->indicator_value() == 0) {
579 SCIPgetNegatedVar(scip_, scip_variables_[ind_index], &ind_var));
582 if (
ct->ub() < std::numeric_limits<double>::infinity()) {
584 scip_, &scip_constraint,
ct->name().c_str(), ind_var, size,
585 vars.get(), coeffs.get(),
ct->ub(),
596 scip_constraints_.push_back(scip_constraint);
598 if (
ct->lb() > -std::numeric_limits<double>::infinity()) {
599 for (
int i = 0; i < size; ++i) {
603 scip_, &scip_constraint,
ct->name().c_str(), ind_var, size,
604 vars.get(), coeffs.get(), -
ct->lb(),
615 scip_constraints_.push_back(scip_constraint);
622 scip_, &scip_constraint,
ct->name().c_str(), size, vars.get(),
623 coeffs.get(),
ct->lb(),
ct->ub(),
635 scip_constraints_.push_back(scip_constraint);
646 for (
const auto& entry :
solver_->objective_->coefficients_) {
647 const int var_index = entry.first->index();
648 const double obj_coef = entry.second;
650 SCIPchgVarObj(scip_, scip_variables_[var_index], obj_coef));
658#define RETURN_ABNORMAL_IF_BAD_STATUS \
660 if (!status_.ok()) { \
661 LOG_IF(INFO, solver_->OutputIsEnabled()) \
662 << "Invalid SCIP status: " << status_; \
663 return result_status_ = MPSolver::ABNORMAL; \
667#define RETURN_ABNORMAL_IF_SCIP_ERROR(x) \
669 RETURN_ABNORMAL_IF_BAD_STATUS; \
670 status_ = SCIP_TO_STATUS(x); \
671 RETURN_ABNORMAL_IF_BAD_STATUS; \
688 branching_priority_reset_ || callback_reset_) {
690 branching_priority_reset_ =
false;
691 callback_reset_ =
false;
695 SCIPsetMessagehdlrQuiet(scip_,
quiet_);
698 if (
solver_->variables_.empty() &&
solver_->constraints_.empty()) {
707 VLOG(1) << absl::StrFormat(
"Model built in %s.",
709 if (scip_constraint_handler_ !=
nullptr) {
714 CHECK_EQ(scip_constraint_handler_->mp_callback(), callback_);
715 }
else if (callback_ !=
nullptr) {
716 scip_constraint_handler_ =
717 absl::make_unique<ScipConstraintHandlerForMPCallback>(callback_);
718 RegisterConstraintHandler<EmptyStruct>(scip_constraint_handler_.get(),
720 AddCallbackConstraint<EmptyStruct>(scip_, scip_constraint_handler_.get(),
721 "mp_solver_callback_constraint_for_scip",
722 &constraint_data_for_handler_,
740 SetParameters(param);
742 solver_->solver_specific_parameter_string_);
745 if (!
solver_->solution_hint_.empty()) {
747 bool is_solution_partial =
false;
748 const int num_vars =
solver_->variables_.size();
749 if (
solver_->solution_hint_.size() != num_vars) {
752 SCIPcreatePartialSol(scip_, &solution,
nullptr));
753 is_solution_partial =
true;
760 for (
const std::pair<const MPVariable*, double>& p :
763 scip_, solution, scip_variables_[p.first->index()], p.second));
766 if (!is_solution_partial) {
767 SCIP_Bool is_feasible;
769 scip_, solution,
false,
true,
772 VLOG(1) <<
"Solution hint is "
773 << (is_feasible ?
"FEASIBLE" :
"INFEASIBLE");
781 if (!is_solution_partial && SCIPisTransformed(scip_)) {
783 scip_, &solution,
false,
true,
788 SCIPaddSolFree(scip_, &solution, &is_stored));
795 ? SCIPsolveConcurrent(scip_)
797 VLOG(1) << absl::StrFormat(
"Solved in %s.",
799 current_solution_index_ = 0;
801 SCIP_SOL*
const solution = SCIPgetBestSol(scip_);
802 if (solution !=
nullptr) {
804 SetSolution(solution);
806 VLOG(1) <<
"No feasible solution found.";
810 SCIP_STATUS scip_status = SCIPgetStatus(scip_);
811 switch (scip_status) {
812 case SCIP_STATUS_OPTIMAL:
815 case SCIP_STATUS_GAPLIMIT:
819 case SCIP_STATUS_INFEASIBLE:
822 case SCIP_STATUS_UNBOUNDED:
825 case SCIP_STATUS_INFORUNBD:
831 if (solution !=
nullptr) {
833 }
else if (scip_status == SCIP_STATUS_TIMELIMIT ||
834 scip_status == SCIP_STATUS_TOTALNODELIMIT) {
848void SCIPInterface::SetSolution(SCIP_SOL* solution) {
853 for (
int i = 0; i <
solver_->variables_.size(); ++i) {
855 const int var_index =
var->index();
857 SCIPgetSolVal(scip_, solution, scip_variables_[var_index]);
858 var->set_solution_value(val);
859 VLOG(3) <<
var->name() <<
"=" << val;
869 if (interrupt !=
nullptr)
return absl::nullopt;
872 if (status_or.ok())
return status_or.value();
875 if (absl::IsUnimplemented(status_or.status()))
return absl::nullopt;
878 LOG(
INFO) <<
"Invalid SCIP status: " << status_or.status();
882 response.set_status_str(status_or.status().ToString());
886int SCIPInterface::SolutionCount() {
return SCIPgetNSols(scip_); }
893 if (current_solution_index_ + 1 >= SolutionCount()) {
896 current_solution_index_++;
897 SCIP_SOL** all_solutions = SCIPgetSols(scip_);
898 SetSolution(all_solutions[current_solution_index_]);
906 return SCIPgetNLPIterations(scip_);
915 return SCIPgetNTotalNodes(scip_);
923void SCIPInterface::SetRelativeMipGap(
double value) {
936 if (status_.ok()) status_ = status;
939void SCIPInterface::SetPrimalTolerance(
double value) {
943 if (status_.ok()) status_ = status;
946void SCIPInterface::SetDualTolerance(
double value) {
949 if (status_.ok()) status_ = status;
952void SCIPInterface::SetPresolveMode(
int presolve) {
957 SCIP_TO_STATUS(SCIPsetIntParam(scip_,
"presolving/maxrounds", 0));
958 if (status_.ok()) status_ = status;
963 SCIP_TO_STATUS(SCIPsetIntParam(scip_,
"presolving/maxrounds", -1));
964 if (status_.ok()) status_ = status;
974void SCIPInterface::SetScalingMode(
int scaling) {
981void SCIPInterface::SetLpAlgorithm(
int lp_algorithm) {
983 switch (lp_algorithm) {
987 if (status_.ok()) status_ = status;
993 if (status_.ok()) status_ = status;
1000 if (status_.ok()) status_ = status;
1011void SCIPInterface::SetUnsupportedIntegerParam(
1015 status_ = absl::InvalidArgumentError(absl::StrFormat(
1016 "Tried to set unsupported integer parameter %d", param));
1020void SCIPInterface::SetIntegerParamToUnsupportedValue(
1024 status_ = absl::InvalidArgumentError(absl::StrFormat(
1025 "Tried to set integer parameter %d to unsupported value %d", param,
1030absl::Status SCIPInterface::SetNumThreads(
int num_threads) {
1031 if (SetSolverSpecificParametersAsString(
1032 absl::StrFormat(
"parallel/maxnthreads = %d\n", num_threads))) {
1033 return absl::OkStatus();
1035 return absl::InternalError(
1036 "Could not set parallel/maxnthreads, which may "
1037 "indicate that SCIP API has changed.");
1040bool SCIPInterface::SetSolverSpecificParametersAsString(
1042 const absl::Status s =
1046 <<
", error is: " << s;
1054 bool at_integer_solution)
1055 : scip_context_(scip_context),
1056 at_integer_solution_(at_integer_solution) {}
1059 if (at_integer_solution_) {
1076 constraint.
is_cut =
true;
1077 constraint.
range = cutting_plane;
1078 constraint.local =
false;
1079 constraints_added_.push_back(std::move(constraint));
1084 constraint.
is_cut =
false;
1085 constraint.
range = lazy_constraint;
1086 constraint.local =
false;
1087 constraints_added_.push_back(std::move(constraint));
1091 const absl::flat_hash_map<const MPVariable*, double>& solution)
override {
1092 LOG(
FATAL) <<
"SuggestSolution() not currently supported for SCIP.";
1107 return constraints_added_;
1112 bool at_integer_solution_;
1114 std::vector<CallbackRangeConstraint> constraints_added_;
1121 {
"mp_solver_constraint_handler",
1123 "A single constraint handler for all MPSolver models."}
1126 mp_callback_(mp_callback) {}
1128std::vector<CallbackRangeConstraint>
1131 return SeparateSolution(
context,
false);
1134std::vector<CallbackRangeConstraint>
1137 return SeparateSolution(
context,
true);
1140std::vector<CallbackRangeConstraint>
1141ScipConstraintHandlerForMPCallback::SeparateSolution(
1143 const bool at_integer_solution) {
1146 return mp_context.constraints_added();
1150 if (callback_ !=
nullptr) {
1151 callback_reset_ =
true;
1153 callback_ = mp_callback;
1163#undef RETURN_AND_STORE_IF_SCIP_ERROR
1164#undef RETURN_IF_ALREADY_IN_ERROR_STATE
1165#undef RETURN_ABNORMAL_IF_BAD_STATUS
1166#undef RETURN_ABNORMAL_IF_SCIP_ERROR
#define LOG_IF(severity, condition)
#define DCHECK_NE(val1, val2)
#define CHECK_EQ(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define VLOG(verboselevel)
absl::Duration GetDuration() const
An expression of the form:
virtual void RunCallback(MPCallbackContext *callback_context)=0
The class for constraints of a Mathematical Programming (MP) model.
int index() const
Returns the index of the constraint in the MPSolver::constraints_.
bool enable_internal_solver_output() const
double offset() const
Gets the constant term in the objective.
This mathematical programming (MP) solver class is the main class though which users build and solve ...
ResultStatus
The status of solving the problem.
@ 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.
bool SetSolverSpecificParametersAsString(const std::string ¶meters)
Advanced usage: pass solver specific parameters in text format.
int GetNumThreads() const
Returns the number of threads to be used during solve.
int64_t time_limit() const
const MPObjective & Objective() const
Returns the objective object.
double time_limit_in_secs() const
BasisStatus
Advanced usage: possible basis status values for a variable and the slack variable of a linear constr...
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
void set_constraint_as_extracted(int ct_index, bool extracted)
MPSolver::ResultStatus result_status_
void InvalidateSolutionSynchronization()
void SetMIPParameters(const MPSolverParameters ¶m)
int last_constraint_index_
bool constraint_is_extracted(int ct_index) const
static constexpr int64_t kUnknownNumberOfNodes
double best_objective_bound_
bool CheckSolutionIsSynchronizedAndExists() const
bool CheckSolutionIsSynchronized() const
void ResetExtractionInformation()
bool variable_is_extracted(int var_index) const
static constexpr int64_t kUnknownNumberOfIterations
virtual void SetUnsupportedIntegerParam(MPSolverParameters::IntegerParam param)
void set_variable_as_extracted(int var_index, bool extracted)
void SetCommonParameters(const MPSolverParameters ¶m)
SynchronizationStatus sync_status_
This class stores parameter settings for LP and MIP solvers.
@ INCREMENTALITY_OFF
Start solve from scratch.
IntegerParam
Enumeration of parameters that take integer or categorical values.
@ LP_ALGORITHM
Algorithm to solve linear programs.
@ SCALING
Advanced usage: enable or disable matrix scaling.
@ PRESOLVE
Advanced usage: presolve mode.
@ INCREMENTALITY
Advanced usage: incrementality from one solve to the next.
@ BARRIER
Barrier algorithm.
@ PRESOLVE_ON
Presolve is on.
@ PRESOLVE_OFF
Presolve is off.
int GetIntegerParam(MPSolverParameters::IntegerParam param) const
Returns the value of an integer parameter.
The class for variables of a Mathematical Programming (MP) model.
int index() const
Returns the index of the variable in the MPSolver::variables_.
void BranchingPriorityChangedForVariable(int var_index) override
~SCIPInterface() override
void SetCoefficient(MPConstraint *constraint, const MPVariable *variable, double new_value, double old_value) override
void AddRowConstraint(MPConstraint *ct) override
void ExtractObjective() override
void * underlying_solver() override
bool IsContinuous() const override
void SetConstraintBounds(int row_index, double lb, double ub) override
bool InterruptSolve() override
MPSolver::ResultStatus Solve(const MPSolverParameters ¶m) override
void ClearConstraint(MPConstraint *constraint) override
MPSolver::BasisStatus row_status(int constraint_index) const override
bool SupportsCallbacks() const override
void SetVariableInteger(int var_index, bool integer) override
void SetCallback(MPCallback *mp_callback) override
void SetObjectiveOffset(double value) override
void AddVariable(MPVariable *var) override
SCIPInterface(MPSolver *solver)
void ExtractNewConstraints() override
absl::optional< MPSolutionResponse > DirectlySolveProto(const MPModelRequest &request, std::atomic< bool > *interrupt) override
std::string SolverVersion() const override
void ExtractNewVariables() override
void SetObjectiveCoefficient(const MPVariable *variable, double coefficient) override
bool AddIndicatorConstraint(MPConstraint *ct) override
int64_t nodes() const override
void SetVariableBounds(int var_index, double lb, double ub) override
bool IsLP() const override
bool IsMIP() const override
int64_t iterations() const override
bool NextSolution() override
void SetOptimizationDirection(bool maximize) override
MPSolver::BasisStatus column_status(int variable_index) const override
void ClearObjective() override
double VariableValue(const MPVariable *variable) const
int64_t NumNodesProcessed() const
bool is_pseudo_solution() const
ScipConstraintHandlerForMPCallback(MPCallback *mp_callback)
std::vector< CallbackRangeConstraint > SeparateFractionalSolution(const ScipConstraintHandlerContext &context, const EmptyStruct &) override
MPCallback *const mp_callback() const
std::vector< CallbackRangeConstraint > SeparateIntegerSolution(const ScipConstraintHandlerContext &context, const EmptyStruct &) override
ScipMPCallbackContext(const ScipConstraintHandlerContext *scip_context, bool at_integer_solution)
int64_t NumExploredNodes() override
bool CanQueryVariableValues() override
const std::vector< CallbackRangeConstraint > & constraints_added()
void AddLazyConstraint(const LinearRange &lazy_constraint) override
MPCallbackEvent Event() override
void AddCut(const LinearRange &cutting_plane) override
double SuggestSolution(const absl::flat_hash_map< const MPVariable *, double > &solution) override
double VariableValue(const MPVariable *variable) override
SharedResponseManager * response
GurobiMPCallbackContext * context
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
const char * SCIPlpiGetSolverName(void)
gets name and version of LP solver
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
Collection of objects used to extend the Constraint Solver library.
MPSolverInterface * BuildSCIPInterface(MPSolver *const solver)
absl::StatusOr< MPSolutionResponse > ScipSolveProto(const MPModelRequest &request)
absl::Status LegacyScipSetSolverSpecificParameters(const std::string ¶meters, 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