25#include "absl/cleanup/cleanup.h"
26#include "absl/container/flat_hash_map.h"
27#include "absl/container/flat_hash_set.h"
28#include "absl/log/check.h"
29#include "absl/memory/memory.h"
30#include "absl/status/status.h"
31#include "absl/status/statusor.h"
32#include "absl/strings/str_cat.h"
33#include "absl/strings/str_format.h"
34#include "absl/strings/string_view.h"
35#include "absl/synchronization/mutex.h"
36#include "absl/types/span.h"
40#include "ortools/gscip/gscip.pb.h"
47#include "scip/cons_and.h"
48#include "scip/cons_indicator.h"
49#include "scip/cons_linear.h"
50#include "scip/cons_or.h"
51#include "scip/cons_quadratic.h"
52#include "scip/cons_sos1.h"
53#include "scip/cons_sos2.h"
55#include "scip/pub_cons.h"
56#include "scip/pub_var.h"
57#include "scip/scip_cons.h"
58#include "scip/scip_general.h"
59#include "scip/scip_message.h"
60#include "scip/scip_numerics.h"
61#include "scip/scip_param.h"
62#include "scip/scip_prob.h"
63#include "scip/scip_sol.h"
64#include "scip/scip_solve.h"
65#include "scip/scip_solvingstats.h"
66#include "scip/scip_var.h"
67#include "scip/scipdefplugins.h"
68#include "scip/type_cons.h"
69#include "scip/type_event.h"
70#include "scip/type_paramset.h"
71#include "scip/type_prob.h"
72#include "scip/type_retcode.h"
73#include "scip/type_scip.h"
74#include "scip/type_set.h"
75#include "scip/type_sol.h"
76#include "scip/type_stat.h"
77#include "scip/type_var.h"
81#define RETURN_ERROR_UNLESS(x) \
83 return util::StatusBuilder(absl::InvalidArgumentError(absl::StrFormat( \
84 "Condition violated at %s:%d: %s", __FILE__, __LINE__, #x)))
88constexpr absl::string_view kLinearConstraintHandlerName =
"linear";
90SCIP_VARTYPE ConvertVarType(
const GScipVarType var_type) {
92 case GScipVarType::kContinuous:
93 return SCIP_VARTYPE_CONTINUOUS;
94 case GScipVarType::kBinary:
95 return SCIP_VARTYPE_BINARY;
96 case GScipVarType::kImpliedInteger:
97 return SCIP_VARTYPE_IMPLINT;
98 case GScipVarType::kInteger:
99 return SCIP_VARTYPE_INTEGER;
103GScipVarType ConvertVarType(
const SCIP_VARTYPE var_type) {
105 case SCIP_VARTYPE_CONTINUOUS:
106 return GScipVarType::kContinuous;
107 case SCIP_VARTYPE_IMPLINT:
108 return GScipVarType::kImpliedInteger;
109 case SCIP_VARTYPE_INTEGER:
110 return GScipVarType::kInteger;
111 case SCIP_VARTYPE_BINARY:
112 return GScipVarType::kBinary;
116GScipOutput::Status ConvertStatus(
const SCIP_STATUS scip_status) {
117 switch (scip_status) {
118 case SCIP_STATUS_UNKNOWN:
119 return GScipOutput::UNKNOWN;
120 case SCIP_STATUS_USERINTERRUPT:
121 return GScipOutput::USER_INTERRUPT;
122 case SCIP_STATUS_BESTSOLLIMIT:
123 return GScipOutput::BEST_SOL_LIMIT;
124 case SCIP_STATUS_MEMLIMIT:
125 return GScipOutput::MEM_LIMIT;
126 case SCIP_STATUS_NODELIMIT:
127 return GScipOutput::NODE_LIMIT;
128 case SCIP_STATUS_RESTARTLIMIT:
129 return GScipOutput::RESTART_LIMIT;
130 case SCIP_STATUS_SOLLIMIT:
131 return GScipOutput::SOL_LIMIT;
132 case SCIP_STATUS_STALLNODELIMIT:
133 return GScipOutput::STALL_NODE_LIMIT;
134 case SCIP_STATUS_TIMELIMIT:
135 return GScipOutput::TIME_LIMIT;
136 case SCIP_STATUS_TOTALNODELIMIT:
137 return GScipOutput::TOTAL_NODE_LIMIT;
138 case SCIP_STATUS_OPTIMAL:
139 return GScipOutput::OPTIMAL;
140 case SCIP_STATUS_GAPLIMIT:
141 return GScipOutput::GAP_LIMIT;
142 case SCIP_STATUS_INFEASIBLE:
143 return GScipOutput::INFEASIBLE;
144 case SCIP_STATUS_UNBOUNDED:
145 return GScipOutput::UNBOUNDED;
146 case SCIP_STATUS_INFORUNBD:
147 return GScipOutput::INF_OR_UNBD;
148 case SCIP_STATUS_TERMINATE:
149 return GScipOutput::TERMINATE;
151 LOG(FATAL) <<
"Unrecognized scip status: " << scip_status;
155SCIP_PARAMEMPHASIS ConvertEmphasis(
156 const GScipParameters::Emphasis gscip_emphasis) {
157 switch (gscip_emphasis) {
158 case GScipParameters::DEFAULT_EMPHASIS:
159 return SCIP_PARAMEMPHASIS_DEFAULT;
160 case GScipParameters::CP_SOLVER:
161 return SCIP_PARAMEMPHASIS_CPSOLVER;
162 case GScipParameters::EASY_CIP:
163 return SCIP_PARAMEMPHASIS_EASYCIP;
164 case GScipParameters::FEASIBILITY:
165 return SCIP_PARAMEMPHASIS_FEASIBILITY;
166 case GScipParameters::HARD_LP:
167 return SCIP_PARAMEMPHASIS_HARDLP;
168 case GScipParameters::OPTIMALITY:
169 return SCIP_PARAMEMPHASIS_OPTIMALITY;
170 case GScipParameters::COUNTER:
171 return SCIP_PARAMEMPHASIS_COUNTER;
172 case GScipParameters::PHASE_FEAS:
173 return SCIP_PARAMEMPHASIS_PHASEFEAS;
174 case GScipParameters::PHASE_IMPROVE:
175 return SCIP_PARAMEMPHASIS_PHASEIMPROVE;
176 case GScipParameters::PHASE_PROOF:
177 return SCIP_PARAMEMPHASIS_PHASEPROOF;
179 LOG(FATAL) <<
"Unrecognized gscip_emphasis: "
184SCIP_PARAMSETTING ConvertMetaParamValue(
185 const GScipParameters::MetaParamValue gscip_meta_param_value) {
186 switch (gscip_meta_param_value) {
187 case GScipParameters::DEFAULT_META_PARAM_VALUE:
188 return SCIP_PARAMSETTING_DEFAULT;
189 case GScipParameters::AGGRESSIVE:
190 return SCIP_PARAMSETTING_AGGRESSIVE;
191 case GScipParameters::FAST:
192 return SCIP_PARAMSETTING_FAST;
193 case GScipParameters::OFF:
194 return SCIP_PARAMSETTING_OFF;
196 LOG(FATAL) <<
"Unrecognized gscip_meta_param_value: "
201absl::Status CheckSolutionsInOrder(
const GScipResult& result,
202 const bool is_maximize) {
203 auto objective_as_good_as = [is_maximize](
double left,
double right) {
205 return left >= right;
207 return left <= right;
209 for (
int i = 1; i < result.objective_values.size(); ++i) {
210 const double previous = result.objective_values[i - 1];
211 const double current = result.objective_values[i];
212 if (!objective_as_good_as(previous, current)) {
214 <<
"Expected SCIP solutions to be in best objective order "
216 << (is_maximize ?
"maximization" :
"minimization")
217 <<
" problem, the " << i - 1 <<
" objective is " << previous
218 <<
" and the " << i <<
" objective is " << current;
221 return absl::OkStatus();
226void GScip::InterruptEventHandler::set_interrupter(
227 const GScip::Interrupter* interrupter) {
228 interrupter_ = interrupter;
231GScip::InterruptEventHandler::InterruptEventHandler()
233 {.name =
"interrupt event handler",
234 .description =
"Event handler to call SCIPinterruptSolve() when a "
235 "user SolveInterrupter is triggered."}) {}
237SCIP_RETCODE GScip::InterruptEventHandler::Init(GScip*
const gscip) {
239 if (interrupter_ ==
nullptr) {
245 CatchEvent(SCIP_EVENTTYPE_PRESOLVEROUND);
246 CatchEvent(SCIP_EVENTTYPE_NODEEVENT);
247 CatchEvent(SCIP_EVENTTYPE_ROWEVENT);
249 return TryCallInterruptIfNeeded(gscip);
252SCIP_RETCODE GScip::InterruptEventHandler::Execute(
253 const GScipEventHandlerContext
context) {
254 return TryCallInterruptIfNeeded(
context.gscip());
257SCIP_RETCODE GScip::InterruptEventHandler::TryCallInterruptIfNeeded(
258 GScip*
const gscip) {
259 if (interrupter_ ==
nullptr) {
260 LOG(WARNING) <<
"TryCallInterruptIfNeeded() called after interrupter has "
265 if (!interrupter_->is_interrupted()) {
269 const SCIP_STAGE stage = SCIPgetStage(gscip->scip());
271 case SCIP_STAGE_INIT:
272 case SCIP_STAGE_FREE:
275 LOG(DFATAL) <<
"TryCallInterruptIfNeeded() called in stage "
276 << (stage == SCIP_STAGE_INIT ?
"INIT" :
"FREE");
278 case SCIP_STAGE_INITSOLVE:
279 LOG(WARNING) <<
"TryCallInterruptIfNeeded() called in INITSOLVE stage; "
280 "we can't call SCIPinterruptSolve() in this stage.";
283 return SCIPinterruptSolve(gscip->scip());
288 static GScipVariableOptions var_options;
293 static GScipConstraintOptions constraint_options;
294 return constraint_options;
297absl::Status GScip::SetParams(
const GScipParameters& params,
298 absl::string_view legacy_params) {
299 if (params.has_silence_output()) {
300 SCIPsetMessagehdlrQuiet(scip_, params.silence_output());
302 if (!params.search_logs_filename().empty()) {
303 SCIPsetMessagehdlrLogfile(scip_, params.search_logs_filename().c_str());
306 const SCIP_Bool set_param_quiet =
307 static_cast<SCIP_Bool
>(!params.silence_output());
310 scip_, ConvertEmphasis(params.emphasis()), set_param_quiet));
311 if (params.has_heuristics()) {
313 scip_, ConvertMetaParamValue(params.heuristics()), set_param_quiet));
315 if (params.has_presolve()) {
317 scip_, ConvertMetaParamValue(params.presolve()), set_param_quiet));
319 if (params.has_separating()) {
321 scip_, ConvertMetaParamValue(params.separating()), set_param_quiet));
323 for (
const auto& bool_param : params.bool_params()) {
325 (SCIPsetBoolParam(scip_, bool_param.first.c_str(), bool_param.second)));
327 for (
const auto& int_param : params.int_params()) {
329 (SCIPsetIntParam(scip_, int_param.first.c_str(), int_param.second)));
331 for (
const auto& long_param : params.long_params()) {
333 long_param.second)));
335 for (
const auto& char_param : params.char_params()) {
336 if (char_param.second.size() != 1) {
337 return absl::InvalidArgumentError(
338 absl::StrCat(
"Character parameters must be single character strings, "
340 char_param.first,
" was: ", char_param.second));
343 char_param.second[0])));
345 for (
const auto& string_param : params.string_params()) {
347 string_param.second.c_str())));
349 for (
const auto& real_param : params.real_params()) {
351 (SCIPsetRealParam(scip_, real_param.first.c_str(), real_param.second)));
353 if (!legacy_params.empty()) {
357 return absl::OkStatus();
360absl::StatusOr<std::unique_ptr<GScip>> GScip::Create(
361 const std::string& problem_name) {
362 SCIP* scip =
nullptr;
364 absl::Cleanup scip_cleanup = [&]() { SCIPfree(&scip); };
367 auto result = absl::WrapUnique(
new GScip(scip));
369 std::move(scip_cleanup).Cancel();
370 RETURN_IF_ERROR(result->interrupt_event_handler_.Register(result.get()));
374GScip::GScip(SCIP* scip) : scip_(scip) {}
376double GScip::ScipInf() {
return SCIPinfinity(scip_); }
378absl::Status GScip::FreeTransform() {
382std::string GScip::ScipVersion() {
383 return absl::StrFormat(
"SCIP %d.%d.%d [LP solver: %s]", SCIPmajorVersion(),
384 SCIPminorVersion(), SCIPtechVersion(),
385 SCIPlpiGetSolverName());
388void GScip::InterruptSolveFromCallbackOnCallbackError(
389 absl::Status error_status) {
390 CHECK(!error_status.ok());
392 const absl::MutexLock lock(&callback_status_mutex_);
393 if (!callback_status_.ok()) {
396 callback_status_ = std::move(error_status);
401 LOG(WARNING) <<
"Error trying to interrupt solve after error in callback: "
406absl::Status GScip::CleanUp() {
407 if (scip_ !=
nullptr) {
408 for (SCIP_VAR* variable : variables_) {
409 if (variable !=
nullptr) {
413 for (SCIP_CONS* constraint : constraints_) {
414 if (constraint !=
nullptr) {
420 return absl::OkStatus();
424 const absl::Status clean_up_status = CleanUp();
425 LOG_IF(DFATAL, !clean_up_status.ok()) << clean_up_status;
428absl::StatusOr<SCIP_VAR*> GScip::AddVariable(
429 double lb,
double ub,
double obj_coef, GScipVarType var_type,
430 const std::string& var_name,
const GScipVariableOptions& options) {
431 SCIP_VAR*
var =
nullptr;
433 _ <<
"invalid lower bound for variable: " << var_name);
435 _ <<
"invalid upper bound for variable: " << var_name);
437 <<
"invalid objective coefficient for variable: " << var_name;
442 ConvertVarType(var_type)));
446 if (options.keep_alive) {
447 variables_.insert(
var);
454absl::Status GScip::MaybeKeepConstraintAlive(
455 SCIP_CONS* constraint,
const GScipConstraintOptions& options) {
456 if (options.keep_alive) {
457 constraints_.insert(constraint);
461 return absl::OkStatus();
464absl::StatusOr<SCIP_CONS*> GScip::AddLinearConstraint(
465 const GScipLinearRange&
range,
const std::string&
name,
466 const GScipConstraintOptions& options) {
467 SCIP_CONS* constraint =
nullptr;
469 <<
"Error adding constraint: " <<
name <<
".";
471 _ <<
"invalid lower bound for constraint: " <<
name);
473 _ <<
"invalid upper bound for constraint: " <<
name);
474 for (
int i = 0;
i <
range.coefficients.size(); ++
i) {
476 <<
"invalid coefficient at index " <<
i <<
" of constraint: " <<
name;
479 scip_, &constraint,
name.c_str(),
range.variables.size(),
480 const_cast<SCIP_VAR**
>(
range.variables.data()),
481 const_cast<double*
>(
range.coefficients.data()),
492 options.sticking_at_node));
498absl::StatusOr<SCIP_CONS*> GScip::AddQuadraticConstraint(
499 const GScipQuadraticRange&
range,
const std::string&
name,
500 const GScipConstraintOptions& options) {
501 SCIP_CONS* constraint =
nullptr;
502 const int num_lin_vars =
range.linear_variables.size();
504 <<
"Error adding quadratic constraint: " <<
name <<
" in linear term.";
505 const int num_quad_vars =
range.quadratic_variables1.size();
507 <<
"Error adding quadratic constraint: " <<
name <<
" in quadratic term.";
509 <<
"Error adding quadratic constraint: " <<
name <<
" in quadratic term.";
511 _ <<
"invalid lower bound for constraint: " <<
name);
513 _ <<
"invalid upper bound for constraint: " <<
name);
514 for (
int i = 0;
i <
range.linear_coefficients.size(); ++
i) {
516 <<
"invalid linear coefficient at index " <<
i
517 <<
" of constraint: " <<
name;
519 for (
int i = 0;
i <
range.quadratic_coefficients.size(); ++
i) {
521 <<
"invalid quadratic coefficient at index " <<
i
522 <<
" of constraint: " <<
name;
525 scip_, &constraint,
name.c_str(), num_lin_vars,
526 const_cast<SCIP_Var**
>(
range.linear_variables.data()),
527 const_cast<double*
>(
range.linear_coefficients.data()), num_quad_vars,
528 const_cast<SCIP_Var**
>(
range.quadratic_variables1.data()),
529 const_cast<SCIP_Var**
>(
range.quadratic_variables2.data()),
530 const_cast<double*
>(
range.quadratic_coefficients.data()),
546absl::StatusOr<SCIP_CONS*> GScip::AddIndicatorConstraint(
547 const GScipIndicatorConstraint& indicator_constraint,
548 const std::string&
name,
const GScipConstraintOptions& options) {
549 SCIP_VAR* indicator = indicator_constraint.indicator_variable;
551 <<
"Error adding indicator constraint: " <<
name <<
".";
552 if (indicator_constraint.negate_indicator) {
556 SCIP_CONS* constraint =
nullptr;
558 indicator_constraint.coefficients.size())
559 <<
"Error adding indicator constraint: " <<
name <<
".";
561 ScipInfClamp(indicator_constraint.upper_bound),
562 _ <<
"invalid upper bound for constraint: " <<
name);
563 for (
int i = 0;
i < indicator_constraint.coefficients.size(); ++
i) {
565 <<
"invalid coefficient at index " <<
i <<
" of constraint: " <<
name;
568 scip_, &constraint,
name.c_str(), indicator,
569 indicator_constraint.variables.size(),
570 const_cast<SCIP_Var**
>(indicator_constraint.variables.data()),
571 const_cast<double*
>(indicator_constraint.coefficients.data()),
581 options.sticking_at_node));
587absl::StatusOr<SCIP_CONS*> GScip::AddAndConstraint(
588 const GScipLogicalConstraintData& logical_data,
const std::string&
name,
589 const GScipConstraintOptions& options) {
591 <<
"Error adding and constraint: " <<
name <<
".";
592 SCIP_CONS* constraint =
nullptr;
594 SCIPcreateConsAnd(scip_, &constraint,
name.c_str(),
595 logical_data.resultant, logical_data.operators.size(),
596 const_cast<SCIP_VAR**
>(logical_data.operators.data()),
606 options.sticking_at_node));
612absl::StatusOr<SCIP_CONS*> GScip::AddOrConstraint(
613 const GScipLogicalConstraintData& logical_data,
const std::string&
name,
614 const GScipConstraintOptions& options) {
616 <<
"Error adding or constraint: " <<
name <<
".";
617 SCIP_CONS* constraint =
nullptr;
619 SCIPcreateConsOr(scip_, &constraint,
name.c_str(), logical_data.resultant,
620 logical_data.operators.size(),
621 const_cast<SCIP_Var**
>(logical_data.operators.data()),
631 options.sticking_at_node));
639absl::Status ValidateSOSData(
const GScipSOSData& sos_data,
640 absl::string_view
name) {
642 <<
"Error adding SOS constraint: " <<
name <<
".";
643 if (!sos_data.weights.empty()) {
645 <<
" Error adding SOS constraint: " <<
name <<
".";
647 absl::flat_hash_set<double> distinct_weights;
648 for (
const double w : sos_data.weights) {
650 <<
"Error adding SOS constraint: " <<
name
651 <<
", weights must be distinct, but found value " <<
w <<
" twice.";
652 distinct_weights.insert(
w);
654 return absl::OkStatus();
659absl::StatusOr<SCIP_CONS*> GScip::AddSOS1Constraint(
660 const GScipSOSData& sos_data,
const std::string&
name,
661 const GScipConstraintOptions& options) {
663 SCIP_CONS* constraint =
nullptr;
664 double* weights =
nullptr;
665 if (!sos_data.weights.empty()) {
666 weights =
const_cast<double*
>(sos_data.weights.data());
670 scip_, &constraint,
name.c_str(), sos_data.variables.size(),
671 const_cast<SCIP_Var**
>(sos_data.variables.data()), weights,
680 options.sticking_at_node));
686absl::StatusOr<SCIP_CONS*> GScip::AddSOS2Constraint(
687 const GScipSOSData& sos_data,
const std::string&
name,
688 const GScipConstraintOptions& options) {
690 SCIP_CONS* constraint =
nullptr;
691 double* weights =
nullptr;
692 if (!sos_data.weights.empty()) {
693 weights =
const_cast<double*
>(sos_data.weights.data());
696 scip_, &constraint,
name.c_str(), sos_data.variables.size(),
697 const_cast<SCIP_Var**
>(sos_data.variables.data()), weights,
706 options.sticking_at_node));
712absl::Status GScip::SetMaximize(
bool is_maximize) {
714 scip_, is_maximize ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
715 return absl::OkStatus();
718absl::Status GScip::SetObjectiveOffset(
double offset) {
719 RETURN_IF_ERROR(CheckScipFinite(offset)) <<
"invalid objective offset";
720 double old_offset = SCIPgetOrigObjoffset(scip_);
721 double delta_offset = offset - old_offset;
723 return absl::OkStatus();
726bool GScip::ObjectiveIsMaximize() {
727 return SCIPgetObjsense(scip_) == SCIP_OBJSENSE_MAXIMIZE;
730double GScip::ObjectiveOffset() {
return SCIPgetOrigObjoffset(scip_); }
732absl::Status GScip::SetBranchingPriority(SCIP_VAR*
var,
int priority) {
734 return absl::OkStatus();
737absl::Status GScip::SetLb(SCIP_VAR*
var,
double lb) {
740 return absl::OkStatus();
743absl::Status GScip::SetUb(SCIP_VAR*
var,
double ub) {
746 return absl::OkStatus();
749absl::Status GScip::SetObjCoef(SCIP_VAR*
var,
double obj_coef) {
750 RETURN_IF_ERROR(CheckScipFinite(obj_coef)) <<
"invalid objective coefficient";
752 return absl::OkStatus();
755absl::Status GScip::SetVarType(SCIP_VAR*
var, GScipVarType var_type) {
758 SCIPchgVarType(scip_,
var, ConvertVarType(var_type), &infeasible));
759 return absl::OkStatus();
762absl::Status GScip::DeleteVariable(SCIP_VAR*
var) {
763 SCIP_Bool did_delete;
766 <<
"Failed to delete variable named: " << Name(
var);
767 variables_.erase(
var);
769 return absl::OkStatus();
772absl::Status GScip::CanSafeBulkDelete(
773 const absl::flat_hash_set<SCIP_VAR*>& vars) {
775 return absl::OkStatus();
777 for (SCIP_CONS* constraint : constraints_) {
778 if (!IsConstraintLinear(constraint)) {
779 return absl::InvalidArgumentError(absl::StrCat(
780 "Model contains nonlinear constraint: ", Name(constraint)));
783 return absl::OkStatus();
786absl::Status GScip::SafeBulkDelete(
const absl::flat_hash_set<SCIP_VAR*>& vars) {
789 return absl::OkStatus();
792 for (SCIP_CONS* constraint : constraints_) {
793 const absl::Span<SCIP_VAR* const> nonzeros =
794 LinearConstraintVariables(constraint);
795 const std::vector<SCIP_VAR*> nonzeros_copy(nonzeros.begin(),
797 for (SCIP_VAR*
var : nonzeros_copy) {
798 if (vars.contains(
var)) {
803 for (SCIP_VAR*
const var : vars) {
806 return absl::OkStatus();
809double GScip::Lb(SCIP_VAR*
var) {
810 return ScipInfUnclamp(SCIPvarGetLbOriginal(
var));
813double GScip::Ub(SCIP_VAR*
var) {
814 return ScipInfUnclamp(SCIPvarGetUbOriginal(
var));
817double GScip::ObjCoef(SCIP_VAR*
var) {
return SCIPvarGetObj(
var); }
819GScipVarType GScip::VarType(SCIP_VAR*
var) {
820 return ConvertVarType(SCIPvarGetType(
var));
823absl::string_view GScip::Name(SCIP_VAR*
var) {
return SCIPvarGetName(
var); }
825absl::string_view GScip::ConstraintType(SCIP_CONS* constraint) {
826 return absl::string_view(SCIPconshdlrGetName(SCIPconsGetHdlr(constraint)));
829bool GScip::IsConstraintLinear(SCIP_CONS* constraint) {
830 return ConstraintType(constraint) == kLinearConstraintHandlerName;
833absl::Span<const double> GScip::LinearConstraintCoefficients(
834 SCIP_CONS* constraint) {
835 int num_vars = SCIPgetNVarsLinear(scip_, constraint);
836 return absl::MakeConstSpan(SCIPgetValsLinear(scip_, constraint), num_vars);
839absl::Span<SCIP_VAR* const> GScip::LinearConstraintVariables(
840 SCIP_CONS* constraint) {
841 int num_vars = SCIPgetNVarsLinear(scip_, constraint);
842 return absl::MakeConstSpan(SCIPgetVarsLinear(scip_, constraint), num_vars);
845double GScip::LinearConstraintLb(SCIP_CONS* constraint) {
846 return ScipInfUnclamp(SCIPgetLhsLinear(scip_, constraint));
849double GScip::LinearConstraintUb(SCIP_CONS* constraint) {
850 return ScipInfUnclamp(SCIPgetRhsLinear(scip_, constraint));
853absl::string_view GScip::Name(SCIP_CONS* constraint) {
854 return SCIPconsGetName(constraint);
857absl::Status GScip::SetLinearConstraintLb(SCIP_CONS* constraint,
double lb) {
860 return absl::OkStatus();
863absl::Status GScip::SetLinearConstraintUb(SCIP_CONS* constraint,
double ub) {
866 return absl::OkStatus();
869absl::Status GScip::DeleteConstraint(SCIP_CONS* constraint) {
871 constraints_.erase(constraint);
873 return absl::OkStatus();
876absl::Status GScip::SetLinearConstraintCoef(SCIP_CONS* constraint,
883 return absl::OkStatus();
886absl::Status GScip::AddLinearConstraintCoef(SCIP_CONS*
const constraint,
888 const double value) {
891 return absl::OkStatus();
894absl::StatusOr<GScipHintResult> GScip::SuggestHint(
895 const GScipSolution& partial_solution) {
897 const int scip_num_vars = SCIPgetNOrigVars(scip_);
898 const bool is_solution_partial = partial_solution.size() < scip_num_vars;
899 if (is_solution_partial) {
904 <<
"Error suggesting hint.";
907 for (
const auto& var_value_pair : partial_solution) {
909 var_value_pair.second));
911 if (!is_solution_partial) {
912 SCIP_Bool is_feasible;
917 if (!
static_cast<bool>(is_feasible)) {
919 return GScipHintResult::kInfeasible;
924 if (
static_cast<bool>(is_stored)) {
925 return GScipHintResult::kAccepted;
927 return GScipHintResult::kRejected;
931absl::StatusOr<GScipResult> GScip::Solve(
932 const GScipParameters& params, absl::string_view legacy_params,
933 const GScipMessageHandler message_handler,
934 const Interrupter*
const interrupter) {
935 if (InErrorState()) {
936 return absl::InvalidArgumentError(
937 "GScip is in an error state due to a previous GScip::Solve()");
939 interrupt_event_handler_.set_interrupter(interrupter);
940 const absl::Cleanup interrupt_cleanup = [
this]() {
941 interrupt_event_handler_.set_interrupter(
nullptr);
953 const absl::Status param_status = SetParams(params, legacy_params);
954 if (!param_status.ok()) {
955 result.gscip_output.set_status(GScipOutput::INVALID_SOLVER_PARAMETERS);
957 result.gscip_output.set_status_detail(
958 std::string(param_status.message()));
961 if (params.print_scip_model()) {
964 if (!params.scip_model_filename().empty()) {
966 scip_, params.scip_model_filename().c_str(),
"cip", FALSE));
968 if (params.has_objective_limit()) {
970 ScipInfClamp(params.objective_limit()),
971 _ <<
"invalid objective_limit");
979 using internal::CaptureMessageHandlerPtr;
980 using internal::MessageHandlerPtr;
983 if (message_handler !=
nullptr) {
986 internal::MakeSCIPMessageHandler(message_handler));
987 SCIPsetMessagehdlr(scip_, new_handler.get());
991 const internal::ScopedSCIPMessageHandlerDisabler new_handler_disabler(
1002 const SCIP_STAGE stage = SCIPgetStage(scip_);
1003 if (stage != SCIP_STAGE_PRESOLVING && stage != SCIP_STAGE_SOLVING &&
1004 stage != SCIP_STAGE_SOLVED) {
1005 result.gscip_output.set_status(GScipOutput::UNKNOWN);
1006 result.gscip_output.set_status_detail(
1007 absl::StrCat(
"Unexpected SCIP final stage= ", stage,
1008 " was expected to be either SCIP_STAGE_PRESOLVING, "
1009 "SCIP_STAGE_SOLVING, or SCIP_STAGE_SOLVED"));
1012 if (params.print_detailed_solving_stats()) {
1015 if (!params.detailed_solving_stats_filename().empty()) {
1016 FILE*
file = fopen(params.detailed_solving_stats_filename().c_str(),
"w");
1017 if (
file ==
nullptr) {
1018 return absl::InvalidArgumentError(absl::StrCat(
1019 "Could not open file: ", params.detailed_solving_stats_filename(),
1020 " to write SCIP solve stats."));
1023 int close_result = fclose(
file);
1024 if (close_result != 0) {
1025 return absl::InvalidArgumentError(absl::StrCat(
1026 "Error: ", close_result,
1027 " closing file: ", params.detailed_solving_stats_filename(),
1028 " when writing solve stats."));
1031 absl::Status callback_status;
1033 const absl::MutexLock callback_status_lock(&callback_status_mutex_);
1035 <<
"error in a callback that interrupted the solve";
1037 if (!callback_status.ok()) {
1038 const absl::Status
status = FreeTransform();
1040 return callback_status;
1042 LOG(ERROR) <<
"GScip::FreeTransform() failed after interrupting "
1043 "the solve due to an error in a callback: "
1050 GScipSolvingStats* stats = result.gscip_output.mutable_stats();
1051 const int num_scip_solutions = SCIPgetNSols(scip_);
1052 const int num_returned_solutions =
1053 std::min(num_scip_solutions, std::max(1, params.num_solutions()));
1054 SCIP_SOL** all_solutions = SCIPgetSols(scip_);
1055 stats->set_best_objective(ScipInfUnclamp(SCIPgetPrimalbound(scip_)));
1056 for (
int i = 0;
i < num_returned_solutions; ++
i) {
1057 SCIP_SOL* scip_sol = all_solutions[
i];
1058 const double obj_value = ScipInfUnclamp(SCIPgetSolOrigObj(scip_, scip_sol));
1060 for (SCIP_VAR* v : variables_) {
1061 solution[v] = SCIPgetSolVal(scip_, scip_sol, v);
1063 result.solutions.push_back(
solution);
1064 result.objective_values.push_back(obj_value);
1066 RETURN_IF_ERROR(CheckSolutionsInOrder(result, ObjectiveIsMaximize()));
1068 if (stage != SCIP_STAGE_PRESOLVING && SCIPhasPrimalRay(scip_)) {
1069 for (SCIP_VAR* v : variables_) {
1070 result.primal_ray[v] = SCIPgetPrimalRayVal(scip_, v);
1074 stats->set_best_bound(ScipInfUnclamp(SCIPgetDualbound(scip_)));
1075 stats->set_node_count(SCIPgetNTotalNodes(scip_));
1076 stats->set_first_lp_relaxation_bound(SCIPgetFirstLPDualboundRoot(scip_));
1077 stats->set_root_node_bound(SCIPgetDualboundRoot(scip_));
1078 if (stage != SCIP_STAGE_PRESOLVING) {
1079 stats->set_total_lp_iterations(SCIPgetNLPIterations(scip_));
1080 stats->set_primal_simplex_iterations(SCIPgetNPrimalLPIterations(scip_));
1081 stats->set_dual_simplex_iterations(SCIPgetNDualLPIterations(scip_));
1082 stats->set_barrier_iterations(SCIPgetNBarrierLPIterations(scip_));
1083 stats->set_deterministic_time(SCIPgetDeterministicTime(scip_));
1085 result.gscip_output.set_status(ConvertStatus(SCIPgetStatus(scip_)));
1093 if (message_handler !=
nullptr) {
1100 new_handler.reset();
1102 if (params.has_objective_limit()) {
1110 SCIPsetMessagehdlrQuiet(scip_,
false);
1111 SCIPsetMessagehdlrLogfile(scip_,
nullptr);
1116absl::StatusOr<bool> GScip::DefaultBoolParamValue(
1117 const std::string& parameter_name) {
1118 SCIP_Bool default_value;
1120 SCIPgetBoolParam(scip_, parameter_name.c_str(), &default_value));
1121 return static_cast<bool>(default_value);
1124absl::StatusOr<int> GScip::DefaultIntParamValue(
1125 const std::string& parameter_name) {
1128 SCIPgetIntParam(scip_, parameter_name.c_str(), &default_value));
1129 return default_value;
1132absl::StatusOr<int64_t> GScip::DefaultLongParamValue(
1133 const std::string& parameter_name) {
1134 SCIP_Longint result;
1136 SCIPgetLongintParam(scip_, parameter_name.c_str(), &result));
1137 return static_cast<int64_t
>(result);
1140absl::StatusOr<double> GScip::DefaultRealParamValue(
1141 const std::string& parameter_name) {
1144 SCIPgetRealParam(scip_, parameter_name.c_str(), &result));
1148absl::StatusOr<char> GScip::DefaultCharParamValue(
1149 const std::string& parameter_name) {
1152 SCIPgetCharParam(scip_, parameter_name.c_str(), &result));
1156absl::StatusOr<std::string> GScip::DefaultStringParamValue(
1157 const std::string& parameter_name) {
1160 SCIPgetStringParam(scip_, parameter_name.c_str(), &result));
1161 return std::string(result);
1164absl::StatusOr<double> GScip::ScipInfClamp(
const double d) {
1165 const double kScipInf = ScipInf();
1166 if (d == std::numeric_limits<double>::infinity()) {
1169 if (d == -std::numeric_limits<double>::infinity()) {
1173 if (d >= kScipInf || d <= -kScipInf) {
1175 << d <<
" is not in SCIP's finite range: (" << -kScipInf <<
", "
1181double GScip::ScipInfUnclamp(
double d) {
1182 const double kScipInf = ScipInf();
1183 if (d >= kScipInf)
return std::numeric_limits<double>::infinity();
1184 if (d <= -kScipInf)
return -std::numeric_limits<double>::infinity();
1188absl::Status GScip::CheckScipFinite(
double d) {
1189 const double kScipInf = ScipInf();
1191 if (d >= kScipInf || d <= -kScipInf) {
1193 << d <<
" is not in SCIP's finite range: (" << -kScipInf <<
", "
1196 return absl::OkStatus();
1199bool GScip::InErrorState() {
1200 const absl::MutexLock lock(&callback_status_mutex_);
1201 return !callback_status_.ok();
1204#undef RETURN_ERROR_UNLESS
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
const std::string name
A name for logging purposes.
#define RETURN_ERROR_UNLESS(x)
GurobiMPCallbackContext * context
MessageHandlerPtr CaptureMessageHandlerPtr(SCIP_MESSAGEHDLR *const handler)
std::unique_ptr< SCIP_MESSAGEHDLR, ReleaseSCIPMessageHandler > MessageHandlerPtr
*If primal *dual status is infeasible
In SWIG mode, we don't want anything besides these top-level includes.
const GScipConstraintOptions & DefaultGScipConstraintOptions()
std::string ProtoEnumToString(ProtoEnumType enum_value)
int GScipMaxNumThreads(const GScipParameters ¶meters)
Returns 1 if the number of threads it not specified.
absl::Status LegacyScipSetSolverSpecificParameters(absl::string_view parameters, SCIP *scip)
const GScipVariableOptions & DefaultGScipVariableOptions()
StatusBuilder InternalErrorBuilder()
StatusBuilder InvalidArgumentErrorBuilder()
trees with all degrees equal w the current value of w
#define SCIP_TO_STATUS(x)
#define RETURN_IF_SCIP_ERROR(x)
const std::optional< Range > & range
#define OR_ASSIGN_OR_RETURN3(lhs, rexpr, error_expression)