22 #include "absl/container/flat_hash_map.h" 23 #include "absl/strings/match.h" 24 #include "absl/strings/str_cat.h" 25 #include "absl/strings/str_format.h" 26 #include "absl/synchronization/mutex.h" 27 #include "google/protobuf/text_format.h" 51 ABSL_FLAG(int64_t, fz_int_max, int64_t{1} << 50,
52 "Default max value for unbounded integer variables.");
67 int TrueLiteral(
int var) {
return var; }
68 int FalseLiteral(
int var) {
return -
var - 1; }
69 int NegatedCpModelVariable(
int var) {
return -
var - 1; }
72 struct CpModelProtoWithMapping {
74 int LookupConstant(int64_t
value);
78 int LookupVar(
const fz::Argument& argument);
79 LinearExpressionProto LookupExpr(
const fz::Argument& argument,
81 LinearExpressionProto LookupExprAt(
const fz::Argument& argument,
int pos,
83 std::vector<int> LookupVars(
const fz::Argument& argument);
84 std::vector<VarOrValue> LookupVarsOrValues(
const fz::Argument& argument);
89 std::vector<int> CreateIntervals(
const std::vector<int>& starts,
90 const std::vector<VarOrValue>& sizes);
97 int GetOrCreateOptionalInterval(
int start_var, VarOrValue size,
int opt_var);
101 ConstraintProto* AddEnforcedConstraint(
int literal);
104 void FillAMinusBInDomain(
const std::vector<int64_t>& domain,
105 const fz::Constraint& fz_ct, ConstraintProto*
ct);
106 void FillLinearConstraintWithGivenDomain(
const std::vector<int64_t>& domain,
107 const fz::Constraint& fz_ct,
108 ConstraintProto*
ct);
109 void FillConstraint(
const fz::Constraint& fz_ct, ConstraintProto*
ct);
110 void FillReifOrImpliedConstraint(
const fz::Constraint& fz_ct,
111 ConstraintProto*
ct);
115 void TranslateSearchAnnotations(
116 const std::vector<fz::Annotation>& search_annotations);
125 absl::flat_hash_map<std::tuple<int, int, int>,
int>
127 absl::flat_hash_map<std::tuple<int, int64_t, int>,
int>
131 int CpModelProtoWithMapping::LookupConstant(int64_t
value) {
140 var_proto->add_domain(
value);
145 int CpModelProtoWithMapping::LookupVar(
const fz::Argument& argument) {
146 if (argument.HasOneValue())
return LookupConstant(argument.Value());
151 LinearExpressionProto CpModelProtoWithMapping::LookupExpr(
152 const fz::Argument& argument,
bool negate) {
153 LinearExpressionProto expr;
154 if (argument.HasOneValue()) {
155 const int64_t
value = argument.Value();
158 expr.add_vars(LookupVar(argument));
159 expr.add_coeffs(negate ? -1 : 1);
164 LinearExpressionProto CpModelProtoWithMapping::LookupExprAt(
165 const fz::Argument& argument,
int pos,
bool negate) {
166 LinearExpressionProto expr;
167 if (argument.HasOneValueAt(pos)) {
168 const int64_t
value = argument.ValueAt(pos);
172 expr.add_coeffs(negate ? -1 : 1);
177 std::vector<int> CpModelProtoWithMapping::LookupVars(
178 const fz::Argument& argument) {
179 std::vector<int> result;
182 for (int64_t
value : argument.values) {
183 result.push_back(LookupConstant(
value));
186 result.push_back(LookupConstant(argument.Value()));
189 for (fz::Variable*
var : argument.variables) {
197 std::vector<VarOrValue> CpModelProtoWithMapping::LookupVarsOrValues(
198 const fz::Argument& argument) {
199 std::vector<VarOrValue> result;
200 const int no_var = kNoVar;
203 for (int64_t
value : argument.values) {
204 result.push_back({no_var,
value});
207 result.push_back({no_var, argument.Value()});
210 for (fz::Variable*
var : argument.variables) {
212 if (
var->domain.HasOneValue()) {
213 result.push_back({no_var,
var->domain.Value()});
222 ConstraintProto* CpModelProtoWithMapping::AddEnforcedConstraint(
int literal) {
230 int CpModelProtoWithMapping::GetOrCreateOptionalInterval(
int start_var,
234 if (size.var == kNoVar) {
235 const std::tuple<int, int64_t, int> key =
236 std::make_tuple(start_var, size.value, opt_var);
237 const auto [it, inserted] =
243 auto*
interval = AddEnforcedConstraint(opt_var)->mutable_interval();
244 interval->mutable_start()->add_vars(start_var);
245 interval->mutable_start()->add_coeffs(1);
246 interval->mutable_size()->set_offset(size.value);
247 interval->mutable_end()->add_vars(start_var);
248 interval->mutable_end()->add_coeffs(1);
249 interval->mutable_end()->set_offset(size.value);
251 return interval_index;
253 const std::tuple<int, int, int> key =
254 std::make_tuple(start_var, size.var, opt_var);
255 const auto [it, inserted] =
268 auto*
interval = AddEnforcedConstraint(opt_var)->mutable_interval();
269 interval->mutable_start()->add_vars(start_var);
270 interval->mutable_start()->add_coeffs(1);
271 interval->mutable_size()->add_vars(size.var);
272 interval->mutable_size()->add_coeffs(1);
273 interval->mutable_end()->add_vars(end_var);
274 interval->mutable_end()->add_coeffs(1);
278 auto* lin = AddEnforcedConstraint(opt_var)->mutable_linear();
279 lin->add_vars(start_var);
281 lin->add_vars(size.var);
283 lin->add_vars(end_var);
288 return interval_index;
292 std::vector<int> CpModelProtoWithMapping::CreateIntervals(
293 const std::vector<int>& starts,
const std::vector<VarOrValue>& sizes) {
294 std::vector<int> intervals;
295 for (
int i = 0; i < starts.size(); ++i) {
297 GetOrCreateOptionalInterval(starts[i], sizes[i], kNoVar));
302 void CpModelProtoWithMapping::FillAMinusBInDomain(
303 const std::vector<int64_t>& domain,
const fz::Constraint& fz_ct,
304 ConstraintProto*
ct) {
305 auto* arg =
ct->mutable_linear();
307 const int64_t
value = fz_ct.arguments[1].Value();
308 const int var_a = LookupVar(fz_ct.arguments[0]);
309 for (
const int64_t domain_bound : domain) {
312 arg->add_domain(domain_bound);
314 arg->add_domain(domain_bound +
value);
317 arg->add_vars(var_a);
320 const int64_t
value = fz_ct.arguments[0].Value();
321 const int var_b = LookupVar(fz_ct.arguments[1]);
328 arg->add_domain(
value - domain_bound);
331 arg->add_vars(var_b);
334 for (
const int64_t domain_bound : domain) arg->add_domain(domain_bound);
335 arg->add_vars(LookupVar(fz_ct.arguments[0]));
337 arg->add_vars(LookupVar(fz_ct.arguments[1]));
342 void CpModelProtoWithMapping::FillLinearConstraintWithGivenDomain(
343 const std::vector<int64_t>& domain,
const fz::Constraint& fz_ct,
344 ConstraintProto*
ct) {
345 auto* arg =
ct->mutable_linear();
346 for (
const int64_t domain_bound : domain) arg->add_domain(domain_bound);
347 std::vector<int> vars = LookupVars(fz_ct.arguments[1]);
348 for (
int i = 0; i < vars.size(); ++i) {
349 arg->add_vars(vars[i]);
350 arg->add_coeffs(fz_ct.arguments[0].values[i]);
354 void CpModelProtoWithMapping::FillConstraint(
const fz::Constraint& fz_ct,
355 ConstraintProto*
ct) {
356 if (fz_ct.type ==
"false_constraint") {
358 ct->mutable_bool_or();
359 }
else if (fz_ct.type ==
"bool_clause") {
360 auto* arg =
ct->mutable_bool_or();
361 for (
const int var : LookupVars(fz_ct.arguments[0])) {
362 arg->add_literals(TrueLiteral(
var));
364 for (
const int var : LookupVars(fz_ct.arguments[1])) {
365 arg->add_literals(FalseLiteral(
var));
367 }
else if (fz_ct.type ==
"bool_xor") {
370 const int a = LookupVar(fz_ct.arguments[0]);
371 const int b = LookupVar(fz_ct.arguments[1]);
372 const int x = LookupVar(fz_ct.arguments[2]);
376 auto*
const refute =
ct->mutable_linear();
378 refute->add_coeffs(1);
380 refute->add_coeffs(-1);
381 refute->add_domain(0);
382 refute->add_domain(0);
385 auto* enforce = AddEnforcedConstraint(x)->mutable_linear();
386 enforce->add_vars(
a);
387 enforce->add_coeffs(1);
388 enforce->add_vars(
b);
389 enforce->add_coeffs(1);
390 enforce->add_domain(1);
391 enforce->add_domain(1);
392 }
else if (fz_ct.type ==
"array_bool_or") {
393 auto* arg =
ct->mutable_bool_or();
394 for (
const int var : LookupVars(fz_ct.arguments[0])) {
395 arg->add_literals(TrueLiteral(
var));
397 }
else if (fz_ct.type ==
"array_bool_or_negated") {
398 auto* arg =
ct->mutable_bool_and();
399 for (
const int var : LookupVars(fz_ct.arguments[0])) {
400 arg->add_literals(FalseLiteral(
var));
402 }
else if (fz_ct.type ==
"array_bool_and") {
403 auto* arg =
ct->mutable_bool_and();
404 for (
const int var : LookupVars(fz_ct.arguments[0])) {
405 arg->add_literals(TrueLiteral(
var));
407 }
else if (fz_ct.type ==
"array_bool_and_negated") {
408 auto* arg =
ct->mutable_bool_or();
409 for (
const int var : LookupVars(fz_ct.arguments[0])) {
410 arg->add_literals(FalseLiteral(
var));
412 }
else if (fz_ct.type ==
"array_bool_xor") {
413 auto* arg =
ct->mutable_bool_xor();
414 for (
const int var : LookupVars(fz_ct.arguments[0])) {
415 arg->add_literals(TrueLiteral(
var));
417 }
else if (fz_ct.type ==
"bool_le" || fz_ct.type ==
"int_le") {
419 }
else if (fz_ct.type ==
"bool_ge" || fz_ct.type ==
"int_ge") {
421 }
else if (fz_ct.type ==
"bool_lt" || fz_ct.type ==
"int_lt") {
423 }
else if (fz_ct.type ==
"bool_gt" || fz_ct.type ==
"int_gt") {
425 }
else if (fz_ct.type ==
"bool_eq" || fz_ct.type ==
"int_eq" ||
426 fz_ct.type ==
"bool2int") {
427 FillAMinusBInDomain({0, 0}, fz_ct,
ct);
428 }
else if (fz_ct.type ==
"bool_ne" || fz_ct.type ==
"bool_not") {
429 auto* arg =
ct->mutable_linear();
430 arg->add_vars(LookupVar(fz_ct.arguments[0]));
432 arg->add_vars(LookupVar(fz_ct.arguments[1]));
436 }
else if (fz_ct.type ==
"int_ne") {
440 }
else if (fz_ct.type ==
"int_lin_eq") {
441 const int64_t rhs = fz_ct.arguments[2].values[0];
442 FillLinearConstraintWithGivenDomain({rhs, rhs}, fz_ct,
ct);
443 }
else if (fz_ct.type ==
"bool_lin_eq") {
444 auto* arg =
ct->mutable_linear();
445 const std::vector<int> vars = LookupVars(fz_ct.arguments[1]);
446 for (
int i = 0; i < vars.size(); ++i) {
447 arg->add_vars(vars[i]);
448 arg->add_coeffs(fz_ct.arguments[0].values[i]);
450 if (fz_ct.arguments[2].IsVariable()) {
451 arg->add_vars(LookupVar(fz_ct.arguments[2]));
456 const int64_t v = fz_ct.arguments[2].Value();
460 }
else if (fz_ct.type ==
"int_lin_le" || fz_ct.type ==
"bool_lin_le") {
461 const int64_t rhs = fz_ct.arguments[2].values[0];
462 FillLinearConstraintWithGivenDomain(
464 }
else if (fz_ct.type ==
"int_lin_lt") {
465 const int64_t rhs = fz_ct.arguments[2].values[0];
466 FillLinearConstraintWithGivenDomain(
468 }
else if (fz_ct.type ==
"int_lin_ge") {
469 const int64_t rhs = fz_ct.arguments[2].values[0];
470 FillLinearConstraintWithGivenDomain(
472 }
else if (fz_ct.type ==
"int_lin_gt") {
473 const int64_t rhs = fz_ct.arguments[2].values[0];
474 FillLinearConstraintWithGivenDomain(
476 }
else if (fz_ct.type ==
"int_lin_ne") {
477 const int64_t rhs = fz_ct.arguments[2].values[0];
478 FillLinearConstraintWithGivenDomain(
482 }
else if (fz_ct.type ==
"set_in") {
483 auto* arg =
ct->mutable_linear();
484 arg->add_vars(LookupVar(fz_ct.arguments[0]));
488 fz_ct.arguments[1].values.begin(),
489 fz_ct.arguments[1].values.end()}),
493 Domain(fz_ct.arguments[1].values[0], fz_ct.arguments[1].values[1]),
498 }
else if (fz_ct.type ==
"set_in_negated") {
499 auto* arg =
ct->mutable_linear();
500 arg->add_vars(LookupVar(fz_ct.arguments[0]));
505 std::vector<int64_t>{fz_ct.arguments[1].values.begin(),
506 fz_ct.arguments[1].values.end()})
511 Domain(fz_ct.arguments[1].values[0], fz_ct.arguments[1].values[1])
517 }
else if (fz_ct.type ==
"int_min") {
518 auto* arg =
ct->mutable_lin_max();
519 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0],
true);
520 *arg->add_exprs() = LookupExpr(fz_ct.arguments[1],
true);
521 *arg->mutable_target() = LookupExpr(fz_ct.arguments[2],
true);
522 }
else if (fz_ct.type ==
"array_int_minimum" || fz_ct.type ==
"minimum_int") {
523 auto* arg =
ct->mutable_lin_max();
524 *arg->mutable_target() = LookupExpr(fz_ct.arguments[0],
true);
525 for (
int i = 0; i < fz_ct.arguments[1].Size(); ++i) {
526 *arg->add_exprs() = LookupExprAt(fz_ct.arguments[1], i,
true);
528 }
else if (fz_ct.type ==
"int_max") {
529 auto* arg =
ct->mutable_lin_max();
530 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0]);
531 *arg->add_exprs() = LookupExpr(fz_ct.arguments[1]);
532 *arg->mutable_target() = LookupExpr(fz_ct.arguments[2]);
533 }
else if (fz_ct.type ==
"array_int_maximum" || fz_ct.type ==
"maximum_int") {
534 auto* arg =
ct->mutable_lin_max();
535 *arg->mutable_target() = LookupExpr(fz_ct.arguments[0]);
536 for (
int i = 0; i < fz_ct.arguments[1].Size(); ++i) {
537 *arg->add_exprs() = LookupExprAt(fz_ct.arguments[1], i);
539 }
else if (fz_ct.type ==
"int_times") {
540 auto* arg =
ct->mutable_int_prod();
541 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0]);
542 *arg->add_exprs() = LookupExpr(fz_ct.arguments[1]);
543 *arg->mutable_target() = LookupExpr(fz_ct.arguments[2]);
544 }
else if (fz_ct.type ==
"int_abs") {
545 auto* arg =
ct->mutable_lin_max();
546 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0]);
547 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0],
true);
548 *arg->mutable_target() = LookupExpr(fz_ct.arguments[1]);
549 }
else if (fz_ct.type ==
"int_plus") {
550 auto* arg =
ct->mutable_linear();
552 arg->add_vars(LookupVar(fz_ct.arguments[0]));
554 arg->add_vars(LookupVar(fz_ct.arguments[1]));
556 arg->add_vars(LookupVar(fz_ct.arguments[2]));
558 }
else if (fz_ct.type ==
"int_div") {
559 auto* arg =
ct->mutable_int_div();
560 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0]);
561 *arg->add_exprs() = LookupExpr(fz_ct.arguments[1]);
562 *arg->mutable_target() = LookupExpr(fz_ct.arguments[2]);
563 }
else if (fz_ct.type ==
"int_mod") {
564 auto* arg =
ct->mutable_int_mod();
565 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0]);
566 *arg->add_exprs() = LookupExpr(fz_ct.arguments[1]);
567 *arg->mutable_target() = LookupExpr(fz_ct.arguments[2]);
568 }
else if (fz_ct.type ==
"array_int_element" ||
569 fz_ct.type ==
"array_bool_element" ||
570 fz_ct.type ==
"array_var_int_element" ||
571 fz_ct.type ==
"array_var_bool_element" ||
572 fz_ct.type ==
"array_int_element_nonshifted") {
575 auto* arg =
ct->mutable_element();
576 arg->set_index(LookupVar(fz_ct.arguments[0]));
577 arg->set_target(LookupVar(fz_ct.arguments[2]));
579 if (!absl::EndsWith(fz_ct.type,
"_nonshifted")) {
583 arg->add_vars(LookupConstant(0));
585 for (
const int var : LookupVars(fz_ct.arguments[1])) arg->add_vars(
var);
589 CHECK(!absl::EndsWith(fz_ct.type,
"_nonshifted"));
590 auto* arg =
ct->mutable_table();
594 for (
const int var : LookupVars(fz_ct.arguments[0])) arg->add_vars(
var);
595 arg->add_vars(LookupVar(fz_ct.arguments[2]));
597 const std::vector<int64_t>& values = fz_ct.arguments[1].values;
598 const int64_t coeff1 = fz_ct.arguments[3].values[0];
599 const int64_t coeff2 = fz_ct.arguments[3].values[1];
600 const int64_t offset = fz_ct.arguments[4].values[0] - 1;
603 for (
const int64_t
b :
605 const int index = coeff1 *
a + coeff2 *
b + offset;
610 arg->add_values(values[
index]);
614 }
else if (fz_ct.type ==
"ortools_table_int") {
615 auto* arg =
ct->mutable_table();
616 for (
const int var : LookupVars(fz_ct.arguments[0])) arg->add_vars(
var);
617 for (
const int64_t
value : fz_ct.arguments[1].values)
618 arg->add_values(
value);
619 }
else if (fz_ct.type ==
"ortools_regular") {
620 auto* arg =
ct->mutable_automaton();
621 for (
const int var : LookupVars(fz_ct.arguments[0])) arg->add_vars(
var);
624 const int num_states = fz_ct.arguments[1].Value();
625 const int num_values = fz_ct.arguments[2].Value();
626 for (
int i = 1; i <= num_states; ++i) {
627 for (
int j = 1; j <= num_values; ++j) {
628 CHECK_LT(count, fz_ct.arguments[3].values.size());
629 const int next = fz_ct.arguments[3].values[count++];
630 if (
next == 0)
continue;
631 arg->add_transition_tail(i);
632 arg->add_transition_label(j);
633 arg->add_transition_head(
next);
637 arg->set_starting_state(fz_ct.arguments[4].Value());
638 switch (fz_ct.arguments[5].type) {
640 arg->add_final_states(fz_ct.arguments[5].values[0]);
644 for (
int v = fz_ct.arguments[5].values[0];
645 v <= fz_ct.arguments[5].values[1]; ++v) {
646 arg->add_final_states(v);
651 for (
const int v : fz_ct.arguments[5].values) {
652 arg->add_final_states(v);
657 LOG(
FATAL) <<
"Wrong constraint " << fz_ct.DebugString();
660 }
else if (fz_ct.type ==
"fzn_all_different_int") {
661 auto* arg =
ct->mutable_all_diff();
662 for (
int i = 0; i < fz_ct.arguments[0].Size(); ++i) {
663 *arg->add_exprs() = LookupExprAt(fz_ct.arguments[0], i);
665 }
else if (fz_ct.type ==
"ortools_circuit" ||
666 fz_ct.type ==
"ortools_subcircuit") {
667 const int64_t min_index = fz_ct.arguments[1].Value();
668 const int size =
std::max(fz_ct.arguments[0].values.size(),
669 fz_ct.arguments[0].variables.size());
671 const int64_t max_index = min_index + size - 1;
673 auto* circuit_arg =
ct->mutable_circuit();
677 int64_t
index = min_index;
678 const bool is_circuit = (fz_ct.type ==
"ortools_circuit");
679 for (
const int var : LookupVars(fz_ct.arguments[0])) {
692 for (
const ClosedInterval
interval : domain.intervals()) {
699 new_var->add_domain(1);
703 circuit_arg->add_tails(
index);
704 circuit_arg->add_heads(
value);
705 circuit_arg->add_literals(
literal);
709 auto* lin = AddEnforcedConstraint(
literal)->mutable_linear();
712 lin->add_domain(
value);
713 lin->add_domain(
value);
723 lin->add_domain(
value - 1);
724 lin->add_domain(
value + 1);
732 }
else if (fz_ct.type ==
"ortools_inverse") {
733 auto* arg =
ct->mutable_inverse();
735 const auto direct_variables = LookupVars(fz_ct.arguments[0]);
736 const auto inverse_variables = LookupVars(fz_ct.arguments[1]);
737 const int base_direct = fz_ct.arguments[2].Value();
738 const int base_inverse = fz_ct.arguments[3].Value();
740 CHECK_EQ(direct_variables.size(), inverse_variables.size());
741 const int num_variables = direct_variables.size();
742 const int end_direct = base_direct + num_variables;
743 const int end_inverse = base_inverse + num_variables;
765 const int arity =
std::max(base_inverse, base_direct) + num_variables;
766 for (
int i = 0; i < arity; ++i) {
768 if (i < base_direct) {
769 if (i < base_inverse) {
770 arg->add_f_direct(LookupConstant(i));
771 }
else if (i >= base_inverse) {
772 arg->add_f_direct(LookupConstant(i + num_variables));
774 }
else if (i >= base_direct && i < end_direct) {
775 arg->add_f_direct(direct_variables[i - base_direct]);
777 arg->add_f_direct(LookupConstant(i - num_variables));
781 if (i < base_inverse) {
782 if (i < base_direct) {
783 arg->add_f_inverse(LookupConstant(i));
784 }
else if (i >= base_direct) {
785 arg->add_f_inverse(LookupConstant(i + num_variables));
787 }
else if (i >= base_inverse && i < end_inverse) {
788 arg->add_f_inverse(inverse_variables[i - base_inverse]);
790 arg->add_f_inverse(LookupConstant(i - num_variables));
793 }
else if (fz_ct.type ==
"fzn_cumulative") {
794 const std::vector<int> starts = LookupVars(fz_ct.arguments[0]);
795 const std::vector<VarOrValue> sizes =
796 LookupVarsOrValues(fz_ct.arguments[1]);
797 const std::vector<VarOrValue> demands =
798 LookupVarsOrValues(fz_ct.arguments[2]);
800 auto* arg =
ct->mutable_cumulative();
801 if (fz_ct.arguments[3].HasOneValue()) {
802 arg->mutable_capacity()->set_offset(fz_ct.arguments[3].Value());
804 arg->mutable_capacity()->add_vars(LookupVar(fz_ct.arguments[3]));
805 arg->mutable_capacity()->add_coeffs(1);
807 for (
int i = 0; i < starts.size(); ++i) {
810 if (demands[i].
var != kNoVar &&
814 fz_ct.arguments[3].HasOneValue() && fz_ct.arguments[3].Value() == 1) {
816 GetOrCreateOptionalInterval(starts[i], sizes[i], demands[i].
var));
817 arg->add_demands()->set_offset(1);
820 GetOrCreateOptionalInterval(starts[i], sizes[i], kNoVar));
821 LinearExpressionProto*
demand = arg->add_demands();
822 if (demands[i].
var == kNoVar) {
830 }
else if (fz_ct.type ==
"fzn_diffn" || fz_ct.type ==
"fzn_diffn_nonstrict") {
831 const std::vector<int> x = LookupVars(fz_ct.arguments[0]);
832 const std::vector<int> y = LookupVars(fz_ct.arguments[1]);
833 const std::vector<VarOrValue> dx = LookupVarsOrValues(fz_ct.arguments[2]);
834 const std::vector<VarOrValue> dy = LookupVarsOrValues(fz_ct.arguments[3]);
835 const std::vector<int> x_intervals = CreateIntervals(x, dx);
836 const std::vector<int> y_intervals = CreateIntervals(y, dy);
837 auto* arg =
ct->mutable_no_overlap_2d();
838 for (
int i = 0; i < x.size(); ++i) {
839 arg->add_x_intervals(x_intervals[i]);
840 arg->add_y_intervals(y_intervals[i]);
842 arg->set_boxes_with_null_area_can_overlap(fz_ct.type ==
843 "fzn_diffn_nonstrict");
844 }
else if (fz_ct.type ==
"ortools_network_flow" ||
845 fz_ct.type ==
"ortools_network_flow_cost") {
848 const bool has_cost = fz_ct.type ==
"ortools_network_flow_cost";
849 const std::vector<int> flow = LookupVars(fz_ct.arguments[has_cost ? 3 : 2]);
852 const int num_nodes = fz_ct.arguments[1].values.size();
853 std::vector<std::vector<int>> flows_per_node(num_nodes);
854 std::vector<std::vector<int>> coeffs_per_node(num_nodes);
855 const int num_arcs = fz_ct.arguments[0].values.size() / 2;
856 for (
int arc = 0; arc < num_arcs; arc++) {
857 const int tail = fz_ct.arguments[0].values[2 * arc] - 1;
858 const int head = fz_ct.arguments[0].values[2 * arc + 1] - 1;
861 flows_per_node[
tail].push_back(flow[arc]);
862 coeffs_per_node[
tail].push_back(1);
863 flows_per_node[
head].push_back(flow[arc]);
864 coeffs_per_node[
head].push_back(-1);
866 for (
int node = 0; node < num_nodes; node++) {
868 arg->
add_domain(fz_ct.arguments[1].values[node]);
869 arg->add_domain(fz_ct.arguments[1].values[node]);
870 for (
int i = 0; i < flows_per_node[node].size(); ++i) {
871 arg->add_vars(flows_per_node[node][i]);
872 arg->add_coeffs(coeffs_per_node[node][i]);
880 for (
int arc = 0; arc < num_arcs; arc++) {
881 const int64_t
weight = fz_ct.arguments[2].values[arc];
883 arg->add_vars(flow[arc]);
887 arg->add_vars(LookupVar(fz_ct.arguments[4]));
891 LOG(
FATAL) <<
" Not supported " << fz_ct.type;
895 void CpModelProtoWithMapping::FillReifOrImpliedConstraint(
896 const fz::Constraint& fz_ct, ConstraintProto*
ct) {
898 std::string simplified_type;
899 if (absl::EndsWith(fz_ct.type,
"_reif")) {
901 simplified_type = fz_ct.type.substr(0, fz_ct.type.size() - 5);
902 }
else if (absl::EndsWith(fz_ct.type,
"_imp")) {
904 simplified_type = fz_ct.type.substr(0, fz_ct.type.size() - 4);
907 simplified_type = fz_ct.type;
911 fz::Constraint copy = fz_ct;
912 copy.type = simplified_type;
915 FillConstraint(copy,
ct);
918 std::string negated_type;
921 if (simplified_type ==
"array_bool_or") {
922 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[1])));
923 negated_type =
"array_bool_or_negated";
924 }
else if (simplified_type ==
"array_bool_and") {
925 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[1])));
926 negated_type =
"array_bool_and_negated";
927 }
else if (simplified_type ==
"set_in") {
928 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[2])));
929 negated_type =
"set_in_negated";
930 }
else if (simplified_type ==
"bool_eq" || simplified_type ==
"int_eq") {
931 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[2])));
932 negated_type =
"int_ne";
933 }
else if (simplified_type ==
"bool_ne" || simplified_type ==
"int_ne") {
934 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[2])));
935 negated_type =
"int_eq";
936 }
else if (simplified_type ==
"bool_le" || simplified_type ==
"int_le") {
937 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[2])));
938 negated_type =
"int_gt";
939 }
else if (simplified_type ==
"bool_lt" || simplified_type ==
"int_lt") {
940 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[2])));
941 negated_type =
"int_ge";
942 }
else if (simplified_type ==
"bool_ge" || simplified_type ==
"int_ge") {
943 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[2])));
944 negated_type =
"int_lt";
945 }
else if (simplified_type ==
"bool_gt" || simplified_type ==
"int_gt") {
946 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[2])));
947 negated_type =
"int_le";
948 }
else if (simplified_type ==
"int_lin_eq") {
949 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[3])));
950 negated_type =
"int_lin_ne";
951 }
else if (simplified_type ==
"int_lin_ne") {
952 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[3])));
953 negated_type =
"int_lin_eq";
954 }
else if (simplified_type ==
"int_lin_le") {
955 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[3])));
956 negated_type =
"int_lin_gt";
957 }
else if (simplified_type ==
"int_lin_ge") {
958 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[3])));
959 negated_type =
"int_lin_lt";
960 }
else if (simplified_type ==
"int_lin_lt") {
961 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[3])));
962 negated_type =
"int_lin_ge";
963 }
else if (simplified_type ==
"int_lin_gt") {
964 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[3])));
965 negated_type =
"int_lin_le";
967 LOG(
FATAL) <<
"Unsupported " << simplified_type;
971 if (absl::EndsWith(fz_ct.type,
"_imp"))
return;
976 negated_ct->
set_name(fz_ct.type +
" (negated)");
977 negated_ct->add_enforcement_literal(
979 copy.type = negated_type;
980 FillConstraint(copy, negated_ct);
983 void CpModelProtoWithMapping::TranslateSearchAnnotations(
984 const std::vector<fz::Annotation>& search_annotations) {
985 std::vector<fz::Annotation> flat_annotations;
986 for (
const fz::Annotation& annotation : search_annotations) {
990 for (
const fz::Annotation& annotation : flat_annotations) {
991 if (annotation.IsFunctionCallWithIdentifier(
"int_search") ||
992 annotation.IsFunctionCallWithIdentifier(
"bool_search")) {
993 const std::vector<fz::Annotation>& args = annotation.annotations;
994 std::vector<fz::Variable*> vars;
995 args[0].AppendAllVariables(&vars);
998 for (fz::Variable* v : vars) {
1002 const fz::Annotation& choose = args[1];
1003 if (choose.id ==
"input_order") {
1004 strategy->set_variable_selection_strategy(
1006 }
else if (choose.id ==
"first_fail") {
1007 strategy->set_variable_selection_strategy(
1009 }
else if (choose.id ==
"anti_first_fail") {
1010 strategy->set_variable_selection_strategy(
1012 }
else if (choose.id ==
"smallest") {
1013 strategy->set_variable_selection_strategy(
1015 }
else if (choose.id ==
"largest") {
1016 strategy->set_variable_selection_strategy(
1019 LOG(
FATAL) <<
"Unsupported order: " << choose.id;
1022 const fz::Annotation& select = args[2];
1023 if (select.id ==
"indomain_min" || select.id ==
"indomain") {
1024 strategy->set_domain_reduction_strategy(
1026 }
else if (select.id ==
"indomain_max") {
1027 strategy->set_domain_reduction_strategy(
1029 }
else if (select.id ==
"indomain_split") {
1030 strategy->set_domain_reduction_strategy(
1032 }
else if (select.id ==
"indomain_reverse_split") {
1033 strategy->set_domain_reduction_strategy(
1035 }
else if (select.id ==
"indomain_median") {
1036 strategy->set_domain_reduction_strategy(
1039 LOG(
FATAL) <<
"Unsupported select: " << select.id;
1046 std::string SolutionString(
1047 const fz::SolutionOutputSpecs& output,
1048 const std::function<int64_t(fz::Variable*)>& value_func) {
1049 if (output.variable !=
nullptr) {
1050 const int64_t
value = value_func(output.variable);
1051 if (output.display_as_boolean) {
1052 return absl::StrCat(output.name,
" = ",
value == 1 ?
"true" :
"false",
1055 return absl::StrCat(output.name,
" = ",
value,
";");
1058 const int bound_size = output.bounds.size();
1059 std::string result =
1060 absl::StrCat(output.name,
" = array", bound_size,
"d(");
1061 for (
int i = 0; i < bound_size; ++i) {
1062 if (output.bounds[i].max_value >= output.bounds[i].min_value) {
1063 absl::StrAppend(&result, output.bounds[i].min_value,
"..",
1064 output.bounds[i].max_value,
", ");
1066 result.append(
"{},");
1070 for (
int i = 0; i < output.flat_variables.size(); ++i) {
1071 const int64_t
value = value_func(output.flat_variables[i]);
1072 if (output.display_as_boolean) {
1073 result.append(
value ?
"true" :
"false");
1075 absl::StrAppend(&result,
value);
1077 if (i != output.flat_variables.size() - 1) {
1078 result.append(
", ");
1081 result.append(
"]);");
1087 std::string SolutionString(
1088 const fz::Model&
model,
1089 const std::function<int64_t(fz::Variable*)>& value_func) {
1090 std::string solution_string;
1091 for (
const auto& output_spec :
model.output()) {
1092 solution_string.append(SolutionString(output_spec, value_func));
1093 solution_string.append(
"\n");
1095 return solution_string;
1098 void OutputFlatzincStats(
const CpSolverResponse&
response,
1099 SolverLogger* solution_logger) {
1101 "%%%mzn-stat: objective=",
response.objective_value());
1103 "%%%mzn-stat: objectiveBound=",
response.best_objective_bound());
1105 "%%%mzn-stat: boolVariables=",
response.num_booleans());
1107 "%%%mzn-stat: failures=",
response.num_conflicts());
1109 solution_logger,
"%%%mzn-stat: propagations=",
1118 const std::string& sat_params,
1121 CpModelProtoWithMapping m;
1122 m.proto.set_name(fz_model.
name());
1127 int num_variables = 0;
1129 if (!fz_var->active)
continue;
1130 CHECK(!fz_var->domain.is_float)
1131 <<
"CP-SAT does not support float variables";
1133 m.fz_var_to_index[fz_var] = num_variables++;
1135 var->set_name(fz_var->name);
1136 if (fz_var->domain.is_interval) {
1137 if (fz_var->domain.values.empty()) {
1142 <<
"Using flag --fz_int_max for unbounded integer variables.";
1144 <<
" actual domain is [" << -absl::GetFlag(FLAGS_fz_int_max)
1145 <<
".." << absl::GetFlag(FLAGS_fz_int_max) <<
"]";
1146 var->add_domain(-absl::GetFlag(FLAGS_fz_int_max));
1147 var->add_domain(absl::GetFlag(FLAGS_fz_int_max));
1149 var->add_domain(fz_var->domain.values[0]);
1150 var->add_domain(fz_var->domain.values[1]);
1159 if (fz_ct ==
nullptr || !fz_ct->active)
continue;
1161 ct->set_name(fz_ct->type);
1162 if (absl::EndsWith(fz_ct->type,
"_reif") ||
1163 absl::EndsWith(fz_ct->type,
"_imp") || fz_ct->type ==
"array_bool_or" ||
1164 fz_ct->type ==
"array_bool_and") {
1165 m.FillReifOrImpliedConstraint(*fz_ct,
ct);
1167 m.FillConstraint(*fz_ct,
ct);
1178 NegatedCpModelVariable(m.fz_var_to_index[fz_model.
objective()]));
1189 m.parameters.set_enumerate_all_solutions(
true);
1195 m.parameters.set_max_domain_size_when_encoding_eq_neq_constraints(32);
1198 int num_workers = 1;
1205 "Search for all solutions of a SAT problem in parallel is not " 1206 "supported. Switching back to sequential search.");
1214 "The number of search workers, is not specified. For better " 1215 "performances, please set the number of workers to 8, 16, or " 1216 "more depending on the number of cores of your computer.");
1220 m.parameters.set_num_search_workers(num_workers);
1223 if (num_workers == 1) {
1226 m.parameters.set_interleave_search(
true);
1227 m.parameters.set_reduce_memory_usage_in_interleave_mode(
true);
1230 m.parameters.set_keep_all_feasible_solutions_in_presolve(
true);
1242 CHECK(google::protobuf::TextFormat::ParseFromString(sat_params,
1245 m.parameters.MergeFrom(flag_parameters);
1250 solution_observer = [&fz_model, &m, &p,
1252 const std::string solution_string =
1256 SOLVER_LOG(solution_logger, solution_string);
1257 if (p.display_statistics) {
1258 OutputFlatzincStats(r, solution_logger);
1266 if (solution_observer !=
nullptr) {
1291 const std::string solution_string =
1295 SOLVER_LOG(solution_logger, solution_string);
1302 SOLVER_LOG(solution_logger,
"=====UNSATISFIABLE=====");
1305 VLOG(1) <<
"%% Error message = '" << error_message <<
"'";
1306 if (absl::StrContains(error_message,
"overflow")) {
1307 SOLVER_LOG(solution_logger,
"=====OVERFLOW=====");
1309 SOLVER_LOG(solution_logger,
"=====MODEL INVALID=====");
1315 OutputFlatzincStats(
response, solution_logger);
bool display_all_solutions
static constexpr DomainReductionStrategy SELECT_MIN_VALUE
void SolveFzWithCpModelProto(const fz::Model &fz_model, const fz::FlatzincSatParameters &p, const std::string &sat_params, SolverLogger *logger, SolverLogger *solution_logger)
void add_enforcement_literal(int32_t value)
#define SOLVER_LOG(logger,...)
#define CHECK_GE(val1, val2)
Class that owns everything related to a particular optimization model.
static constexpr DomainReductionStrategy SELECT_MEDIAN_VALUE
static constexpr VariableSelectionStrategy CHOOSE_LOWEST_MIN
static Domain FromIntervals(absl::Span< const ClosedInterval > intervals)
Creates a domain from the union of an unsorted list of intervals.
ABSL_FLAG(int64_t, fz_int_max, int64_t{1}<< 50, "Default max value for unbounded integer variables.")
#define VLOG(verboselevel)
std::vector< int64_t > AllValuesInDomain(const ProtoWithDomain &proto)
void FlattenAnnotations(const Annotation &ann, std::vector< Annotation > *out)
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
static constexpr VariableSelectionStrategy CHOOSE_HIGHEST_MAX
void add_variables(int32_t value)
int variables_size() const
::operations_research::sat::IntegerVariableProto * add_variables()
std::function< void(Model *)> NewFeasibleSolutionObserver(const std::function< void(const CpSolverResponse &response)> &observer)
Creates a solution observer with the model with model.Add(NewFeasibleSolutionObserver([](response){....
static constexpr VariableSelectionStrategy CHOOSE_FIRST
void add_vars(int32_t value)
#define CHECK_LT(val1, val2)
double max_time_in_seconds
ReverseView< Container > reversed_view(const Container &c)
static constexpr VariableSelectionStrategy CHOOSE_MAX_DOMAIN_SIZE
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
static Domain FromValues(std::vector< int64_t > values)
Creates a domain from the union of an unsorted list of integer values.
std::string ValidateCpModel(const CpModelProto &model, bool after_presolve)
const std::string & name() const
::operations_research::sat::LinearConstraintProto * mutable_linear()
::operations_research::sat::ConstraintProto * add_constraints()
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
absl::flat_hash_map< int64_t, int > constant_value_to_index
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
const std::vector< Constraint * > & constraints() const
int constraints_size() const
SharedResponseManager * response
static constexpr DomainReductionStrategy SELECT_MAX_VALUE
static constexpr SearchBranching AUTOMATIC_SEARCH
void add_domain(int64_t value)
absl::flat_hash_map< std::tuple< int, int, int >, int > start_size_opt_tuple_to_interval
#define CHECK_EQ(val1, val2)
::operations_research::sat::DecisionStrategyProto * add_search_strategy()
static constexpr DomainReductionStrategy SELECT_UPPER_HALF
void add_coeffs(int64_t value)
void set_name(ArgT0 &&arg0, ArgT... args)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
T Add(std::function< T(Model *)> f)
This makes it possible to have a nicer API on the client side, and it allows both of these forms:
static constexpr VariableSelectionStrategy CHOOSE_MIN_DOMAIN_SIZE
bool LoggingIsEnabled() const
Variable * objective() const
#define LOG_FIRST_N(severity, n)
void add_domain(int64_t value)
absl::flat_hash_map< fz::Variable *, int > fz_var_to_index
bool CheckSolution(const Model &model, const std::function< int64_t(Variable *)> &evaluator, SolverLogger *logger)
static constexpr SearchBranching FIXED_SEARCH
Collection of objects used to extend the Constraint Solver library.
void set_scaling_factor(double value)
const std::vector< Annotation > & search_annotations() const
static constexpr DomainReductionStrategy SELECT_LOWER_HALF
const std::vector< Variable * > & variables() const
absl::flat_hash_map< std::tuple< int, int64_t, int >, int > start_fixed_size_opt_tuple_to_interval
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
void Register(T *non_owned_class)
Register a non-owned class that will be "singleton" in the model.
::operations_research::sat::IntegerVariableProto * mutable_variables(int index)
std::function< SatParameters(Model *)> NewSatParameters(const std::string ¶ms)
Creates parameters for the solver, which you can add to the model with.
CpSolverResponse SolveCpModel(const CpModelProto &model_proto, Model *model)
Solves the given CpModelProto.
int64_t domain(int index) const