28#include "absl/cleanup/cleanup.h"
29#include "absl/container/btree_set.h"
30#include "absl/status/status.h"
31#include "absl/status/statusor.h"
32#include "absl/strings/ascii.h"
33#include "absl/strings/numbers.h"
34#include "absl/strings/str_cat.h"
35#include "absl/strings/str_format.h"
36#include "absl/strings/str_split.h"
37#include "absl/strings/string_view.h"
38#include "absl/time/time.h"
44#include "ortools/linear_solver/linear_solver.pb.h"
48#include "scip/cons_disjunction.h"
49#include "scip/cons_linear.h"
50#include "scip/cons_quadratic.h"
51#include "scip/pub_var.h"
53#include "scip/scip_numerics.h"
54#include "scip/scip_param.h"
55#include "scip/scip_prob.h"
56#include "scip/scip_var.h"
57#include "scip/scipdefplugins.h"
59#include "scip/struct_paramset.h"
60#include "scip/type_cons.h"
61#include "scip/type_paramset.h"
62#include "scip/type_var.h"
64ABSL_FLAG(std::string, scip_proto_solver_output_cip_file,
"",
65 "If given, saves the generated CIP file here. Useful for "
66 "reporting bugs to SCIP.");
71absl::StatusOr<double> ScipInfClamp(
SCIP* scip,
const double d) {
73 return absl::InvalidArgumentError(
"Cannot clamp a NaN.");
75 const double kScipInf = SCIPinfinity(scip);
76 const double clamped = std::clamp(d, -kScipInf, kScipInf);
78 VLOG_EVERY_N_SEC(1, 5)
79 <<
"A bound was clamped to SCIP's 'infinite' value for safety; this "
80 "may affect results. Was: "
81 << d <<
", is now: " << clamped;
88absl::Status AddIndicatorConstraint(
const MPGeneralConstraintProto& gen_cst,
89 SCIP* scip, SCIP_CONS** scip_cst,
90 std::vector<SCIP_VAR*>* scip_variables,
91 std::vector<SCIP_CONS*>* scip_constraints,
92 std::vector<SCIP_VAR*>* tmp_variables,
93 std::vector<double>* tmp_coefficients) {
94 CHECK(scip !=
nullptr);
95 CHECK(scip_cst !=
nullptr);
96 CHECK(scip_variables !=
nullptr);
97 CHECK(scip_constraints !=
nullptr);
98 CHECK(tmp_variables !=
nullptr);
99 CHECK(tmp_coefficients !=
nullptr);
100 CHECK(gen_cst.has_indicator_constraint());
101 constexpr double kInfinity = std::numeric_limits<double>::infinity();
103 const auto& ind = gen_cst.indicator_constraint();
104 if (!ind.has_constraint())
return absl::OkStatus();
106 const MPConstraintProto& constraint = ind.constraint();
107 const int size = constraint.var_index_size();
108 tmp_variables->resize(
size,
nullptr);
109 tmp_coefficients->resize(
size, 0);
110 for (
int i = 0;
i <
size; ++
i) {
111 (*tmp_variables)[
i] = (*scip_variables)[constraint.var_index(i)];
112 (*tmp_coefficients)[
i] = constraint.coefficient(i);
115 SCIP_VAR* ind_var = (*scip_variables)[ind.var_index()];
116 if (ind.var_value() == 0) {
118 SCIPgetNegatedVar(scip, (*scip_variables)[ind.var_index()], &ind_var));
121 if (ind.constraint().upper_bound() < kInfinity) {
123 ScipInfClamp(scip, ind.constraint().upper_bound()));
125 scip, scip_cst, gen_cst.name().c_str(), ind_var,
size,
126 tmp_variables->data(), tmp_coefficients->data(),
upper_bound,
127 !ind.constraint().is_lazy(),
134 ind.constraint().is_lazy(),
137 scip_constraints->push_back(
nullptr);
138 scip_cst = &scip_constraints->back();
140 if (ind.constraint().lower_bound() > -kInfinity) {
142 ScipInfClamp(scip, ind.constraint().lower_bound()));
143 for (
int i = 0;
i <
size; ++
i) {
144 (*tmp_coefficients)[
i] *= -1;
147 scip, scip_cst, gen_cst.name().c_str(), ind_var,
size,
148 tmp_variables->data(), tmp_coefficients->data(), -
lower_bound,
149 !ind.constraint().is_lazy(),
156 ind.constraint().is_lazy(),
161 return absl::OkStatus();
164absl::Status AddSosConstraint(
const MPGeneralConstraintProto& gen_cst,
165 const std::vector<SCIP_VAR*>& scip_variables,
166 SCIP* scip, SCIP_CONS** scip_cst,
167 std::vector<SCIP_VAR*>* tmp_variables,
168 std::vector<double>* tmp_weights) {
169 CHECK(scip !=
nullptr);
170 CHECK(scip_cst !=
nullptr);
171 CHECK(tmp_variables !=
nullptr);
172 CHECK(tmp_weights !=
nullptr);
174 CHECK(gen_cst.has_sos_constraint());
175 const MPSosConstraint& sos_cst = gen_cst.sos_constraint();
180 if (sos_cst.var_index_size() <= 1)
return absl::OkStatus();
181 if (sos_cst.type() == MPSosConstraint::SOS2 &&
182 sos_cst.var_index_size() <= 2) {
183 return absl::OkStatus();
186 tmp_variables->resize(sos_cst.var_index_size(),
nullptr);
187 for (
int v = 0; v < sos_cst.var_index_size(); ++v) {
188 (*tmp_variables)[v] = scip_variables[sos_cst.var_index(v)];
190 tmp_weights->resize(sos_cst.var_index_size(), 0);
191 if (sos_cst.weight_size() == sos_cst.var_index_size()) {
192 for (
int w = 0;
w < sos_cst.weight_size(); ++
w) {
193 (*tmp_weights)[
w] = sos_cst.weight(
w);
198 std::iota(tmp_weights->begin(), tmp_weights->end(), 1);
200 switch (sos_cst.type()) {
201 case MPSosConstraint::SOS1_DEFAULT:
203 SCIPcreateConsBasicSOS1(scip,
205 gen_cst.name().c_str(),
206 sos_cst.var_index_size(),
207 tmp_variables->data(),
208 tmp_weights->data()));
210 case MPSosConstraint::SOS2:
212 SCIPcreateConsBasicSOS2(scip,
214 gen_cst.name().c_str(),
215 sos_cst.var_index_size(),
216 tmp_variables->data(),
217 tmp_weights->data()));
221 return absl::OkStatus();
224absl::Status AddQuadraticConstraint(
225 const MPGeneralConstraintProto& gen_cst,
226 const std::vector<SCIP_VAR*>& scip_variables,
SCIP* scip,
227 SCIP_CONS** scip_cst, std::vector<SCIP_VAR*>* tmp_variables,
228 std::vector<double>* tmp_coefficients,
229 std::vector<SCIP_VAR*>* tmp_qvariables1,
230 std::vector<SCIP_VAR*>* tmp_qvariables2,
231 std::vector<double>* tmp_qcoefficients) {
232 CHECK(scip !=
nullptr);
233 CHECK(scip_cst !=
nullptr);
234 CHECK(tmp_variables !=
nullptr);
235 CHECK(tmp_coefficients !=
nullptr);
236 CHECK(tmp_qvariables1 !=
nullptr);
237 CHECK(tmp_qvariables2 !=
nullptr);
238 CHECK(tmp_qcoefficients !=
nullptr);
240 CHECK(gen_cst.has_quadratic_constraint());
241 const MPQuadraticConstraint& quad_cst = gen_cst.quadratic_constraint();
244 const int lsize = quad_cst.var_index_size();
245 CHECK_EQ(quad_cst.coefficient_size(), lsize);
246 tmp_variables->resize(lsize,
nullptr);
247 tmp_coefficients->resize(lsize, 0.0);
248 for (
int i = 0;
i < lsize; ++
i) {
249 (*tmp_variables)[
i] = scip_variables[quad_cst.var_index(i)];
250 (*tmp_coefficients)[
i] = quad_cst.coefficient(i);
254 const int qsize = quad_cst.qvar1_index_size();
255 CHECK_EQ(quad_cst.qvar2_index_size(), qsize);
256 CHECK_EQ(quad_cst.qcoefficient_size(), qsize);
257 tmp_qvariables1->resize(qsize,
nullptr);
258 tmp_qvariables2->resize(qsize,
nullptr);
259 tmp_qcoefficients->resize(qsize, 0.0);
260 for (
int i = 0;
i < qsize; ++
i) {
261 (*tmp_qvariables1)[
i] = scip_variables[quad_cst.qvar1_index(i)];
262 (*tmp_qvariables2)[
i] = scip_variables[quad_cst.qvar2_index(i)];
263 (*tmp_qcoefficients)[
i] = quad_cst.qcoefficient(i);
267 ScipInfClamp(scip, quad_cst.lower_bound()));
269 ScipInfClamp(scip, quad_cst.upper_bound()));
271 SCIPcreateConsBasicQuadratic(scip,
273 gen_cst.name().c_str(),
275 tmp_variables->data(),
276 tmp_coefficients->data(),
278 tmp_qvariables1->data(),
279 tmp_qvariables2->data(),
280 tmp_qcoefficients->data(),
284 return absl::OkStatus();
289absl::Status AddAbsConstraint(
const MPGeneralConstraintProto& gen_cst,
290 const std::vector<SCIP_VAR*>& scip_variables,
291 SCIP* scip, SCIP_CONS** scip_cst) {
292 CHECK(scip !=
nullptr);
293 CHECK(scip_cst !=
nullptr);
294 CHECK(gen_cst.has_abs_constraint());
295 const auto& abs = gen_cst.abs_constraint();
296 SCIP_VAR* scip_var = scip_variables[abs.var_index()];
297 SCIP_VAR* scip_resultant_var = scip_variables[abs.resultant_var_index()];
300 if (SCIPvarGetLbLocal(scip_resultant_var) < 0.0) {
304 std::vector<SCIP_VAR*> vars;
305 std::vector<double> vals;
306 std::vector<SCIP_CONS*> cons;
307 auto add_abs_constraint = [&](absl::string_view name_prefix) -> absl::Status {
308 SCIP_CONS* scip_cons =
nullptr;
309 CHECK(vars.size() == vals.size());
310 const std::string
name =
311 gen_cst.has_name() ? absl::StrCat(gen_cst.name(), name_prefix) :
"";
314 name.c_str(), vars.size(), vars.data(),
315 vals.data(), 0.0, 0.0));
318 cons.push_back(scip_cons);
319 return absl::OkStatus();
323 vars = {scip_resultant_var, scip_var};
332 const std::string
name =
333 gen_cst.has_name() ? absl::StrCat(gen_cst.name(),
"_disj") :
"";
335 scip, scip_cst,
name.c_str(),
336 cons.size(), cons.data(),
nullptr));
339 return absl::OkStatus();
342absl::Status AddAndConstraint(
const MPGeneralConstraintProto& gen_cst,
343 const std::vector<SCIP_VAR*>& scip_variables,
344 SCIP* scip, SCIP_CONS** scip_cst,
345 std::vector<SCIP_VAR*>* tmp_variables) {
346 CHECK(scip !=
nullptr);
347 CHECK(scip_cst !=
nullptr);
348 CHECK(tmp_variables !=
nullptr);
349 CHECK(gen_cst.has_and_constraint());
350 const auto& andcst = gen_cst.and_constraint();
352 tmp_variables->resize(andcst.var_index_size(),
nullptr);
353 for (
int i = 0;
i < andcst.var_index_size(); ++
i) {
354 (*tmp_variables)[
i] = scip_variables[andcst.var_index(i)];
358 gen_cst.name().c_str(),
359 scip_variables[andcst.resultant_var_index()],
360 andcst.var_index_size(),
361 tmp_variables->data()));
363 return absl::OkStatus();
366absl::Status AddOrConstraint(
const MPGeneralConstraintProto& gen_cst,
367 const std::vector<SCIP_VAR*>& scip_variables,
368 SCIP* scip, SCIP_CONS** scip_cst,
369 std::vector<SCIP_VAR*>* tmp_variables) {
370 CHECK(scip !=
nullptr);
371 CHECK(scip_cst !=
nullptr);
372 CHECK(tmp_variables !=
nullptr);
373 CHECK(gen_cst.has_or_constraint());
374 const auto& orcst = gen_cst.or_constraint();
376 tmp_variables->resize(orcst.var_index_size(),
nullptr);
377 for (
int i = 0;
i < orcst.var_index_size(); ++
i) {
378 (*tmp_variables)[
i] = scip_variables[orcst.var_index(i)];
382 gen_cst.name().c_str(),
383 scip_variables[orcst.resultant_var_index()],
384 orcst.var_index_size(),
385 tmp_variables->data()));
387 return absl::OkStatus();
395absl::Status AddMinMaxConstraint(
const MPGeneralConstraintProto& gen_cst,
396 const std::vector<SCIP_VAR*>& scip_variables,
397 SCIP* scip, SCIP_CONS** scip_cst,
398 std::vector<SCIP_CONS*>* scip_constraints,
399 std::vector<SCIP_VAR*>* tmp_variables) {
400 CHECK(scip !=
nullptr);
401 CHECK(scip_cst !=
nullptr);
402 CHECK(tmp_variables !=
nullptr);
403 CHECK(gen_cst.has_min_constraint() || gen_cst.has_max_constraint());
404 constexpr double kInfinity = std::numeric_limits<double>::infinity();
406 const auto& minmax = gen_cst.has_min_constraint() ? gen_cst.min_constraint()
407 : gen_cst.max_constraint();
408 const absl::btree_set<int> unique_var_indices(minmax.var_index().begin(),
409 minmax.var_index().end());
410 SCIP_VAR* scip_resultant_var = scip_variables[minmax.resultant_var_index()];
412 std::vector<SCIP_VAR*> vars;
413 std::vector<double> vals;
414 std::vector<SCIP_CONS*> cons;
415 auto add_lin_constraint = [&](absl::string_view name_prefix,
418 SCIP_CONS* scip_cons =
nullptr;
419 CHECK(vars.size() == vals.size());
420 const std::string
name =
421 gen_cst.has_name() ? absl::StrCat(gen_cst.name(), name_prefix) :
"";
428 name.c_str(), vars.size(), vars.data(),
429 vals.data(), scip_lower_bound,
433 cons.push_back(scip_cons);
434 return absl::OkStatus();
438 for (
const int var_index : unique_var_indices) {
439 vars = {scip_resultant_var, scip_variables[
var_index]};
445 if (minmax.has_constant()) {
446 vars = {scip_resultant_var};
449 add_lin_constraint(
"_constant", minmax.constant(), minmax.constant()));
453 const std::string
name =
454 gen_cst.has_name() ? absl::StrCat(gen_cst.name(),
"_disj") :
"";
456 scip, scip_cst,
name.c_str(),
457 cons.size(), cons.data(),
nullptr));
462 for (
const int var_index : unique_var_indices) {
463 vars = {scip_resultant_var, scip_variables[
var_index]};
465 if (gen_cst.has_min_constraint()) {
473 if (minmax.has_constant()) {
474 vars = {scip_resultant_var};
476 if (gen_cst.has_min_constraint()) {
478 add_lin_constraint(
"_ineq_constant", -kInfinity, minmax.constant()));
481 add_lin_constraint(
"_ineq_constant", minmax.constant(), kInfinity));
484 for (SCIP_CONS* scip_cons : cons) {
485 scip_constraints->push_back(scip_cons);
488 return absl::OkStatus();
491absl::Status AddQuadraticObjective(
const MPQuadraticObjective& quadobj,
493 std::vector<SCIP_VAR*>* scip_variables,
494 std::vector<SCIP_CONS*>* scip_constraints) {
495 CHECK(scip !=
nullptr);
496 CHECK(scip_variables !=
nullptr);
497 CHECK(scip_constraints !=
nullptr);
499 constexpr double kInfinity = std::numeric_limits<double>::infinity();
501 const int size = quadobj.coefficient_size();
502 if (
size == 0)
return absl::OkStatus();
506 scip_variables->push_back(
nullptr);
509 -kInfinity, kInfinity,
511 SCIP_VARTYPE_CONTINUOUS));
514 scip_constraints->push_back(
nullptr);
515 SCIP_VAR* linvars[1] = {scip_variables->back()};
516 double lincoefs[1] = {-1};
517 std::vector<SCIP_VAR*> quadvars1(
size,
nullptr);
518 std::vector<SCIP_VAR*> quadvars2(
size,
nullptr);
519 std::vector<double> quadcoefs(
size, 0);
520 for (
int i = 0;
i <
size; ++
i) {
521 quadvars1[
i] = scip_variables->at(quadobj.qvar1_index(i));
522 quadvars2[
i] = scip_variables->at(quadobj.qvar2_index(i));
523 quadcoefs[
i] = quadobj.coefficient(i);
526 scip, &scip_constraints->back(),
"quadobj",
527 1, linvars, lincoefs,
528 size, quadvars1.data(),
529 quadvars2.data(), quadcoefs.data(),
533 return absl::OkStatus();
536absl::Status AddSolutionHint(
const MPModelProto&
model,
SCIP* scip,
537 const std::vector<SCIP_VAR*>& scip_variables) {
538 CHECK(scip !=
nullptr);
539 if (!
model.has_solution_hint())
return absl::OkStatus();
541 const PartialVariableAssignment& solution_hint =
model.solution_hint();
543 bool is_solution_partial =
544 solution_hint.var_index_size() !=
model.variable_size();
545 if (is_solution_partial) {
547 SCIPcreatePartialSol(scip, &
solution,
nullptr));
550 SCIPcreateSol(scip, &
solution,
nullptr));
553 for (
int i = 0;
i < solution_hint.var_index_size(); ++
i) {
555 scip,
solution, scip_variables[solution_hint.var_index(i)],
556 solution_hint.var_value(i)));
562 return absl::OkStatus();
569 CHECK(scip !=
nullptr);
570 const double infinity = SCIPinfinity(scip);
572 for (
int v = 0; v <
model.variable_size(); ++v) {
573 const MPVariableProto& variable =
model.variable(v);
574 if (variable.lower_bound() >= infinity) {
575 return absl::StrFormat(
576 "Variable %i's lower bound is considered +infinity", v);
578 if (variable.upper_bound() <= -infinity) {
579 return absl::StrFormat(
580 "Variable %i's upper bound is considered -infinity", v);
582 const double coeff = variable.objective_coefficient();
583 if (coeff >= infinity || coeff <= -infinity) {
584 return absl::StrFormat(
585 "Variable %i's objective coefficient is considered infinite", v);
589 for (
int c = 0; c <
model.constraint_size(); ++c) {
590 const MPConstraintProto& cst =
model.constraint(c);
591 if (cst.lower_bound() >= infinity) {
592 return absl::StrFormat(
593 "Constraint %d's lower_bound is considered +infinity", c);
595 if (cst.upper_bound() <= -infinity) {
596 return absl::StrFormat(
597 "Constraint %d's upper_bound is considered -infinity", c);
599 for (
int i = 0; i < cst.coefficient_size(); ++i) {
600 if (std::abs(cst.coefficient(i)) >= infinity) {
601 return absl::StrFormat(
602 "Constraint %d's coefficient #%d is considered infinite", c, i);
607 for (
int c = 0; c <
model.general_constraint_size(); ++c) {
608 const MPGeneralConstraintProto& cst =
model.general_constraint(c);
609 switch (cst.general_constraint_case()) {
610 case MPGeneralConstraintProto::kQuadraticConstraint:
611 if (cst.quadratic_constraint().lower_bound() >= infinity) {
612 return absl::StrFormat(
613 "Quadratic constraint %d's lower_bound is considered +infinity",
616 if (cst.quadratic_constraint().upper_bound() <= -infinity) {
617 return absl::StrFormat(
618 "Quadratic constraint %d's upper_bound is considered -infinity",
621 for (
int i = 0; i < cst.quadratic_constraint().coefficient_size();
623 const double coefficient = cst.quadratic_constraint().coefficient(i);
625 return absl::StrFormat(
626 "Quadratic constraint %d's linear coefficient #%d considered "
631 for (
int i = 0; i < cst.quadratic_constraint().qcoefficient_size();
633 const double qcoefficient =
634 cst.quadratic_constraint().qcoefficient(i);
635 if (qcoefficient >= infinity || qcoefficient <= -infinity) {
636 return absl::StrFormat(
637 "Quadratic constraint %d's quadratic coefficient #%d "
638 "considered infinite",
643 case MPGeneralConstraintProto::kMinConstraint:
644 if (cst.min_constraint().constant() >= infinity ||
645 cst.min_constraint().constant() <= -infinity) {
646 return absl::StrFormat(
647 "Min constraint %d's coefficient constant considered infinite",
651 case MPGeneralConstraintProto::kMaxConstraint:
652 if (cst.max_constraint().constant() >= infinity ||
653 cst.max_constraint().constant() <= -infinity) {
654 return absl::StrFormat(
655 "Max constraint %d's coefficient constant considered infinite",
664 const MPQuadraticObjective& quad_obj =
model.quadratic_objective();
665 for (
int i = 0; i < quad_obj.coefficient_size(); ++i) {
666 if (std::abs(quad_obj.coefficient(i)) >= infinity) {
667 return absl::StrFormat(
668 "Quadratic objective term #%d's coefficient is considered infinite",
673 if (
model.has_solution_hint()) {
674 for (
int i = 0; i <
model.solution_hint().var_value_size(); ++i) {
675 const double value =
model.solution_hint().var_value(i);
676 if (
value >= infinity ||
value <= -infinity) {
677 return absl::StrFormat(
678 "Variable %i's solution hint is considered infinite",
679 model.solution_hint().var_index(i));
684 if (
model.objective_offset() >= infinity ||
685 model.objective_offset() <= -infinity) {
686 return "Model's objective offset is considered infinite.";
694 MPSolutionResponse response;
695 const absl::optional<LazyMutableCopy<MPModelProto>> optional_model =
697 if (!optional_model)
return response;
698 const MPModelProto&
model = **optional_model;
700 SCIP* scip =
nullptr;
701 std::vector<SCIP_VAR*> scip_variables(
model.variable_size(),
nullptr);
702 std::vector<SCIP_CONS*> scip_constraints(
703 model.constraint_size() +
model.general_constraint_size(),
nullptr);
705 auto delete_scip_objects = [&]() -> absl::Status {
707 if (scip ==
nullptr)
return absl::OkStatus();
708 for (SCIP_VAR* variable : scip_variables) {
709 if (variable !=
nullptr) {
713 for (SCIP_CONS* constraint : scip_constraints) {
714 if (constraint !=
nullptr) {
719 return absl::OkStatus();
722 auto scip_deleter = absl::MakeCleanup([delete_scip_objects]() {
723 const absl::Status deleter_status = delete_scip_objects();
724 LOG_IF(DFATAL, !deleter_status.ok()) << deleter_status;
729 const std::string scip_model_invalid_error =
731 if (!scip_model_invalid_error.empty()) {
732 response.set_status(MPSOLVER_MODEL_INVALID);
733 response.set_status_str(scip_model_invalid_error);
738 request->solver_specific_parameters(), scip);
739 if (!parameters_status.ok()) {
740 response.set_status(MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS);
741 response.set_status_str(
742 std::string(parameters_status.message()));
752 SCIPsetIntParam(scip,
"timing/clocktype", SCIP_CLOCKTYPE_WALL));
753 if (request->solver_time_limit_seconds() > 0 &&
754 request->solver_time_limit_seconds() < 1e20) {
756 scip,
"limits/time", request->solver_time_limit_seconds()));
758 SCIPsetMessagehdlrQuiet(scip, !request->enable_internal_solver_output());
761 if (
model.maximize()) {
765 for (
int v = 0; v <
model.variable_size(); ++v) {
766 const MPVariableProto& variable =
model.variable(v);
768 ScipInfClamp(scip, variable.lower_bound()));
770 ScipInfClamp(scip, variable.upper_bound()));
772 scip, &scip_variables[v], variable.name().c_str(),
774 variable.objective_coefficient(),
775 variable.is_integer() ? SCIP_VARTYPE_INTEGER
776 : SCIP_VARTYPE_CONTINUOUS));
781 std::vector<SCIP_VAR*> ct_variables;
782 std::vector<double> ct_coefficients;
783 for (
int c = 0; c <
model.constraint_size(); ++c) {
784 const MPConstraintProto& constraint =
model.constraint(c);
785 const int size = constraint.var_index_size();
786 ct_variables.resize(
size,
nullptr);
787 ct_coefficients.resize(
size, 0);
788 for (
int i = 0; i <
size; ++i) {
789 ct_variables[i] = scip_variables[constraint.var_index(i)];
790 ct_coefficients[i] = constraint.coefficient(i);
793 ScipInfClamp(scip, constraint.lower_bound()));
795 ScipInfClamp(scip, constraint.upper_bound()));
797 scip, &scip_constraints[c],
798 constraint.name().c_str(),
799 constraint.var_index_size(), ct_variables.data(),
800 ct_coefficients.data(),
802 !constraint.is_lazy(),
810 constraint.is_lazy(),
816 std::vector<SCIP_VAR*> ct_qvariables1;
817 std::vector<SCIP_VAR*> ct_qvariables2;
818 std::vector<double> ct_qcoefficients;
819 const int lincst_size =
model.constraint_size();
820 for (
int c = 0; c <
model.general_constraint_size(); ++c) {
821 const MPGeneralConstraintProto& gen_cst =
model.general_constraint(c);
822 switch (gen_cst.general_constraint_case()) {
823 case MPGeneralConstraintProto::kIndicatorConstraint: {
825 gen_cst, scip, &scip_constraints[lincst_size + c],
826 &scip_variables, &scip_constraints, &ct_variables,
830 case MPGeneralConstraintProto::kSosConstraint: {
832 &scip_constraints[lincst_size + c],
833 &ct_variables, &ct_coefficients));
836 case MPGeneralConstraintProto::kQuadraticConstraint: {
838 gen_cst, scip_variables, scip, &scip_constraints[lincst_size + c],
839 &ct_variables, &ct_coefficients, &ct_qvariables1, &ct_qvariables2,
843 case MPGeneralConstraintProto::kAbsConstraint: {
845 &scip_constraints[lincst_size + c]));
848 case MPGeneralConstraintProto::kAndConstraint: {
850 &scip_constraints[lincst_size + c],
854 case MPGeneralConstraintProto::kOrConstraint: {
856 &scip_constraints[lincst_size + c],
860 case MPGeneralConstraintProto::kMinConstraint:
861 case MPGeneralConstraintProto::kMaxConstraint: {
863 gen_cst, scip_variables, scip, &scip_constraints[lincst_size + c],
864 &scip_constraints, &ct_variables));
868 return absl::UnimplementedError(
869 absl::StrFormat(
"General constraints of type %i not supported.",
870 gen_cst.general_constraint_case()));
875 if (
model.has_quadratic_objective()) {
877 &scip_variables, &scip_constraints));
882 if (!absl::GetFlag(FLAGS_scip_proto_solver_output_cip_file).empty()) {
883 SCIPwriteOrigProblem(
884 scip, absl::GetFlag(FLAGS_scip_proto_solver_output_cip_file).c_str(),
887 const absl::Time time_before = absl::Now();
893 const absl::Duration solving_duration = absl::Now() - time_before;
895 VLOG(1) <<
"Finished solving in ScipSolveProto(), walltime = "
896 << solving_duration <<
", usertime = " << user_timer.
GetDuration();
898 response.mutable_solve_info()->set_solve_wall_time_seconds(
899 absl::ToDoubleSeconds(solving_duration));
900 response.mutable_solve_info()->set_solve_user_time_seconds(
903 const int solution_count =
904 std::min(SCIPgetNSols(scip),
905 std::min(request->populate_additional_solutions_up_to(),
906 std::numeric_limits<int32_t>::max() - 1) +
908 if (solution_count > 0) {
911 auto scip_solution_to_repeated_field = [&](SCIP_SOL* scip_solution) {
912 google::protobuf::RepeatedField<double> variable_value;
913 variable_value.Reserve(
model.variable_size());
914 for (
int v = 0; v <
model.variable_size(); ++v) {
915 double value = SCIPgetSolVal(scip, scip_solution, scip_variables[v]);
916 if (
model.variable(v).is_integer()) {
919 variable_value.AddAlreadyReserved(
value);
921 return variable_value;
927 SCIP_SOL**
const scip_solutions = SCIPgetSols(scip);
928 response.set_objective_value(SCIPgetSolOrigObj(scip, scip_solutions[0]));
929 response.set_best_objective_bound(SCIPgetDualbound(scip));
930 *response.mutable_variable_value() =
931 scip_solution_to_repeated_field(scip_solutions[0]);
932 for (
int i = 1; i < solution_count; ++i) {
933 MPSolution*
solution = response.add_additional_solutions();
934 solution->set_objective_value(SCIPgetSolOrigObj(scip, scip_solutions[i]));
935 *
solution->mutable_variable_value() =
936 scip_solution_to_repeated_field(scip_solutions[i]);
940 const SCIP_STATUS scip_status = SCIPgetStatus(scip);
941 switch (scip_status) {
942 case SCIP_STATUS_OPTIMAL:
943 response.set_status(MPSOLVER_OPTIMAL);
945 case SCIP_STATUS_GAPLIMIT:
947 response.set_status(MPSOLVER_OPTIMAL);
949 case SCIP_STATUS_INFORUNBD:
956 DLOG(INFO) <<
"SCIP solve returned SCIP_STATUS_INFORUNBD, which we treat "
957 "as INFEASIBLE even though it may mean UNBOUNDED.";
958 response.set_status_str(
959 "The model may actually be unbounded: SCIP returned "
960 "SCIP_STATUS_INFORUNBD");
961 ABSL_FALLTHROUGH_INTENDED;
962 case SCIP_STATUS_INFEASIBLE:
963 response.set_status(MPSOLVER_INFEASIBLE);
965 case SCIP_STATUS_UNBOUNDED:
966 response.set_status(MPSOLVER_UNBOUNDED);
969 if (solution_count > 0) {
970 response.set_status(MPSOLVER_FEASIBLE);
972 response.set_status(MPSOLVER_NOT_SOLVED);
973 response.set_status_str(absl::StrFormat(
"SCIP status code %d",
974 static_cast<int>(scip_status)));
979 VLOG(1) <<
"ScipSolveProto() status="
980 << 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.
const std::string name
A name for logging purposes.
In SWIG mode, we don't want anything besides these top-level includes.
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.")