Google OR-Tools: ortools/linear_solver/scip_interface.cc Source File
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"
44#include "scip/cons_indicator.h"
45#include "scip/cons_linear.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"
55#include "scip/scip_solve.h"
56#include "scip/scip_solvingstats.h"
58#include "scip/scipdefplugins.h"
59#include "scip/type_clock.h"
61#include "scip/type_paramset.h"
63#include "scip/type_retcode.h"
69ABSL_FLAG(bool, scip_feasibility_emphasis, false,
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;
97 void SetVariableBounds(int var_index, double lb, double ub) override;
105 double new_value, double old_value) override;
108 double coefficient) override;
114 int64_t nodes() const override;
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(),
139 const absl::MutexLock lock(hold_interruptions_mutex_);
141 LOG_IF(DFATAL, status_.ok()) << "scip_ is null is unexpected here, since "
142 "status_ did not report any error";
148 void* underlying_solver() override { return reinterpret_cast<void*>(scip_); }
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);
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;
258#define RETURN_IF_ALREADY_IN_ERROR_STATE \
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); \
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); });
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));
333SCIP* SCIPInterface::DeleteSCIP(bool return_scip) {
339 for (int i = 0; i < scip_variables_.size(); ++i) {
340 CHECK_EQ(SCIPreleaseVar(scip_, &scip_variables_[i]), SCIP_OKAY);
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);
373 SCIPchgVarLb(scip_, scip_variables_[var_index], lb));
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));
419 const MPVariable* variable, double new_value,
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],
477 for (const auto& entry : solver_->objective_->coefficients_) {
478 const int var_index = entry.first->index();
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],
564 int total_num_rows = solver_->constraints_.size();
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];
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(),
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 \
682 LOG_IF(INFO, solver_->OutputIsEnabled()) \
683 << "Invalid SCIP status: " << status_; \
688#define RETURN_ABNORMAL_IF_SCIP_ERROR(x) \
690 RETURN_ABNORMAL_IF_BAD_STATUS; \
691 status_ = SCIP_TO_STATUS(x); \
709 branching_priority_reset_ || callback_reset_) {
711 branching_priority_reset_ = false;
716 SCIPsetMessagehdlrQuiet(scip_, quiet_);
719 if (solver_->variables_.empty() && solver_->constraints_.empty()) {
728 VLOG(1) << absl::StrFormat("Model built in %s.",
729 absl::FormatDuration(timer.GetDuration()));
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()));
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 :
782 solver_->solution_hint_) {
784 scip_, solution, scip_variables_[p.first->index()], p.second));
787 if (!is_solution_partial) {
790 scip_, solution, false, true,
793 VLOG(1) << "Solution hint is "
794 << (is_feasible ? "FEASIBLE" : "INFEASIBLE");
802 if (!is_solution_partial && SCIPisTransformed(scip_)) {
804 scip_, &solution, false, true,
809 SCIPaddSolFree(scip_, &solution, &is_stored));
816 ? SCIPsolveConcurrent(scip_)
818 VLOG(1) << absl::StrFormat("Solved in %s.",
819 absl::FormatDuration(timer.GetDuration()));
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_);
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 ||
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;
902int SCIPInterface::SolutionCount() { return SCIPgetNSols(scip_); }
909 if (current_solution_index_ + 1 >= SolutionCount()) {
912 current_solution_index_++;
913 SCIP_SOL** all_solutions = SCIPgetSols(scip_);
939void SCIPInterface::SetRelativeMipGap(double value) {
951 SCIP_TO_STATUS(SCIPsetRealParam(scip_, "limits/gap", 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) {
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))) {
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) {
1061 LOG(WARNING) << "Failed to set SCIP parameter string: " << parameters
1092 constraint.is_cut = true;
1093 constraint.range = cutting_plane;
1094 constraint.local = false;
1100 constraint.is_cut = false;
1101 constraint.range = lazy_constraint;
1102 constraint.local = false;
1107 const absl::flat_hash_map<const MPVariable*, double>& solution) override {
1108 LOG(FATAL) << "SuggestSolution() not currently supported for SCIP.";
1128 bool at_integer_solution_;
1130 std::vector<CallbackRangeConstraint> constraints_added_;
1138std::vector<CallbackRangeConstraint>
1144std::vector<CallbackRangeConstraint>
1150std::vector<CallbackRangeConstraint>
1151ScipConstraintHandlerForMPCallback::SeparateSolution(
1153 const bool at_integer_solution) {
1156 return mp_context.constraints_added();
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_.
Definition scip_interface.cc:81
void ExtractObjective() override
Definition scip_interface.cc:662
bool InterruptSolve() override
Definition scip_interface.cc:138
void ExtractNewConstraints() override
Definition scip_interface.cc:562
void AddVariable(MPVariable *var) override
Definition scip_interface.cc:515
int64_t nodes() const override
Definition scip_interface.cc:925
double infinity() override
Definition scip_interface.cc:331
void SetObjectiveCoefficient(const MPVariable *variable, double coefficient) override
Definition scip_interface.cc:462
void SetObjectiveOffset(double value) override
Definition scip_interface.cc:467
void ExtractNewVariables() override
Definition scip_interface.cc:517
void * underlying_solver() override
Definition scip_interface.cc:148
bool IsContinuous() const override
Definition scip_interface.cc:124
~SCIPInterface() override
Definition scip_interface.cc:277
int64_t iterations() const override
Definition scip_interface.cc:918
void SetVariableInteger(int var_index, bool integer) override
Definition scip_interface.cc:381
SCIPInterface(MPSolver *solver)
Definition scip_interface.cc:272
void ClearConstraint(MPConstraint *constraint) override
Definition scip_interface.cc:443
void SetCallback(MPCallback *mp_callback) override
Definition scip_interface.cc:1159
void ClearObjective() override
Definition scip_interface.cc:470
void Reset() override
Definition scip_interface.cc:279
MPSolver::ResultStatus Solve(const MPSolverParameters ¶m) override
Definition scip_interface.cc:695
void BranchingPriorityChangedForVariable(int var_index) override
Definition scip_interface.cc:494
void SetVariableBounds(int var_index, double lb, double ub) override
Definition scip_interface.cc:365
bool IsMIP() const override
Definition scip_interface.cc:126
void SetCoefficient(MPConstraint *constraint, const MPVariable *variable, double new_value, double old_value) override
Definition scip_interface.cc:418
void SetConstraintBounds(int row_index, double lb, double ub) override
Definition scip_interface.cc:402
bool SupportsCallbacks() const override
Definition scip_interface.cc:174
void AddRowConstraint(MPConstraint *ct) override
Definition scip_interface.cc:506
std::string SolverVersion() const override
Definition scip_interface.cc:132
MPSolver::BasisStatus row_status(int) const override
Definition scip_interface.cc:115
bool IsLP() const override
Definition scip_interface.cc:125
bool SupportsDirectlySolveProto(std::atomic< bool > *interrupt) const override
Definition scip_interface.cc:884
bool NextSolution() override
Definition scip_interface.cc:904
MPSolutionResponse DirectlySolveProto(LazyMutableCopy< MPModelRequest > request, std::atomic< bool > *interrupt) override
Definition scip_interface.cc:895
void SetOptimizationDirection(bool maximize) override
Definition scip_interface.cc:357
bool AddIndicatorConstraint(MPConstraint *ct) override
Definition scip_interface.cc:510
MPSolver::BasisStatus column_status(int) const override
Definition scip_interface.cc:119
Definition scip_interface.cc:239
std::vector< CallbackRangeConstraint > SeparateIntegerSolution(const ScipConstraintHandlerContext &context, const EmptyStruct &) override
Definition scip_interface.cc:1145
MPCallback * mp_callback() const
Definition scip_interface.cc:249
std::vector< CallbackRangeConstraint > SeparateFractionalSolution(const ScipConstraintHandlerContext &context, const EmptyStruct &) override
Definition scip_interface.cc:1139
ScipConstraintHandlerForMPCallback(MPCallback *mp_callback)
Definition scip_interface.cc:1133
ScipConstraintHandler(const ScipConstraintHandlerDescription &description)
Definition scip_interface.cc:1067
void AddLazyConstraint(const LinearRange &lazy_constraint) override
Definition scip_interface.cc:1098
void AddCut(const LinearRange &cutting_plane) override
Definition scip_interface.cc:1090
int64_t NumExploredNodes() override
Definition scip_interface.cc:1111
MPCallbackEvent Event() override
Definition scip_interface.cc:1074
bool CanQueryVariableValues() override
Definition scip_interface.cc:1081
double VariableValue(const MPVariable *variable) override
Definition scip_interface.cc:1085
ScipMPCallbackContext(const ScipConstraintHandlerContext *scip_context, bool at_integer_solution)
Definition scip_interface.cc:1069
double SuggestSolution(const absl::flat_hash_map< const MPVariable *, double > &solution) override
Definition scip_interface.cc:1106
const std::vector< CallbackRangeConstraint > & constraints_added()
Definition scip_interface.cc:1122
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
Definition scip_interface.cc:258
#define RETURN_ABNORMAL_IF_SCIP_ERROR(x)
Definition scip_interface.cc:688
#define RETURN_AND_STORE_IF_SCIP_ERROR(x)
Definition scip_interface.cc:266
#define RETURN_ABNORMAL_IF_BAD_STATUS
Definition scip_interface.cc:679