24#include "absl/container/flat_hash_map.h"
25#include "absl/container/flat_hash_set.h"
26#include "absl/flags/flag.h"
27#include "absl/log/check.h"
28#include "absl/strings/match.h"
29#include "absl/strings/str_cat.h"
30#include "absl/types/span.h"
31#include "google/protobuf/text_format.h"
36#include "ortools/sat/cp_model.pb.h"
41#include "ortools/sat/sat_parameters.pb.h"
46 "Default max value for unbounded integer variables.");
53static const int kNoVar = std::numeric_limits<int>::min();
61int TrueLiteral(
int var) {
return var; }
62int FalseLiteral(
int var) {
return -
var - 1; }
65struct CpModelProtoWithMapping {
67 int LookupConstant(int64_t
value);
71 int LookupVar(
const fz::Argument& argument);
72 LinearExpressionProto LookupExpr(
const fz::Argument& argument,
74 LinearExpressionProto LookupExprAt(
const fz::Argument& argument,
int pos,
76 std::vector<int> LookupVars(
const fz::Argument& argument);
77 std::vector<VarOrValue> LookupVarsOrValues(
const fz::Argument& argument);
82 std::vector<int> CreateIntervals(absl::Span<const VarOrValue> starts,
83 absl::Span<const VarOrValue> sizes);
92 std::vector<int> CreateNonZeroOrOptionalIntervals(
93 absl::Span<const VarOrValue> starts, absl::Span<const VarOrValue> sizes);
100 int GetOrCreateOptionalInterval(VarOrValue start_var, VarOrValue
size,
105 int NonZeroLiteralFrom(VarOrValue
size);
109 ConstraintProto* AddEnforcedConstraint(
int literal);
112 void FillAMinusBInDomain(
const std::vector<int64_t>& domain,
113 const fz::Constraint& fz_ct, ConstraintProto*
ct);
114 void FillLinearConstraintWithGivenDomain(absl::Span<const int64_t> domain,
115 const fz::Constraint& fz_ct,
116 ConstraintProto*
ct);
117 void FillConstraint(
const fz::Constraint& fz_ct, ConstraintProto*
ct);
118 void FillReifOrImpliedConstraint(
const fz::Constraint& fz_ct,
119 ConstraintProto*
ct);
123 void TranslateSearchAnnotations(
124 absl::Span<const fz::Annotation> search_annotations,
125 SolverLogger* logger);
134 absl::flat_hash_map<std::tuple<int, int64_t, int, int64_t, int>,
int>
139int CpModelProtoWithMapping::LookupConstant(int64_t
value) {
146 IntegerVariableProto* var_proto =
proto.add_variables();
147 var_proto->add_domain(
value);
148 var_proto->add_domain(
value);
153int CpModelProtoWithMapping::LookupVar(
const fz::Argument& argument) {
154 if (argument.HasOneValue())
return LookupConstant(argument.Value());
155 CHECK_EQ(argument.type, fz::Argument::VAR_REF);
159LinearExpressionProto CpModelProtoWithMapping::LookupExpr(
160 const fz::Argument& argument,
bool negate) {
161 LinearExpressionProto expr;
162 if (argument.HasOneValue()) {
163 const int64_t
value = argument.Value();
166 expr.add_vars(LookupVar(argument));
167 expr.add_coeffs(negate ? -1 : 1);
172LinearExpressionProto CpModelProtoWithMapping::LookupExprAt(
173 const fz::Argument& argument,
int pos,
bool negate) {
174 LinearExpressionProto expr;
175 if (argument.HasOneValueAt(pos)) {
176 const int64_t
value = argument.ValueAt(pos);
180 expr.add_coeffs(negate ? -1 : 1);
185std::vector<int> CpModelProtoWithMapping::LookupVars(
186 const fz::Argument& argument) {
187 std::vector<int> result;
188 if (argument.type == fz::Argument::VOID_ARGUMENT)
return result;
189 if (argument.type == fz::Argument::INT_LIST) {
190 for (int64_t
value : argument.values) {
191 result.push_back(LookupConstant(
value));
193 }
else if (argument.type == fz::Argument::INT_VALUE) {
194 result.push_back(LookupConstant(argument.Value()));
196 CHECK_EQ(argument.type, fz::Argument::VAR_REF_ARRAY);
197 for (fz::Variable*
var : argument.variables) {
198 CHECK(
var !=
nullptr);
205std::vector<VarOrValue> CpModelProtoWithMapping::LookupVarsOrValues(
206 const fz::Argument& argument) {
207 std::vector<VarOrValue> result;
208 const int no_var = kNoVar;
209 if (argument.type == fz::Argument::VOID_ARGUMENT)
return result;
210 if (argument.type == fz::Argument::INT_LIST) {
211 for (int64_t
value : argument.values) {
212 result.push_back({no_var,
value});
214 }
else if (argument.type == fz::Argument::INT_VALUE) {
215 result.push_back({no_var, argument.Value()});
217 CHECK_EQ(argument.type, fz::Argument::VAR_REF_ARRAY);
218 for (fz::Variable*
var : argument.variables) {
219 CHECK(
var !=
nullptr);
220 if (
var->domain.HasOneValue()) {
221 result.push_back({no_var,
var->domain.Value()});
230ConstraintProto* CpModelProtoWithMapping::AddEnforcedConstraint(
int literal) {
231 ConstraintProto* result =
proto.add_constraints();
233 result->add_enforcement_literal(
literal);
238int CpModelProtoWithMapping::GetOrCreateOptionalInterval(VarOrValue
start,
242 const std::tuple<int, int64_t, int, int64_t, int> key =
244 const auto [it, inserted] =
250 if (
size.var == kNoVar ||
start.var == kNoVar) {
251 auto*
interval = AddEnforcedConstraint(opt_var)->mutable_interval();
252 if (
start.var != kNoVar) {
254 interval->mutable_start()->add_coeffs(1);
256 interval->mutable_end()->add_coeffs(1);
262 if (
size.var != kNoVar) {
264 interval->mutable_size()->add_coeffs(1);
266 interval->mutable_end()->add_coeffs(1);
274 const int end_var =
proto.variables_size();
278 proto.add_variables());
281 auto*
interval = AddEnforcedConstraint(opt_var)->mutable_interval();
283 interval->mutable_start()->add_coeffs(1);
285 interval->mutable_size()->add_coeffs(1);
286 interval->mutable_end()->add_vars(end_var);
287 interval->mutable_end()->add_coeffs(1);
291 auto* lin = AddEnforcedConstraint(opt_var)->mutable_linear();
292 lin->add_vars(
start.var);
294 lin->add_vars(
size.var);
296 lin->add_vars(end_var);
305std::vector<int> CpModelProtoWithMapping::CreateIntervals(
306 absl::Span<const VarOrValue> starts, absl::Span<const VarOrValue> sizes) {
307 std::vector<int> intervals;
308 for (
int i = 0;
i < starts.size(); ++
i) {
310 GetOrCreateOptionalInterval(starts[i], sizes[i], kNoVar));
315std::vector<int> CpModelProtoWithMapping::CreateNonZeroOrOptionalIntervals(
316 absl::Span<const VarOrValue> starts, absl::Span<const VarOrValue> sizes) {
317 std::vector<int> intervals;
318 for (
int i = 0;
i < starts.size(); ++
i) {
319 const int opt_var = NonZeroLiteralFrom(sizes[i]);
321 GetOrCreateOptionalInterval(starts[i], sizes[i], opt_var));
326int CpModelProtoWithMapping::NonZeroLiteralFrom(VarOrValue
size) {
327 if (
size.var == kNoVar) {
328 return LookupConstant(
size.value > 0);
335 const IntegerVariableProto& var_proto =
proto.variables(
size.var);
337 DCHECK_GE(domain.Min(), 0);
338 if (domain.Min() > 0)
return LookupConstant(1);
339 if (domain.Max() == 0) {
340 return LookupConstant(0);
343 const int var_greater_than_zero_lit =
proto.variables_size();
344 IntegerVariableProto* lit_proto =
proto.add_variables();
345 lit_proto->add_domain(0);
346 lit_proto->add_domain(1);
348 ConstraintProto* is_greater =
349 AddEnforcedConstraint(TrueLiteral(var_greater_than_zero_lit));
350 is_greater->mutable_linear()->add_vars(
size.var);
351 is_greater->mutable_linear()->add_coeffs(1);
352 const Domain positive = domain.IntersectionWith({1, domain.Max()});
355 ConstraintProto* is_null =
356 AddEnforcedConstraint(FalseLiteral(var_greater_than_zero_lit));
357 is_null->mutable_linear()->add_vars(
size.var);
358 is_null->mutable_linear()->add_coeffs(1);
359 is_null->mutable_linear()->add_domain(0);
360 is_null->mutable_linear()->add_domain(0);
363 return var_greater_than_zero_lit;
366void CpModelProtoWithMapping::FillAMinusBInDomain(
367 const std::vector<int64_t>& domain,
const fz::Constraint& fz_ct,
368 ConstraintProto*
ct) {
369 auto* arg =
ct->mutable_linear();
370 if (fz_ct.arguments[1].type == fz::Argument::INT_VALUE) {
371 const int64_t
value = fz_ct.arguments[1].Value();
372 const int var_a = LookupVar(fz_ct.arguments[0]);
373 for (
const int64_t domain_bound : domain) {
374 if (domain_bound == std::numeric_limits<int64_t>::min() ||
375 domain_bound == std::numeric_limits<int64_t>::max()) {
376 arg->add_domain(domain_bound);
378 arg->add_domain(domain_bound +
value);
381 arg->add_vars(var_a);
383 }
else if (fz_ct.arguments[0].type == fz::Argument::INT_VALUE) {
384 const int64_t
value = fz_ct.arguments[0].Value();
385 const int var_b = LookupVar(fz_ct.arguments[1]);
387 if (domain_bound == std::numeric_limits<int64_t>::min()) {
388 arg->add_domain(std::numeric_limits<int64_t>::max());
389 }
else if (domain_bound == std::numeric_limits<int64_t>::max()) {
390 arg->add_domain(std::numeric_limits<int64_t>::min());
392 arg->add_domain(
value - domain_bound);
395 arg->add_vars(var_b);
398 for (
const int64_t domain_bound : domain) arg->add_domain(domain_bound);
399 arg->add_vars(LookupVar(fz_ct.arguments[0]));
401 arg->add_vars(LookupVar(fz_ct.arguments[1]));
406void CpModelProtoWithMapping::FillLinearConstraintWithGivenDomain(
407 absl::Span<const int64_t> domain,
const fz::Constraint& fz_ct,
408 ConstraintProto*
ct) {
409 auto* arg =
ct->mutable_linear();
410 for (
const int64_t domain_bound : domain) arg->add_domain(domain_bound);
411 std::vector<int> vars = LookupVars(fz_ct.arguments[1]);
412 for (
int i = 0;
i < vars.size(); ++
i) {
413 arg->add_vars(vars[i]);
414 arg->add_coeffs(fz_ct.arguments[0].values[i]);
418void CpModelProtoWithMapping::FillConstraint(
const fz::Constraint& fz_ct,
419 ConstraintProto*
ct) {
420 if (fz_ct.type ==
"false_constraint") {
422 ct->mutable_bool_or();
423 }
else if (fz_ct.type ==
"bool_clause") {
424 auto* arg =
ct->mutable_bool_or();
425 for (
const int var : LookupVars(fz_ct.arguments[0])) {
426 arg->add_literals(TrueLiteral(
var));
428 for (
const int var : LookupVars(fz_ct.arguments[1])) {
429 arg->add_literals(FalseLiteral(
var));
431 }
else if (fz_ct.type ==
"bool_xor") {
434 const int a = LookupVar(fz_ct.arguments[0]);
435 const int b = LookupVar(fz_ct.arguments[1]);
436 const int x = LookupVar(fz_ct.arguments[2]);
440 auto*
const refute =
ct->mutable_linear();
442 refute->add_coeffs(1);
444 refute->add_coeffs(-1);
445 refute->add_domain(0);
446 refute->add_domain(0);
449 auto* enforce = AddEnforcedConstraint(
x)->mutable_linear();
450 enforce->add_vars(
a);
451 enforce->add_coeffs(1);
452 enforce->add_vars(
b);
453 enforce->add_coeffs(1);
454 enforce->add_domain(1);
455 enforce->add_domain(1);
456 }
else if (fz_ct.type ==
"array_bool_or") {
457 auto* arg =
ct->mutable_bool_or();
458 for (
const int var : LookupVars(fz_ct.arguments[0])) {
459 arg->add_literals(TrueLiteral(
var));
461 }
else if (fz_ct.type ==
"array_bool_or_negated") {
462 auto* arg =
ct->mutable_bool_and();
463 for (
const int var : LookupVars(fz_ct.arguments[0])) {
464 arg->add_literals(FalseLiteral(
var));
466 }
else if (fz_ct.type ==
"array_bool_and") {
467 auto* arg =
ct->mutable_bool_and();
468 for (
const int var : LookupVars(fz_ct.arguments[0])) {
469 arg->add_literals(TrueLiteral(
var));
471 }
else if (fz_ct.type ==
"array_bool_and_negated") {
472 auto* arg =
ct->mutable_bool_or();
473 for (
const int var : LookupVars(fz_ct.arguments[0])) {
474 arg->add_literals(FalseLiteral(
var));
476 }
else if (fz_ct.type ==
"array_bool_xor") {
477 auto* arg =
ct->mutable_bool_xor();
478 for (
const int var : LookupVars(fz_ct.arguments[0])) {
479 arg->add_literals(TrueLiteral(
var));
481 }
else if (fz_ct.type ==
"bool_le" || fz_ct.type ==
"int_le") {
482 FillAMinusBInDomain({std::numeric_limits<int64_t>::min(), 0}, fz_ct,
ct);
483 }
else if (fz_ct.type ==
"bool_ge" || fz_ct.type ==
"int_ge") {
484 FillAMinusBInDomain({0, std::numeric_limits<int64_t>::max()}, fz_ct,
ct);
485 }
else if (fz_ct.type ==
"bool_lt" || fz_ct.type ==
"int_lt") {
486 FillAMinusBInDomain({std::numeric_limits<int64_t>::min(), -1}, fz_ct,
ct);
487 }
else if (fz_ct.type ==
"bool_gt" || fz_ct.type ==
"int_gt") {
488 FillAMinusBInDomain({1, std::numeric_limits<int64_t>::max()}, fz_ct,
ct);
489 }
else if (fz_ct.type ==
"bool_eq" || fz_ct.type ==
"int_eq" ||
490 fz_ct.type ==
"bool2int") {
491 FillAMinusBInDomain({0, 0}, fz_ct,
ct);
492 }
else if (fz_ct.type ==
"bool_ne" || fz_ct.type ==
"bool_not") {
493 auto* arg =
ct->mutable_linear();
494 arg->add_vars(LookupVar(fz_ct.arguments[0]));
496 arg->add_vars(LookupVar(fz_ct.arguments[1]));
500 }
else if (fz_ct.type ==
"int_ne") {
501 FillAMinusBInDomain({std::numeric_limits<int64_t>::min(), -1, 1,
502 std::numeric_limits<int64_t>::max()},
504 }
else if (fz_ct.type ==
"int_lin_eq") {
505 const int64_t rhs = fz_ct.arguments[2].values[0];
506 FillLinearConstraintWithGivenDomain({rhs, rhs}, fz_ct,
ct);
507 }
else if (fz_ct.type ==
"bool_lin_eq") {
508 auto* arg =
ct->mutable_linear();
509 const std::vector<int> vars = LookupVars(fz_ct.arguments[1]);
510 for (
int i = 0;
i < vars.size(); ++
i) {
511 arg->add_vars(vars[i]);
512 arg->add_coeffs(fz_ct.arguments[0].values[i]);
514 if (fz_ct.arguments[2].IsVariable()) {
515 arg->add_vars(LookupVar(fz_ct.arguments[2]));
520 const int64_t v = fz_ct.arguments[2].Value();
524 }
else if (fz_ct.type ==
"int_lin_le" || fz_ct.type ==
"bool_lin_le") {
525 const int64_t rhs = fz_ct.arguments[2].values[0];
526 FillLinearConstraintWithGivenDomain(
527 {std::numeric_limits<int64_t>::min(), rhs}, fz_ct,
ct);
528 }
else if (fz_ct.type ==
"int_lin_lt") {
529 const int64_t rhs = fz_ct.arguments[2].values[0];
530 FillLinearConstraintWithGivenDomain(
531 {std::numeric_limits<int64_t>::min(), rhs - 1}, fz_ct,
ct);
532 }
else if (fz_ct.type ==
"int_lin_ge") {
533 const int64_t rhs = fz_ct.arguments[2].values[0];
534 FillLinearConstraintWithGivenDomain(
535 {rhs, std::numeric_limits<int64_t>::max()}, fz_ct,
ct);
536 }
else if (fz_ct.type ==
"int_lin_gt") {
537 const int64_t rhs = fz_ct.arguments[2].values[0];
538 FillLinearConstraintWithGivenDomain(
539 {rhs + 1, std::numeric_limits<int64_t>::max()}, fz_ct,
ct);
540 }
else if (fz_ct.type ==
"int_lin_ne") {
541 const int64_t rhs = fz_ct.arguments[2].values[0];
542 FillLinearConstraintWithGivenDomain(
543 {std::numeric_limits<int64_t>::min(), rhs - 1, rhs + 1,
544 std::numeric_limits<int64_t>::max()},
546 }
else if (fz_ct.type ==
"set_in") {
547 auto* arg =
ct->mutable_linear();
548 arg->add_vars(LookupVar(fz_ct.arguments[0]));
550 if (fz_ct.arguments[1].type == fz::Argument::INT_LIST) {
552 fz_ct.arguments[1].values.begin(),
553 fz_ct.arguments[1].values.end()}),
555 }
else if (fz_ct.arguments[1].type == fz::Argument::INT_INTERVAL) {
557 Domain(fz_ct.arguments[1].values[0], fz_ct.arguments[1].values[1]),
560 LOG(FATAL) <<
"Wrong format";
562 }
else if (fz_ct.type ==
"set_in_negated") {
563 auto* arg =
ct->mutable_linear();
564 arg->add_vars(LookupVar(fz_ct.arguments[0]));
566 if (fz_ct.arguments[1].type == fz::Argument::INT_LIST) {
569 std::vector<int64_t>{fz_ct.arguments[1].values.begin(),
570 fz_ct.arguments[1].values.end()})
573 }
else if (fz_ct.arguments[1].type == fz::Argument::INT_INTERVAL) {
575 Domain(fz_ct.arguments[1].values[0], fz_ct.arguments[1].values[1])
579 LOG(FATAL) <<
"Wrong format";
581 }
else if (fz_ct.type ==
"int_min") {
582 auto* arg =
ct->mutable_lin_max();
583 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0],
true);
584 *arg->add_exprs() = LookupExpr(fz_ct.arguments[1],
true);
585 *arg->mutable_target() = LookupExpr(fz_ct.arguments[2],
true);
586 }
else if (fz_ct.type ==
"array_int_minimum" || fz_ct.type ==
"minimum_int") {
587 auto* arg =
ct->mutable_lin_max();
588 *arg->mutable_target() = LookupExpr(fz_ct.arguments[0],
true);
589 for (
int i = 0;
i < fz_ct.arguments[1].Size(); ++
i) {
590 *arg->add_exprs() = LookupExprAt(fz_ct.arguments[1], i,
true);
592 }
else if (fz_ct.type ==
"int_max") {
593 auto* arg =
ct->mutable_lin_max();
594 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0]);
595 *arg->add_exprs() = LookupExpr(fz_ct.arguments[1]);
596 *arg->mutable_target() = LookupExpr(fz_ct.arguments[2]);
597 }
else if (fz_ct.type ==
"array_int_maximum" || fz_ct.type ==
"maximum_int") {
598 auto* arg =
ct->mutable_lin_max();
599 *arg->mutable_target() = LookupExpr(fz_ct.arguments[0]);
600 for (
int i = 0;
i < fz_ct.arguments[1].Size(); ++
i) {
601 *arg->add_exprs() = LookupExprAt(fz_ct.arguments[1], i);
603 }
else if (fz_ct.type ==
"int_times") {
604 auto* arg =
ct->mutable_int_prod();
605 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0]);
606 *arg->add_exprs() = LookupExpr(fz_ct.arguments[1]);
607 *arg->mutable_target() = LookupExpr(fz_ct.arguments[2]);
608 }
else if (fz_ct.type ==
"int_abs") {
609 auto* arg =
ct->mutable_lin_max();
610 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0]);
611 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0],
true);
612 *arg->mutable_target() = LookupExpr(fz_ct.arguments[1]);
613 }
else if (fz_ct.type ==
"int_plus") {
614 auto* arg =
ct->mutable_linear();
616 arg->add_vars(LookupVar(fz_ct.arguments[0]));
618 arg->add_vars(LookupVar(fz_ct.arguments[1]));
620 arg->add_vars(LookupVar(fz_ct.arguments[2]));
622 }
else if (fz_ct.type ==
"int_div") {
623 auto* arg =
ct->mutable_int_div();
624 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0]);
625 *arg->add_exprs() = LookupExpr(fz_ct.arguments[1]);
626 *arg->mutable_target() = LookupExpr(fz_ct.arguments[2]);
627 }
else if (fz_ct.type ==
"int_mod") {
628 auto* arg =
ct->mutable_int_mod();
629 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0]);
630 *arg->add_exprs() = LookupExpr(fz_ct.arguments[1]);
631 *arg->mutable_target() = LookupExpr(fz_ct.arguments[2]);
632 }
else if (fz_ct.type ==
"array_int_element" ||
633 fz_ct.type ==
"array_bool_element" ||
634 fz_ct.type ==
"array_var_int_element" ||
635 fz_ct.type ==
"array_var_bool_element" ||
636 fz_ct.type ==
"array_int_element_nonshifted") {
637 if (fz_ct.arguments[0].type == fz::Argument::VAR_REF ||
638 fz_ct.arguments[0].type == fz::Argument::INT_VALUE) {
639 auto* arg =
ct->mutable_element();
640 arg->set_index(LookupVar(fz_ct.arguments[0]));
641 arg->set_target(LookupVar(fz_ct.arguments[2]));
643 if (!absl::EndsWith(fz_ct.type,
"_nonshifted")) {
647 arg->add_vars(LookupConstant(0));
649 for (
const int var : LookupVars(fz_ct.arguments[1])) arg->add_vars(
var);
653 CHECK(!absl::EndsWith(fz_ct.type,
"_nonshifted"));
654 auto* arg =
ct->mutable_table();
658 for (
const int var : LookupVars(fz_ct.arguments[0])) arg->add_vars(
var);
659 arg->add_vars(LookupVar(fz_ct.arguments[2]));
661 const std::vector<int64_t>& values = fz_ct.arguments[1].values;
662 const int64_t coeff1 = fz_ct.arguments[3].values[0];
663 const int64_t coeff2 = fz_ct.arguments[3].values[1];
664 const int64_t offset = fz_ct.arguments[4].values[0] - 1;
667 for (
const int64_t
b :
669 const int index = coeff1 *
a + coeff2 *
b + offset;
671 CHECK_LT(
index, values.size());
674 arg->add_values(values[
index]);
678 }
else if (fz_ct.type ==
"ortools_table_int") {
679 auto* arg =
ct->mutable_table();
680 for (
const int var : LookupVars(fz_ct.arguments[0])) arg->add_vars(
var);
681 for (
const int64_t
value : fz_ct.arguments[1].values)
682 arg->add_values(
value);
683 }
else if (fz_ct.type ==
"ortools_regular") {
684 auto* arg =
ct->mutable_automaton();
685 for (
const int var : LookupVars(fz_ct.arguments[0])) arg->add_vars(
var);
688 const int num_states = fz_ct.arguments[1].Value();
689 const int num_values = fz_ct.arguments[2].Value();
690 for (
int i = 1;
i <= num_states; ++
i) {
691 for (
int j = 1; j <= num_values; ++j) {
692 CHECK_LT(count, fz_ct.arguments[3].values.size());
693 const int next = fz_ct.arguments[3].values[count++];
694 if (
next == 0)
continue;
695 arg->add_transition_tail(i);
696 arg->add_transition_label(j);
697 arg->add_transition_head(
next);
701 arg->set_starting_state(fz_ct.arguments[4].Value());
702 switch (fz_ct.arguments[5].type) {
703 case fz::Argument::INT_VALUE: {
704 arg->add_final_states(fz_ct.arguments[5].values[0]);
707 case fz::Argument::INT_INTERVAL: {
708 for (
int v = fz_ct.arguments[5].values[0];
709 v <= fz_ct.arguments[5].values[1]; ++v) {
710 arg->add_final_states(v);
714 case fz::Argument::INT_LIST: {
715 for (
const int v : fz_ct.arguments[5].values) {
716 arg->add_final_states(v);
721 LOG(FATAL) <<
"Wrong constraint " << fz_ct.DebugString();
724 }
else if (fz_ct.type ==
"fzn_all_different_int") {
725 auto* arg =
ct->mutable_all_diff();
726 for (
int i = 0;
i < fz_ct.arguments[0].Size(); ++
i) {
727 *arg->add_exprs() = LookupExprAt(fz_ct.arguments[0], i);
729 }
else if (fz_ct.type ==
"ortools_circuit" ||
730 fz_ct.type ==
"ortools_subcircuit") {
731 const int64_t min_index = fz_ct.arguments[1].Value();
732 const int size = std::max(fz_ct.arguments[0].values.size(),
733 fz_ct.arguments[0].variables.size());
735 const int64_t max_index = min_index +
size - 1;
737 auto* circuit_arg =
ct->mutable_circuit();
741 int64_t
index = min_index;
742 const bool is_circuit = (fz_ct.type ==
"ortools_circuit");
743 for (
const int var : LookupVars(fz_ct.arguments[0])) {
747 domain = domain.IntersectionWith(Domain(min_index, max_index));
750 domain = domain.IntersectionWith(Domain::FromIntervals(
751 {{std::numeric_limits<int64_t>::min(),
index - 1},
752 {
index + 1, std::numeric_limits<int64_t>::max()}}));
756 for (
const ClosedInterval
interval : domain) {
761 auto* new_var =
proto.add_variables();
762 new_var->add_domain(0);
763 new_var->add_domain(1);
767 circuit_arg->add_tails(
index);
768 circuit_arg->add_heads(
value);
769 circuit_arg->add_literals(
literal);
773 auto* lin = AddEnforcedConstraint(
literal)->mutable_linear();
776 lin->add_domain(
value);
777 lin->add_domain(
value);
786 lin->add_domain(std::numeric_limits<int64_t>::min());
787 lin->add_domain(
value - 1);
788 lin->add_domain(
value + 1);
789 lin->add_domain(std::numeric_limits<int64_t>::max());
796 }
else if (fz_ct.type ==
"ortools_inverse") {
797 auto* arg =
ct->mutable_inverse();
799 const auto direct_variables = LookupVars(fz_ct.arguments[0]);
800 const auto inverse_variables = LookupVars(fz_ct.arguments[1]);
801 const int base_direct = fz_ct.arguments[2].Value();
802 const int base_inverse = fz_ct.arguments[3].Value();
804 CHECK_EQ(direct_variables.size(), inverse_variables.size());
805 const int num_variables = direct_variables.size();
806 const int end_direct = base_direct + num_variables;
807 const int end_inverse = base_inverse + num_variables;
829 const int arity = std::max(base_inverse, base_direct) + num_variables;
830 for (
int i = 0;
i < arity; ++
i) {
832 if (i < base_direct) {
833 if (i < base_inverse) {
834 arg->add_f_direct(LookupConstant(i));
835 }
else if (i >= base_inverse) {
836 arg->add_f_direct(LookupConstant(i + num_variables));
838 }
else if (i >= base_direct && i < end_direct) {
839 arg->add_f_direct(direct_variables[i - base_direct]);
841 arg->add_f_direct(LookupConstant(i - num_variables));
845 if (i < base_inverse) {
846 if (i < base_direct) {
847 arg->add_f_inverse(LookupConstant(i));
848 }
else if (i >= base_direct) {
849 arg->add_f_inverse(LookupConstant(i + num_variables));
851 }
else if (i >= base_inverse && i < end_inverse) {
852 arg->add_f_inverse(inverse_variables[i - base_inverse]);
854 arg->add_f_inverse(LookupConstant(i - num_variables));
857 }
else if (fz_ct.type ==
"fzn_disjunctive") {
858 const std::vector<VarOrValue> starts =
859 LookupVarsOrValues(fz_ct.arguments[0]);
860 const std::vector<VarOrValue> sizes =
861 LookupVarsOrValues(fz_ct.arguments[1]);
863 auto* arg =
ct->mutable_cumulative();
864 arg->mutable_capacity()->set_offset(1);
865 for (
int i = 0;
i < starts.size(); ++
i) {
867 GetOrCreateOptionalInterval(starts[i], sizes[i], kNoVar));
868 arg->add_demands()->set_offset(1);
870 }
else if (fz_ct.type ==
"fzn_disjunctive_strict") {
871 const std::vector<VarOrValue> starts =
872 LookupVarsOrValues(fz_ct.arguments[0]);
873 const std::vector<VarOrValue> sizes =
874 LookupVarsOrValues(fz_ct.arguments[1]);
876 auto* arg =
ct->mutable_no_overlap();
877 for (
int i = 0;
i < starts.size(); ++
i) {
879 GetOrCreateOptionalInterval(starts[i], sizes[i], kNoVar));
881 }
else if (fz_ct.type ==
"fzn_cumulative") {
882 const std::vector<VarOrValue> starts =
883 LookupVarsOrValues(fz_ct.arguments[0]);
884 const std::vector<VarOrValue> sizes =
885 LookupVarsOrValues(fz_ct.arguments[1]);
886 const std::vector<VarOrValue> demands =
887 LookupVarsOrValues(fz_ct.arguments[2]);
889 auto* arg =
ct->mutable_cumulative();
890 if (fz_ct.arguments[3].HasOneValue()) {
891 arg->mutable_capacity()->set_offset(fz_ct.arguments[3].Value());
893 arg->mutable_capacity()->add_vars(LookupVar(fz_ct.arguments[3]));
894 arg->mutable_capacity()->add_coeffs(1);
896 for (
int i = 0;
i < starts.size(); ++
i) {
899 if (demands[i].
var != kNoVar &&
900 proto.variables(demands[i].var).domain().size() == 2 &&
901 proto.variables(demands[i].var).domain(0) == 0 &&
902 proto.variables(demands[i].var).domain(1) == 1 &&
903 fz_ct.arguments[3].HasOneValue() && fz_ct.arguments[3].Value() == 1) {
905 GetOrCreateOptionalInterval(starts[i], sizes[i], demands[i].
var));
906 arg->add_demands()->set_offset(1);
909 GetOrCreateOptionalInterval(starts[i], sizes[i], kNoVar));
910 LinearExpressionProto*
demand = arg->add_demands();
911 if (demands[i].
var == kNoVar) {
919 }
else if (fz_ct.type ==
"ortools_cumulative_opt") {
920 const std::vector<int> occurs = LookupVars(fz_ct.arguments[0]);
921 const std::vector<VarOrValue> starts =
922 LookupVarsOrValues(fz_ct.arguments[1]);
923 const std::vector<VarOrValue> durations =
924 LookupVarsOrValues(fz_ct.arguments[2]);
925 const std::vector<VarOrValue> demands =
926 LookupVarsOrValues(fz_ct.arguments[3]);
928 auto* arg =
ct->mutable_cumulative();
929 if (fz_ct.arguments[3].HasOneValue()) {
930 arg->mutable_capacity()->set_offset(fz_ct.arguments[4].Value());
932 arg->mutable_capacity()->add_vars(LookupVar(fz_ct.arguments[4]));
933 arg->mutable_capacity()->add_coeffs(1);
935 for (
int i = 0;
i < starts.size(); ++
i) {
937 GetOrCreateOptionalInterval(starts[i], durations[i], occurs[i]));
938 LinearExpressionProto*
demand = arg->add_demands();
939 if (demands[i].
var == kNoVar) {
946 }
else if (fz_ct.type ==
"ortools_disjunctive_strict_opt") {
947 const std::vector<int> occurs = LookupVars(fz_ct.arguments[0]);
948 const std::vector<VarOrValue> starts =
949 LookupVarsOrValues(fz_ct.arguments[1]);
950 const std::vector<VarOrValue> durations =
951 LookupVarsOrValues(fz_ct.arguments[2]);
953 auto* arg =
ct->mutable_no_overlap();
954 for (
int i = 0;
i < starts.size(); ++
i) {
956 GetOrCreateOptionalInterval(starts[i], durations[i], occurs[i]));
958 }
else if (fz_ct.type ==
"fzn_diffn" || fz_ct.type ==
"fzn_diffn_nonstrict") {
959 const bool is_strict = fz_ct.type ==
"fzn_diffn";
960 const std::vector<VarOrValue>
x = LookupVarsOrValues(fz_ct.arguments[0]);
961 const std::vector<VarOrValue>
y = LookupVarsOrValues(fz_ct.arguments[1]);
962 const std::vector<VarOrValue> dx = LookupVarsOrValues(fz_ct.arguments[2]);
963 const std::vector<VarOrValue> dy = LookupVarsOrValues(fz_ct.arguments[3]);
964 const std::vector<int> x_intervals =
965 is_strict ? CreateIntervals(
x, dx)
966 : CreateNonZeroOrOptionalIntervals(
x, dx);
967 const std::vector<int> y_intervals =
968 is_strict ? CreateIntervals(
y, dy)
969 : CreateNonZeroOrOptionalIntervals(
y, dy);
970 auto* arg =
ct->mutable_no_overlap_2d();
971 for (
int i = 0;
i <
x.size(); ++
i) {
972 arg->add_x_intervals(x_intervals[i]);
973 arg->add_y_intervals(y_intervals[i]);
975 }
else if (fz_ct.type ==
"ortools_network_flow" ||
976 fz_ct.type ==
"ortools_network_flow_cost") {
979 const bool has_cost = fz_ct.type ==
"ortools_network_flow_cost";
980 const std::vector<int> flow = LookupVars(fz_ct.arguments[3]);
983 const int num_nodes = fz_ct.arguments[1].values.size();
984 const int base_node = fz_ct.arguments[2].Value();
985 std::vector<std::vector<int>> flows_per_node(num_nodes);
986 std::vector<std::vector<int>> coeffs_per_node(num_nodes);
988 const int num_arcs = fz_ct.arguments[0].values.size() / 2;
989 for (
int arc = 0;
arc < num_arcs;
arc++) {
990 const int tail = fz_ct.arguments[0].values[2 *
arc] - base_node;
991 const int head = fz_ct.arguments[0].values[2 *
arc + 1] - base_node;
994 flows_per_node[
tail].push_back(flow[
arc]);
995 coeffs_per_node[
tail].push_back(1);
996 flows_per_node[
head].push_back(flow[
arc]);
997 coeffs_per_node[
head].push_back(-1);
999 for (
int node = 0; node < num_nodes; node++) {
1000 auto* arg =
proto.add_constraints()->mutable_linear();
1001 arg->add_domain(fz_ct.arguments[1].values[node]);
1002 arg->add_domain(fz_ct.arguments[1].values[node]);
1003 for (
int i = 0;
i < flows_per_node[node].size(); ++
i) {
1004 arg->add_vars(flows_per_node[node][i]);
1005 arg->add_coeffs(coeffs_per_node[node][i]);
1010 auto* arg =
proto.add_constraints()->mutable_linear();
1013 for (
int arc = 0;
arc < num_arcs;
arc++) {
1014 const int64_t
weight = fz_ct.arguments[4].values[
arc];
1016 arg->add_vars(flow[
arc]);
1020 arg->add_vars(LookupVar(fz_ct.arguments[5]));
1021 arg->add_coeffs(-1);
1024 LOG(FATAL) <<
" Not supported " << fz_ct.type;
1028void CpModelProtoWithMapping::FillReifOrImpliedConstraint(
1029 const fz::Constraint& fz_ct, ConstraintProto*
ct) {
1031 std::string simplified_type;
1032 if (absl::EndsWith(fz_ct.type,
"_reif")) {
1034 simplified_type = fz_ct.type.substr(0, fz_ct.type.size() - 5);
1035 }
else if (absl::EndsWith(fz_ct.type,
"_imp")) {
1037 simplified_type = fz_ct.type.substr(0, fz_ct.type.size() - 4);
1040 simplified_type = fz_ct.type;
1044 fz::Constraint copy = fz_ct;
1045 copy.type = simplified_type;
1048 FillConstraint(copy,
ct);
1051 std::string negated_type;
1054 if (simplified_type ==
"array_bool_or") {
1055 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[1])));
1056 negated_type =
"array_bool_or_negated";
1057 }
else if (simplified_type ==
"array_bool_and") {
1058 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[1])));
1059 negated_type =
"array_bool_and_negated";
1060 }
else if (simplified_type ==
"set_in") {
1061 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[2])));
1062 negated_type =
"set_in_negated";
1063 }
else if (simplified_type ==
"bool_eq" || simplified_type ==
"int_eq") {
1064 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[2])));
1065 negated_type =
"int_ne";
1066 }
else if (simplified_type ==
"bool_ne" || simplified_type ==
"int_ne") {
1067 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[2])));
1068 negated_type =
"int_eq";
1069 }
else if (simplified_type ==
"bool_le" || simplified_type ==
"int_le") {
1070 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[2])));
1071 negated_type =
"int_gt";
1072 }
else if (simplified_type ==
"bool_lt" || simplified_type ==
"int_lt") {
1073 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[2])));
1074 negated_type =
"int_ge";
1075 }
else if (simplified_type ==
"bool_ge" || simplified_type ==
"int_ge") {
1076 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[2])));
1077 negated_type =
"int_lt";
1078 }
else if (simplified_type ==
"bool_gt" || simplified_type ==
"int_gt") {
1079 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[2])));
1080 negated_type =
"int_le";
1081 }
else if (simplified_type ==
"int_lin_eq") {
1082 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[3])));
1083 negated_type =
"int_lin_ne";
1084 }
else if (simplified_type ==
"int_lin_ne") {
1085 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[3])));
1086 negated_type =
"int_lin_eq";
1087 }
else if (simplified_type ==
"int_lin_le") {
1088 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[3])));
1089 negated_type =
"int_lin_gt";
1090 }
else if (simplified_type ==
"int_lin_ge") {
1091 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[3])));
1092 negated_type =
"int_lin_lt";
1093 }
else if (simplified_type ==
"int_lin_lt") {
1094 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[3])));
1095 negated_type =
"int_lin_ge";
1096 }
else if (simplified_type ==
"int_lin_gt") {
1097 ct->add_enforcement_literal(TrueLiteral(LookupVar(fz_ct.arguments[3])));
1098 negated_type =
"int_lin_le";
1100 LOG(FATAL) <<
"Unsupported " << simplified_type;
1104 if (absl::EndsWith(fz_ct.type,
"_imp"))
return;
1108 ConstraintProto* negated_ct =
proto.add_constraints();
1109 negated_ct->set_name(fz_ct.type +
" (negated)");
1110 negated_ct->add_enforcement_literal(
1111 sat::NegatedRef(
ct->enforcement_literal(0)));
1112 copy.type = negated_type;
1113 FillConstraint(copy, negated_ct);
1116void CpModelProtoWithMapping::TranslateSearchAnnotations(
1117 absl::Span<const fz::Annotation> search_annotations, SolverLogger* logger) {
1118 std::vector<fz::Annotation> flat_annotations;
1119 for (
const fz::Annotation& annotation : search_annotations) {
1120 fz::FlattenAnnotations(annotation, &flat_annotations);
1124 absl::flat_hash_set<int> hinted_vars;
1126 for (
const fz::Annotation& annotation : flat_annotations) {
1127 if (annotation.IsFunctionCallWithIdentifier(
"warm_start")) {
1128 CHECK_EQ(2, annotation.annotations.size());
1129 const fz::Annotation& vars = annotation.annotations[0];
1130 const fz::Annotation& values = annotation.annotations[1];
1131 if (vars.type != fz::Annotation::VAR_REF_ARRAY ||
1132 values.type != fz::Annotation::INT_LIST) {
1135 for (
int i = 0;
i < vars.variables.size(); ++
i) {
1136 fz::Variable* fz_var = vars.variables[
i];
1138 const int64_t
value = values.values[
i];
1139 if (hinted_vars.insert(
var).second) {
1140 proto.mutable_solution_hint()->add_vars(
var);
1141 proto.mutable_solution_hint()->add_values(
value);
1144 }
else if (annotation.IsFunctionCallWithIdentifier(
"int_search") ||
1145 annotation.IsFunctionCallWithIdentifier(
"bool_search")) {
1146 const std::vector<fz::Annotation>& args = annotation.annotations;
1147 std::vector<fz::Variable*> vars;
1148 args[0].AppendAllVariables(&vars);
1150 DecisionStrategyProto* strategy =
proto.add_search_strategy();
1151 for (fz::Variable* v : vars) {
1155 const fz::Annotation& choose = args[1];
1156 if (choose.id ==
"input_order") {
1157 strategy->set_variable_selection_strategy(
1158 DecisionStrategyProto::CHOOSE_FIRST);
1159 }
else if (choose.id ==
"first_fail") {
1160 strategy->set_variable_selection_strategy(
1161 DecisionStrategyProto::CHOOSE_MIN_DOMAIN_SIZE);
1162 }
else if (choose.id ==
"anti_first_fail") {
1163 strategy->set_variable_selection_strategy(
1164 DecisionStrategyProto::CHOOSE_MAX_DOMAIN_SIZE);
1165 }
else if (choose.id ==
"smallest") {
1166 strategy->set_variable_selection_strategy(
1167 DecisionStrategyProto::CHOOSE_LOWEST_MIN);
1168 }
else if (choose.id ==
"largest") {
1169 strategy->set_variable_selection_strategy(
1170 DecisionStrategyProto::CHOOSE_HIGHEST_MAX);
1172 SOLVER_LOG(logger,
"Unsupported variable selection strategy '",
1173 choose.id,
"', falling back to 'smallest'");
1174 strategy->set_variable_selection_strategy(
1175 DecisionStrategyProto::CHOOSE_LOWEST_MIN);
1178 const fz::Annotation& select = args[2];
1179 if (select.id ==
"indomain_min" || select.id ==
"indomain") {
1180 strategy->set_domain_reduction_strategy(
1181 DecisionStrategyProto::SELECT_MIN_VALUE);
1182 }
else if (select.id ==
"indomain_max") {
1183 strategy->set_domain_reduction_strategy(
1184 DecisionStrategyProto::SELECT_MAX_VALUE);
1185 }
else if (select.id ==
"indomain_split") {
1186 strategy->set_domain_reduction_strategy(
1187 DecisionStrategyProto::SELECT_LOWER_HALF);
1188 }
else if (select.id ==
"indomain_reverse_split") {
1189 strategy->set_domain_reduction_strategy(
1190 DecisionStrategyProto::SELECT_UPPER_HALF);
1191 }
else if (select.id ==
"indomain_median") {
1192 strategy->set_domain_reduction_strategy(
1193 DecisionStrategyProto::SELECT_MEDIAN_VALUE);
1195 SOLVER_LOG(logger,
"Unsupported value selection strategy '", select.id,
1196 "', falling back to 'indomain_min'");
1197 strategy->set_domain_reduction_strategy(
1198 DecisionStrategyProto::SELECT_MIN_VALUE);
1205std::string SolutionString(
1206 const fz::SolutionOutputSpecs& output,
1207 const std::function<int64_t(fz::Variable*)>& value_func,
1209 if (output.variable !=
nullptr && !output.variable->domain.is_float) {
1210 const int64_t
value = value_func(output.variable);
1211 if (output.display_as_boolean) {
1212 return absl::StrCat(output.name,
" = ",
value == 1 ?
"true" :
"false",
1215 return absl::StrCat(output.name,
" = ",
value,
";");
1217 }
else if (output.variable !=
nullptr && output.variable->domain.is_float) {
1220 const int bound_size = output.bounds.size();
1221 std::string result =
1222 absl::StrCat(output.name,
" = array", bound_size,
"d(");
1223 for (
int i = 0;
i < bound_size; ++
i) {
1224 if (output.bounds[
i].max_value >= output.bounds[
i].min_value) {
1225 absl::StrAppend(&result, output.bounds[
i].min_value,
"..",
1226 output.bounds[
i].max_value,
", ");
1228 result.append(
"{},");
1232 for (
int i = 0;
i < output.flat_variables.size(); ++
i) {
1233 const int64_t
value = value_func(output.flat_variables[
i]);
1234 if (output.display_as_boolean) {
1235 result.append(
value ?
"true" :
"false");
1237 absl::StrAppend(&result,
value);
1239 if (
i != output.flat_variables.size() - 1) {
1240 result.append(
", ");
1243 result.append(
"]);");
1249std::string SolutionString(
1250 const fz::Model&
model,
1251 const std::function<int64_t(fz::Variable*)>& value_func,
1253 std::string solution_string;
1254 for (
const auto& output_spec :
model.output()) {
1255 solution_string.append(
1257 solution_string.append(
"\n");
1259 return solution_string;
1262void OutputFlatzincStats(
const CpSolverResponse& response,
1263 SolverLogger* solution_logger) {
1265 "%%%mzn-stat: objective=", response.objective_value());
1267 "%%%mzn-stat: objectiveBound=", response.best_objective_bound());
1269 "%%%mzn-stat: boolVariables=", response.num_booleans());
1271 "%%%mzn-stat: failures=", response.num_conflicts());
1273 solution_logger,
"%%%mzn-stat: propagations=",
1274 response.num_binary_propagations() + response.num_integer_propagations());
1275 SOLVER_LOG(solution_logger,
"%%%mzn-stat: solveTime=", response.wall_time());
1282 const std::string& sat_params,
1285 CpModelProtoWithMapping m;
1286 m.proto.set_name(fz_model.
name());
1291 int num_variables = 0;
1293 if (!fz_var->active)
continue;
1294 CHECK(!fz_var->domain.is_float)
1295 <<
"CP-SAT does not support float variables";
1297 m.fz_var_to_index[fz_var] = num_variables++;
1298 IntegerVariableProto*
var = m.proto.add_variables();
1299 var->set_name(fz_var->name);
1300 if (fz_var->domain.is_interval) {
1301 if (fz_var->domain.values.empty()) {
1305 LOG_FIRST_N(WARNING, 1)
1306 <<
"Using flag --fz_int_max for unbounded integer variables.";
1307 LOG_FIRST_N(WARNING, 1)
1308 <<
" actual domain is [" << -absl::GetFlag(FLAGS_fz_int_max)
1309 <<
".." << absl::GetFlag(FLAGS_fz_int_max) <<
"]";
1310 var->add_domain(-absl::GetFlag(FLAGS_fz_int_max));
1311 var->add_domain(absl::GetFlag(FLAGS_fz_int_max));
1313 var->add_domain(fz_var->domain.values[0]);
1314 var->add_domain(fz_var->domain.values[1]);
1323 if (fz_ct ==
nullptr || !fz_ct->active)
continue;
1324 ConstraintProto*
ct = m.proto.add_constraints();
1325 ct->set_name(fz_ct->type);
1326 if (absl::EndsWith(fz_ct->type,
"_reif") ||
1327 absl::EndsWith(fz_ct->type,
"_imp") || fz_ct->type ==
"array_bool_or" ||
1328 fz_ct->type ==
"array_bool_and") {
1329 m.FillReifOrImpliedConstraint(*fz_ct,
ct);
1331 m.FillConstraint(*fz_ct,
ct);
1337 CpObjectiveProto* objective = m.proto.mutable_objective();
1339 objective->set_scaling_factor(-1);
1340 objective->add_coeffs(-1);
1341 objective->add_vars(m.fz_var_to_index[fz_model.
objective()]);
1343 objective->add_coeffs(1);
1344 objective->add_vars(m.fz_var_to_index[fz_model.
objective()]);
1347 FloatObjectiveProto* objective = m.proto.mutable_floating_point_objective();
1349 objective->add_vars(
1354 objective->set_maximize(fz_model.
maximize());
1362 m.parameters.set_enumerate_all_solutions(
true);
1369 m.parameters.set_max_domain_size_when_encoding_eq_neq_constraints(32);
1372 int num_workers = 1;
1379 "Search for all solutions of a SAT problem in parallel is not "
1380 "supported. Switching back to sequential search.");
1388 "The number of search workers, is not specified. For better "
1389 "performances, please set the number of workers to 8, 16, or "
1390 "more depending on the number of cores of your computer.");
1397 m.parameters.set_search_branching(SatParameters::FIXED_SEARCH);
1398 m.parameters.set_keep_all_feasible_solutions_in_presolve(
true);
1400 m.parameters.set_search_branching(SatParameters::AUTOMATIC_SEARCH);
1402 m.parameters.set_interleave_search(
true);
1404 m.parameters.add_subsolvers(
"default_lp");
1405 m.parameters.add_subsolvers(
1406 m.proto.search_strategy().empty() ?
"quick_restart" :
"fixed");
1407 m.parameters.add_subsolvers(
"core_or_no_lp"),
1408 m.parameters.add_subsolvers(
"max_lp");
1411 m.parameters.add_subsolvers(
"default_lp");
1412 m.parameters.add_subsolvers(
1413 m.proto.search_strategy().empty() ?
"no_lp" :
"fixed");
1414 m.parameters.add_subsolvers(
"less_encoding");
1415 m.parameters.add_subsolvers(
"max_lp");
1416 m.parameters.add_subsolvers(
"quick_restart");
1419 }
else if (num_workers > 1 && num_workers < 8 && p.
ortools_mode) {
1420 SOLVER_LOG(logger,
"Bumping number of workers from ", num_workers,
" to 8");
1423 m.parameters.set_num_workers(num_workers);
1432 sat::SatParameters flag_parameters;
1433 CHECK(google::protobuf::TextFormat::ParseFromString(sat_params,
1436 m.parameters.MergeFrom(flag_parameters);
1440 std::function<void(
const CpSolverResponse&)> solution_observer =
nullptr;
1442 solution_observer = [&fz_model, &m, &p,
1443 solution_logger](
const CpSolverResponse& r) {
1444 const std::string solution_string = SolutionString(
1447 return r.solution(m.fz_var_to_index.at(v));
1449 r.objective_value());
1450 SOLVER_LOG(solution_logger, solution_string);
1451 if (p.display_statistics) {
1452 OutputFlatzincStats(r, solution_logger);
1466 if (solution_observer !=
nullptr) {
1470 const CpSolverResponse response =
SolveCpModel(m.proto, &sat_model);
1473 if (response.status() == CpSolverStatus::FEASIBLE ||
1474 response.status() == CpSolverStatus::OPTIMAL) {
1475 CHECK(CheckSolution(
1478 return response.solution(m.fz_var_to_index.at(v));
1485 if (response.status() == CpSolverStatus::FEASIBLE ||
1486 response.status() == CpSolverStatus::OPTIMAL) {
1489 const std::string solution_string = SolutionString(
1492 return response.solution(m.fz_var_to_index.at(v));
1494 response.objective_value());
1495 SOLVER_LOG(solution_logger, solution_string);
1498 if (response.status() == CpSolverStatus::OPTIMAL) {
1501 }
else if (response.status() == CpSolverStatus::INFEASIBLE) {
1502 SOLVER_LOG(solution_logger,
"=====UNSATISFIABLE=====");
1503 }
else if (response.status() == CpSolverStatus::MODEL_INVALID) {
1505 VLOG(1) <<
"%% Error message = '" << error_message <<
"'";
1506 if (absl::StrContains(error_message,
"overflow")) {
1507 SOLVER_LOG(solution_logger,
"=====OVERFLOW=====");
1509 SOLVER_LOG(solution_logger,
"=====MODEL INVALID=====");
1515 OutputFlatzincStats(response, solution_logger);
static Domain FromValues(std::vector< int64_t > values)
Domain AdditionWith(const Domain &domain) const
const std::vector< double > & float_objective_coefficients() const
const std::vector< Variable * > & float_objective_variables() const
const std::vector< Constraint * > & constraints() const
const std::vector< Variable * > & variables() const
--— Accessors and mutators --—
double float_objective_offset() const
const std::string & name() const
const std::vector< Annotation > & search_annotations() const
Variable * objective() const
T Add(std::function< T(Model *)> f)
void Register(T *non_owned_class)
absl::flat_hash_map< int, int > var_to_lit_implies_greater_than_zero
absl::flat_hash_map< std::tuple< int, int64_t, int, int64_t, int >, int > interval_key_to_index
absl::flat_hash_map< int64_t, int > constant_value_to_index
absl::flat_hash_map< fz::Variable *, int > fz_var_to_index
Mapping from flatzinc variables to CpModelProto variables.
ABSL_FLAG(int64_t, fz_int_max, int64_t{1}<< 50, "Default max value for unbounded integer variables.")
CpModelProto proto
The output proto.
ReverseView< Container > reversed_view(const Container &c)
std::function< SatParameters(Model *)> NewSatParameters(const std::string ¶ms)
std::string ValidateCpModel(const CpModelProto &model, bool after_presolve)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
Serializes a Domain into the domain field of a proto.
CpSolverResponse SolveCpModel(const CpModelProto &model_proto, Model *model)
void SolveFzWithCpModelProto(const fz::Model &fz_model, const fz::FlatzincSatParameters &p, const std::string &sat_params, SolverLogger *logger, SolverLogger *solution_logger)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Reads a Domain from the domain field of a proto.
std::function< void(Model *)> NewFeasibleSolutionObserver(const std::function< void(const CpSolverResponse &response)> &callback)
int NegatedRef(int ref)
Small utility functions to deal with negative variable/literal references.
std::vector< int64_t > AllValuesInDomain(const ProtoWithDomain &proto)
In SWIG mode, we don't want anything besides these top-level includes.
bool search_all_solutions
double max_time_in_seconds
bool display_all_solutions
double objective_value
The value objective_vector^T * (solution - center_point).
#define SOLVER_LOG(logger,...)