24 #include "absl/status/status.h"
25 #include "absl/strings/str_format.h"
26 #include "absl/types/optional.h"
40 #include "scip/cons_indicator.h"
41 #include "scip/scip.h"
42 #include "scip/scip_param.h"
43 #include "scip/scip_prob.h"
44 #include "scip/scipdefplugins.h"
47 "When true, emphasize search towards feasibility. This may or "
48 "may not result in speedups in some problems.");
53 struct EmptyStruct {};
56 class ScipConstraintHandlerForMPCallback;
66 const MPModelRequest& request)
override;
67 void Reset()
override;
77 double new_value,
double old_value)
override;
88 LOG(DFATAL) <<
"Basis status only available for continuous problems";
92 LOG(DFATAL) <<
"Basis status only available for continuous problems";
97 bool IsLP()
const override {
return false; }
98 bool IsMIP()
const override {
return true; }
105 return absl::StrFormat(
"SCIP %d.%d.%d [LP solver: %s]", SCIPmajorVersion(),
106 SCIPminorVersion(), SCIPtechVersion(),
111 if (scip_ ==
nullptr)
return true;
112 return SCIPinterruptSolve(scip_) == SCIP_OKAY;
146 void SetRelativeMipGap(
double value)
override;
147 void SetPrimalTolerance(
double value)
override;
148 void SetDualTolerance(
double value)
override;
149 void SetPresolveMode(
int presolve)
override;
150 void SetScalingMode(
int scaling)
override;
151 void SetLpAlgorithm(
int lp_algorithm)
override;
161 absl::Status SetNumThreads(
int num_threads)
override;
163 bool SetSolverSpecificParametersAsString(
166 void SetUnsupportedIntegerParam(
173 void SetSolution(SCIP_SOL* solution);
175 absl::Status CreateSCIP();
184 absl::Status status_;
187 std::vector<SCIP_VAR*> scip_variables_;
188 std::vector<SCIP_CONS*> scip_constraints_;
189 int current_solution_index_ = 0;
191 std::unique_ptr<ScipConstraintHandlerForMPCallback> scip_constraint_handler_;
193 EmptyStruct constraint_data_for_handler_;
194 bool branching_priority_reset_ =
false;
195 bool callback_reset_ =
false;
210 std::vector<CallbackRangeConstraint> SeparateSolution(
212 const bool at_integer_solution);
219 status_ = CreateSCIP();
226 scip_constraint_handler_.reset();
227 status_ = CreateSCIP();
231 absl::Status SCIPInterface::CreateSCIP() {
236 if (absl::GetFlag(FLAGS_scip_feasibility_emphasis)) {
247 SCIPsetIntParam(scip_,
"timing/clocktype", SCIP_CLOCKTYPE_WALL));
249 nullptr,
nullptr,
nullptr,
nullptr,
252 scip_,
maximize_ ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
253 return absl::OkStatus();
256 void SCIPInterface::DeleteSCIP() {
261 CHECK(scip_ !=
nullptr);
262 for (
int i = 0; i < scip_variables_.size(); ++i) {
263 CHECK_EQ(SCIPreleaseVar(scip_, &scip_variables_[i]), SCIP_OKAY);
265 scip_variables_.clear();
266 for (
int j = 0; j < scip_constraints_.size(); ++j) {
267 CHECK_EQ(SCIPreleaseCons(scip_, &scip_constraints_[j]), SCIP_OKAY);
269 scip_constraints_.clear();
270 CHECK_EQ(SCIPfree(&scip_), SCIP_OKAY);
274 #define RETURN_IF_ALREADY_IN_ERROR_STATE \
276 if (!status_.ok()) { \
277 VLOG_EVERY_N(1, 10) << "Early abort: SCIP is in error state."; \
282 #define RETURN_AND_STORE_IF_SCIP_ERROR(x) \
284 status_ = SCIP_TO_STATUS(x); \
285 if (!status_.ok()) return; \
294 scip_, maximize ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
305 SCIPchgVarLb(scip_, scip_variables_[var_index], lb));
307 SCIPchgVarUb(scip_, scip_variables_[var_index], ub));
319 #if (SCIP_VERSION >= 210)
320 SCIP_Bool infeasible =
false;
322 scip_, scip_variables_[var_index],
323 integer ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS, &infeasible));
326 scip_, scip_variables_[var_index],
327 integer ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS));
328 #endif // SCIP_VERSION >= 210
342 SCIPchgLhsLinear(scip_, scip_constraints_[
index], lb));
344 SCIPchgRhsLinear(scip_, scip_constraints_[
index], ub));
365 scip_, scip_constraints_[constraint->
index()],
366 scip_variables_[variable->
index()], new_value - old_value));
378 const int constraint_index = constraint->
index();
381 for (
const auto& entry : constraint->coefficients_) {
382 const int var_index = entry.first->index();
383 const double old_coef_value = entry.second;
388 SCIPaddCoefLinear(scip_, scip_constraints_[constraint_index],
389 scip_variables_[var_index], -old_coef_value));
412 for (
const auto& entry :
solver_->objective_->coefficients_) {
413 const int var_index = entry.first->index();
419 SCIPchgVarObj(scip_, scip_variables_[var_index], 0.0));
437 branching_priority_reset_ =
true;
454 int total_num_vars =
solver_->variables_.size();
462 SCIP_VAR* scip_var =
nullptr;
464 double tmp_obj_coef = 0.0;
466 scip_, &scip_var,
var->name().c_str(),
var->lb(),
var->ub(),
468 var->integer() ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS,
true,
469 false,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr));
471 scip_variables_.push_back(scip_var);
472 const int branching_priority =
var->branching_priority();
473 if (branching_priority != 0) {
476 scip_, scip_variables_[
index], branching_priority));
482 for (
const auto& entry :
ct->coefficients_) {
483 const int var_index = entry.first->index();
489 SCIPaddCoefLinear(scip_, scip_constraints_[i],
490 scip_variables_[var_index], entry.second));
499 int total_num_rows =
solver_->constraints_.size();
503 int max_row_length = 0;
508 if (
ct->coefficients_.size() > max_row_length) {
509 max_row_length =
ct->coefficients_.size();
512 std::unique_ptr<SCIP_VAR*[]> vars(
new SCIP_VAR*[max_row_length]);
513 std::unique_ptr<double[]> coeffs(
new double[max_row_length]);
518 const int size =
ct->coefficients_.size();
520 for (
const auto& entry :
ct->coefficients_) {
521 const int var_index = entry.first->index();
523 vars[j] = scip_variables_[var_index];
524 coeffs[j] = entry.second;
527 SCIP_CONS* scip_constraint =
nullptr;
528 const bool is_lazy =
ct->is_lazy();
529 if (
ct->indicator_variable() !=
nullptr) {
530 const int ind_index =
ct->indicator_variable()->index();
532 SCIP_VAR* ind_var = scip_variables_[ind_index];
533 if (
ct->indicator_value() == 0) {
535 SCIPgetNegatedVar(scip_, scip_variables_[ind_index], &ind_var));
538 if (
ct->ub() < std::numeric_limits<double>::infinity()) {
540 scip_, &scip_constraint,
ct->name().c_str(), ind_var, size,
541 vars.get(), coeffs.get(),
ct->ub(),
552 scip_constraints_.push_back(scip_constraint);
554 if (
ct->lb() > -std::numeric_limits<double>::infinity()) {
555 for (
int i = 0; i < size; ++i) {
559 scip_, &scip_constraint,
ct->name().c_str(), ind_var, size,
560 vars.get(), coeffs.get(), -
ct->lb(),
571 scip_constraints_.push_back(scip_constraint);
578 scip_, &scip_constraint,
ct->name().c_str(), size, vars.get(),
579 coeffs.get(),
ct->lb(),
ct->ub(),
591 scip_constraints_.push_back(scip_constraint);
602 for (
const auto& entry :
solver_->objective_->coefficients_) {
603 const int var_index = entry.first->index();
604 const double obj_coef = entry.second;
606 SCIPchgVarObj(scip_, scip_variables_[var_index], obj_coef));
614 #define RETURN_ABNORMAL_IF_BAD_STATUS \
616 if (!status_.ok()) { \
617 LOG_IF(INFO, solver_->OutputIsEnabled()) \
618 << "Invalid SCIP status: " << status_; \
619 return result_status_ = MPSolver::ABNORMAL; \
623 #define RETURN_ABNORMAL_IF_SCIP_ERROR(x) \
625 RETURN_ABNORMAL_IF_BAD_STATUS; \
626 status_ = SCIP_TO_STATUS(x); \
627 RETURN_ABNORMAL_IF_BAD_STATUS; \
644 branching_priority_reset_ || callback_reset_) {
646 branching_priority_reset_ =
false;
647 callback_reset_ =
false;
651 SCIPsetMessagehdlrQuiet(scip_,
quiet_);
654 if (
solver_->variables_.empty() &&
solver_->constraints_.empty()) {
663 VLOG(1) << absl::StrFormat(
"Model built in %s.",
665 if (callback_ !=
nullptr) {
666 scip_constraint_handler_ =
667 absl::make_unique<ScipConstraintHandlerForMPCallback>(callback_);
668 RegisterConstraintHandler<EmptyStruct>(scip_constraint_handler_.get(),
670 AddCallbackConstraint<EmptyStruct>(scip_, scip_constraint_handler_.get(),
671 "mp_solver_callback_constraint_for_scip",
672 &constraint_data_for_handler_,
690 SetParameters(param);
692 solver_->solver_specific_parameter_string_);
695 if (!
solver_->solution_hint_.empty()) {
697 bool is_solution_partial =
false;
698 const int num_vars =
solver_->variables_.size();
699 if (
solver_->solution_hint_.size() != num_vars) {
702 SCIPcreatePartialSol(scip_, &solution,
nullptr));
703 is_solution_partial =
true;
710 for (
const std::pair<const MPVariable*, double>& p :
713 scip_, solution, scip_variables_[p.first->index()], p.second));
716 if (!is_solution_partial) {
717 SCIP_Bool is_feasible;
719 scip_, solution,
false,
true,
722 VLOG(1) <<
"Solution hint is "
723 << (is_feasible ?
"FEASIBLE" :
"INFEASIBLE");
731 if (!is_solution_partial && SCIPisTransformed(scip_)) {
733 scip_, &solution,
false,
true,
738 SCIPaddSolFree(scip_, &solution, &is_stored));
745 ? SCIPsolveConcurrent(scip_)
747 VLOG(1) << absl::StrFormat(
"Solved in %s.",
749 current_solution_index_ = 0;
751 SCIP_SOL*
const solution = SCIPgetBestSol(scip_);
752 if (solution !=
nullptr) {
754 SetSolution(solution);
756 VLOG(1) <<
"No feasible solution found.";
760 SCIP_STATUS scip_status = SCIPgetStatus(scip_);
761 switch (scip_status) {
762 case SCIP_STATUS_OPTIMAL:
765 case SCIP_STATUS_GAPLIMIT:
769 case SCIP_STATUS_INFEASIBLE:
772 case SCIP_STATUS_UNBOUNDED:
775 case SCIP_STATUS_INFORUNBD:
781 if (solution !=
nullptr) {
783 }
else if (scip_status == SCIP_STATUS_TIMELIMIT ||
784 scip_status == SCIP_STATUS_TOTALNODELIMIT) {
798 void SCIPInterface::SetSolution(SCIP_SOL* solution) {
803 for (
int i = 0; i <
solver_->variables_.size(); ++i) {
805 const int var_index =
var->index();
807 SCIPgetSolVal(scip_, solution, scip_variables_[var_index]);
808 var->set_solution_value(val);
809 VLOG(3) <<
var->name() <<
"=" << val;
814 const MPModelRequest& request) {
819 if (status_or.ok())
return status_or.value();
822 if (absl::IsUnimplemented(status_or.status()))
return absl::nullopt;
824 if (request.enable_internal_solver_output()) {
825 LOG(
INFO) <<
"Invalid SCIP status: " << status_or.status();
829 response.set_status_str(status_or.status().ToString());
833 int SCIPInterface::SolutionCount() {
return SCIPgetNSols(scip_); }
840 if (current_solution_index_ + 1 >= SolutionCount()) {
843 current_solution_index_++;
844 SCIP_SOL** all_solutions = SCIPgetSols(scip_);
845 SetSolution(all_solutions[current_solution_index_]);
853 return SCIPgetNLPIterations(scip_);
862 return SCIPgetNTotalNodes(scip_);
870 void SCIPInterface::SetRelativeMipGap(
double value) {
883 if (status_.ok()) status_ = status;
886 void SCIPInterface::SetPrimalTolerance(
double value) {
890 if (status_.ok()) status_ = status;
893 void SCIPInterface::SetDualTolerance(
double value) {
896 if (status_.ok()) status_ = status;
899 void SCIPInterface::SetPresolveMode(
int presolve) {
904 SCIP_TO_STATUS(SCIPsetIntParam(scip_,
"presolving/maxrounds", 0));
905 if (status_.ok()) status_ = status;
910 SCIP_TO_STATUS(SCIPsetIntParam(scip_,
"presolving/maxrounds", -1));
911 if (status_.ok()) status_ = status;
921 void SCIPInterface::SetScalingMode(
int scaling) {
928 void SCIPInterface::SetLpAlgorithm(
int lp_algorithm) {
930 switch (lp_algorithm) {
934 if (status_.ok()) status_ = status;
940 if (status_.ok()) status_ = status;
947 if (status_.ok()) status_ = status;
958 void SCIPInterface::SetUnsupportedIntegerParam(
962 status_ = absl::InvalidArgumentError(absl::StrFormat(
963 "Tried to set unsupported integer parameter %d", param));
967 void SCIPInterface::SetIntegerParamToUnsupportedValue(
971 status_ = absl::InvalidArgumentError(absl::StrFormat(
972 "Tried to set integer parameter %d to unsupported value %d", param,
977 absl::Status SCIPInterface::SetNumThreads(
int num_threads) {
978 if (SetSolverSpecificParametersAsString(
979 absl::StrFormat(
"parallel/maxnthreads = %d\n", num_threads))) {
980 return absl::OkStatus();
982 return absl::InternalError(
983 "Could not set parallel/maxnthreads, which may "
984 "indicate that SCIP API has changed.");
987 bool SCIPInterface::SetSolverSpecificParametersAsString(
989 const absl::Status s =
993 <<
", error is: " << s;
1001 bool at_integer_solution)
1002 : scip_context_(scip_context),
1003 at_integer_solution_(at_integer_solution) {}
1006 if (at_integer_solution_) {
1007 return MPCallbackEvent::kMipSolution;
1009 return MPCallbackEvent::kMipNode;
1023 constraint.
is_cut =
true;
1024 constraint.
range = cutting_plane;
1025 constraint.local =
false;
1026 constraints_added_.push_back(std::move(constraint));
1031 constraint.
is_cut =
false;
1032 constraint.
range = lazy_constraint;
1033 constraint.local =
false;
1034 constraints_added_.push_back(std::move(constraint));
1038 const absl::flat_hash_map<const MPVariable*, double>& solution)
override {
1039 LOG(
FATAL) <<
"SuggestSolution() not currently supported for SCIP.";
1054 return constraints_added_;
1059 bool at_integer_solution_;
1061 std::vector<CallbackRangeConstraint> constraints_added_;
1068 {
"mp_solver_constraint_handler",
1070 "A single constraint handler for all MPSolver models."}
1073 mp_callback_(mp_callback) {}
1075 std::vector<CallbackRangeConstraint>
1078 return SeparateSolution(
context,
false);
1081 std::vector<CallbackRangeConstraint>
1084 return SeparateSolution(
context,
true);
1087 std::vector<CallbackRangeConstraint>
1088 ScipConstraintHandlerForMPCallback::SeparateSolution(
1090 const bool at_integer_solution) {
1093 return mp_context.constraints_added();
1097 if (callback_ !=
nullptr) {
1098 callback_reset_ =
true;
1100 callback_ = mp_callback;
1108 #endif // #if defined(USE_SCIP)
1110 #undef RETURN_AND_STORE_IF_SCIP_ERROR
1111 #undef RETURN_IF_ALREADY_IN_ERROR_STATE
1112 #undef RETURN_ABNORMAL_IF_BAD_STATUS
1113 #undef RETURN_ABNORMAL_IF_SCIP_ERROR