25#include "absl/status/status.h"
26#include "absl/strings/str_format.h"
27#include "absl/types/optional.h"
42#include "scip/cons_indicator.h"
44#include "scip/scip_copy.h"
45#include "scip/scip_param.h"
46#include "scip/scip_prob.h"
47#include "scip/scipdefplugins.h"
50 "When true, emphasize search towards feasibility. This may or "
51 "may not result in speedups in some problems.");
59class ScipConstraintHandlerForMPCallback;
69 const MPModelRequest& request, std::atomic<bool>* interrupt)
override;
70 void Reset()
override;
80 double new_value,
double old_value)
override;
89 int64_t
nodes()
const override;
91 LOG(DFATAL) <<
"Basis status only available for continuous problems";
95 LOG(DFATAL) <<
"Basis status only available for continuous problems";
100 bool IsLP()
const override {
return false; }
101 bool IsMIP()
const override {
return true; }
108 return absl::StrFormat(
"SCIP %d.%d.%d [LP solver: %s]", SCIPmajorVersion(),
109 SCIPminorVersion(), SCIPtechVersion(),
114 const absl::MutexLock lock(&hold_interruptions_mutex_);
115 if (scip_ ==
nullptr) {
116 LOG_IF(DFATAL, status_.ok()) <<
"scip_ is null is unexpected here, since "
117 "status_ did not report any error";
120 return SCIPinterruptSolve(scip_) == SCIP_OKAY;
154 void SetRelativeMipGap(
double value)
override;
155 void SetPrimalTolerance(
double value)
override;
156 void SetDualTolerance(
double value)
override;
157 void SetPresolveMode(
int presolve)
override;
158 void SetScalingMode(
int scaling)
override;
159 void SetLpAlgorithm(
int lp_algorithm)
override;
169 absl::Status SetNumThreads(
int num_threads)
override;
171 bool SetSolverSpecificParametersAsString(
174 void SetUnsupportedIntegerParam(
181 void SetSolution(SCIP_SOL* solution);
183 absl::Status CreateSCIP();
187 SCIP* DeleteSCIP(
bool return_scip =
false);
195 absl::Status status_;
198 std::vector<SCIP_VAR*> scip_variables_;
199 std::vector<SCIP_CONS*> scip_constraints_;
200 int current_solution_index_ = 0;
202 std::unique_ptr<ScipConstraintHandlerForMPCallback> scip_constraint_handler_;
204 EmptyStruct constraint_data_for_handler_;
205 bool branching_priority_reset_ =
false;
206 bool callback_reset_ =
false;
211 mutable absl::Mutex hold_interruptions_mutex_;
228 std::vector<CallbackRangeConstraint> SeparateSolution(
230 const bool at_integer_solution);
235#define RETURN_IF_ALREADY_IN_ERROR_STATE \
237 if (!status_.ok()) { \
238 VLOG_EVERY_N(1, 10) << "Early abort: SCIP is in error state."; \
243#define RETURN_AND_STORE_IF_SCIP_ERROR(x) \
245 status_ = SCIP_TO_STATUS(x); \
246 if (!status_.ok()) return; \
251 status_ = CreateSCIP();
258 const absl::MutexLock lock(&hold_interruptions_mutex_);
261 SCIP* old_scip = DeleteSCIP(
true);
263 [&old_scip]() {
CHECK_EQ(SCIPfree(&old_scip), SCIP_OKAY); });
265 scip_constraint_handler_.reset();
269 status_ = CreateSCIP();
283absl::Status SCIPInterface::CreateSCIP() {
288 if (absl::GetFlag(FLAGS_scip_feasibility_emphasis)) {
299 SCIPsetIntParam(scip_,
"timing/clocktype", SCIP_CLOCKTYPE_WALL));
301 nullptr,
nullptr,
nullptr,
nullptr,
304 scip_,
maximize_ ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
305 return absl::OkStatus();
308SCIP* SCIPInterface::DeleteSCIP(
bool return_scip) {
313 CHECK(scip_ !=
nullptr);
314 for (
int i = 0; i < scip_variables_.size(); ++i) {
315 CHECK_EQ(SCIPreleaseVar(scip_, &scip_variables_[i]), SCIP_OKAY);
317 scip_variables_.clear();
318 for (
int j = 0; j < scip_constraints_.size(); ++j) {
319 CHECK_EQ(SCIPreleaseCons(scip_, &scip_constraints_[j]), SCIP_OKAY);
321 scip_constraints_.clear();
323 SCIP* old_scip = scip_;
326 CHECK_EQ(SCIPfree(&old_scip), SCIP_OKAY);
337 scip_, maximize ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
348 SCIPchgVarLb(scip_, scip_variables_[var_index], lb));
350 SCIPchgVarUb(scip_, scip_variables_[var_index], ub));
362#if (SCIP_VERSION >= 210)
363 SCIP_Bool infeasible =
false;
365 scip_, scip_variables_[var_index],
366 integer ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS, &infeasible));
369 scip_, scip_variables_[var_index],
370 integer ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS));
385 SCIPchgLhsLinear(scip_, scip_constraints_[
index], lb));
387 SCIPchgRhsLinear(scip_, scip_constraints_[
index], ub));
408 scip_, scip_constraints_[constraint->
index()],
409 scip_variables_[variable->
index()], new_value - old_value));
421 const int constraint_index = constraint->
index();
424 for (
const auto& entry : constraint->coefficients_) {
425 const int var_index = entry.first->index();
426 const double old_coef_value = entry.second;
431 SCIPaddCoefLinear(scip_, scip_constraints_[constraint_index],
432 scip_variables_[var_index], -old_coef_value));
455 for (
const auto& entry :
solver_->objective_->coefficients_) {
456 const int var_index = entry.first->index();
462 SCIPchgVarObj(scip_, scip_variables_[var_index], 0.0));
480 branching_priority_reset_ =
true;
497 int total_num_vars =
solver_->variables_.size();
505 SCIP_VAR* scip_var =
nullptr;
507 double tmp_obj_coef = 0.0;
509 scip_, &scip_var,
var->name().c_str(),
var->lb(),
var->ub(),
511 var->integer() ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS,
true,
512 false,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr));
514 scip_variables_.push_back(scip_var);
515 const int branching_priority =
var->branching_priority();
516 if (branching_priority != 0) {
519 scip_, scip_variables_[
index], branching_priority));
525 for (
const auto& entry :
ct->coefficients_) {
526 const int var_index = entry.first->index();
532 SCIPaddCoefLinear(scip_, scip_constraints_[i],
533 scip_variables_[var_index], entry.second));
542 int total_num_rows =
solver_->constraints_.size();
546 int max_row_length = 0;
551 if (
ct->coefficients_.size() > max_row_length) {
552 max_row_length =
ct->coefficients_.size();
555 std::unique_ptr<SCIP_VAR*[]> vars(
new SCIP_VAR*[max_row_length]);
556 std::unique_ptr<double[]> coeffs(
new double[max_row_length]);
561 const int size =
ct->coefficients_.size();
563 for (
const auto& entry :
ct->coefficients_) {
564 const int var_index = entry.first->index();
566 vars[j] = scip_variables_[var_index];
567 coeffs[j] = entry.second;
570 SCIP_CONS* scip_constraint =
nullptr;
571 const bool is_lazy =
ct->is_lazy();
572 if (
ct->indicator_variable() !=
nullptr) {
573 const int ind_index =
ct->indicator_variable()->index();
575 SCIP_VAR* ind_var = scip_variables_[ind_index];
576 if (
ct->indicator_value() == 0) {
578 SCIPgetNegatedVar(scip_, scip_variables_[ind_index], &ind_var));
581 if (
ct->ub() < std::numeric_limits<double>::infinity()) {
583 scip_, &scip_constraint,
ct->name().c_str(), ind_var, size,
584 vars.get(), coeffs.get(),
ct->ub(),
595 scip_constraints_.push_back(scip_constraint);
597 if (
ct->lb() > -std::numeric_limits<double>::infinity()) {
598 for (
int i = 0; i < size; ++i) {
602 scip_, &scip_constraint,
ct->name().c_str(), ind_var, size,
603 vars.get(), coeffs.get(), -
ct->lb(),
614 scip_constraints_.push_back(scip_constraint);
621 scip_, &scip_constraint,
ct->name().c_str(), size, vars.get(),
622 coeffs.get(),
ct->lb(),
ct->ub(),
634 scip_constraints_.push_back(scip_constraint);
645 for (
const auto& entry :
solver_->objective_->coefficients_) {
646 const int var_index = entry.first->index();
647 const double obj_coef = entry.second;
649 SCIPchgVarObj(scip_, scip_variables_[var_index], obj_coef));
657#define RETURN_ABNORMAL_IF_BAD_STATUS \
659 if (!status_.ok()) { \
660 LOG_IF(INFO, solver_->OutputIsEnabled()) \
661 << "Invalid SCIP status: " << status_; \
662 return result_status_ = MPSolver::ABNORMAL; \
666#define RETURN_ABNORMAL_IF_SCIP_ERROR(x) \
668 RETURN_ABNORMAL_IF_BAD_STATUS; \
669 status_ = SCIP_TO_STATUS(x); \
670 RETURN_ABNORMAL_IF_BAD_STATUS; \
687 branching_priority_reset_ || callback_reset_) {
689 branching_priority_reset_ =
false;
690 callback_reset_ =
false;
694 SCIPsetMessagehdlrQuiet(scip_,
quiet_);
697 if (
solver_->variables_.empty() &&
solver_->constraints_.empty()) {
706 VLOG(1) << absl::StrFormat(
"Model built in %s.",
708 if (scip_constraint_handler_ !=
nullptr) {
713 CHECK_EQ(scip_constraint_handler_->mp_callback(), callback_);
714 }
else if (callback_ !=
nullptr) {
715 scip_constraint_handler_ =
716 absl::make_unique<ScipConstraintHandlerForMPCallback>(callback_);
717 RegisterConstraintHandler<EmptyStruct>(scip_constraint_handler_.get(),
719 AddCallbackConstraint<EmptyStruct>(scip_, scip_constraint_handler_.get(),
720 "mp_solver_callback_constraint_for_scip",
721 &constraint_data_for_handler_,
739 SetParameters(param);
741 solver_->solver_specific_parameter_string_);
744 if (!
solver_->solution_hint_.empty()) {
746 bool is_solution_partial =
false;
747 const int num_vars =
solver_->variables_.size();
748 if (
solver_->solution_hint_.size() != num_vars) {
751 SCIPcreatePartialSol(scip_, &solution,
nullptr));
752 is_solution_partial =
true;
759 for (
const std::pair<const MPVariable*, double>& p :
762 scip_, solution, scip_variables_[p.first->index()], p.second));
765 if (!is_solution_partial) {
766 SCIP_Bool is_feasible;
768 scip_, solution,
false,
true,
771 VLOG(1) <<
"Solution hint is "
772 << (is_feasible ?
"FEASIBLE" :
"INFEASIBLE");
780 if (!is_solution_partial && SCIPisTransformed(scip_)) {
782 scip_, &solution,
false,
true,
787 SCIPaddSolFree(scip_, &solution, &is_stored));
794 ? SCIPsolveConcurrent(scip_)
796 VLOG(1) << absl::StrFormat(
"Solved in %s.",
798 current_solution_index_ = 0;
800 SCIP_SOL*
const solution = SCIPgetBestSol(scip_);
801 if (solution !=
nullptr) {
803 SetSolution(solution);
805 VLOG(1) <<
"No feasible solution found.";
809 SCIP_STATUS scip_status = SCIPgetStatus(scip_);
810 switch (scip_status) {
811 case SCIP_STATUS_OPTIMAL:
814 case SCIP_STATUS_GAPLIMIT:
818 case SCIP_STATUS_INFEASIBLE:
821 case SCIP_STATUS_UNBOUNDED:
824 case SCIP_STATUS_INFORUNBD:
830 if (solution !=
nullptr) {
832 }
else if (scip_status == SCIP_STATUS_TIMELIMIT ||
833 scip_status == SCIP_STATUS_TOTALNODELIMIT) {
847void SCIPInterface::SetSolution(SCIP_SOL* solution) {
852 for (
int i = 0; i <
solver_->variables_.size(); ++i) {
854 const int var_index =
var->index();
856 SCIPgetSolVal(scip_, solution, scip_variables_[var_index]);
857 var->set_solution_value(val);
858 VLOG(3) <<
var->name() <<
"=" << val;
868 if (interrupt !=
nullptr)
return absl::nullopt;
871 if (status_or.ok())
return status_or.value();
874 if (absl::IsUnimplemented(status_or.status()))
return absl::nullopt;
877 LOG(
INFO) <<
"Invalid SCIP status: " << status_or.status();
881 response.set_status_str(status_or.status().ToString());
885int SCIPInterface::SolutionCount() {
return SCIPgetNSols(scip_); }
892 if (current_solution_index_ + 1 >= SolutionCount()) {
895 current_solution_index_++;
896 SCIP_SOL** all_solutions = SCIPgetSols(scip_);
897 SetSolution(all_solutions[current_solution_index_]);
905 return SCIPgetNLPIterations(scip_);
914 return SCIPgetNTotalNodes(scip_);
922void SCIPInterface::SetRelativeMipGap(
double value) {
935 if (status_.ok()) status_ = status;
938void SCIPInterface::SetPrimalTolerance(
double value) {
942 if (status_.ok()) status_ = status;
945void SCIPInterface::SetDualTolerance(
double value) {
948 if (status_.ok()) status_ = status;
951void SCIPInterface::SetPresolveMode(
int presolve) {
956 SCIP_TO_STATUS(SCIPsetIntParam(scip_,
"presolving/maxrounds", 0));
957 if (status_.ok()) status_ = status;
962 SCIP_TO_STATUS(SCIPsetIntParam(scip_,
"presolving/maxrounds", -1));
963 if (status_.ok()) status_ = status;
973void SCIPInterface::SetScalingMode(
int scaling) {
980void SCIPInterface::SetLpAlgorithm(
int lp_algorithm) {
982 switch (lp_algorithm) {
986 if (status_.ok()) status_ = status;
992 if (status_.ok()) status_ = status;
999 if (status_.ok()) status_ = status;
1010void SCIPInterface::SetUnsupportedIntegerParam(
1014 status_ = absl::InvalidArgumentError(absl::StrFormat(
1015 "Tried to set unsupported integer parameter %d", param));
1019void SCIPInterface::SetIntegerParamToUnsupportedValue(
1023 status_ = absl::InvalidArgumentError(absl::StrFormat(
1024 "Tried to set integer parameter %d to unsupported value %d", param,
1029absl::Status SCIPInterface::SetNumThreads(
int num_threads) {
1030 if (SetSolverSpecificParametersAsString(
1031 absl::StrFormat(
"parallel/maxnthreads = %d\n", num_threads))) {
1032 return absl::OkStatus();
1034 return absl::InternalError(
1035 "Could not set parallel/maxnthreads, which may "
1036 "indicate that SCIP API has changed.");
1039bool SCIPInterface::SetSolverSpecificParametersAsString(
1041 const absl::Status s =
1045 <<
", error is: " << s;
1053 bool at_integer_solution)
1054 : scip_context_(scip_context),
1055 at_integer_solution_(at_integer_solution) {}
1058 if (at_integer_solution_) {
1075 constraint.
is_cut =
true;
1076 constraint.
range = cutting_plane;
1077 constraint.local =
false;
1078 constraints_added_.push_back(std::move(constraint));
1083 constraint.
is_cut =
false;
1084 constraint.
range = lazy_constraint;
1085 constraint.local =
false;
1086 constraints_added_.push_back(std::move(constraint));
1090 const absl::flat_hash_map<const MPVariable*, double>& solution)
override {
1091 LOG(
FATAL) <<
"SuggestSolution() not currently supported for SCIP.";
1106 return constraints_added_;
1111 bool at_integer_solution_;
1113 std::vector<CallbackRangeConstraint> constraints_added_;
1120 {
"mp_solver_constraint_handler",
1122 "A single constraint handler for all MPSolver models."}
1125 mp_callback_(mp_callback) {}
1127std::vector<CallbackRangeConstraint>
1130 return SeparateSolution(
context,
false);
1133std::vector<CallbackRangeConstraint>
1136 return SeparateSolution(
context,
true);
1139std::vector<CallbackRangeConstraint>
1140ScipConstraintHandlerForMPCallback::SeparateSolution(
1142 const bool at_integer_solution) {
1145 return mp_context.constraints_added();
1149 if (callback_ !=
nullptr) {
1150 callback_reset_ =
true;
1152 callback_ = mp_callback;
1162#undef RETURN_AND_STORE_IF_SCIP_ERROR
1163#undef RETURN_IF_ALREADY_IN_ERROR_STATE
1164#undef RETURN_ABNORMAL_IF_BAD_STATUS
1165#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