24 #include "absl/status/status.h"
25 #include "absl/strings/str_format.h"
26 #include "absl/types/optional.h"
37 #include "scip/cons_indicator.h"
38 #include "scip/scip.h"
39 #include "scip/scip_prob.h"
40 #include "scip/scipdefplugins.h"
43 "When true, emphasize search towards feasibility. This may or "
44 "may not result in speedups in some problems.");
56 const MPModelRequest& request)
override;
57 void Reset()
override;
67 double new_value,
double old_value)
override;
79 LOG(DFATAL) <<
"Basis status only available for continuous problems";
83 LOG(DFATAL) <<
"Basis status only available for continuous problems";
88 bool IsLP()
const override {
return false; }
89 bool IsMIP()
const override {
return true; }
96 return absl::StrFormat(
"SCIP %d.%d.%d [LP solver: %s]", SCIPmajorVersion(),
97 SCIPminorVersion(), SCIPtechVersion(),
102 if (scip_ ==
nullptr)
return true;
103 return SCIPinterruptSolve(scip_) == SCIP_OKAY;
110 void SetRelativeMipGap(
double value)
override;
111 void SetPrimalTolerance(
double value)
override;
112 void SetDualTolerance(
double value)
override;
113 void SetPresolveMode(
int presolve)
override;
114 void SetScalingMode(
int scaling)
override;
115 void SetLpAlgorithm(
int lp_algorithm)
override;
125 absl::Status SetNumThreads(
int num_threads)
override;
127 bool SetSolverSpecificParametersAsString(
130 void SetUnsupportedIntegerParam(
135 void SetSolution(SCIP_SOL* solution);
136 absl::Status CreateSCIP();
145 absl::Status status_;
148 std::vector<SCIP_VAR*> scip_variables_;
149 std::vector<SCIP_CONS*> scip_constraints_;
150 bool branching_priority_reset_ =
false;
155 status_ = CreateSCIP();
162 status_ = CreateSCIP();
166 absl::Status SCIPInterface::CreateSCIP() {
171 if (FLAGS_scip_feasibility_emphasis) {
182 SCIPsetIntParam(scip_,
"timing/clocktype", SCIP_CLOCKTYPE_WALL));
184 nullptr,
nullptr,
nullptr,
nullptr,
187 scip_,
maximize_ ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
188 return absl::OkStatus();
191 void SCIPInterface::DeleteSCIP() {
196 CHECK(scip_ !=
nullptr);
197 for (
int i = 0; i < scip_variables_.size(); ++i) {
198 CHECK_EQ(SCIPreleaseVar(scip_, &scip_variables_[i]), SCIP_OKAY);
200 scip_variables_.clear();
201 for (
int j = 0; j < scip_constraints_.size(); ++j) {
202 CHECK_EQ(SCIPreleaseCons(scip_, &scip_constraints_[j]), SCIP_OKAY);
204 scip_constraints_.clear();
205 CHECK_EQ(SCIPfree(&scip_), SCIP_OKAY);
209 #define RETURN_IF_ALREADY_IN_ERROR_STATE \
211 if (!status_.ok()) { \
212 LOG_EVERY_N(INFO, 10) << "Early abort: SCIP is in error state."; \
217 #define RETURN_AND_STORE_IF_SCIP_ERROR(x) \
219 status_ = SCIP_TO_STATUS(x); \
220 if (!status_.ok()) return; \
229 scip_, maximize ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
240 SCIPchgVarLb(scip_, scip_variables_[var_index], lb));
242 SCIPchgVarUb(scip_, scip_variables_[var_index], ub));
254 #if (SCIP_VERSION >= 210)
255 SCIP_Bool infeasible =
false;
257 scip_, scip_variables_[var_index],
258 integer ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS, &infeasible));
261 scip_, scip_variables_[var_index],
262 integer ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS));
263 #endif // SCIP_VERSION >= 210
277 SCIPchgLhsLinear(scip_, scip_constraints_[
index], lb));
279 SCIPchgRhsLinear(scip_, scip_constraints_[
index], ub));
300 scip_, scip_constraints_[constraint->
index()],
301 scip_variables_[variable->
index()], new_value - old_value));
313 const int constraint_index = constraint->
index();
316 for (
const auto& entry : constraint->coefficients_) {
317 const int var_index = entry.first->index();
318 const double old_coef_value = entry.second;
323 SCIPaddCoefLinear(scip_, scip_constraints_[constraint_index],
324 scip_variables_[var_index], -old_coef_value));
347 for (
const auto& entry :
solver_->objective_->coefficients_) {
348 const int var_index = entry.first->index();
354 SCIPchgVarObj(scip_, scip_variables_[var_index], 0.0));
372 branching_priority_reset_ =
true;
389 int total_num_vars =
solver_->variables_.size();
397 SCIP_VAR* scip_var =
nullptr;
399 double tmp_obj_coef = 0.0;
401 scip_, &scip_var,
var->name().c_str(),
var->lb(),
var->ub(),
403 var->integer() ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS,
true,
404 false,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr));
406 scip_variables_.push_back(scip_var);
407 const int branching_priority =
var->branching_priority();
408 if (branching_priority != 0) {
411 scip_, scip_variables_[
index], branching_priority));
417 for (
const auto& entry :
ct->coefficients_) {
418 const int var_index = entry.first->index();
424 SCIPaddCoefLinear(scip_, scip_constraints_[i],
425 scip_variables_[var_index], entry.second));
434 int total_num_rows =
solver_->constraints_.size();
438 int max_row_length = 0;
443 if (
ct->coefficients_.size() > max_row_length) {
444 max_row_length =
ct->coefficients_.size();
447 std::unique_ptr<SCIP_VAR*[]> vars(
new SCIP_VAR*[max_row_length]);
448 std::unique_ptr<double[]> coeffs(
new double[max_row_length]);
453 const int size =
ct->coefficients_.size();
455 for (
const auto& entry :
ct->coefficients_) {
456 const int var_index = entry.first->index();
458 vars[j] = scip_variables_[var_index];
459 coeffs[j] = entry.second;
462 SCIP_CONS* scip_constraint =
nullptr;
463 const bool is_lazy =
ct->is_lazy();
464 if (
ct->indicator_variable() !=
nullptr) {
465 const int ind_index =
ct->indicator_variable()->index();
467 SCIP_VAR* ind_var = scip_variables_[ind_index];
468 if (
ct->indicator_value() == 0) {
470 SCIPgetNegatedVar(scip_, scip_variables_[ind_index], &ind_var));
473 if (
ct->ub() < std::numeric_limits<double>::infinity()) {
475 scip_, &scip_constraint,
ct->name().c_str(), ind_var, size,
476 vars.get(), coeffs.get(),
ct->ub(),
487 scip_constraints_.push_back(scip_constraint);
489 if (
ct->lb() > -std::numeric_limits<double>::infinity()) {
490 for (
int i = 0; i < size; ++i) {
494 scip_, &scip_constraint,
ct->name().c_str(), ind_var, size,
495 vars.get(), coeffs.get(), -
ct->lb(),
506 scip_constraints_.push_back(scip_constraint);
513 scip_, &scip_constraint,
ct->name().c_str(), size, vars.get(),
514 coeffs.get(),
ct->lb(),
ct->ub(),
526 scip_constraints_.push_back(scip_constraint);
537 for (
const auto& entry :
solver_->objective_->coefficients_) {
538 const int var_index = entry.first->index();
539 const double obj_coef = entry.second;
541 SCIPchgVarObj(scip_, scip_variables_[var_index], obj_coef));
549 #define RETURN_ABNORMAL_IF_BAD_STATUS \
551 if (!status_.ok()) { \
552 LOG_IF(INFO, solver_->OutputIsEnabled()) \
553 << "Invalid SCIP status: " << status_; \
554 return result_status_ = MPSolver::ABNORMAL; \
558 #define RETURN_ABNORMAL_IF_SCIP_ERROR(x) \
560 RETURN_ABNORMAL_IF_BAD_STATUS; \
561 status_ = SCIP_TO_STATUS(x); \
562 RETURN_ABNORMAL_IF_BAD_STATUS; \
579 branching_priority_reset_) {
581 branching_priority_reset_ =
false;
585 SCIPsetMessagehdlrQuiet(scip_,
quiet_);
588 if (
solver_->variables_.empty() &&
solver_->constraints_.empty()) {
596 VLOG(1) << absl::StrFormat(
"Model built in %s.",
613 SetParameters(param);
615 solver_->solver_specific_parameter_string_);
618 if (!
solver_->solution_hint_.empty()) {
620 bool is_solution_partial =
false;
621 const int num_vars =
solver_->variables_.size();
622 if (
solver_->solution_hint_.size() != num_vars) {
625 SCIPcreatePartialSol(scip_, &solution,
nullptr));
626 is_solution_partial =
true;
633 for (
const std::pair<const MPVariable*, double>& p :
636 scip_, solution, scip_variables_[p.first->index()], p.second));
639 if (!is_solution_partial) {
640 SCIP_Bool is_feasible;
642 scip_, solution,
false,
true,
645 VLOG(1) <<
"Solution hint is "
646 << (is_feasible ?
"FEASIBLE" :
"INFEASIBLE");
654 if (!is_solution_partial && SCIPisTransformed(scip_)) {
656 scip_, &solution,
false,
true,
661 SCIPaddSolFree(scip_, &solution, &is_stored));
668 ? SCIPsolveConcurrent(scip_)
670 VLOG(1) << absl::StrFormat(
"Solved in %s.",
674 SCIP_SOL*
const solution = SCIPgetBestSol(scip_);
675 if (solution !=
nullptr) {
677 SetSolution(solution);
679 VLOG(1) <<
"No feasible solution found.";
683 SCIP_STATUS scip_status = SCIPgetStatus(scip_);
684 switch (scip_status) {
685 case SCIP_STATUS_OPTIMAL:
688 case SCIP_STATUS_GAPLIMIT:
692 case SCIP_STATUS_INFEASIBLE:
695 case SCIP_STATUS_UNBOUNDED:
698 case SCIP_STATUS_INFORUNBD:
704 if (solution !=
nullptr) {
706 }
else if (scip_status == SCIP_STATUS_TIMELIMIT ||
707 scip_status == SCIP_STATUS_TOTALNODELIMIT) {
721 void SCIPInterface::SetSolution(SCIP_SOL* solution) {
724 for (
int i = 0; i <
solver_->variables_.size(); ++i) {
726 const int var_index =
var->index();
728 SCIPgetSolVal(scip_, solution, scip_variables_[var_index]);
729 var->set_solution_value(val);
730 VLOG(3) <<
var->name() <<
"=" << val;
735 const MPModelRequest& request) {
740 if (status_or.ok())
return status_or.value();
743 if (absl::IsUnimplemented(status_or.status()))
return absl::nullopt;
745 if (request.enable_internal_solver_output()) {
746 LOG(INFO) <<
"Invalid SCIP status: " << status_or.status();
750 response.set_status_str(status_or.status().ToString());
758 return SCIPgetNLPIterations(scip_);
767 return SCIPgetNTotalNodes(scip_);
775 if (
solver_->variables_.empty() &&
solver_->constraints_.empty()) {
779 return SCIPgetDualbound(scip_);
788 void SCIPInterface::SetRelativeMipGap(
double value) {
801 if (status_.ok()) status_ = status;
804 void SCIPInterface::SetPrimalTolerance(
double value) {
820 if (status_.ok()) status_ = status;
823 void SCIPInterface::SetDualTolerance(
double value) {
826 if (status_.ok()) status_ = status;
829 void SCIPInterface::SetPresolveMode(
int presolve) {
834 SCIP_TO_STATUS(SCIPsetIntParam(scip_,
"presolving/maxrounds", 0));
835 if (status_.ok()) status_ = status;
840 SCIP_TO_STATUS(SCIPsetIntParam(scip_,
"presolving/maxrounds", -1));
841 if (status_.ok()) status_ = status;
851 void SCIPInterface::SetScalingMode(
int scaling) {
858 void SCIPInterface::SetLpAlgorithm(
int lp_algorithm) {
860 switch (lp_algorithm) {
864 if (status_.ok()) status_ = status;
870 if (status_.ok()) status_ = status;
877 if (status_.ok()) status_ = status;
888 void SCIPInterface::SetUnsupportedIntegerParam(
892 status_ = absl::InvalidArgumentError(absl::StrFormat(
893 "Tried to set unsupported integer parameter %d", param));
897 void SCIPInterface::SetIntegerParamToUnsupportedValue(
901 status_ = absl::InvalidArgumentError(absl::StrFormat(
902 "Tried to set integer parameter %d to unsupported value %d", param,
907 absl::Status SCIPInterface::SetNumThreads(
int num_threads) {
908 if (SetSolverSpecificParametersAsString(
909 absl::StrFormat(
"parallel/maxnthreads = %d\n", num_threads))) {
910 return absl::OkStatus();
912 return absl::InternalError(
913 "Could not set parallel/maxnthreads, which may "
914 "indicate that SCIP API has changed.");
917 bool SCIPInterface::SetSolverSpecificParametersAsString(
919 const absl::Status s =
922 LOG(WARNING) <<
"Failed to set SCIP parameter string: " <<
parameters
923 <<
", error is: " << s;
933 #endif // #if defined(USE_SCIP)
935 #undef RETURN_AND_STORE_IF_SCIP_ERROR
936 #undef RETURN_IF_ALREADY_IN_ERROR_STATE
937 #undef RETURN_ABNORMAL_IF_BAD_STATUS
938 #undef RETURN_ABNORMAL_IF_SCIP_ERROR