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" 44 #include "scip/scip.h" 45 #include "scip/scip_copy.h" 46 #include "scip/scip_param.h" 47 #include "scip/scip_prob.h" 48 #include "scip/scipdefplugins.h" 50 ABSL_FLAG(
bool, scip_feasibility_emphasis,
false,
51 "When true, emphasize search towards feasibility. This may or " 52 "may not result in speedups in some problems.");
57 struct EmptyStruct {};
60 class 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();
284 absl::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();
309 SCIP* 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));
372 #endif // SCIP_VERSION >= 210 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) {
848 void 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());
886 int 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_);
923 void SCIPInterface::SetRelativeMipGap(
double value) {
936 if (status_.ok()) status_ = status;
939 void SCIPInterface::SetPrimalTolerance(
double value) {
943 if (status_.ok()) status_ = status;
946 void SCIPInterface::SetDualTolerance(
double value) {
949 if (status_.ok()) status_ = status;
952 void 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;
974 void SCIPInterface::SetScalingMode(
int scaling) {
981 void 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;
1011 void SCIPInterface::SetUnsupportedIntegerParam(
1015 status_ = absl::InvalidArgumentError(absl::StrFormat(
1016 "Tried to set unsupported integer parameter %d", param));
1020 void SCIPInterface::SetIntegerParamToUnsupportedValue(
1024 status_ = absl::InvalidArgumentError(absl::StrFormat(
1025 "Tried to set integer parameter %d to unsupported value %d", param,
1030 absl::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.");
1040 bool 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) {}
1128 std::vector<CallbackRangeConstraint>
1131 return SeparateSolution(
context,
false);
1134 std::vector<CallbackRangeConstraint>
1137 return SeparateSolution(
context,
true);
1140 std::vector<CallbackRangeConstraint>
1141 ScipConstraintHandlerForMPCallback::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;
1161 #endif // #if defined(USE_SCIP) 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 ResultStatus
The status of solving the problem.
void set_variable_as_extracted(int var_index, bool extracted)
void SetVariableInteger(int var_index, bool integer) override
absl::Status LegacyScipSetSolverSpecificParameters(const std::string ¶meters, SCIP *scip)
void AddCut(const LinearRange &cutting_plane) override
Advanced usage: incrementality from one solve to the next.
#define RETURN_AND_STORE_IF_SCIP_ERROR(x)
void BranchingPriorityChangedForVariable(int var_index) override
double best_objective_bound_
double VariableValue(const MPVariable *variable) const
void set_constraint_as_extracted(int ct_index, bool extracted)
SynchronizationStatus sync_status_
MPSolver::BasisStatus column_status(int variable_index) const override
SCIPInterface(MPSolver *solver)
#define VLOG(verboselevel)
const MPObjective & Objective() const
Returns the objective object.
int64_t NumNodesProcessed() const
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
MPSolver::BasisStatus row_status(int constraint_index) const override
#define RETURN_IF_ALREADY_IN_ERROR_STATE
void ExtractNewVariables() override
#define SCIP_TO_STATUS(x)
bool CanQueryVariableValues() override
const char * SCIPlpiGetSolverName(void)
gets name and version of LP solver
std::vector< CallbackRangeConstraint > SeparateIntegerSolution(const ScipConstraintHandlerContext &context, const EmptyStruct &) override
Advanced usage: presolve mode.
BasisStatus
Advanced usage: possible basis status values for a variable and the slack variable of a linear constr...
bool NextSolution() override
IntegerParam
Enumeration of parameters that take integer or categorical values.
MPSolver::ResultStatus result_status_
#define RETURN_ABNORMAL_IF_SCIP_ERROR(x)
ScipConstraintHandlerForMPCallback(MPCallback *mp_callback)
int GetIntegerParam(MPSolverParameters::IntegerParam param) const
Returns the value of an integer parameter.
The class for variables of a Mathematical Programming (MP) model.
void ClearObjective() override
bool SetSolverSpecificParametersAsString(const std::string ¶meters)
Advanced usage: pass solver specific parameters in text format.
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
bool is_pseudo_solution() const
void SetObjectiveOffset(double value) override
void SetCallback(MPCallback *mp_callback) override
bool AddIndicatorConstraint(MPConstraint *ct) override
#define DCHECK_NE(val1, val2)
absl::StatusOr< MPSolutionResponse > ScipSolveProto(const MPModelRequest &request)
virtual void RunCallback(MPCallbackContext *callback_context)=0
std::vector< CallbackRangeConstraint > SeparateFractionalSolution(const ScipConstraintHandlerContext &context, const EmptyStruct &) override
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
void AddRowConstraint(MPConstraint *ct) override
void ClearConstraint(MPConstraint *constraint) override
ScipMPCallbackContext(const ScipConstraintHandlerContext *scip_context, bool at_integer_solution)
void SetOptimizationDirection(bool maximize) override
void ExtractObjective() override
void SetCommonParameters(const MPSolverParameters ¶m)
int64_t time_limit() const
bool SupportsCallbacks() const override
static constexpr int64_t kUnknownNumberOfIterations
virtual void SetUnsupportedIntegerParam(MPSolverParameters::IntegerParam param)
void SetConstraintBounds(int row_index, double lb, double ub) override
double VariableValue(const MPVariable *variable) override
The class for constraints of a Mathematical Programming (MP) model.
feasible, or stopped by limit.
const std::vector< CallbackRangeConstraint > & constraints_added()
MPCallback *const mp_callback() const
void SetMIPParameters(const MPSolverParameters ¶m)
SharedResponseManager * response
#define CHECK_EQ(val1, val2)
bool constraint_is_extracted(int ct_index) const
MPSolver::ResultStatus Solve(const MPSolverParameters ¶m) override
void InvalidateSolutionSynchronization()
void SetObjectiveCoefficient(const MPVariable *variable, double coefficient) override
#define RETURN_IF_SCIP_ERROR(x)
bool InterruptSolve() override
int index() const
Returns the index of the constraint in the MPSolver::constraints_.
void AddLazyConstraint(const LinearRange &lazy_constraint) override
#define LOG_IF(severity, condition)
#define DCHECK(condition)
int last_constraint_index_
int index() const
Returns the index of the variable in the MPSolver::variables_.
absl::Duration GetDuration() const
Advanced usage: enable or disable matrix scaling.
bool CheckSolutionIsSynchronized() const
static constexpr int64_t kUnknownNumberOfNodes
bool CheckSolutionIsSynchronizedAndExists() const
~SCIPInterface() override
bool IsContinuous() const override
bool IsLP() const override
MPCallbackEvent Event() override
double SuggestSolution(const absl::flat_hash_map< const MPVariable *, double > &solution) override
This mathematical programming (MP) solver class is the main class though which users build and solve ...
Collection of objects used to extend the Constraint Solver library.
int64_t iterations() const override
abnormal, i.e., error of some kind.
Start solve from scratch.
void SetVariableBounds(int var_index, double lb, double ub) override
int64_t NumExploredNodes() override
ABSL_FLAG(bool, scip_feasibility_emphasis, false, "When true, emphasize search towards feasibility. This may or " "may not result in speedups in some problems.")
bool IsMIP() const override
absl::optional< MPSolutionResponse > DirectlySolveProto(const MPModelRequest &request, std::atomic< bool > *interrupt) override
This class stores parameter settings for LP and MIP solvers.
#define RETURN_ABNORMAL_IF_BAD_STATUS
void AddVariable(MPVariable *var) override
Algorithm to solve linear programs.
An expression of the form:
void ExtractNewConstraints() override
void ResetExtractionInformation()
GurobiMPCallbackContext * context
int GetNumThreads() const
Returns the number of threads to be used during solve.
bool enable_internal_solver_output() const
int64_t nodes() const override
double offset() const
Gets the constant term in the objective.
double time_limit_in_secs() const
std::string SolverVersion() const override
bool variable_is_extracted(int var_index) const
#define DCHECK_LT(val1, val2)
void * underlying_solver() override
MPSolverInterface * BuildSCIPInterface(MPSolver *const solver)
void SetCoefficient(MPConstraint *constraint, const MPVariable *variable, double new_value, double old_value) override