29#include "absl/cleanup/cleanup.h"
30#include "absl/container/btree_set.h"
31#include "absl/status/status.h"
32#include "absl/status/statusor.h"
33#include "absl/strings/ascii.h"
34#include "absl/strings/numbers.h"
35#include "absl/strings/str_cat.h"
36#include "absl/strings/str_format.h"
37#include "absl/strings/str_split.h"
38#include "absl/strings/string_view.h"
39#include "absl/time/time.h"
45#include "ortools/linear_solver/linear_solver.pb.h"
49#include "scip/cons_disjunction.h"
50#include "scip/cons_linear.h"
51#include "scip/cons_quadratic.h"
52#include "scip/pub_var.h"
54#include "scip/scip_numerics.h"
55#include "scip/scip_param.h"
56#include "scip/scip_prob.h"
57#include "scip/scip_var.h"
58#include "scip/scipdefplugins.h"
60#include "scip/struct_paramset.h"
61#include "scip/type_cons.h"
62#include "scip/type_paramset.h"
63#include "scip/type_var.h"
65ABSL_FLAG(std::string, scip_proto_solver_output_cip_file,
"",
66 "If given, saves the generated CIP file here. Useful for "
67 "reporting bugs to SCIP.");
72absl::StatusOr<double> ScipInfClamp(
SCIP* scip,
const double d) {
74 return absl::InvalidArgumentError(
"Cannot clamp a NaN.");
76 const double kScipInf = SCIPinfinity(scip);
77 const double clamped = std::clamp(d, -kScipInf, kScipInf);
79 VLOG_EVERY_N_SEC(1, 5)
80 <<
"A bound was clamped to SCIP's 'infinite' value for safety; this "
81 "may affect results. Was: "
82 << d <<
", is now: " << clamped;
89absl::Status AddIndicatorConstraint(
const MPGeneralConstraintProto& gen_cst,
90 SCIP* scip, SCIP_CONS** scip_cst,
91 std::vector<SCIP_VAR*>* scip_variables,
92 std::vector<SCIP_CONS*>* scip_constraints,
93 std::vector<SCIP_VAR*>* tmp_variables,
94 std::vector<double>* tmp_coefficients) {
95 CHECK(scip !=
nullptr);
96 CHECK(scip_cst !=
nullptr);
97 CHECK(scip_variables !=
nullptr);
98 CHECK(scip_constraints !=
nullptr);
99 CHECK(tmp_variables !=
nullptr);
100 CHECK(tmp_coefficients !=
nullptr);
101 CHECK(gen_cst.has_indicator_constraint());
102 constexpr double kInfinity = std::numeric_limits<double>::infinity();
104 const auto& ind = gen_cst.indicator_constraint();
105 if (!ind.has_constraint())
return absl::OkStatus();
107 const MPConstraintProto& constraint = ind.constraint();
108 const int size = constraint.var_index_size();
109 tmp_variables->resize(size,
nullptr);
110 tmp_coefficients->resize(size, 0);
111 for (
int i = 0;
i < size; ++
i) {
112 (*tmp_variables)[
i] = (*scip_variables)[constraint.var_index(i)];
113 (*tmp_coefficients)[
i] = constraint.coefficient(i);
116 SCIP_VAR* ind_var = (*scip_variables)[ind.var_index()];
117 if (ind.var_value() == 0) {
119 SCIPgetNegatedVar(scip, (*scip_variables)[ind.var_index()], &ind_var));
122 if (ind.constraint().upper_bound() <
kInfinity) {
124 ScipInfClamp(scip, ind.constraint().upper_bound()));
126 scip, scip_cst, gen_cst.name().c_str(), ind_var, size,
127 tmp_variables->data(), tmp_coefficients->data(), upper_bound,
128 !ind.constraint().is_lazy(),
135 ind.constraint().is_lazy(),
138 scip_constraints->push_back(
nullptr);
139 scip_cst = &scip_constraints->back();
141 if (ind.constraint().lower_bound() > -
kInfinity) {
143 ScipInfClamp(scip, ind.constraint().lower_bound()));
144 for (
int i = 0;
i < size; ++
i) {
145 (*tmp_coefficients)[
i] *= -1;
148 scip, scip_cst, gen_cst.name().c_str(), ind_var, size,
149 tmp_variables->data(), tmp_coefficients->data(), -lower_bound,
150 !ind.constraint().is_lazy(),
157 ind.constraint().is_lazy(),
162 return absl::OkStatus();
165absl::Status AddSosConstraint(
const MPGeneralConstraintProto& gen_cst,
166 const std::vector<SCIP_VAR*>& scip_variables,
167 SCIP* scip, SCIP_CONS** scip_cst,
168 std::vector<SCIP_VAR*>* tmp_variables,
169 std::vector<double>* tmp_weights) {
170 CHECK(scip !=
nullptr);
171 CHECK(scip_cst !=
nullptr);
172 CHECK(tmp_variables !=
nullptr);
173 CHECK(tmp_weights !=
nullptr);
175 CHECK(gen_cst.has_sos_constraint());
176 const MPSosConstraint& sos_cst = gen_cst.sos_constraint();
181 if (sos_cst.var_index_size() <= 1)
return absl::OkStatus();
182 if (sos_cst.type() == MPSosConstraint::SOS2 &&
183 sos_cst.var_index_size() <= 2) {
184 return absl::OkStatus();
187 tmp_variables->resize(sos_cst.var_index_size(),
nullptr);
188 for (
int v = 0; v < sos_cst.var_index_size(); ++v) {
189 (*tmp_variables)[v] = scip_variables[sos_cst.var_index(v)];
191 tmp_weights->resize(sos_cst.var_index_size(), 0);
192 if (sos_cst.weight_size() == sos_cst.var_index_size()) {
193 for (
int w = 0;
w < sos_cst.weight_size(); ++
w) {
194 (*tmp_weights)[
w] = sos_cst.weight(
w);
199 std::iota(tmp_weights->begin(), tmp_weights->end(), 1);
201 switch (sos_cst.type()) {
202 case MPSosConstraint::SOS1_DEFAULT:
204 SCIPcreateConsBasicSOS1(scip,
206 gen_cst.name().c_str(),
207 sos_cst.var_index_size(),
208 tmp_variables->data(),
209 tmp_weights->data()));
211 case MPSosConstraint::SOS2:
213 SCIPcreateConsBasicSOS2(scip,
215 gen_cst.name().c_str(),
216 sos_cst.var_index_size(),
217 tmp_variables->data(),
218 tmp_weights->data()));
222 return absl::OkStatus();
225absl::Status AddQuadraticConstraint(
226 const MPGeneralConstraintProto& gen_cst,
227 const std::vector<SCIP_VAR*>& scip_variables,
SCIP* scip,
228 SCIP_CONS** scip_cst, std::vector<SCIP_VAR*>* tmp_variables,
229 std::vector<double>* tmp_coefficients,
230 std::vector<SCIP_VAR*>* tmp_qvariables1,
231 std::vector<SCIP_VAR*>* tmp_qvariables2,
232 std::vector<double>* tmp_qcoefficients) {
233 CHECK(scip !=
nullptr);
234 CHECK(scip_cst !=
nullptr);
235 CHECK(tmp_variables !=
nullptr);
236 CHECK(tmp_coefficients !=
nullptr);
237 CHECK(tmp_qvariables1 !=
nullptr);
238 CHECK(tmp_qvariables2 !=
nullptr);
239 CHECK(tmp_qcoefficients !=
nullptr);
241 CHECK(gen_cst.has_quadratic_constraint());
242 const MPQuadraticConstraint& quad_cst = gen_cst.quadratic_constraint();
245 const int lsize = quad_cst.var_index_size();
246 CHECK_EQ(quad_cst.coefficient_size(), lsize);
247 tmp_variables->resize(lsize,
nullptr);
248 tmp_coefficients->resize(lsize, 0.0);
249 for (
int i = 0;
i < lsize; ++
i) {
250 (*tmp_variables)[
i] = scip_variables[quad_cst.var_index(i)];
251 (*tmp_coefficients)[
i] = quad_cst.coefficient(i);
255 const int qsize = quad_cst.qvar1_index_size();
256 CHECK_EQ(quad_cst.qvar2_index_size(), qsize);
257 CHECK_EQ(quad_cst.qcoefficient_size(), qsize);
258 tmp_qvariables1->resize(qsize,
nullptr);
259 tmp_qvariables2->resize(qsize,
nullptr);
260 tmp_qcoefficients->resize(qsize, 0.0);
261 for (
int i = 0;
i < qsize; ++
i) {
262 (*tmp_qvariables1)[
i] = scip_variables[quad_cst.qvar1_index(i)];
263 (*tmp_qvariables2)[
i] = scip_variables[quad_cst.qvar2_index(i)];
264 (*tmp_qcoefficients)[
i] = quad_cst.qcoefficient(i);
268 ScipInfClamp(scip, quad_cst.lower_bound()));
270 ScipInfClamp(scip, quad_cst.upper_bound()));
272 SCIPcreateConsBasicQuadratic(scip,
274 gen_cst.name().c_str(),
276 tmp_variables->data(),
277 tmp_coefficients->data(),
279 tmp_qvariables1->data(),
280 tmp_qvariables2->data(),
281 tmp_qcoefficients->data(),
285 return absl::OkStatus();
290absl::Status AddAbsConstraint(
const MPGeneralConstraintProto& gen_cst,
291 const std::vector<SCIP_VAR*>& scip_variables,
292 SCIP* scip, SCIP_CONS** scip_cst) {
293 CHECK(scip !=
nullptr);
294 CHECK(scip_cst !=
nullptr);
295 CHECK(gen_cst.has_abs_constraint());
296 const auto& abs = gen_cst.abs_constraint();
297 SCIP_VAR* scip_var = scip_variables[abs.var_index()];
298 SCIP_VAR* scip_resultant_var = scip_variables[abs.resultant_var_index()];
301 if (SCIPvarGetLbLocal(scip_resultant_var) < 0.0) {
305 std::vector<SCIP_VAR*> vars;
306 std::vector<double> vals;
307 std::vector<SCIP_CONS*> cons;
308 auto add_abs_constraint = [&](absl::string_view name_prefix) -> absl::Status {
309 SCIP_CONS* scip_cons =
nullptr;
310 CHECK(vars.size() == vals.size());
311 const std::string name =
312 gen_cst.has_name() ? absl::StrCat(gen_cst.name(), name_prefix) :
"";
315 name.c_str(), vars.size(), vars.data(),
316 vals.data(), 0.0, 0.0));
319 cons.push_back(scip_cons);
320 return absl::OkStatus();
324 vars = {scip_resultant_var, scip_var};
333 const std::string name =
334 gen_cst.has_name() ? absl::StrCat(gen_cst.name(),
"_disj") :
"";
336 scip, scip_cst, name.c_str(),
337 cons.size(), cons.data(),
nullptr));
340 return absl::OkStatus();
343absl::Status AddAndConstraint(
const MPGeneralConstraintProto& gen_cst,
344 const std::vector<SCIP_VAR*>& scip_variables,
345 SCIP* scip, SCIP_CONS** scip_cst,
346 std::vector<SCIP_VAR*>* tmp_variables) {
347 CHECK(scip !=
nullptr);
348 CHECK(scip_cst !=
nullptr);
349 CHECK(tmp_variables !=
nullptr);
350 CHECK(gen_cst.has_and_constraint());
351 const auto& andcst = gen_cst.and_constraint();
353 tmp_variables->resize(andcst.var_index_size(),
nullptr);
354 for (
int i = 0;
i < andcst.var_index_size(); ++
i) {
355 (*tmp_variables)[
i] = scip_variables[andcst.var_index(i)];
359 gen_cst.name().c_str(),
360 scip_variables[andcst.resultant_var_index()],
361 andcst.var_index_size(),
362 tmp_variables->data()));
364 return absl::OkStatus();
367absl::Status AddOrConstraint(
const MPGeneralConstraintProto& gen_cst,
368 const std::vector<SCIP_VAR*>& scip_variables,
369 SCIP* scip, SCIP_CONS** scip_cst,
370 std::vector<SCIP_VAR*>* tmp_variables) {
371 CHECK(scip !=
nullptr);
372 CHECK(scip_cst !=
nullptr);
373 CHECK(tmp_variables !=
nullptr);
374 CHECK(gen_cst.has_or_constraint());
375 const auto& orcst = gen_cst.or_constraint();
377 tmp_variables->resize(orcst.var_index_size(),
nullptr);
378 for (
int i = 0;
i < orcst.var_index_size(); ++
i) {
379 (*tmp_variables)[
i] = scip_variables[orcst.var_index(i)];
383 gen_cst.name().c_str(),
384 scip_variables[orcst.resultant_var_index()],
385 orcst.var_index_size(),
386 tmp_variables->data()));
388 return absl::OkStatus();
396absl::Status AddMinMaxConstraint(
const MPGeneralConstraintProto& gen_cst,
397 const std::vector<SCIP_VAR*>& scip_variables,
398 SCIP* scip, SCIP_CONS** scip_cst,
399 std::vector<SCIP_CONS*>* scip_constraints,
400 std::vector<SCIP_VAR*>* tmp_variables) {
401 CHECK(scip !=
nullptr);
402 CHECK(scip_cst !=
nullptr);
403 CHECK(tmp_variables !=
nullptr);
404 CHECK(gen_cst.has_min_constraint() || gen_cst.has_max_constraint());
405 constexpr double kInfinity = std::numeric_limits<double>::infinity();
407 const auto& minmax = gen_cst.has_min_constraint() ? gen_cst.min_constraint()
408 : gen_cst.max_constraint();
409 const absl::btree_set<int> unique_var_indices(minmax.var_index().begin(),
410 minmax.var_index().end());
411 SCIP_VAR* scip_resultant_var = scip_variables[minmax.resultant_var_index()];
413 std::vector<SCIP_VAR*> vars;
414 std::vector<double> vals;
415 std::vector<SCIP_CONS*> cons;
416 auto add_lin_constraint = [&](absl::string_view name_prefix,
417 double lower_bound = 0.0,
418 double upper_bound = 0.0) -> absl::Status {
419 SCIP_CONS* scip_cons =
nullptr;
420 CHECK(vars.size() == vals.size());
421 const std::string name =
422 gen_cst.has_name() ? absl::StrCat(gen_cst.name(), name_prefix) :
"";
424 ScipInfClamp(scip, lower_bound));
426 ScipInfClamp(scip, upper_bound));
429 name.c_str(), vars.size(), vars.data(),
430 vals.data(), scip_lower_bound,
434 cons.push_back(scip_cons);
435 return absl::OkStatus();
439 for (
const int var_index : unique_var_indices) {
440 vars = {scip_resultant_var, scip_variables[var_index]};
446 if (minmax.has_constant()) {
447 vars = {scip_resultant_var};
450 add_lin_constraint(
"_constant", minmax.constant(), minmax.constant()));
454 const std::string name =
455 gen_cst.has_name() ? absl::StrCat(gen_cst.name(),
"_disj") :
"";
457 scip, scip_cst, name.c_str(),
458 cons.size(), cons.data(),
nullptr));
463 for (
const int var_index : unique_var_indices) {
464 vars = {scip_resultant_var, scip_variables[var_index]};
466 if (gen_cst.has_min_constraint()) {
470 RETURN_IF_ERROR(add_lin_constraint(absl::StrCat(
"_ineq_", var_index), 0.0,
474 if (minmax.has_constant()) {
475 vars = {scip_resultant_var};
477 if (gen_cst.has_min_constraint()) {
479 add_lin_constraint(
"_ineq_constant", -
kInfinity, minmax.constant()));
482 add_lin_constraint(
"_ineq_constant", minmax.constant(),
kInfinity));
485 for (SCIP_CONS* scip_cons : cons) {
486 scip_constraints->push_back(scip_cons);
489 return absl::OkStatus();
492absl::Status AddQuadraticObjective(
const MPQuadraticObjective& quadobj,
494 std::vector<SCIP_VAR*>* scip_variables,
495 std::vector<SCIP_CONS*>* scip_constraints) {
496 CHECK(scip !=
nullptr);
497 CHECK(scip_variables !=
nullptr);
498 CHECK(scip_constraints !=
nullptr);
500 constexpr double kInfinity = std::numeric_limits<double>::infinity();
502 const int size = quadobj.coefficient_size();
503 if (size == 0)
return absl::OkStatus();
507 scip_variables->push_back(
nullptr);
512 SCIP_VARTYPE_CONTINUOUS));
515 scip_constraints->push_back(
nullptr);
516 SCIP_VAR* linvars[1] = {scip_variables->back()};
517 double lincoefs[1] = {-1};
518 std::vector<SCIP_VAR*> quadvars1(size,
nullptr);
519 std::vector<SCIP_VAR*> quadvars2(size,
nullptr);
520 std::vector<double> quadcoefs(size, 0);
521 for (
int i = 0;
i < size; ++
i) {
522 quadvars1[
i] = scip_variables->at(quadobj.qvar1_index(i));
523 quadvars2[
i] = scip_variables->at(quadobj.qvar2_index(i));
524 quadcoefs[
i] = quadobj.coefficient(i);
527 scip, &scip_constraints->back(),
"quadobj",
528 1, linvars, lincoefs,
529 size, quadvars1.data(),
530 quadvars2.data(), quadcoefs.data(),
534 return absl::OkStatus();
537absl::Status AddSolutionHint(
const MPModelProto& model,
SCIP* scip,
538 const std::vector<SCIP_VAR*>& scip_variables) {
539 CHECK(scip !=
nullptr);
540 if (!model.has_solution_hint())
return absl::OkStatus();
542 const PartialVariableAssignment& solution_hint = model.solution_hint();
544 bool is_solution_partial =
545 solution_hint.var_index_size() != model.variable_size();
546 if (is_solution_partial) {
548 SCIPcreatePartialSol(scip, &
solution,
nullptr));
551 SCIPcreateSol(scip, &
solution,
nullptr));
554 for (
int i = 0;
i < solution_hint.var_index_size(); ++
i) {
556 scip,
solution, scip_variables[solution_hint.var_index(i)],
557 solution_hint.var_value(i)));
563 return absl::OkStatus();
570 CHECK(scip !=
nullptr);
571 const double infinity = SCIPinfinity(scip);
573 for (
int v = 0; v < model.variable_size(); ++v) {
574 const MPVariableProto& variable = model.variable(v);
575 if (variable.lower_bound() >= infinity) {
576 return absl::StrFormat(
577 "Variable %i's lower bound is considered +infinity", v);
579 if (variable.upper_bound() <= -infinity) {
580 return absl::StrFormat(
581 "Variable %i's upper bound is considered -infinity", v);
583 const double coeff = variable.objective_coefficient();
584 if (coeff >= infinity || coeff <= -infinity) {
585 return absl::StrFormat(
586 "Variable %i's objective coefficient is considered infinite", v);
590 for (
int c = 0; c < model.constraint_size(); ++c) {
591 const MPConstraintProto& cst = model.constraint(c);
592 if (cst.lower_bound() >= infinity) {
593 return absl::StrFormat(
594 "Constraint %d's lower_bound is considered +infinity", c);
596 if (cst.upper_bound() <= -infinity) {
597 return absl::StrFormat(
598 "Constraint %d's upper_bound is considered -infinity", c);
600 for (
int i = 0; i < cst.coefficient_size(); ++i) {
601 if (std::abs(cst.coefficient(i)) >= infinity) {
602 return absl::StrFormat(
603 "Constraint %d's coefficient #%d is considered infinite", c, i);
608 for (
int c = 0; c < model.general_constraint_size(); ++c) {
609 const MPGeneralConstraintProto& cst = model.general_constraint(c);
610 switch (cst.general_constraint_case()) {
611 case MPGeneralConstraintProto::kQuadraticConstraint:
612 if (cst.quadratic_constraint().lower_bound() >= infinity) {
613 return absl::StrFormat(
614 "Quadratic constraint %d's lower_bound is considered +infinity",
617 if (cst.quadratic_constraint().upper_bound() <= -infinity) {
618 return absl::StrFormat(
619 "Quadratic constraint %d's upper_bound is considered -infinity",
622 for (
int i = 0; i < cst.quadratic_constraint().coefficient_size();
624 const double coefficient = cst.quadratic_constraint().coefficient(i);
625 if (coefficient >= infinity || coefficient <= -infinity) {
626 return absl::StrFormat(
627 "Quadratic constraint %d's linear coefficient #%d considered "
632 for (
int i = 0; i < cst.quadratic_constraint().qcoefficient_size();
634 const double qcoefficient =
635 cst.quadratic_constraint().qcoefficient(i);
636 if (qcoefficient >= infinity || qcoefficient <= -infinity) {
637 return absl::StrFormat(
638 "Quadratic constraint %d's quadratic coefficient #%d "
639 "considered infinite",
644 case MPGeneralConstraintProto::kMinConstraint:
645 if (cst.min_constraint().constant() >= infinity ||
646 cst.min_constraint().constant() <= -infinity) {
647 return absl::StrFormat(
648 "Min constraint %d's coefficient constant considered infinite",
652 case MPGeneralConstraintProto::kMaxConstraint:
653 if (cst.max_constraint().constant() >= infinity ||
654 cst.max_constraint().constant() <= -infinity) {
655 return absl::StrFormat(
656 "Max constraint %d's coefficient constant considered infinite",
665 const MPQuadraticObjective& quad_obj = model.quadratic_objective();
666 for (
int i = 0; i < quad_obj.coefficient_size(); ++i) {
667 if (std::abs(quad_obj.coefficient(i)) >= infinity) {
668 return absl::StrFormat(
669 "Quadratic objective term #%d's coefficient is considered infinite",
674 if (model.has_solution_hint()) {
675 for (
int i = 0; i < model.solution_hint().var_value_size(); ++i) {
676 const double value = model.solution_hint().var_value(i);
677 if (value >= infinity || value <= -infinity) {
678 return absl::StrFormat(
679 "Variable %i's solution hint is considered infinite",
680 model.solution_hint().var_index(i));
685 if (model.objective_offset() >= infinity ||
686 model.objective_offset() <= -infinity) {
687 return "Model's objective offset is considered infinite.";
695 MPSolutionResponse response;
696 const std::optional<LazyMutableCopy<MPModelProto>> optional_model =
698 if (!optional_model)
return response;
699 const MPModelProto& model = **optional_model;
701 SCIP* scip =
nullptr;
702 std::vector<SCIP_VAR*> scip_variables(model.variable_size(),
nullptr);
703 std::vector<SCIP_CONS*> scip_constraints(
704 model.constraint_size() + model.general_constraint_size(),
nullptr);
706 auto delete_scip_objects = [&]() -> absl::Status {
708 if (scip ==
nullptr)
return absl::OkStatus();
709 for (SCIP_VAR* variable : scip_variables) {
710 if (variable !=
nullptr) {
714 for (SCIP_CONS* constraint : scip_constraints) {
715 if (constraint !=
nullptr) {
720 return absl::OkStatus();
723 auto scip_deleter = absl::MakeCleanup([delete_scip_objects]() {
724 const absl::Status deleter_status = delete_scip_objects();
725 LOG_IF(DFATAL, !deleter_status.ok()) << deleter_status;
730 const std::string scip_model_invalid_error =
732 if (!scip_model_invalid_error.empty()) {
733 response.set_status(MPSOLVER_MODEL_INVALID);
734 response.set_status_str(scip_model_invalid_error);
739 request->solver_specific_parameters(), scip);
740 if (!parameters_status.ok()) {
741 response.set_status(MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS);
742 response.set_status_str(
743 std::string(parameters_status.message()));
753 SCIPsetIntParam(scip,
"timing/clocktype", SCIP_CLOCKTYPE_WALL));
754 if (request->solver_time_limit_seconds() > 0 &&
755 request->solver_time_limit_seconds() < 1e20) {
757 scip,
"limits/time", request->solver_time_limit_seconds()));
759 SCIPsetMessagehdlrQuiet(scip, !request->enable_internal_solver_output());
762 if (model.maximize()) {
766 for (
int v = 0; v < model.variable_size(); ++v) {
767 const MPVariableProto& variable = model.variable(v);
769 ScipInfClamp(scip, variable.lower_bound()));
771 ScipInfClamp(scip, variable.upper_bound()));
773 scip, &scip_variables[v], variable.name().c_str(),
774 lower_bound, upper_bound,
775 variable.objective_coefficient(),
776 variable.is_integer() ? SCIP_VARTYPE_INTEGER
777 : SCIP_VARTYPE_CONTINUOUS));
782 std::vector<SCIP_VAR*> ct_variables;
783 std::vector<double> ct_coefficients;
784 for (
int c = 0; c < model.constraint_size(); ++c) {
785 const MPConstraintProto& constraint = model.constraint(c);
786 const int size = constraint.var_index_size();
787 ct_variables.resize(size,
nullptr);
788 ct_coefficients.resize(size, 0);
789 for (
int i = 0; i < size; ++i) {
790 ct_variables[i] = scip_variables[constraint.var_index(i)];
791 ct_coefficients[i] = constraint.coefficient(i);
794 ScipInfClamp(scip, constraint.lower_bound()));
796 ScipInfClamp(scip, constraint.upper_bound()));
798 scip, &scip_constraints[c],
799 constraint.name().c_str(),
800 constraint.var_index_size(), ct_variables.data(),
801 ct_coefficients.data(),
802 lower_bound, upper_bound,
803 !constraint.is_lazy(),
811 constraint.is_lazy(),
817 std::vector<SCIP_VAR*> ct_qvariables1;
818 std::vector<SCIP_VAR*> ct_qvariables2;
819 std::vector<double> ct_qcoefficients;
820 const int lincst_size = model.constraint_size();
821 for (
int c = 0; c < model.general_constraint_size(); ++c) {
822 const MPGeneralConstraintProto& gen_cst = model.general_constraint(c);
823 switch (gen_cst.general_constraint_case()) {
824 case MPGeneralConstraintProto::kIndicatorConstraint: {
826 gen_cst, scip, &scip_constraints[lincst_size + c],
827 &scip_variables, &scip_constraints, &ct_variables,
831 case MPGeneralConstraintProto::kSosConstraint: {
833 &scip_constraints[lincst_size + c],
834 &ct_variables, &ct_coefficients));
837 case MPGeneralConstraintProto::kQuadraticConstraint: {
839 gen_cst, scip_variables, scip, &scip_constraints[lincst_size + c],
840 &ct_variables, &ct_coefficients, &ct_qvariables1, &ct_qvariables2,
844 case MPGeneralConstraintProto::kAbsConstraint: {
846 &scip_constraints[lincst_size + c]));
849 case MPGeneralConstraintProto::kAndConstraint: {
851 &scip_constraints[lincst_size + c],
855 case MPGeneralConstraintProto::kOrConstraint: {
857 &scip_constraints[lincst_size + c],
861 case MPGeneralConstraintProto::kMinConstraint:
862 case MPGeneralConstraintProto::kMaxConstraint: {
864 gen_cst, scip_variables, scip, &scip_constraints[lincst_size + c],
865 &scip_constraints, &ct_variables));
869 return absl::UnimplementedError(
870 absl::StrFormat(
"General constraints of type %i not supported.",
871 gen_cst.general_constraint_case()));
876 if (model.has_quadratic_objective()) {
877 RETURN_IF_ERROR(AddQuadraticObjective(model.quadratic_objective(), scip,
878 &scip_variables, &scip_constraints));
883 if (!absl::GetFlag(FLAGS_scip_proto_solver_output_cip_file).empty()) {
884 SCIPwriteOrigProblem(
885 scip, absl::GetFlag(FLAGS_scip_proto_solver_output_cip_file).c_str(),
888 const absl::Time time_before = absl::Now();
894 const absl::Duration solving_duration = absl::Now() - time_before;
896 VLOG(1) <<
"Finished solving in ScipSolveProto(), walltime = "
897 << solving_duration <<
", usertime = " << user_timer.
GetDuration();
899 response.mutable_solve_info()->set_solve_wall_time_seconds(
900 absl::ToDoubleSeconds(solving_duration));
901 response.mutable_solve_info()->set_solve_user_time_seconds(
904 const int solution_count =
905 std::min(SCIPgetNSols(scip),
906 std::min(request->populate_additional_solutions_up_to(),
907 std::numeric_limits<int32_t>::max() - 1) +
909 if (solution_count > 0) {
912 auto scip_solution_to_repeated_field = [&](SCIP_SOL* scip_solution) {
913 google::protobuf::RepeatedField<double> variable_value;
914 variable_value.Reserve(model.variable_size());
915 for (
int v = 0; v < model.variable_size(); ++v) {
916 double value = SCIPgetSolVal(scip, scip_solution, scip_variables[v]);
917 if (model.variable(v).is_integer()) {
918 value = std::round(value);
920 variable_value.AddAlreadyReserved(value);
922 return variable_value;
928 SCIP_SOL**
const scip_solutions = SCIPgetSols(scip);
929 response.set_objective_value(SCIPgetSolOrigObj(scip, scip_solutions[0]));
930 response.set_best_objective_bound(SCIPgetDualbound(scip));
931 *response.mutable_variable_value() =
932 scip_solution_to_repeated_field(scip_solutions[0]);
933 for (
int i = 1; i < solution_count; ++i) {
934 MPSolution*
solution = response.add_additional_solutions();
935 solution->set_objective_value(SCIPgetSolOrigObj(scip, scip_solutions[i]));
936 *
solution->mutable_variable_value() =
937 scip_solution_to_repeated_field(scip_solutions[i]);
941 const SCIP_STATUS scip_status = SCIPgetStatus(scip);
942 switch (scip_status) {
943 case SCIP_STATUS_OPTIMAL:
944 response.set_status(MPSOLVER_OPTIMAL);
946 case SCIP_STATUS_GAPLIMIT:
948 response.set_status(MPSOLVER_OPTIMAL);
950 case SCIP_STATUS_INFORUNBD:
957 DLOG(INFO) <<
"SCIP solve returned SCIP_STATUS_INFORUNBD, which we treat "
958 "as INFEASIBLE even though it may mean UNBOUNDED.";
959 response.set_status_str(
960 "The model may actually be unbounded: SCIP returned "
961 "SCIP_STATUS_INFORUNBD");
962 ABSL_FALLTHROUGH_INTENDED;
963 case SCIP_STATUS_INFEASIBLE:
964 response.set_status(MPSOLVER_INFEASIBLE);
966 case SCIP_STATUS_UNBOUNDED:
967 response.set_status(MPSOLVER_UNBOUNDED);
970 if (solution_count > 0) {
971 response.set_status(MPSOLVER_FEASIBLE);
973 response.set_status(MPSOLVER_NOT_SOLVED);
974 response.set_status_str(absl::StrFormat(
"SCIP status code %d",
975 static_cast<int>(scip_status)));
980 VLOG(1) <<
"ScipSolveProto() status="
981 << MPSolverResponseStatus_Name(response.status()) <<
".";
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
absl::Duration GetDuration() const
void Start()
When Start() is called multiple times, only the most recent is used.
In SWIG mode, we don't want anything besides these top-level includes.
static constexpr double kInfinity
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)
std::string FindErrorInMPModelForScip(const MPModelProto &model, SCIP *scip)
std::optional< LazyMutableCopy< MPModelProto > > GetMPModelOrPopulateResponse(LazyMutableCopy< MPModelRequest > &request, MPSolutionResponse *response)
absl::Status LegacyScipSetSolverSpecificParameters(absl::string_view parameters, SCIP *scip)
trees with all degrees equal w the current value of w
#define RETURN_IF_SCIP_ERROR(x)
ABSL_FLAG(std::string, scip_proto_solver_output_cip_file, "", "If given, saves the generated CIP file here. Useful for " "reporting bugs to SCIP.")