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"
37 #include "ortools/linear_solver/linear_solver.pb.h"
41 #include "scip/cons_disjunction.h"
42 #include "scip/cons_linear.h"
43 #include "scip/pub_var.h"
44 #include "scip/scip.h"
45 #include "scip/scip_param.h"
46 #include "scip/scip_prob.h"
47 #include "scip/scip_var.h"
48 #include "scip/scipdefplugins.h"
50 #include "scip/struct_paramset.h"
51 #include "scip/type_cons.h"
52 #include "scip/type_paramset.h"
53 #include "scip/type_var.h"
55 ABSL_FLAG(std::string, scip_proto_solver_output_cip_file,
"",
56 "If given, saves the generated CIP file here. Useful for "
57 "reporting bugs to SCIP.");
64 absl::Status AddIndicatorConstraint(
const MPGeneralConstraintProto& gen_cst,
65 SCIP* scip, SCIP_CONS** scip_cst,
66 std::vector<SCIP_VAR*>* scip_variables,
67 std::vector<SCIP_CONS*>* scip_constraints,
68 std::vector<SCIP_VAR*>* tmp_variables,
69 std::vector<double>* tmp_coefficients) {
70 CHECK(scip !=
nullptr);
71 CHECK(scip_cst !=
nullptr);
72 CHECK(scip_variables !=
nullptr);
73 CHECK(scip_constraints !=
nullptr);
74 CHECK(tmp_variables !=
nullptr);
75 CHECK(tmp_coefficients !=
nullptr);
76 CHECK(gen_cst.has_indicator_constraint());
77 constexpr
double kInfinity = std::numeric_limits<double>::infinity();
79 const auto& ind = gen_cst.indicator_constraint();
80 if (!ind.has_constraint())
return absl::OkStatus();
82 const MPConstraintProto& constraint = ind.constraint();
83 const int size = constraint.var_index_size();
84 tmp_variables->resize(size,
nullptr);
85 tmp_coefficients->resize(size, 0);
86 for (
int i = 0; i < size; ++i) {
87 (*tmp_variables)[i] = (*scip_variables)[constraint.var_index(i)];
88 (*tmp_coefficients)[i] = constraint.coefficient(i);
91 SCIP_VAR* ind_var = (*scip_variables)[ind.var_index()];
92 if (ind.var_value() == 0) {
94 SCIPgetNegatedVar(scip, (*scip_variables)[ind.var_index()], &ind_var));
97 if (ind.constraint().upper_bound() <
kInfinity) {
99 scip, scip_cst, gen_cst.name().c_str(), ind_var, size,
100 tmp_variables->data(), tmp_coefficients->data(),
101 ind.constraint().upper_bound(),
102 !ind.constraint().is_lazy(),
109 ind.constraint().is_lazy(),
112 scip_constraints->push_back(
nullptr);
113 scip_cst = &scip_constraints->back();
115 if (ind.constraint().lower_bound() > -
kInfinity) {
116 for (
int i = 0; i < size; ++i) {
117 (*tmp_coefficients)[i] *= -1;
120 scip, scip_cst, gen_cst.name().c_str(), ind_var, size,
121 tmp_variables->data(), tmp_coefficients->data(),
122 -ind.constraint().lower_bound(),
123 !ind.constraint().is_lazy(),
130 ind.constraint().is_lazy(),
135 return absl::OkStatus();
138 absl::Status AddSosConstraint(
const MPGeneralConstraintProto& gen_cst,
139 const std::vector<SCIP_VAR*>& scip_variables,
140 SCIP* scip, SCIP_CONS** scip_cst,
141 std::vector<SCIP_VAR*>* tmp_variables,
142 std::vector<double>* tmp_weights) {
143 CHECK(scip !=
nullptr);
144 CHECK(scip_cst !=
nullptr);
145 CHECK(tmp_variables !=
nullptr);
146 CHECK(tmp_weights !=
nullptr);
148 CHECK(gen_cst.has_sos_constraint());
149 const MPSosConstraint& sos_cst = gen_cst.sos_constraint();
154 if (sos_cst.var_index_size() <= 1)
return absl::OkStatus();
155 if (sos_cst.type() == MPSosConstraint::SOS2 &&
156 sos_cst.var_index_size() <= 2) {
157 return absl::OkStatus();
160 tmp_variables->resize(sos_cst.var_index_size(),
nullptr);
161 for (
int v = 0; v < sos_cst.var_index_size(); ++v) {
162 (*tmp_variables)[v] = scip_variables[sos_cst.var_index(v)];
164 tmp_weights->resize(sos_cst.var_index_size(), 0);
165 if (sos_cst.weight_size() == sos_cst.var_index_size()) {
166 for (
int w = 0; w < sos_cst.weight_size(); ++w) {
167 (*tmp_weights)[w] = sos_cst.weight(w);
172 std::iota(tmp_weights->begin(), tmp_weights->end(), 1);
174 switch (sos_cst.type()) {
175 case MPSosConstraint::SOS1_DEFAULT:
177 SCIPcreateConsBasicSOS1(scip,
179 gen_cst.name().c_str(),
180 sos_cst.var_index_size(),
181 tmp_variables->data(),
182 tmp_weights->data()));
184 case MPSosConstraint::SOS2:
186 SCIPcreateConsBasicSOS2(scip,
188 gen_cst.name().c_str(),
189 sos_cst.var_index_size(),
190 tmp_variables->data(),
191 tmp_weights->data()));
195 return absl::OkStatus();
198 absl::Status AddQuadraticConstraint(
199 const MPGeneralConstraintProto& gen_cst,
200 const std::vector<SCIP_VAR*>& scip_variables, SCIP* scip,
201 SCIP_CONS** scip_cst, std::vector<SCIP_VAR*>* tmp_variables,
202 std::vector<double>* tmp_coefficients,
203 std::vector<SCIP_VAR*>* tmp_qvariables1,
204 std::vector<SCIP_VAR*>* tmp_qvariables2,
205 std::vector<double>* tmp_qcoefficients) {
206 CHECK(scip !=
nullptr);
207 CHECK(scip_cst !=
nullptr);
208 CHECK(tmp_variables !=
nullptr);
209 CHECK(tmp_coefficients !=
nullptr);
210 CHECK(tmp_qvariables1 !=
nullptr);
211 CHECK(tmp_qvariables2 !=
nullptr);
212 CHECK(tmp_qcoefficients !=
nullptr);
214 CHECK(gen_cst.has_quadratic_constraint());
215 const MPQuadraticConstraint& quad_cst = gen_cst.quadratic_constraint();
218 const int lsize = quad_cst.var_index_size();
219 CHECK_EQ(quad_cst.coefficient_size(), lsize);
220 tmp_variables->resize(lsize,
nullptr);
221 tmp_coefficients->resize(lsize, 0.0);
222 for (
int i = 0; i < lsize; ++i) {
223 (*tmp_variables)[i] = scip_variables[quad_cst.var_index(i)];
224 (*tmp_coefficients)[i] = quad_cst.coefficient(i);
228 const int qsize = quad_cst.qvar1_index_size();
229 CHECK_EQ(quad_cst.qvar2_index_size(), qsize);
230 CHECK_EQ(quad_cst.qcoefficient_size(), qsize);
231 tmp_qvariables1->resize(qsize,
nullptr);
232 tmp_qvariables2->resize(qsize,
nullptr);
233 tmp_qcoefficients->resize(qsize, 0.0);
234 for (
int i = 0; i < qsize; ++i) {
235 (*tmp_qvariables1)[i] = scip_variables[quad_cst.qvar1_index(i)];
236 (*tmp_qvariables2)[i] = scip_variables[quad_cst.qvar2_index(i)];
237 (*tmp_qcoefficients)[i] = quad_cst.qcoefficient(i);
241 SCIPcreateConsBasicQuadratic(scip,
243 gen_cst.name().c_str(),
245 tmp_variables->data(),
246 tmp_coefficients->data(),
248 tmp_qvariables1->data(),
249 tmp_qvariables2->data(),
250 tmp_qcoefficients->data(),
251 quad_cst.lower_bound(),
252 quad_cst.upper_bound()));
254 return absl::OkStatus();
259 absl::Status AddAbsConstraint(
const MPGeneralConstraintProto& gen_cst,
260 const std::vector<SCIP_VAR*>& scip_variables,
261 SCIP* scip, SCIP_CONS** scip_cst) {
262 CHECK(scip !=
nullptr);
263 CHECK(scip_cst !=
nullptr);
264 CHECK(gen_cst.has_abs_constraint());
265 const auto& abs = gen_cst.abs_constraint();
266 SCIP_VAR* scip_var = scip_variables[abs.var_index()];
267 SCIP_VAR* scip_resultant_var = scip_variables[abs.resultant_var_index()];
270 if (SCIPvarGetLbLocal(scip_resultant_var) < 0.0) {
274 std::vector<SCIP_VAR*> vars;
275 std::vector<double> vals;
276 std::vector<SCIP_CONS*> cons;
277 auto add_abs_constraint =
278 [&](
const std::string& name_prefix) -> absl::Status {
279 SCIP_CONS* scip_cons =
nullptr;
280 CHECK(vars.size() == vals.size());
281 const std::string
name =
282 gen_cst.has_name() ? absl::StrCat(gen_cst.name(), name_prefix) :
"";
285 name.c_str(), vars.size(), vars.data(),
286 vals.data(), 0.0, 0.0));
289 cons.push_back(scip_cons);
290 return absl::OkStatus();
294 vars = {scip_resultant_var, scip_var};
303 const std::string
name =
304 gen_cst.has_name() ? absl::StrCat(gen_cst.name(),
"_disj") :
"";
306 scip, scip_cst,
name.c_str(),
307 cons.size(), cons.data(),
nullptr));
310 return absl::OkStatus();
313 absl::Status AddAndConstraint(
const MPGeneralConstraintProto& gen_cst,
314 const std::vector<SCIP_VAR*>& scip_variables,
315 SCIP* scip, SCIP_CONS** scip_cst,
316 std::vector<SCIP_VAR*>* tmp_variables) {
317 CHECK(scip !=
nullptr);
318 CHECK(scip_cst !=
nullptr);
319 CHECK(tmp_variables !=
nullptr);
320 CHECK(gen_cst.has_and_constraint());
321 const auto& andcst = gen_cst.and_constraint();
323 tmp_variables->resize(andcst.var_index_size(),
nullptr);
324 for (
int i = 0; i < andcst.var_index_size(); ++i) {
325 (*tmp_variables)[i] = scip_variables[andcst.var_index(i)];
329 gen_cst.name().c_str(),
330 scip_variables[andcst.resultant_var_index()],
331 andcst.var_index_size(),
332 tmp_variables->data()));
334 return absl::OkStatus();
337 absl::Status AddOrConstraint(
const MPGeneralConstraintProto& gen_cst,
338 const std::vector<SCIP_VAR*>& scip_variables,
339 SCIP* scip, SCIP_CONS** scip_cst,
340 std::vector<SCIP_VAR*>* tmp_variables) {
341 CHECK(scip !=
nullptr);
342 CHECK(scip_cst !=
nullptr);
343 CHECK(tmp_variables !=
nullptr);
344 CHECK(gen_cst.has_or_constraint());
345 const auto& orcst = gen_cst.or_constraint();
347 tmp_variables->resize(orcst.var_index_size(),
nullptr);
348 for (
int i = 0; i < orcst.var_index_size(); ++i) {
349 (*tmp_variables)[i] = scip_variables[orcst.var_index(i)];
353 gen_cst.name().c_str(),
354 scip_variables[orcst.resultant_var_index()],
355 orcst.var_index_size(),
356 tmp_variables->data()));
358 return absl::OkStatus();
366 absl::Status AddMinMaxConstraint(
const MPGeneralConstraintProto& gen_cst,
367 const std::vector<SCIP_VAR*>& scip_variables,
368 SCIP* scip, SCIP_CONS** scip_cst,
369 std::vector<SCIP_CONS*>* scip_constraints,
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_min_constraint() || gen_cst.has_max_constraint());
375 const auto& minmax = gen_cst.has_min_constraint() ? gen_cst.min_constraint()
376 : gen_cst.max_constraint();
377 const std::set<int> unique_var_indices(minmax.var_index().begin(),
378 minmax.var_index().end());
379 SCIP_VAR* scip_resultant_var = scip_variables[minmax.resultant_var_index()];
381 std::vector<SCIP_VAR*> vars;
382 std::vector<double> vals;
383 std::vector<SCIP_CONS*> cons;
384 auto add_lin_constraint = [&](
const std::string& name_prefix,
385 double lower_bound = 0.0,
386 double upper_bound = 0.0) -> absl::Status {
387 SCIP_CONS* scip_cons =
nullptr;
388 CHECK(vars.size() == vals.size());
389 const std::string
name =
390 gen_cst.has_name() ? absl::StrCat(gen_cst.name(), name_prefix) :
"";
393 name.c_str(), vars.size(), vars.data(),
394 vals.data(), lower_bound, upper_bound));
397 cons.push_back(scip_cons);
398 return absl::OkStatus();
402 for (
const int var_index : unique_var_indices) {
403 vars = {scip_resultant_var, scip_variables[var_index]};
409 if (minmax.has_constant()) {
410 vars = {scip_resultant_var};
413 add_lin_constraint(
"_constant", minmax.constant(), minmax.constant()));
417 const std::string
name =
418 gen_cst.has_name() ? absl::StrCat(gen_cst.name(),
"_disj") :
"";
420 scip, scip_cst,
name.c_str(),
421 cons.size(), cons.data(),
nullptr));
425 constexpr
double kInfinity = std::numeric_limits<double>::infinity();
427 for (
const int var_index : unique_var_indices) {
428 vars = {scip_resultant_var, scip_variables[var_index]};
430 if (gen_cst.has_min_constraint()) {
434 RETURN_IF_ERROR(add_lin_constraint(absl::StrCat(
"_ineq_", var_index), 0.0,
438 if (minmax.has_constant()) {
439 vars = {scip_resultant_var};
441 if (gen_cst.has_min_constraint()) {
449 for (SCIP_CONS* scip_cons : cons) {
450 scip_constraints->push_back(scip_cons);
453 return absl::OkStatus();
456 absl::Status AddQuadraticObjective(
const MPQuadraticObjective& quadobj,
458 std::vector<SCIP_VAR*>* scip_variables,
459 std::vector<SCIP_CONS*>* scip_constraints) {
460 CHECK(scip !=
nullptr);
461 CHECK(scip_variables !=
nullptr);
462 CHECK(scip_constraints !=
nullptr);
464 constexpr
double kInfinity = std::numeric_limits<double>::infinity();
466 const int size = quadobj.coefficient_size();
467 if (size == 0)
return absl::OkStatus();
471 scip_variables->push_back(
nullptr);
476 SCIP_VARTYPE_CONTINUOUS));
479 scip_constraints->push_back(
nullptr);
480 SCIP_VAR* linvars[1] = {scip_variables->back()};
481 double lincoefs[1] = {-1};
482 std::vector<SCIP_VAR*> quadvars1(size,
nullptr);
483 std::vector<SCIP_VAR*> quadvars2(size,
nullptr);
484 std::vector<double> quadcoefs(size, 0);
485 for (
int i = 0; i < size; ++i) {
486 quadvars1[i] = scip_variables->at(quadobj.qvar1_index(i));
487 quadvars2[i] = scip_variables->at(quadobj.qvar2_index(i));
488 quadcoefs[i] = quadobj.coefficient(i);
491 scip, &scip_constraints->back(),
"quadobj",
492 1, linvars, lincoefs,
493 size, quadvars1.data(),
494 quadvars2.data(), quadcoefs.data(),
498 return absl::OkStatus();
501 absl::Status AddSolutionHint(
const MPModelProto&
model, SCIP* scip,
502 const std::vector<SCIP_VAR*>& scip_variables) {
503 CHECK(scip !=
nullptr);
504 if (!
model.has_solution_hint())
return absl::OkStatus();
506 const PartialVariableAssignment& solution_hint =
model.solution_hint();
508 bool is_solution_partial =
509 solution_hint.var_index_size() !=
model.variable_size();
510 if (is_solution_partial) {
512 SCIPcreatePartialSol(scip, &solution,
nullptr));
515 SCIPcreateSol(scip, &solution,
nullptr));
518 for (
int i = 0; i < solution_hint.var_index_size(); ++i) {
520 scip, solution, scip_variables[solution_hint.var_index(i)],
521 solution_hint.var_value(i)));
527 return absl::OkStatus();
534 CHECK(scip !=
nullptr);
535 const double infinity = SCIPinfinity(scip);
537 for (
int v = 0; v <
model.variable_size(); ++v) {
538 const MPVariableProto& variable =
model.variable(v);
539 if (variable.lower_bound() >= infinity) {
540 return absl::StrFormat(
541 "Variable %i's lower bound is considered +infinity", v);
543 if (variable.upper_bound() <= -infinity) {
544 return absl::StrFormat(
545 "Variable %i's upper bound is considered -infinity", v);
547 const double coeff = variable.objective_coefficient();
548 if (coeff >= infinity || coeff <= -infinity) {
549 return absl::StrFormat(
550 "Variable %i's objective coefficient is considered infinite", v);
554 for (
int c = 0; c <
model.constraint_size(); ++c) {
555 const MPConstraintProto& cst =
model.constraint(c);
556 if (cst.lower_bound() >= infinity) {
557 return absl::StrFormat(
558 "Constraint %d's lower_bound is considered +infinity", c);
560 if (cst.upper_bound() <= -infinity) {
561 return absl::StrFormat(
562 "Constraint %d's upper_bound is considered -infinity", c);
564 for (
int i = 0; i < cst.coefficient_size(); ++i) {
565 if (std::abs(cst.coefficient(i)) >= infinity) {
566 return absl::StrFormat(
567 "Constraint %d's coefficient #%d is considered infinite", c, i);
572 for (
int c = 0; c <
model.general_constraint_size(); ++c) {
573 const MPGeneralConstraintProto& cst =
model.general_constraint(c);
574 switch (cst.general_constraint_case()) {
575 case MPGeneralConstraintProto::kQuadraticConstraint:
576 if (cst.quadratic_constraint().lower_bound() >= infinity) {
577 return absl::StrFormat(
578 "Quadratic constraint %d's lower_bound is considered +infinity",
581 if (cst.quadratic_constraint().upper_bound() <= -infinity) {
582 return absl::StrFormat(
583 "Quadratic constraint %d's upper_bound is considered -infinity",
586 for (
int i = 0; i < cst.quadratic_constraint().coefficient_size();
588 const double coefficient = cst.quadratic_constraint().coefficient(i);
590 return absl::StrFormat(
591 "Quadratic constraint %d's linear coefficient #%d considered "
596 for (
int i = 0; i < cst.quadratic_constraint().qcoefficient_size();
598 const double qcoefficient =
599 cst.quadratic_constraint().qcoefficient(i);
600 if (qcoefficient >= infinity || qcoefficient <= -infinity) {
601 return absl::StrFormat(
602 "Quadratic constraint %d's quadratic coefficient #%d "
603 "considered infinite",
608 case MPGeneralConstraintProto::kMinConstraint:
609 if (cst.min_constraint().constant() >= infinity ||
610 cst.min_constraint().constant() <= -infinity) {
611 return absl::StrFormat(
612 "Min constraint %d's coefficient constant considered infinite",
616 case MPGeneralConstraintProto::kMaxConstraint:
617 if (cst.max_constraint().constant() >= infinity ||
618 cst.max_constraint().constant() <= -infinity) {
619 return absl::StrFormat(
620 "Max constraint %d's coefficient constant considered infinite",
629 const MPQuadraticObjective& quad_obj =
model.quadratic_objective();
630 for (
int i = 0; i < quad_obj.coefficient_size(); ++i) {
631 if (std::abs(quad_obj.coefficient(i)) >= infinity) {
632 return absl::StrFormat(
633 "Quadratic objective term #%d's coefficient is considered infinite",
638 if (
model.has_solution_hint()) {
639 for (
int i = 0; i <
model.solution_hint().var_value_size(); ++i) {
640 const double value =
model.solution_hint().var_value(i);
641 if (
value >= infinity ||
value <= -infinity) {
642 return absl::StrFormat(
643 "Variable %i's solution hint is considered infinite",
644 model.solution_hint().var_index(i));
649 if (
model.objective_offset() >= infinity ||
650 model.objective_offset() <= -infinity) {
651 return "Model's objective offset is considered infinite.";
658 const MPModelRequest& request) {
660 const absl::optional<LazyMutableCopy<MPModelProto>> optional_model =
662 if (!optional_model)
return response;
663 const MPModelProto&
model = optional_model->get();
664 SCIP* scip =
nullptr;
665 std::vector<SCIP_VAR*> scip_variables(
model.variable_size(),
nullptr);
666 std::vector<SCIP_CONS*> scip_constraints(
667 model.constraint_size() +
model.general_constraint_size(),
nullptr);
669 auto delete_scip_objects = [&]() -> absl::Status {
671 if (scip ==
nullptr)
return absl::OkStatus();
672 for (SCIP_VAR* variable : scip_variables) {
673 if (variable !=
nullptr) {
677 for (SCIP_CONS* constraint : scip_constraints) {
678 if (constraint !=
nullptr) {
683 return absl::OkStatus();
687 const absl::Status deleter_status = delete_scip_objects();
688 LOG_IF(DFATAL, !deleter_status.ok()) << deleter_status;
693 const std::string scip_model_invalid_error =
695 if (!scip_model_invalid_error.empty()) {
696 response.set_status(MPSOLVER_MODEL_INVALID);
697 response.set_status_str(scip_model_invalid_error);
702 request.solver_specific_parameters(), scip);
703 if (!parameters_status.ok()) {
704 response.set_status(MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS);
706 std::string(parameters_status.message()));
716 SCIPsetIntParam(scip,
"timing/clocktype", SCIP_CLOCKTYPE_WALL));
717 if (request.solver_time_limit_seconds() > 0 &&
718 request.solver_time_limit_seconds() < 1e20) {
720 request.solver_time_limit_seconds()));
722 SCIPsetMessagehdlrQuiet(scip, !request.enable_internal_solver_output());
725 if (
model.maximize()) {
729 for (
int v = 0; v <
model.variable_size(); ++v) {
730 const MPVariableProto& variable =
model.variable(v);
732 scip, &scip_variables[v], variable.name().c_str(),
733 variable.lower_bound(), variable.upper_bound(),
734 variable.objective_coefficient(),
735 variable.is_integer() ? SCIP_VARTYPE_INTEGER
736 : SCIP_VARTYPE_CONTINUOUS));
741 std::vector<SCIP_VAR*> ct_variables;
742 std::vector<double> ct_coefficients;
743 for (
int c = 0; c <
model.constraint_size(); ++c) {
744 const MPConstraintProto& constraint =
model.constraint(c);
745 const int size = constraint.var_index_size();
746 ct_variables.resize(size,
nullptr);
747 ct_coefficients.resize(size, 0);
748 for (
int i = 0; i < size; ++i) {
749 ct_variables[i] = scip_variables[constraint.var_index(i)];
750 ct_coefficients[i] = constraint.coefficient(i);
753 scip, &scip_constraints[c],
754 constraint.name().c_str(),
755 constraint.var_index_size(), ct_variables.data(),
756 ct_coefficients.data(),
757 constraint.lower_bound(), constraint.upper_bound(),
758 !constraint.is_lazy(),
766 constraint.is_lazy(),
772 std::vector<SCIP_VAR*> ct_qvariables1;
773 std::vector<SCIP_VAR*> ct_qvariables2;
774 std::vector<double> ct_qcoefficients;
775 const int lincst_size =
model.constraint_size();
776 for (
int c = 0; c <
model.general_constraint_size(); ++c) {
777 const MPGeneralConstraintProto& gen_cst =
model.general_constraint(c);
778 switch (gen_cst.general_constraint_case()) {
779 case MPGeneralConstraintProto::kIndicatorConstraint: {
781 gen_cst, scip, &scip_constraints[lincst_size + c],
782 &scip_variables, &scip_constraints, &ct_variables,
786 case MPGeneralConstraintProto::kSosConstraint: {
788 &scip_constraints[lincst_size + c],
789 &ct_variables, &ct_coefficients));
792 case MPGeneralConstraintProto::kQuadraticConstraint: {
794 gen_cst, scip_variables, scip, &scip_constraints[lincst_size + c],
795 &ct_variables, &ct_coefficients, &ct_qvariables1, &ct_qvariables2,
799 case MPGeneralConstraintProto::kAbsConstraint: {
801 &scip_constraints[lincst_size + c]));
804 case MPGeneralConstraintProto::kAndConstraint: {
806 &scip_constraints[lincst_size + c],
810 case MPGeneralConstraintProto::kOrConstraint: {
812 &scip_constraints[lincst_size + c],
816 case MPGeneralConstraintProto::kMinConstraint:
817 case MPGeneralConstraintProto::kMaxConstraint: {
819 gen_cst, scip_variables, scip, &scip_constraints[lincst_size + c],
820 &scip_constraints, &ct_variables));
824 return absl::UnimplementedError(
825 absl::StrFormat(
"General constraints of type %i not supported.",
826 gen_cst.general_constraint_case()));
831 if (
model.has_quadratic_objective()) {
833 &scip_variables, &scip_constraints));
838 if (!absl::GetFlag(FLAGS_scip_proto_solver_output_cip_file).empty()) {
839 SCIPwriteOrigProblem(
840 scip, absl::GetFlag(FLAGS_scip_proto_solver_output_cip_file).c_str(),
845 SCIP_SOL*
const solution = SCIPgetBestSol(scip);
846 if (solution !=
nullptr) {
847 response.set_objective_value(SCIPgetSolOrigObj(scip, solution));
848 response.set_best_objective_bound(SCIPgetDualbound(scip));
849 for (
int v = 0; v <
model.variable_size(); ++v) {
850 double value = SCIPgetSolVal(scip, solution, scip_variables[v]);
856 const SCIP_STATUS scip_status = SCIPgetStatus(scip);
857 switch (scip_status) {
858 case SCIP_STATUS_OPTIMAL:
859 response.set_status(MPSOLVER_OPTIMAL);
861 case SCIP_STATUS_GAPLIMIT:
863 response.set_status(MPSOLVER_OPTIMAL);
865 case SCIP_STATUS_INFORUNBD:
872 DLOG(
INFO) <<
"SCIP solve returned SCIP_STATUS_INFORUNBD, which we treat "
873 "as INFEASIBLE even though it may mean UNBOUNDED.";
875 "The model may actually be unbounded: SCIP returned "
876 "SCIP_STATUS_INFORUNBD");
877 ABSL_FALLTHROUGH_INTENDED;
878 case SCIP_STATUS_INFEASIBLE:
879 response.set_status(MPSOLVER_INFEASIBLE);
881 case SCIP_STATUS_UNBOUNDED:
882 response.set_status(MPSOLVER_UNBOUNDED);
885 if (solution !=
nullptr) {
886 response.set_status(MPSOLVER_FEASIBLE);
888 response.set_status(MPSOLVER_NOT_SOLVED);
889 response.set_status_str(absl::StrFormat(
"SCIP status code %d",
890 static_cast<int>(scip_status)));
895 VLOG(1) <<
"ScipSolveProto() status="
896 << MPSolverResponseStatus_Name(
response.status()) <<
".";
#define LOG_IF(severity, condition)
#define CHECK_EQ(val1, val2)
#define VLOG(verboselevel)
SharedResponseManager * response
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
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.")
#define RETURN_IF_ERROR(expr)