27 #include "absl/status/status.h"
28 #include "absl/status/statusor.h"
29 #include "absl/strings/ascii.h"
30 #include "absl/strings/numbers.h"
31 #include "absl/strings/str_cat.h"
32 #include "absl/strings/str_format.h"
33 #include "absl/strings/str_split.h"
34 #include "absl/strings/string_view.h"
35 #include "absl/time/time.h"
41 #include "ortools/linear_solver/linear_solver.pb.h"
45 #include "scip/cons_disjunction.h"
46 #include "scip/cons_linear.h"
47 #include "scip/cons_quadratic.h"
48 #include "scip/pub_var.h"
49 #include "scip/scip.h"
50 #include "scip/scip_param.h"
51 #include "scip/scip_prob.h"
52 #include "scip/scip_var.h"
53 #include "scip/scipdefplugins.h"
55 #include "scip/struct_paramset.h"
56 #include "scip/type_cons.h"
57 #include "scip/type_paramset.h"
58 #include "scip/type_var.h"
60 ABSL_FLAG(std::string, scip_proto_solver_output_cip_file,
"",
61 "If given, saves the generated CIP file here. Useful for "
62 "reporting bugs to SCIP.");
68 absl::Status AddIndicatorConstraint(
const MPGeneralConstraintProto& gen_cst,
69 SCIP* scip, SCIP_CONS** scip_cst,
70 std::vector<SCIP_VAR*>* scip_variables,
71 std::vector<SCIP_CONS*>* scip_constraints,
72 std::vector<SCIP_VAR*>* tmp_variables,
73 std::vector<double>* tmp_coefficients) {
74 CHECK(scip !=
nullptr);
75 CHECK(scip_cst !=
nullptr);
76 CHECK(scip_variables !=
nullptr);
77 CHECK(scip_constraints !=
nullptr);
78 CHECK(tmp_variables !=
nullptr);
79 CHECK(tmp_coefficients !=
nullptr);
80 CHECK(gen_cst.has_indicator_constraint());
81 constexpr
double kInfinity = std::numeric_limits<double>::infinity();
83 const auto& ind = gen_cst.indicator_constraint();
84 if (!ind.has_constraint())
return absl::OkStatus();
86 const MPConstraintProto& constraint = ind.constraint();
87 const int size = constraint.var_index_size();
88 tmp_variables->resize(size,
nullptr);
89 tmp_coefficients->resize(size, 0);
90 for (
int i = 0; i < size; ++i) {
91 (*tmp_variables)[i] = (*scip_variables)[constraint.var_index(i)];
92 (*tmp_coefficients)[i] = constraint.coefficient(i);
95 SCIP_VAR* ind_var = (*scip_variables)[ind.var_index()];
96 if (ind.var_value() == 0) {
98 SCIPgetNegatedVar(scip, (*scip_variables)[ind.var_index()], &ind_var));
101 if (ind.constraint().upper_bound() <
kInfinity) {
103 scip, scip_cst, gen_cst.name().c_str(), ind_var, size,
104 tmp_variables->data(), tmp_coefficients->data(),
105 ind.constraint().upper_bound(),
106 !ind.constraint().is_lazy(),
113 ind.constraint().is_lazy(),
116 scip_constraints->push_back(
nullptr);
117 scip_cst = &scip_constraints->back();
119 if (ind.constraint().lower_bound() > -
kInfinity) {
120 for (
int i = 0; i < size; ++i) {
121 (*tmp_coefficients)[i] *= -1;
124 scip, scip_cst, gen_cst.name().c_str(), ind_var, size,
125 tmp_variables->data(), tmp_coefficients->data(),
126 -ind.constraint().lower_bound(),
127 !ind.constraint().is_lazy(),
134 ind.constraint().is_lazy(),
139 return absl::OkStatus();
142 absl::Status AddSosConstraint(
const MPGeneralConstraintProto& gen_cst,
143 const std::vector<SCIP_VAR*>& scip_variables,
144 SCIP* scip, SCIP_CONS** scip_cst,
145 std::vector<SCIP_VAR*>* tmp_variables,
146 std::vector<double>* tmp_weights) {
147 CHECK(scip !=
nullptr);
148 CHECK(scip_cst !=
nullptr);
149 CHECK(tmp_variables !=
nullptr);
150 CHECK(tmp_weights !=
nullptr);
152 CHECK(gen_cst.has_sos_constraint());
153 const MPSosConstraint& sos_cst = gen_cst.sos_constraint();
158 if (sos_cst.var_index_size() <= 1)
return absl::OkStatus();
159 if (sos_cst.type() == MPSosConstraint::SOS2 &&
160 sos_cst.var_index_size() <= 2) {
161 return absl::OkStatus();
164 tmp_variables->resize(sos_cst.var_index_size(),
nullptr);
165 for (
int v = 0; v < sos_cst.var_index_size(); ++v) {
166 (*tmp_variables)[v] = scip_variables[sos_cst.var_index(v)];
168 tmp_weights->resize(sos_cst.var_index_size(), 0);
169 if (sos_cst.weight_size() == sos_cst.var_index_size()) {
170 for (
int w = 0; w < sos_cst.weight_size(); ++w) {
171 (*tmp_weights)[w] = sos_cst.weight(w);
176 std::iota(tmp_weights->begin(), tmp_weights->end(), 1);
178 switch (sos_cst.type()) {
179 case MPSosConstraint::SOS1_DEFAULT:
181 SCIPcreateConsBasicSOS1(scip,
183 gen_cst.name().c_str(),
184 sos_cst.var_index_size(),
185 tmp_variables->data(),
186 tmp_weights->data()));
188 case MPSosConstraint::SOS2:
190 SCIPcreateConsBasicSOS2(scip,
192 gen_cst.name().c_str(),
193 sos_cst.var_index_size(),
194 tmp_variables->data(),
195 tmp_weights->data()));
199 return absl::OkStatus();
202 absl::Status AddQuadraticConstraint(
203 const MPGeneralConstraintProto& gen_cst,
204 const std::vector<SCIP_VAR*>& scip_variables, SCIP* scip,
205 SCIP_CONS** scip_cst, std::vector<SCIP_VAR*>* tmp_variables,
206 std::vector<double>* tmp_coefficients,
207 std::vector<SCIP_VAR*>* tmp_qvariables1,
208 std::vector<SCIP_VAR*>* tmp_qvariables2,
209 std::vector<double>* tmp_qcoefficients) {
210 CHECK(scip !=
nullptr);
211 CHECK(scip_cst !=
nullptr);
212 CHECK(tmp_variables !=
nullptr);
213 CHECK(tmp_coefficients !=
nullptr);
214 CHECK(tmp_qvariables1 !=
nullptr);
215 CHECK(tmp_qvariables2 !=
nullptr);
216 CHECK(tmp_qcoefficients !=
nullptr);
218 CHECK(gen_cst.has_quadratic_constraint());
219 const MPQuadraticConstraint& quad_cst = gen_cst.quadratic_constraint();
222 const int lsize = quad_cst.var_index_size();
223 CHECK_EQ(quad_cst.coefficient_size(), lsize);
224 tmp_variables->resize(lsize,
nullptr);
225 tmp_coefficients->resize(lsize, 0.0);
226 for (
int i = 0; i < lsize; ++i) {
227 (*tmp_variables)[i] = scip_variables[quad_cst.var_index(i)];
228 (*tmp_coefficients)[i] = quad_cst.coefficient(i);
232 const int qsize = quad_cst.qvar1_index_size();
233 CHECK_EQ(quad_cst.qvar2_index_size(), qsize);
234 CHECK_EQ(quad_cst.qcoefficient_size(), qsize);
235 tmp_qvariables1->resize(qsize,
nullptr);
236 tmp_qvariables2->resize(qsize,
nullptr);
237 tmp_qcoefficients->resize(qsize, 0.0);
238 for (
int i = 0; i < qsize; ++i) {
239 (*tmp_qvariables1)[i] = scip_variables[quad_cst.qvar1_index(i)];
240 (*tmp_qvariables2)[i] = scip_variables[quad_cst.qvar2_index(i)];
241 (*tmp_qcoefficients)[i] = quad_cst.qcoefficient(i);
245 SCIPcreateConsBasicQuadratic(scip,
247 gen_cst.name().c_str(),
249 tmp_variables->data(),
250 tmp_coefficients->data(),
252 tmp_qvariables1->data(),
253 tmp_qvariables2->data(),
254 tmp_qcoefficients->data(),
255 quad_cst.lower_bound(),
256 quad_cst.upper_bound()));
258 return absl::OkStatus();
263 absl::Status AddAbsConstraint(
const MPGeneralConstraintProto& gen_cst,
264 const std::vector<SCIP_VAR*>& scip_variables,
265 SCIP* scip, SCIP_CONS** scip_cst) {
266 CHECK(scip !=
nullptr);
267 CHECK(scip_cst !=
nullptr);
268 CHECK(gen_cst.has_abs_constraint());
269 const auto& abs = gen_cst.abs_constraint();
270 SCIP_VAR* scip_var = scip_variables[abs.var_index()];
271 SCIP_VAR* scip_resultant_var = scip_variables[abs.resultant_var_index()];
274 if (SCIPvarGetLbLocal(scip_resultant_var) < 0.0) {
278 std::vector<SCIP_VAR*> vars;
279 std::vector<double> vals;
280 std::vector<SCIP_CONS*> cons;
281 auto add_abs_constraint = [&](absl::string_view name_prefix) -> absl::Status {
282 SCIP_CONS* scip_cons =
nullptr;
283 CHECK(vars.size() == vals.size());
284 const std::string
name =
285 gen_cst.has_name() ? absl::StrCat(gen_cst.name(), name_prefix) :
"";
288 name.c_str(), vars.size(), vars.data(),
289 vals.data(), 0.0, 0.0));
292 cons.push_back(scip_cons);
293 return absl::OkStatus();
297 vars = {scip_resultant_var, scip_var};
306 const std::string
name =
307 gen_cst.has_name() ? absl::StrCat(gen_cst.name(),
"_disj") :
"";
309 scip, scip_cst,
name.c_str(),
310 cons.size(), cons.data(),
nullptr));
313 return absl::OkStatus();
316 absl::Status AddAndConstraint(
const MPGeneralConstraintProto& gen_cst,
317 const std::vector<SCIP_VAR*>& scip_variables,
318 SCIP* scip, SCIP_CONS** scip_cst,
319 std::vector<SCIP_VAR*>* tmp_variables) {
320 CHECK(scip !=
nullptr);
321 CHECK(scip_cst !=
nullptr);
322 CHECK(tmp_variables !=
nullptr);
323 CHECK(gen_cst.has_and_constraint());
324 const auto& andcst = gen_cst.and_constraint();
326 tmp_variables->resize(andcst.var_index_size(),
nullptr);
327 for (
int i = 0; i < andcst.var_index_size(); ++i) {
328 (*tmp_variables)[i] = scip_variables[andcst.var_index(i)];
332 gen_cst.name().c_str(),
333 scip_variables[andcst.resultant_var_index()],
334 andcst.var_index_size(),
335 tmp_variables->data()));
337 return absl::OkStatus();
340 absl::Status AddOrConstraint(
const MPGeneralConstraintProto& gen_cst,
341 const std::vector<SCIP_VAR*>& scip_variables,
342 SCIP* scip, SCIP_CONS** scip_cst,
343 std::vector<SCIP_VAR*>* tmp_variables) {
344 CHECK(scip !=
nullptr);
345 CHECK(scip_cst !=
nullptr);
346 CHECK(tmp_variables !=
nullptr);
347 CHECK(gen_cst.has_or_constraint());
348 const auto& orcst = gen_cst.or_constraint();
350 tmp_variables->resize(orcst.var_index_size(),
nullptr);
351 for (
int i = 0; i < orcst.var_index_size(); ++i) {
352 (*tmp_variables)[i] = scip_variables[orcst.var_index(i)];
356 gen_cst.name().c_str(),
357 scip_variables[orcst.resultant_var_index()],
358 orcst.var_index_size(),
359 tmp_variables->data()));
361 return absl::OkStatus();
369 absl::Status AddMinMaxConstraint(
const MPGeneralConstraintProto& gen_cst,
370 const std::vector<SCIP_VAR*>& scip_variables,
371 SCIP* scip, SCIP_CONS** scip_cst,
372 std::vector<SCIP_CONS*>* scip_constraints,
373 std::vector<SCIP_VAR*>* tmp_variables) {
374 CHECK(scip !=
nullptr);
375 CHECK(scip_cst !=
nullptr);
376 CHECK(tmp_variables !=
nullptr);
377 CHECK(gen_cst.has_min_constraint() || gen_cst.has_max_constraint());
378 const auto& minmax = gen_cst.has_min_constraint() ? gen_cst.min_constraint()
379 : gen_cst.max_constraint();
380 const std::set<int> unique_var_indices(minmax.var_index().begin(),
381 minmax.var_index().end());
382 SCIP_VAR* scip_resultant_var = scip_variables[minmax.resultant_var_index()];
384 std::vector<SCIP_VAR*> vars;
385 std::vector<double> vals;
386 std::vector<SCIP_CONS*> cons;
387 auto add_lin_constraint = [&](absl::string_view name_prefix,
390 SCIP_CONS* scip_cons =
nullptr;
391 CHECK(vars.size() == vals.size());
392 const std::string
name =
393 gen_cst.has_name() ? absl::StrCat(gen_cst.name(), name_prefix) :
"";
396 name.c_str(), vars.size(), vars.data(),
400 cons.push_back(scip_cons);
401 return absl::OkStatus();
405 for (
const int var_index : unique_var_indices) {
406 vars = {scip_resultant_var, scip_variables[var_index]};
412 if (minmax.has_constant()) {
413 vars = {scip_resultant_var};
416 add_lin_constraint(
"_constant", minmax.constant(), minmax.constant()));
420 const std::string
name =
421 gen_cst.has_name() ? absl::StrCat(gen_cst.name(),
"_disj") :
"";
423 scip, scip_cst,
name.c_str(),
424 cons.size(), cons.data(),
nullptr));
428 constexpr
double kInfinity = std::numeric_limits<double>::infinity();
430 for (
const int var_index : unique_var_indices) {
431 vars = {scip_resultant_var, scip_variables[var_index]};
433 if (gen_cst.has_min_constraint()) {
437 RETURN_IF_ERROR(add_lin_constraint(absl::StrCat(
"_ineq_", var_index), 0.0,
441 if (minmax.has_constant()) {
442 vars = {scip_resultant_var};
444 if (gen_cst.has_min_constraint()) {
452 for (SCIP_CONS* scip_cons : cons) {
453 scip_constraints->push_back(scip_cons);
456 return absl::OkStatus();
459 absl::Status AddQuadraticObjective(
const MPQuadraticObjective& quadobj,
461 std::vector<SCIP_VAR*>* scip_variables,
462 std::vector<SCIP_CONS*>* scip_constraints) {
463 CHECK(scip !=
nullptr);
464 CHECK(scip_variables !=
nullptr);
465 CHECK(scip_constraints !=
nullptr);
467 constexpr
double kInfinity = std::numeric_limits<double>::infinity();
469 const int size = quadobj.coefficient_size();
470 if (size == 0)
return absl::OkStatus();
474 scip_variables->push_back(
nullptr);
479 SCIP_VARTYPE_CONTINUOUS));
482 scip_constraints->push_back(
nullptr);
483 SCIP_VAR* linvars[1] = {scip_variables->back()};
484 double lincoefs[1] = {-1};
485 std::vector<SCIP_VAR*> quadvars1(size,
nullptr);
486 std::vector<SCIP_VAR*> quadvars2(size,
nullptr);
487 std::vector<double> quadcoefs(size, 0);
488 for (
int i = 0; i < size; ++i) {
489 quadvars1[i] = scip_variables->at(quadobj.qvar1_index(i));
490 quadvars2[i] = scip_variables->at(quadobj.qvar2_index(i));
491 quadcoefs[i] = quadobj.coefficient(i);
494 scip, &scip_constraints->back(),
"quadobj",
495 1, linvars, lincoefs,
496 size, quadvars1.data(),
497 quadvars2.data(), quadcoefs.data(),
501 return absl::OkStatus();
504 absl::Status AddSolutionHint(
const MPModelProto&
model, SCIP* scip,
505 const std::vector<SCIP_VAR*>& scip_variables) {
506 CHECK(scip !=
nullptr);
507 if (!
model.has_solution_hint())
return absl::OkStatus();
509 const PartialVariableAssignment& solution_hint =
model.solution_hint();
511 bool is_solution_partial =
512 solution_hint.var_index_size() !=
model.variable_size();
513 if (is_solution_partial) {
515 SCIPcreatePartialSol(scip, &solution,
nullptr));
518 SCIPcreateSol(scip, &solution,
nullptr));
521 for (
int i = 0; i < solution_hint.var_index_size(); ++i) {
523 scip, solution, scip_variables[solution_hint.var_index(i)],
524 solution_hint.var_value(i)));
530 return absl::OkStatus();
537 CHECK(scip !=
nullptr);
538 const double infinity = SCIPinfinity(scip);
540 for (
int v = 0; v <
model.variable_size(); ++v) {
541 const MPVariableProto& variable =
model.variable(v);
542 if (variable.lower_bound() >= infinity) {
543 return absl::StrFormat(
544 "Variable %i's lower bound is considered +infinity", v);
546 if (variable.upper_bound() <= -infinity) {
547 return absl::StrFormat(
548 "Variable %i's upper bound is considered -infinity", v);
550 const double coeff = variable.objective_coefficient();
551 if (coeff >= infinity || coeff <= -infinity) {
552 return absl::StrFormat(
553 "Variable %i's objective coefficient is considered infinite", v);
557 for (
int c = 0; c <
model.constraint_size(); ++c) {
558 const MPConstraintProto& cst =
model.constraint(c);
559 if (cst.lower_bound() >= infinity) {
560 return absl::StrFormat(
561 "Constraint %d's lower_bound is considered +infinity", c);
563 if (cst.upper_bound() <= -infinity) {
564 return absl::StrFormat(
565 "Constraint %d's upper_bound is considered -infinity", c);
567 for (
int i = 0; i < cst.coefficient_size(); ++i) {
568 if (std::abs(cst.coefficient(i)) >= infinity) {
569 return absl::StrFormat(
570 "Constraint %d's coefficient #%d is considered infinite", c, i);
575 for (
int c = 0; c <
model.general_constraint_size(); ++c) {
576 const MPGeneralConstraintProto& cst =
model.general_constraint(c);
577 switch (cst.general_constraint_case()) {
578 case MPGeneralConstraintProto::kQuadraticConstraint:
579 if (cst.quadratic_constraint().lower_bound() >= infinity) {
580 return absl::StrFormat(
581 "Quadratic constraint %d's lower_bound is considered +infinity",
584 if (cst.quadratic_constraint().upper_bound() <= -infinity) {
585 return absl::StrFormat(
586 "Quadratic constraint %d's upper_bound is considered -infinity",
589 for (
int i = 0; i < cst.quadratic_constraint().coefficient_size();
591 const double coefficient = cst.quadratic_constraint().coefficient(i);
593 return absl::StrFormat(
594 "Quadratic constraint %d's linear coefficient #%d considered "
599 for (
int i = 0; i < cst.quadratic_constraint().qcoefficient_size();
601 const double qcoefficient =
602 cst.quadratic_constraint().qcoefficient(i);
603 if (qcoefficient >= infinity || qcoefficient <= -infinity) {
604 return absl::StrFormat(
605 "Quadratic constraint %d's quadratic coefficient #%d "
606 "considered infinite",
611 case MPGeneralConstraintProto::kMinConstraint:
612 if (cst.min_constraint().constant() >= infinity ||
613 cst.min_constraint().constant() <= -infinity) {
614 return absl::StrFormat(
615 "Min constraint %d's coefficient constant considered infinite",
619 case MPGeneralConstraintProto::kMaxConstraint:
620 if (cst.max_constraint().constant() >= infinity ||
621 cst.max_constraint().constant() <= -infinity) {
622 return absl::StrFormat(
623 "Max constraint %d's coefficient constant considered infinite",
632 const MPQuadraticObjective& quad_obj =
model.quadratic_objective();
633 for (
int i = 0; i < quad_obj.coefficient_size(); ++i) {
634 if (std::abs(quad_obj.coefficient(i)) >= infinity) {
635 return absl::StrFormat(
636 "Quadratic objective term #%d's coefficient is considered infinite",
641 if (
model.has_solution_hint()) {
642 for (
int i = 0; i <
model.solution_hint().var_value_size(); ++i) {
643 const double value =
model.solution_hint().var_value(i);
644 if (
value >= infinity ||
value <= -infinity) {
645 return absl::StrFormat(
646 "Variable %i's solution hint is considered infinite",
647 model.solution_hint().var_index(i));
652 if (
model.objective_offset() >= infinity ||
653 model.objective_offset() <= -infinity) {
654 return "Model's objective offset is considered infinite.";
661 const MPModelRequest& request) {
663 const absl::optional<LazyMutableCopy<MPModelProto>> optional_model =
665 if (!optional_model)
return response;
666 const MPModelProto&
model = optional_model->get();
667 SCIP* scip =
nullptr;
668 std::vector<SCIP_VAR*> scip_variables(
model.variable_size(),
nullptr);
669 std::vector<SCIP_CONS*> scip_constraints(
670 model.constraint_size() +
model.general_constraint_size(),
nullptr);
672 auto delete_scip_objects = [&]() -> absl::Status {
674 if (scip ==
nullptr)
return absl::OkStatus();
675 for (SCIP_VAR* variable : scip_variables) {
676 if (variable !=
nullptr) {
680 for (SCIP_CONS* constraint : scip_constraints) {
681 if (constraint !=
nullptr) {
686 return absl::OkStatus();
690 const absl::Status deleter_status = delete_scip_objects();
691 LOG_IF(DFATAL, !deleter_status.ok()) << deleter_status;
696 const std::string scip_model_invalid_error =
698 if (!scip_model_invalid_error.empty()) {
699 response.set_status(MPSOLVER_MODEL_INVALID);
700 response.set_status_str(scip_model_invalid_error);
705 request.solver_specific_parameters(), scip);
706 if (!parameters_status.ok()) {
707 response.set_status(MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS);
709 std::string(parameters_status.message()));
719 SCIPsetIntParam(scip,
"timing/clocktype", SCIP_CLOCKTYPE_WALL));
720 if (request.solver_time_limit_seconds() > 0 &&
721 request.solver_time_limit_seconds() < 1e20) {
723 request.solver_time_limit_seconds()));
725 SCIPsetMessagehdlrQuiet(scip, !request.enable_internal_solver_output());
728 if (
model.maximize()) {
732 for (
int v = 0; v <
model.variable_size(); ++v) {
733 const MPVariableProto& variable =
model.variable(v);
735 scip, &scip_variables[v], variable.name().c_str(),
736 variable.lower_bound(), variable.upper_bound(),
737 variable.objective_coefficient(),
738 variable.is_integer() ? SCIP_VARTYPE_INTEGER
739 : SCIP_VARTYPE_CONTINUOUS));
744 std::vector<SCIP_VAR*> ct_variables;
745 std::vector<double> ct_coefficients;
746 for (
int c = 0; c <
model.constraint_size(); ++c) {
747 const MPConstraintProto& constraint =
model.constraint(c);
748 const int size = constraint.var_index_size();
749 ct_variables.resize(size,
nullptr);
750 ct_coefficients.resize(size, 0);
751 for (
int i = 0; i < size; ++i) {
752 ct_variables[i] = scip_variables[constraint.var_index(i)];
753 ct_coefficients[i] = constraint.coefficient(i);
756 scip, &scip_constraints[c],
757 constraint.name().c_str(),
758 constraint.var_index_size(), ct_variables.data(),
759 ct_coefficients.data(),
760 constraint.lower_bound(), constraint.upper_bound(),
761 !constraint.is_lazy(),
769 constraint.is_lazy(),
775 std::vector<SCIP_VAR*> ct_qvariables1;
776 std::vector<SCIP_VAR*> ct_qvariables2;
777 std::vector<double> ct_qcoefficients;
778 const int lincst_size =
model.constraint_size();
779 for (
int c = 0; c <
model.general_constraint_size(); ++c) {
780 const MPGeneralConstraintProto& gen_cst =
model.general_constraint(c);
781 switch (gen_cst.general_constraint_case()) {
782 case MPGeneralConstraintProto::kIndicatorConstraint: {
784 gen_cst, scip, &scip_constraints[lincst_size + c],
785 &scip_variables, &scip_constraints, &ct_variables,
789 case MPGeneralConstraintProto::kSosConstraint: {
791 &scip_constraints[lincst_size + c],
792 &ct_variables, &ct_coefficients));
795 case MPGeneralConstraintProto::kQuadraticConstraint: {
797 gen_cst, scip_variables, scip, &scip_constraints[lincst_size + c],
798 &ct_variables, &ct_coefficients, &ct_qvariables1, &ct_qvariables2,
802 case MPGeneralConstraintProto::kAbsConstraint: {
804 &scip_constraints[lincst_size + c]));
807 case MPGeneralConstraintProto::kAndConstraint: {
809 &scip_constraints[lincst_size + c],
813 case MPGeneralConstraintProto::kOrConstraint: {
815 &scip_constraints[lincst_size + c],
819 case MPGeneralConstraintProto::kMinConstraint:
820 case MPGeneralConstraintProto::kMaxConstraint: {
822 gen_cst, scip_variables, scip, &scip_constraints[lincst_size + c],
823 &scip_constraints, &ct_variables));
827 return absl::UnimplementedError(
828 absl::StrFormat(
"General constraints of type %i not supported.",
829 gen_cst.general_constraint_case()));
834 if (
model.has_quadratic_objective()) {
836 &scip_variables, &scip_constraints));
841 if (!absl::GetFlag(FLAGS_scip_proto_solver_output_cip_file).empty()) {
842 SCIPwriteOrigProblem(
843 scip, absl::GetFlag(FLAGS_scip_proto_solver_output_cip_file).c_str(),
846 const absl::Time time_before = absl::Now();
852 const absl::Duration solving_duration = absl::Now() - time_before;
854 VLOG(1) <<
"Finished solving in ScipSolveProto(), walltime = "
855 << solving_duration <<
", usertime = " << user_timer.
GetDuration();
857 response.mutable_solve_info()->set_solve_wall_time_seconds(
858 absl::ToDoubleSeconds(solving_duration));
859 response.mutable_solve_info()->set_solve_user_time_seconds(
862 const int solution_count =
864 std::min(request.populate_additional_solutions_up_to(),
867 if (solution_count > 0) {
870 auto scip_solution_to_repeated_field = [&](SCIP_SOL* scip_solution) {
871 google::protobuf::RepeatedField<double> variable_value;
872 variable_value.Reserve(
model.variable_size());
873 for (
int v = 0; v <
model.variable_size(); ++v) {
874 double value = SCIPgetSolVal(scip, scip_solution, scip_variables[v]);
875 if (
model.variable(v).is_integer()) {
878 variable_value.AddAlreadyReserved(
value);
880 return variable_value;
886 SCIP_SOL**
const scip_solutions = SCIPgetSols(scip);
887 response.set_objective_value(SCIPgetSolOrigObj(scip, scip_solutions[0]));
888 response.set_best_objective_bound(SCIPgetDualbound(scip));
889 *
response.mutable_variable_value() =
890 scip_solution_to_repeated_field(scip_solutions[0]);
891 for (
int i = 1; i < solution_count; ++i) {
892 MPSolution* solution =
response.add_additional_solutions();
893 solution->set_objective_value(SCIPgetSolOrigObj(scip, scip_solutions[i]));
894 *solution->mutable_variable_value() =
895 scip_solution_to_repeated_field(scip_solutions[i]);
899 const SCIP_STATUS scip_status = SCIPgetStatus(scip);
900 switch (scip_status) {
901 case SCIP_STATUS_OPTIMAL:
902 response.set_status(MPSOLVER_OPTIMAL);
904 case SCIP_STATUS_GAPLIMIT:
906 response.set_status(MPSOLVER_OPTIMAL);
908 case SCIP_STATUS_INFORUNBD:
915 DLOG(INFO) <<
"SCIP solve returned SCIP_STATUS_INFORUNBD, which we treat "
916 "as INFEASIBLE even though it may mean UNBOUNDED.";
918 "The model may actually be unbounded: SCIP returned "
919 "SCIP_STATUS_INFORUNBD");
920 ABSL_FALLTHROUGH_INTENDED;
921 case SCIP_STATUS_INFEASIBLE:
922 response.set_status(MPSOLVER_INFEASIBLE);
924 case SCIP_STATUS_UNBOUNDED:
925 response.set_status(MPSOLVER_UNBOUNDED);
928 if (solution_count > 0) {
929 response.set_status(MPSOLVER_FEASIBLE);
931 response.set_status(MPSOLVER_NOT_SOLVED);
932 response.set_status_str(absl::StrFormat(
"SCIP status code %d",
933 static_cast<int>(scip_status)));
938 VLOG(1) <<
"ScipSolveProto() status="
939 << MPSolverResponseStatus_Name(
response.status()) <<
".";
#define RETURN_IF_ERROR(expr)
absl::Duration GetDuration() const
SharedResponseManager * response
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
Collection of objects used to extend the Constraint Solver library.
std::string FindErrorInMPModelForScip(const MPModelProto &model, SCIP *scip)
absl::StatusOr< MPSolutionResponse > ScipSolveProto(const MPModelRequest &request)
absl::Status LegacyScipSetSolverSpecificParameters(absl::string_view parameters, SCIP *scip)
std::optional< LazyMutableCopy< MPModelProto > > ExtractValidMPModelOrPopulateResponseStatus(const MPModelRequest &request, MPSolutionResponse *response)
If the model is valid and non-empty, returns it (possibly after extracting the model_delta).
#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.")
constexpr double kInfinity
#define VLOG(verboselevel)