26#include "absl/status/status.h"
27#include "absl/status/statusor.h"
28#include "absl/strings/ascii.h"
29#include "absl/strings/numbers.h"
30#include "absl/strings/str_cat.h"
31#include "absl/strings/str_format.h"
32#include "absl/strings/str_split.h"
33#include "absl/time/time.h"
39#include "ortools/linear_solver/linear_solver.pb.h"
43#include "scip/cons_disjunction.h"
44#include "scip/cons_linear.h"
45#include "scip/cons_quadratic.h"
46#include "scip/pub_var.h"
48#include "scip/scip_param.h"
49#include "scip/scip_prob.h"
50#include "scip/scip_var.h"
51#include "scip/scipdefplugins.h"
53#include "scip/struct_paramset.h"
54#include "scip/type_cons.h"
55#include "scip/type_paramset.h"
56#include "scip/type_var.h"
58ABSL_FLAG(std::string, scip_proto_solver_output_cip_file,
"",
59 "If given, saves the generated CIP file here. Useful for "
60 "reporting bugs to SCIP.");
66absl::Status AddIndicatorConstraint(
const MPGeneralConstraintProto& gen_cst,
67 SCIP* scip, SCIP_CONS** scip_cst,
68 std::vector<SCIP_VAR*>* scip_variables,
69 std::vector<SCIP_CONS*>* scip_constraints,
70 std::vector<SCIP_VAR*>* tmp_variables,
71 std::vector<double>* tmp_coefficients) {
72 CHECK(scip !=
nullptr);
73 CHECK(scip_cst !=
nullptr);
74 CHECK(scip_variables !=
nullptr);
75 CHECK(scip_constraints !=
nullptr);
76 CHECK(tmp_variables !=
nullptr);
77 CHECK(tmp_coefficients !=
nullptr);
78 CHECK(gen_cst.has_indicator_constraint());
79 constexpr double kInfinity = std::numeric_limits<double>::infinity();
81 const auto& ind = gen_cst.indicator_constraint();
82 if (!ind.has_constraint())
return absl::OkStatus();
84 const MPConstraintProto& constraint = ind.constraint();
85 const int size = constraint.var_index_size();
86 tmp_variables->resize(size,
nullptr);
87 tmp_coefficients->resize(size, 0);
88 for (
int i = 0; i < size; ++i) {
89 (*tmp_variables)[i] = (*scip_variables)[constraint.var_index(i)];
90 (*tmp_coefficients)[i] = constraint.coefficient(i);
93 SCIP_VAR* ind_var = (*scip_variables)[ind.var_index()];
94 if (ind.var_value() == 0) {
96 SCIPgetNegatedVar(scip, (*scip_variables)[ind.var_index()], &ind_var));
99 if (ind.constraint().upper_bound() <
kInfinity) {
101 scip, scip_cst, gen_cst.name().c_str(), ind_var, size,
102 tmp_variables->data(), tmp_coefficients->data(),
103 ind.constraint().upper_bound(),
104 !ind.constraint().is_lazy(),
111 ind.constraint().is_lazy(),
114 scip_constraints->push_back(
nullptr);
115 scip_cst = &scip_constraints->back();
117 if (ind.constraint().lower_bound() > -
kInfinity) {
118 for (
int i = 0; i < size; ++i) {
119 (*tmp_coefficients)[i] *= -1;
122 scip, scip_cst, gen_cst.name().c_str(), ind_var, size,
123 tmp_variables->data(), tmp_coefficients->data(),
124 -ind.constraint().lower_bound(),
125 !ind.constraint().is_lazy(),
132 ind.constraint().is_lazy(),
137 return absl::OkStatus();
140absl::Status AddSosConstraint(
const MPGeneralConstraintProto& gen_cst,
141 const std::vector<SCIP_VAR*>& scip_variables,
142 SCIP* scip, SCIP_CONS** scip_cst,
143 std::vector<SCIP_VAR*>* tmp_variables,
144 std::vector<double>* tmp_weights) {
145 CHECK(scip !=
nullptr);
146 CHECK(scip_cst !=
nullptr);
147 CHECK(tmp_variables !=
nullptr);
148 CHECK(tmp_weights !=
nullptr);
150 CHECK(gen_cst.has_sos_constraint());
151 const MPSosConstraint& sos_cst = gen_cst.sos_constraint();
156 if (sos_cst.var_index_size() <= 1)
return absl::OkStatus();
157 if (sos_cst.type() == MPSosConstraint::SOS2 &&
158 sos_cst.var_index_size() <= 2) {
159 return absl::OkStatus();
162 tmp_variables->resize(sos_cst.var_index_size(),
nullptr);
163 for (
int v = 0; v < sos_cst.var_index_size(); ++v) {
164 (*tmp_variables)[v] = scip_variables[sos_cst.var_index(v)];
166 tmp_weights->resize(sos_cst.var_index_size(), 0);
167 if (sos_cst.weight_size() == sos_cst.var_index_size()) {
168 for (
int w = 0; w < sos_cst.weight_size(); ++w) {
169 (*tmp_weights)[w] = sos_cst.weight(w);
174 std::iota(tmp_weights->begin(), tmp_weights->end(), 1);
176 switch (sos_cst.type()) {
177 case MPSosConstraint::SOS1_DEFAULT:
179 SCIPcreateConsBasicSOS1(scip,
181 gen_cst.name().c_str(),
182 sos_cst.var_index_size(),
183 tmp_variables->data(),
184 tmp_weights->data()));
186 case MPSosConstraint::SOS2:
188 SCIPcreateConsBasicSOS2(scip,
190 gen_cst.name().c_str(),
191 sos_cst.var_index_size(),
192 tmp_variables->data(),
193 tmp_weights->data()));
197 return absl::OkStatus();
200absl::Status AddQuadraticConstraint(
201 const MPGeneralConstraintProto& gen_cst,
202 const std::vector<SCIP_VAR*>& scip_variables, SCIP* scip,
203 SCIP_CONS** scip_cst, std::vector<SCIP_VAR*>* tmp_variables,
204 std::vector<double>* tmp_coefficients,
205 std::vector<SCIP_VAR*>* tmp_qvariables1,
206 std::vector<SCIP_VAR*>* tmp_qvariables2,
207 std::vector<double>* tmp_qcoefficients) {
208 CHECK(scip !=
nullptr);
209 CHECK(scip_cst !=
nullptr);
210 CHECK(tmp_variables !=
nullptr);
211 CHECK(tmp_coefficients !=
nullptr);
212 CHECK(tmp_qvariables1 !=
nullptr);
213 CHECK(tmp_qvariables2 !=
nullptr);
214 CHECK(tmp_qcoefficients !=
nullptr);
216 CHECK(gen_cst.has_quadratic_constraint());
217 const MPQuadraticConstraint& quad_cst = gen_cst.quadratic_constraint();
220 const int lsize = quad_cst.var_index_size();
221 CHECK_EQ(quad_cst.coefficient_size(), lsize);
222 tmp_variables->resize(lsize,
nullptr);
223 tmp_coefficients->resize(lsize, 0.0);
224 for (
int i = 0; i < lsize; ++i) {
225 (*tmp_variables)[i] = scip_variables[quad_cst.var_index(i)];
226 (*tmp_coefficients)[i] = quad_cst.coefficient(i);
230 const int qsize = quad_cst.qvar1_index_size();
231 CHECK_EQ(quad_cst.qvar2_index_size(), qsize);
232 CHECK_EQ(quad_cst.qcoefficient_size(), qsize);
233 tmp_qvariables1->resize(qsize,
nullptr);
234 tmp_qvariables2->resize(qsize,
nullptr);
235 tmp_qcoefficients->resize(qsize, 0.0);
236 for (
int i = 0; i < qsize; ++i) {
237 (*tmp_qvariables1)[i] = scip_variables[quad_cst.qvar1_index(i)];
238 (*tmp_qvariables2)[i] = scip_variables[quad_cst.qvar2_index(i)];
239 (*tmp_qcoefficients)[i] = quad_cst.qcoefficient(i);
243 SCIPcreateConsBasicQuadratic(scip,
245 gen_cst.name().c_str(),
247 tmp_variables->data(),
248 tmp_coefficients->data(),
250 tmp_qvariables1->data(),
251 tmp_qvariables2->data(),
252 tmp_qcoefficients->data(),
253 quad_cst.lower_bound(),
254 quad_cst.upper_bound()));
256 return absl::OkStatus();
261absl::Status AddAbsConstraint(
const MPGeneralConstraintProto& gen_cst,
262 const std::vector<SCIP_VAR*>& scip_variables,
263 SCIP* scip, SCIP_CONS** scip_cst) {
264 CHECK(scip !=
nullptr);
265 CHECK(scip_cst !=
nullptr);
266 CHECK(gen_cst.has_abs_constraint());
267 const auto& abs = gen_cst.abs_constraint();
268 SCIP_VAR* scip_var = scip_variables[abs.var_index()];
269 SCIP_VAR* scip_resultant_var = scip_variables[abs.resultant_var_index()];
272 if (SCIPvarGetLbLocal(scip_resultant_var) < 0.0) {
276 std::vector<SCIP_VAR*> vars;
277 std::vector<double> vals;
278 std::vector<SCIP_CONS*> cons;
279 auto add_abs_constraint =
280 [&](
const std::string& name_prefix) -> absl::Status {
281 SCIP_CONS* scip_cons =
nullptr;
282 CHECK(vars.size() == vals.size());
283 const std::string
name =
284 gen_cst.has_name() ? absl::StrCat(gen_cst.name(), name_prefix) :
"";
287 name.c_str(), vars.size(), vars.data(),
288 vals.data(), 0.0, 0.0));
291 cons.push_back(scip_cons);
292 return absl::OkStatus();
296 vars = {scip_resultant_var, scip_var};
305 const std::string
name =
306 gen_cst.has_name() ? absl::StrCat(gen_cst.name(),
"_disj") :
"";
308 scip, scip_cst,
name.c_str(),
309 cons.size(), cons.data(),
nullptr));
312 return absl::OkStatus();
315absl::Status AddAndConstraint(
const MPGeneralConstraintProto& gen_cst,
316 const std::vector<SCIP_VAR*>& scip_variables,
317 SCIP* scip, SCIP_CONS** scip_cst,
318 std::vector<SCIP_VAR*>* tmp_variables) {
319 CHECK(scip !=
nullptr);
320 CHECK(scip_cst !=
nullptr);
321 CHECK(tmp_variables !=
nullptr);
322 CHECK(gen_cst.has_and_constraint());
323 const auto& andcst = gen_cst.and_constraint();
325 tmp_variables->resize(andcst.var_index_size(),
nullptr);
326 for (
int i = 0; i < andcst.var_index_size(); ++i) {
327 (*tmp_variables)[i] = scip_variables[andcst.var_index(i)];
331 gen_cst.name().c_str(),
332 scip_variables[andcst.resultant_var_index()],
333 andcst.var_index_size(),
334 tmp_variables->data()));
336 return absl::OkStatus();
339absl::Status AddOrConstraint(
const MPGeneralConstraintProto& gen_cst,
340 const std::vector<SCIP_VAR*>& scip_variables,
341 SCIP* scip, SCIP_CONS** scip_cst,
342 std::vector<SCIP_VAR*>* tmp_variables) {
343 CHECK(scip !=
nullptr);
344 CHECK(scip_cst !=
nullptr);
345 CHECK(tmp_variables !=
nullptr);
346 CHECK(gen_cst.has_or_constraint());
347 const auto& orcst = gen_cst.or_constraint();
349 tmp_variables->resize(orcst.var_index_size(),
nullptr);
350 for (
int i = 0; i < orcst.var_index_size(); ++i) {
351 (*tmp_variables)[i] = scip_variables[orcst.var_index(i)];
355 gen_cst.name().c_str(),
356 scip_variables[orcst.resultant_var_index()],
357 orcst.var_index_size(),
358 tmp_variables->data()));
360 return absl::OkStatus();
368absl::Status AddMinMaxConstraint(
const MPGeneralConstraintProto& gen_cst,
369 const std::vector<SCIP_VAR*>& scip_variables,
370 SCIP* scip, SCIP_CONS** scip_cst,
371 std::vector<SCIP_CONS*>* scip_constraints,
372 std::vector<SCIP_VAR*>* tmp_variables) {
373 CHECK(scip !=
nullptr);
374 CHECK(scip_cst !=
nullptr);
375 CHECK(tmp_variables !=
nullptr);
376 CHECK(gen_cst.has_min_constraint() || gen_cst.has_max_constraint());
377 const auto& minmax = gen_cst.has_min_constraint() ? gen_cst.min_constraint()
378 : gen_cst.max_constraint();
379 const std::set<int> unique_var_indices(minmax.var_index().begin(),
380 minmax.var_index().end());
381 SCIP_VAR* scip_resultant_var = scip_variables[minmax.resultant_var_index()];
383 std::vector<SCIP_VAR*> vars;
384 std::vector<double> vals;
385 std::vector<SCIP_CONS*> cons;
386 auto add_lin_constraint = [&](
const std::string& name_prefix,
389 SCIP_CONS* scip_cons =
nullptr;
390 CHECK(vars.size() == vals.size());
391 const std::string
name =
392 gen_cst.has_name() ? absl::StrCat(gen_cst.name(), name_prefix) :
"";
395 name.c_str(), vars.size(), vars.data(),
399 cons.push_back(scip_cons);
400 return absl::OkStatus();
404 for (
const int var_index : unique_var_indices) {
405 vars = {scip_resultant_var, scip_variables[var_index]};
411 if (minmax.has_constant()) {
412 vars = {scip_resultant_var};
415 add_lin_constraint(
"_constant", minmax.constant(), minmax.constant()));
419 const std::string
name =
420 gen_cst.has_name() ? absl::StrCat(gen_cst.name(),
"_disj") :
"";
422 scip, scip_cst,
name.c_str(),
423 cons.size(), cons.data(),
nullptr));
427 constexpr double kInfinity = std::numeric_limits<double>::infinity();
429 for (
const int var_index : unique_var_indices) {
430 vars = {scip_resultant_var, scip_variables[var_index]};
432 if (gen_cst.has_min_constraint()) {
436 RETURN_IF_ERROR(add_lin_constraint(absl::StrCat(
"_ineq_", var_index), 0.0,
440 if (minmax.has_constant()) {
441 vars = {scip_resultant_var};
443 if (gen_cst.has_min_constraint()) {
451 for (SCIP_CONS* scip_cons : cons) {
452 scip_constraints->push_back(scip_cons);
455 return absl::OkStatus();
458absl::Status AddQuadraticObjective(
const MPQuadraticObjective& quadobj,
460 std::vector<SCIP_VAR*>* scip_variables,
461 std::vector<SCIP_CONS*>* scip_constraints) {
462 CHECK(scip !=
nullptr);
463 CHECK(scip_variables !=
nullptr);
464 CHECK(scip_constraints !=
nullptr);
466 constexpr double kInfinity = std::numeric_limits<double>::infinity();
468 const int size = quadobj.coefficient_size();
469 if (size == 0)
return absl::OkStatus();
473 scip_variables->push_back(
nullptr);
478 SCIP_VARTYPE_CONTINUOUS));
481 scip_constraints->push_back(
nullptr);
482 SCIP_VAR* linvars[1] = {scip_variables->back()};
483 double lincoefs[1] = {-1};
484 std::vector<SCIP_VAR*> quadvars1(size,
nullptr);
485 std::vector<SCIP_VAR*> quadvars2(size,
nullptr);
486 std::vector<double> quadcoefs(size, 0);
487 for (
int i = 0; i < size; ++i) {
488 quadvars1[i] = scip_variables->at(quadobj.qvar1_index(i));
489 quadvars2[i] = scip_variables->at(quadobj.qvar2_index(i));
490 quadcoefs[i] = quadobj.coefficient(i);
493 scip, &scip_constraints->back(),
"quadobj",
494 1, linvars, lincoefs,
495 size, quadvars1.data(),
496 quadvars2.data(), quadcoefs.data(),
500 return absl::OkStatus();
503absl::Status AddSolutionHint(
const MPModelProto&
model, SCIP* scip,
504 const std::vector<SCIP_VAR*>& scip_variables) {
505 CHECK(scip !=
nullptr);
506 if (!
model.has_solution_hint())
return absl::OkStatus();
508 const PartialVariableAssignment& solution_hint =
model.solution_hint();
510 bool is_solution_partial =
511 solution_hint.var_index_size() !=
model.variable_size();
512 if (is_solution_partial) {
514 SCIPcreatePartialSol(scip, &solution,
nullptr));
517 SCIPcreateSol(scip, &solution,
nullptr));
520 for (
int i = 0; i < solution_hint.var_index_size(); ++i) {
522 scip, solution, scip_variables[solution_hint.var_index(i)],
523 solution_hint.var_value(i)));
529 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;
885 SCIP_SOL**
const scip_solutions = SCIPgetSols(scip);
886 response.set_objective_value(SCIPgetSolOrigObj(scip, scip_solutions[0]));
887 response.set_best_objective_bound(SCIPgetDualbound(scip));
888 *
response.mutable_variable_value() =
889 scip_solution_to_repeated_field(scip_solutions[0]);
890 for (
int i = 1; i < solution_count; ++i) {
891 MPSolution* solution =
response.add_additional_solutions();
892 solution->set_objective_value(SCIPgetSolOrigObj(scip, scip_solutions[i]));
893 *solution->mutable_variable_value() =
894 scip_solution_to_repeated_field(scip_solutions[i]);
898 const SCIP_STATUS scip_status = SCIPgetStatus(scip);
899 switch (scip_status) {
900 case SCIP_STATUS_OPTIMAL:
901 response.set_status(MPSOLVER_OPTIMAL);
903 case SCIP_STATUS_GAPLIMIT:
905 response.set_status(MPSOLVER_OPTIMAL);
907 case SCIP_STATUS_INFORUNBD:
914 DLOG(
INFO) <<
"SCIP solve returned SCIP_STATUS_INFORUNBD, which we treat "
915 "as INFEASIBLE even though it may mean UNBOUNDED.";
917 "The model may actually be unbounded: SCIP returned "
918 "SCIP_STATUS_INFORUNBD");
919 ABSL_FALLTHROUGH_INTENDED;
920 case SCIP_STATUS_INFEASIBLE:
921 response.set_status(MPSOLVER_INFEASIBLE);
923 case SCIP_STATUS_UNBOUNDED:
924 response.set_status(MPSOLVER_UNBOUNDED);
927 if (solution_count > 0) {
928 response.set_status(MPSOLVER_FEASIBLE);
930 response.set_status(MPSOLVER_NOT_SOLVED);
931 response.set_status_str(absl::StrFormat(
"SCIP status code %d",
932 static_cast<int>(scip_status)));
937 VLOG(1) <<
"ScipSolveProto() status="
938 << MPSolverResponseStatus_Name(
response.status()) <<
".";
#define LOG_IF(severity, condition)
#define CHECK_EQ(val1, val2)
#define VLOG(verboselevel)
#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(const std::string ¶meters, SCIP *scip)
absl::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.")