24 #include "absl/container/flat_hash_map.h"
25 #include "absl/container/flat_hash_set.h"
57 template <
typename Values>
58 std::vector<int64> ValuesFromProto(
const Values& values) {
59 return std::vector<int64>(values.begin(), values.end());
62 void ComputeLinearBounds(
const LinearConstraintProto&
proto,
63 CpModelMapping* mapping, IntegerTrail* integer_trail,
68 for (
int i = 0; i <
proto.vars_size(); ++i) {
70 const IntegerVariable
var = mapping->Integer(
proto.vars(i));
71 const int64 lb = integer_trail->LowerBound(
var).value();
72 const int64 ub = integer_trail->UpperBound(
var).value();
74 (*sum_min) += coeff * lb;
75 (*sum_max) += coeff * ub;
77 (*sum_min) += coeff * ub;
78 (*sum_max) += coeff * lb;
84 bool ConstraintIsEq(
const LinearConstraintProto&
proto) {
89 bool ConstraintIsNEq(
const LinearConstraintProto&
proto,
90 CpModelMapping* mapping, IntegerTrail* integer_trail,
91 int64* single_value) {
94 ComputeLinearBounds(
proto, mapping, integer_trail, &sum_min, &sum_max);
96 const Domain complement =
97 Domain(sum_min, sum_max)
99 if (complement.IsEmpty())
return false;
102 if (complement.Size() == 1) {
103 if (single_value !=
nullptr) {
104 *single_value =
value;
114 bool view_all_booleans_as_integers,
116 const int num_proto_variables =
model_proto.variables_size();
122 CHECK_EQ(sat_solver->NumVariables(), 0);
124 BooleanVariable new_var(0);
125 std::vector<BooleanVariable> false_variables;
126 std::vector<BooleanVariable> true_variables;
129 reverse_boolean_map_.
resize(num_proto_variables, -1);
130 for (
int i = 0; i < num_proto_variables; ++i) {
131 const auto& domain =
model_proto.variables(i).domain();
132 if (domain.size() != 2)
continue;
133 if (domain[0] >= 0 && domain[1] <= 1) {
134 booleans_[i] = new_var;
135 reverse_boolean_map_[new_var] = i;
136 if (domain[1] == 0) {
137 false_variables.push_back(new_var);
138 }
else if (domain[0] == 1) {
139 true_variables.push_back(new_var);
145 sat_solver->SetNumVariables(new_var.value());
146 for (
const BooleanVariable
var : true_variables) {
149 for (
const BooleanVariable
var : false_variables) {
156 std::vector<int> var_to_instantiate_as_integer;
157 if (view_all_booleans_as_integers) {
158 var_to_instantiate_as_integer.resize(num_proto_variables);
159 for (
int i = 0; i < num_proto_variables; ++i) {
160 var_to_instantiate_as_integer[i] = i;
164 absl::flat_hash_set<int> used_variables;
167 for (
int c = 0; c <
model_proto.constraints_size(); ++c) {
178 for (
const int obj_var :
model_proto.objective().vars()) {
182 for (
const DecisionStrategyProto& strategy :
184 for (
const int var : strategy.variables()) {
191 for (
int i = 0; i < num_proto_variables; ++i) {
193 used_variables.insert(i);
198 var_to_instantiate_as_integer.assign(used_variables.begin(),
199 used_variables.end());
206 var_to_instantiate_as_integer.size());
207 reverse_integer_map_.
resize(2 * var_to_instantiate_as_integer.size(), -1);
208 for (
const int i : var_to_instantiate_as_integer) {
212 DCHECK_LT(integers_[i], reverse_integer_map_.
size());
213 reverse_integer_map_[integers_[i]] = i;
219 for (
int i = 0; i < num_proto_variables; ++i) {
225 integers_[i], IntegerValue(1));
230 for (
int c = 0; c <
model_proto.constraints_size(); ++c) {
232 if (
ct.constraint_case() != ConstraintProto::ConstraintCase::kInterval) {
243 Integer(
ct.interval().size()), enforcement_literal));
249 already_loaded_ct_.insert(&
ct);
266 if (sat_solver->IsModelUnsat())
return;
271 struct EqualityDetectionHelper {
272 const ConstraintProto*
ct;
277 bool operator<(
const EqualityDetectionHelper& o)
const {
278 if (
literal.Variable() == o.literal.Variable()) {
279 if (
value == o.value)
return is_equality && !o.is_equality;
280 return value < o.value;
282 return literal.Variable() < o.literal.Variable();
285 std::vector<std::vector<EqualityDetectionHelper>> var_to_equalities(
297 struct InequalityDetectionHelper {
298 const ConstraintProto*
ct;
302 bool operator<(
const InequalityDetectionHelper& o)
const {
303 if (
literal.Variable() == o.literal.Variable()) {
304 return i_lit.
var < o.i_lit.var;
306 return literal.Variable() < o.literal.Variable();
309 std::vector<InequalityDetectionHelper> inequalities;
312 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
313 if (
ct.constraint_case() != ConstraintProto::ConstraintCase::kLinear) {
316 if (
ct.enforcement_literal().size() != 1)
continue;
317 if (
ct.linear().vars_size() != 1)
continue;
321 const int ref =
ct.linear().vars(0);
325 const Domain domain_if_enforced =
332 if (domain_if_enforced.
Max() >= domain.
Max() &&
333 domain_if_enforced.
Min() > domain.
Min()) {
334 inequalities.push_back(
335 {&
ct, enforcement_literal,
338 implied_bounds->Add(enforcement_literal, inequalities.back().i_lit);
339 }
else if (domain_if_enforced.
Min() <= domain.
Min() &&
340 domain_if_enforced.
Max() < domain.
Max()) {
341 inequalities.push_back(
342 {&
ct, enforcement_literal,
345 implied_bounds->Add(enforcement_literal, inequalities.back().i_lit);
357 var_to_equalities[
var].push_back(
358 {&
ct, enforcement_literal, inter.
Min(),
true});
360 variables_to_encoded_values_[
var].insert(inter.
Min());
368 var_to_equalities[
var].push_back(
369 {&
ct, enforcement_literal, inter.
Min(),
false});
371 variables_to_encoded_values_[
var].insert(inter.
Min());
378 int num_inequalities = 0;
379 std::sort(inequalities.begin(), inequalities.end());
380 for (
int i = 0; i + 1 < inequalities.size(); i++) {
381 if (inequalities[i].
literal != inequalities[i + 1].
literal.Negated()) {
388 if (integer_trail->IntegerLiteralIsTrue(inequalities[i].i_lit) ||
389 integer_trail->IntegerLiteralIsFalse(inequalities[i].i_lit)) {
392 if (integer_trail->IntegerLiteralIsTrue(inequalities[i + 1].i_lit) ||
393 integer_trail->IntegerLiteralIsFalse(inequalities[i + 1].i_lit)) {
397 const auto pair_a = encoder->Canonicalize(inequalities[i].i_lit);
398 const auto pair_b = encoder->Canonicalize(inequalities[i + 1].i_lit);
399 if (pair_a.first == pair_b.second) {
401 encoder->AssociateToIntegerLiteral(inequalities[i].
literal,
402 inequalities[i].i_lit);
403 already_loaded_ct_.insert(inequalities[i].
ct);
404 already_loaded_ct_.insert(inequalities[i + 1].
ct);
409 int num_half_inequalities = 0;
410 for (
const auto inequality : inequalities) {
414 encoder->GetOrCreateAssociatedLiteral(inequality.i_lit)));
415 if (sat_solver->IsModelUnsat())
return;
417 ++num_half_inequalities;
418 already_loaded_ct_.insert(inequality.ct);
419 is_half_encoding_ct_.insert(inequality.ct);
422 if (!inequalities.empty()) {
423 VLOG(1) << num_inequalities <<
" literals associated to VAR >= value, and "
424 << num_half_inequalities <<
" half-associations.";
430 int num_constraints = 0;
431 int num_equalities = 0;
432 int num_half_equalities = 0;
433 int num_fully_encoded = 0;
434 int num_partially_encoded = 0;
435 for (
int i = 0; i < var_to_equalities.size(); ++i) {
436 std::vector<EqualityDetectionHelper>& encoding = var_to_equalities[i];
437 std::sort(encoding.begin(), encoding.end());
438 if (encoding.empty())
continue;
439 num_constraints += encoding.size();
441 absl::flat_hash_set<int64> values;
442 for (
int j = 0; j + 1 < encoding.size(); j++) {
443 if ((encoding[j].
value != encoding[j + 1].
value) ||
445 (encoding[j].is_equality !=
true) ||
446 (encoding[j + 1].is_equality !=
false)) {
451 encoder->AssociateToIntegerEqualValue(encoding[j].
literal, integers_[i],
452 IntegerValue(encoding[j].
value));
453 already_loaded_ct_.insert(encoding[j].
ct);
454 already_loaded_ct_.insert(encoding[j + 1].
ct);
455 values.insert(encoding[j].
value);
461 if (sat_solver->IsModelUnsat())
return;
469 for (
const auto equality : encoding) {
471 const class Literal eq = encoder->GetOrCreateLiteralAssociatedToEquality(
472 integers_[i], IntegerValue(equality.value));
473 if (equality.is_equality) {
479 ++num_half_equalities;
480 already_loaded_ct_.insert(equality.ct);
481 is_half_encoding_ct_.insert(equality.ct);
486 if (encoder->VariableIsFullyEncoded(integers_[i])) {
489 ++num_partially_encoded;
494 if (num_constraints > 0) {
495 VLOG(1) << num_equalities <<
" literals associated to VAR == value, and "
496 << num_half_equalities <<
" half-associations.";
498 if (num_fully_encoded > 0) {
499 VLOG(1) <<
"num_fully_encoded_variables: " << num_fully_encoded;
501 if (num_partially_encoded > 0) {
502 VLOG(1) <<
"num_partially_encoded_variables: " << num_partially_encoded;
512 int64 num_associations = 0;
513 int64 num_set_to_false = 0;
514 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
515 if (!
ct.enforcement_literal().empty())
continue;
516 if (
ct.constraint_case() != ConstraintProto::kLinear)
continue;
517 if (
ct.linear().vars_size() != 2)
continue;
518 if (!ConstraintIsEq(
ct.linear()))
continue;
520 const IntegerValue rhs(
ct.linear().domain(0));
523 IntegerVariable var1 =
Integer(
ct.linear().vars(0));
524 IntegerVariable var2 =
Integer(
ct.linear().vars(1));
525 IntegerValue coeff1(
ct.linear().coeffs(0));
526 IntegerValue coeff2(
ct.linear().coeffs(1));
538 if (coeff1 == 0 || coeff2 == 0)
continue;
543 for (
int i = 0; i < 2; ++i) {
544 for (
const auto value_literal :
545 encoder->PartialGreaterThanEncoding(var1)) {
546 const IntegerValue value1 = value_literal.first;
547 const IntegerValue bound2 =
FloorRatio(rhs - value1 * coeff1, coeff2);
549 encoder->AssociateToIntegerLiteral(
552 std::swap(var1, var2);
553 std::swap(coeff1, coeff2);
561 for (
int i = 0; i < 2; ++i) {
562 for (
const auto value_literal : encoder->PartialDomainEncoding(var1)) {
563 const IntegerValue value1 = value_literal.value;
564 const IntegerValue intermediate = rhs - value1 * coeff1;
565 if (intermediate % coeff2 != 0) {
568 sat_solver->AddUnitClause(value_literal.literal.Negated());
573 intermediate / coeff2);
575 std::swap(var1, var2);
576 std::swap(coeff1, coeff2);
580 if (num_associations > 0) {
581 VLOG(1) <<
"Num associations from equivalences = " << num_associations;
583 if (num_set_to_false > 0) {
584 VLOG(1) <<
"Num literals set to false from equivalences = "
592 if (!
parameters.use_optional_variables())
return;
593 if (
parameters.enumerate_all_solutions())
return;
596 const int num_proto_variables =
model_proto.variables_size();
597 std::vector<bool> already_seen(num_proto_variables,
false);
599 for (
const int ref :
model_proto.objective().vars()) {
612 std::vector<std::vector<int>> enforcement_intersection(num_proto_variables);
613 std::set<int> literals_set;
614 for (
int c = 0; c <
model_proto.constraints_size(); ++c) {
616 if (
ct.enforcement_literal().empty()) {
618 already_seen[
var] =
true;
619 enforcement_intersection[
var].clear();
622 literals_set.clear();
623 literals_set.insert(
ct.enforcement_literal().begin(),
624 ct.enforcement_literal().end());
626 if (!already_seen[
var]) {
627 enforcement_intersection[
var].assign(
ct.enforcement_literal().begin(),
628 ct.enforcement_literal().end());
631 std::vector<int>& vector_ref = enforcement_intersection[
var];
633 for (
const int literal : vector_ref) {
635 vector_ref[new_size++] =
literal;
638 vector_ref.resize(new_size);
640 already_seen[
var] =
true;
646 int num_optionals = 0;
648 for (
int var = 0;
var < num_proto_variables; ++
var) {
649 const IntegerVariableProto& var_proto =
model_proto.variables(
var);
650 const int64 min = var_proto.domain(0);
651 const int64 max = var_proto.domain(var_proto.domain().size() - 1);
653 if (
min == 0 &&
max == 1)
continue;
654 if (enforcement_intersection[
var].empty())
continue;
657 integer_trail->MarkIntegerVariableAsOptional(
660 VLOG(2) <<
"Auto-detected " << num_optionals <<
" optional variables.";
672 parameters_(*(
model->GetOrCreate<SatParameters>())),
681 DEFINE_INT_TYPE(ConstraintIndex,
int32);
684 void Register(ConstraintIndex ct_index,
int variable) {
686 constraint_is_registered_[ct_index] =
true;
687 if (variable_watchers_.size() <= variable) {
688 variable_watchers_.resize(variable + 1);
689 variable_was_added_in_to_propagate_.resize(variable + 1);
691 variable_watchers_[variable].push_back(ct_index);
694 void AddVariableToPropagationQueue(
int variable) {
696 if (variable_was_added_in_to_propagate_.size() <= variable) {
697 variable_watchers_.resize(variable + 1);
698 variable_was_added_in_to_propagate_.resize(variable + 1);
700 if (!variable_was_added_in_to_propagate_[variable]) {
701 variable_was_added_in_to_propagate_[variable] =
true;
702 variables_to_propagate_.push_back(variable);
707 const bool IsFullyEncoded(
int v) {
708 const IntegerVariable variable = mapping_->
Integer(v);
710 return integer_trail_->
IsFixed(variable) ||
714 const bool VariableIsFixed(
int v) {
715 const IntegerVariable variable = mapping_->
Integer(v);
717 return integer_trail_->
IsFixed(variable);
720 void FullyEncode(
int v) {
722 const IntegerVariable variable = mapping_->
Integer(v);
724 if (!integer_trail_->
IsFixed(variable)) {
727 AddVariableToPropagationQueue(v);
730 bool ProcessConstraint(ConstraintIndex ct_index);
731 bool ProcessElement(ConstraintIndex ct_index);
732 bool ProcessTable(ConstraintIndex ct_index);
733 bool ProcessAutomaton(ConstraintIndex ct_index);
734 bool ProcessLinear(ConstraintIndex ct_index);
736 const CpModelProto& model_proto_;
737 const SatParameters& parameters_;
740 CpModelMapping* mapping_;
741 IntegerEncoder* integer_encoder_;
742 IntegerTrail* integer_trail_;
744 std::vector<bool> variable_was_added_in_to_propagate_;
745 std::vector<int> variables_to_propagate_;
746 std::vector<std::vector<ConstraintIndex>> variable_watchers_;
751 absl::flat_hash_map<int, absl::flat_hash_set<int>>
752 variables_to_equal_or_diff_variables_;
758 const int num_constraints = model_proto_.constraints_size();
759 const int num_vars = model_proto_.variables_size();
760 constraint_is_finished_.
assign(num_constraints,
false);
761 constraint_is_registered_.
assign(num_constraints,
false);
764 for (ConstraintIndex ct_index(0); ct_index < num_constraints; ++ct_index) {
765 constraint_is_finished_[ct_index] = ProcessConstraint(ct_index);
775 int num_variables_fully_encoded_by_heuristics = 0;
776 for (
int var = 0;
var < num_vars; ++
var) {
778 const IntegerVariableProto& int_var_proto = model_proto_.variables(
var);
781 int64 num_diff_or_equal_var_constraints = 0;
782 int64 num_potential_encoded_values_without_bounds = 0;
784 if (domain_size <= 2)
continue;
786 const absl::flat_hash_set<int64>& value_set =
788 for (
const int value : value_set) {
791 num_potential_encoded_values_without_bounds++;
795 const auto& it = variables_to_equal_or_diff_variables_.find(
var);
796 if (it != variables_to_equal_or_diff_variables_.end()) {
797 num_diff_or_equal_var_constraints = it->second.size();
800 if (num_potential_encoded_values_without_bounds >= domain_size / 2 ||
801 (num_diff_or_equal_var_constraints >= domain_size / 2 &&
803 VLOG(3) << model_proto_.variables(
var).ShortDebugString()
804 <<
" is encoded with "
805 << num_potential_encoded_values_without_bounds
806 <<
" unary constraints, and " << num_diff_or_equal_var_constraints
807 <<
" binary constraints on a domain of size " << domain_size;
809 num_variables_fully_encoded_by_heuristics++;
812 if (num_variables_fully_encoded_by_heuristics > 0) {
813 VLOG(2) << num_variables_fully_encoded_by_heuristics
814 <<
" variables fully encoded after model introspection.";
818 for (
int v = 0; v < variable_watchers_.size(); v++) {
819 if (!variable_watchers_[v].empty() && IsFullyEncoded(v)) {
820 AddVariableToPropagationQueue(v);
825 while (!variables_to_propagate_.empty()) {
826 const int variable = variables_to_propagate_.back();
827 variables_to_propagate_.pop_back();
828 for (
const ConstraintIndex ct_index : variable_watchers_[variable]) {
829 if (constraint_is_finished_[ct_index])
continue;
830 constraint_is_finished_[ct_index] = ProcessConstraint(ct_index);
836 bool FullEncodingFixedPointComputer::ProcessConstraint(
837 ConstraintIndex ct_index) {
838 const ConstraintProto&
ct = model_proto_.constraints(ct_index.value());
839 switch (
ct.constraint_case()) {
840 case ConstraintProto::ConstraintProto::kElement:
841 return ProcessElement(ct_index);
842 case ConstraintProto::ConstraintProto::kTable:
843 return ProcessTable(ct_index);
844 case ConstraintProto::ConstraintProto::kAutomaton:
845 return ProcessAutomaton(ct_index);
846 case ConstraintProto::ConstraintProto::kLinear:
847 return ProcessLinear(ct_index);
853 bool FullEncodingFixedPointComputer::ProcessElement(ConstraintIndex ct_index) {
854 const ConstraintProto&
ct = model_proto_.constraints(ct_index.value());
857 FullyEncode(
ct.element().index());
859 const int target =
ct.element().target();
862 if (VariableIsFixed(target))
return true;
865 if (IsFullyEncoded(target)) {
866 for (
const int v :
ct.element().vars()) FullyEncode(v);
870 bool all_variables_are_fully_encoded =
true;
871 for (
const int v :
ct.element().vars()) {
872 if (v == target)
continue;
873 if (!IsFullyEncoded(v)) {
874 all_variables_are_fully_encoded =
false;
878 if (all_variables_are_fully_encoded) {
879 if (!IsFullyEncoded(target)) FullyEncode(target);
884 if (constraint_is_registered_[ct_index]) {
885 for (
const int v :
ct.element().vars()) Register(ct_index, v);
886 Register(ct_index, target);
891 bool FullEncodingFixedPointComputer::ProcessTable(ConstraintIndex ct_index) {
892 const ConstraintProto&
ct = model_proto_.constraints(ct_index.value());
894 if (
ct.table().negated())
return true;
896 for (
const int variable :
ct.table().vars()) {
897 FullyEncode(variable);
903 bool FullEncodingFixedPointComputer::ProcessAutomaton(
904 ConstraintIndex ct_index) {
905 const ConstraintProto&
ct = model_proto_.constraints(ct_index.value());
906 for (
const int variable :
ct.automaton().vars()) {
907 FullyEncode(variable);
912 bool FullEncodingFixedPointComputer::ProcessLinear(ConstraintIndex ct_index) {
915 const ConstraintProto&
ct = model_proto_.constraints(ct_index.value());
916 if (parameters_.boolean_encoding_level() == 0 ||
917 ct.linear().vars_size() != 2) {
921 if (!ConstraintIsEq(
ct.linear()) &&
922 !ConstraintIsNEq(
ct.linear(), mapping_, integer_trail_,
nullptr)) {
926 const int var0 =
ct.linear().vars(0);
927 const int var1 =
ct.linear().vars(1);
928 if (!IsFullyEncoded(var0)) {
929 variables_to_equal_or_diff_variables_[var0].insert(var1);
931 if (!IsFullyEncoded(var1)) {
932 variables_to_equal_or_diff_variables_[var1].insert(var0);
948 std::vector<Literal> literals = mapping->
Literals(
ct.bool_or().literals());
949 for (
const int ref :
ct.enforcement_literal()) {
950 literals.push_back(mapping->Literal(ref).Negated());
957 std::vector<Literal> literals;
958 for (
const int ref :
ct.enforcement_literal()) {
959 literals.push_back(mapping->Literal(ref).Negated());
985 void LoadEquivalenceAC(
const std::vector<Literal> enforcement_literal,
986 IntegerValue coeff1, IntegerVariable var1,
987 IntegerValue coeff2, IntegerVariable var2,
988 const IntegerValue rhs, Model* m) {
989 auto* encoder = m->GetOrCreate<IntegerEncoder>();
990 CHECK(encoder->VariableIsFullyEncoded(var1));
991 CHECK(encoder->VariableIsFullyEncoded(var2));
992 absl::flat_hash_map<IntegerValue, Literal> term1_value_to_literal;
993 for (
const auto value_literal : encoder->FullDomainEncoding(var1)) {
994 term1_value_to_literal[coeff1 * value_literal.value] =
995 value_literal.literal;
997 for (
const auto value_literal : encoder->FullDomainEncoding(var2)) {
998 const IntegerValue target = rhs - value_literal.value * coeff2;
1001 {value_literal.literal.Negated()}));
1003 const Literal target_literal = term1_value_to_literal[target];
1005 {value_literal.literal.Negated(), target_literal}));
1007 {value_literal.literal, target_literal.Negated()}));
1011 term1_value_to_literal.erase(target);
1017 std::vector<Literal> implied_false;
1018 for (
const auto entry : term1_value_to_literal) {
1019 implied_false.push_back(entry.second);
1021 std::sort(implied_false.begin(), implied_false.end());
1022 for (
const Literal l : implied_false) {
1029 void LoadEquivalenceNeqAC(
const std::vector<Literal> enforcement_literal,
1030 IntegerValue coeff1, IntegerVariable var1,
1031 IntegerValue coeff2, IntegerVariable var2,
1032 const IntegerValue rhs, Model* m) {
1033 auto* encoder = m->GetOrCreate<IntegerEncoder>();
1034 CHECK(encoder->VariableIsFullyEncoded(var1));
1035 CHECK(encoder->VariableIsFullyEncoded(var2));
1036 absl::flat_hash_map<IntegerValue, Literal> term1_value_to_literal;
1037 for (
const auto value_literal : encoder->FullDomainEncoding(var1)) {
1038 term1_value_to_literal[coeff1 * value_literal.value] =
1039 value_literal.literal;
1041 for (
const auto value_literal : encoder->FullDomainEncoding(var2)) {
1042 const IntegerValue target_value = rhs - value_literal.value * coeff2;
1043 const auto& it = term1_value_to_literal.find(target_value);
1044 if (it != term1_value_to_literal.end()) {
1045 const Literal target_literal = it->second;
1047 enforcement_literal,
1048 {value_literal.literal.Negated(), target_literal.Negated()}));
1058 if (
ct.linear().vars().empty()) {
1062 std::vector<Literal> clause;
1063 for (
const int ref :
ct.enforcement_literal()) {
1064 clause.push_back(mapping->Literal(ref).Negated());
1068 VLOG(1) <<
"Trivially UNSAT constraint: " <<
ct.DebugString();
1075 const std::vector<IntegerVariable> vars =
1076 mapping->Integers(
ct.linear().vars());
1077 const std::vector<int64> coeffs = ValuesFromProto(
ct.linear().coeffs());
1083 IntegerValue min_sum(0);
1084 IntegerValue max_sum(0);
1085 IntegerValue max_domain_size(0);
1086 bool all_booleans =
true;
1087 for (
int i = 0; i < vars.size(); ++i) {
1088 if (all_booleans && !mapping->IsBoolean(
ct.linear().vars(i))) {
1089 all_booleans =
false;
1091 const IntegerValue lb = integer_trail->LowerBound(vars[i]);
1092 const IntegerValue ub = integer_trail->UpperBound(vars[i]);
1093 max_domain_size =
std::max(max_domain_size, ub - lb + 1);
1094 const IntegerValue term_a = coeffs[i] * lb;
1095 const IntegerValue term_b = coeffs[i] * ub;
1096 min_sum +=
std::min(term_a, term_b);
1097 max_sum +=
std::max(term_a, term_b);
1100 if (
ct.linear().vars_size() == 2 && !integer_trail->IsFixed(vars[0]) &&
1101 !integer_trail->IsFixed(vars[1]) && max_domain_size < 16) {
1102 const SatParameters& params = *m->
GetOrCreate<SatParameters>();
1104 if (params.boolean_encoding_level() > 0 && ConstraintIsEq(
ct.linear()) &&
1105 ct.linear().domain(0) != min_sum &&
ct.linear().domain(0) != max_sum &&
1106 encoder->VariableIsFullyEncoded(vars[0]) &&
1107 encoder->VariableIsFullyEncoded(vars[1])) {
1108 VLOG(3) <<
"Load AC version of " <<
ct.DebugString() <<
", var0 domain = "
1109 << integer_trail->InitialVariableDomain(vars[0])
1110 <<
", var1 domain = "
1111 << integer_trail->InitialVariableDomain(vars[1]);
1112 return LoadEquivalenceAC(mapping->Literals(
ct.enforcement_literal()),
1113 IntegerValue(coeffs[0]), vars[0],
1114 IntegerValue(coeffs[1]), vars[1],
1115 IntegerValue(
ct.linear().domain(0)), m);
1118 int64 single_value = 0;
1119 if (params.boolean_encoding_level() > 0 &&
1120 ConstraintIsNEq(
ct.linear(), mapping, integer_trail, &single_value) &&
1121 single_value != min_sum && single_value != max_sum &&
1122 encoder->VariableIsFullyEncoded(vars[0]) &&
1123 encoder->VariableIsFullyEncoded(vars[1])) {
1124 VLOG(3) <<
"Load NAC version of " <<
ct.DebugString()
1125 <<
", var0 domain = "
1126 << integer_trail->InitialVariableDomain(vars[0])
1127 <<
", var1 domain = "
1128 << integer_trail->InitialVariableDomain(vars[1])
1129 <<
", value = " << single_value;
1130 return LoadEquivalenceNeqAC(mapping->Literals(
ct.enforcement_literal()),
1131 IntegerValue(coeffs[0]), vars[0],
1132 IntegerValue(coeffs[1]), vars[1],
1133 IntegerValue(single_value), m);
1137 if (
ct.linear().domain_size() == 2) {
1138 int64 lb =
ct.linear().domain(0);
1139 int64 ub =
ct.linear().domain(1);
1147 std::vector<LiteralWithCoeff> cst;
1148 for (
int i = 0; i < vars.size(); ++i) {
1149 const int ref =
ct.linear().vars(i);
1150 cst.push_back({mapping->Literal(ref), coeffs[i]});
1162 const std::vector<Literal> enforcement_literals =
1163 mapping->Literals(
ct.enforcement_literal());
1174 std::vector<Literal> clause;
1175 for (
int i = 0; i <
ct.linear().domain_size(); i += 2) {
1176 int64 lb =
ct.linear().domain(i);
1177 int64 ub =
ct.linear().domain(i + 1);
1182 clause.push_back(subdomain_literal);
1192 for (
const int ref :
ct.enforcement_literal()) {
1193 clause.push_back(mapping->Literal(ref).Negated());
1204 const std::vector<IntegerVariable> vars =
1210 int num_fully_encoded = 0;
1211 int64 max_domain_size = 0;
1212 for (
const IntegerVariable variable : vars) {
1213 if (encoder->VariableIsFullyEncoded(variable)) num_fully_encoded++;
1215 IntegerValue lb = integer_trail->
LowerBound(variable);
1216 IntegerValue ub = integer_trail->
UpperBound(variable);
1217 const int64 domain_size = ub.value() - lb.value() + 1;
1218 max_domain_size =
std::max(max_domain_size, domain_size);
1221 if (num_fully_encoded == vars.size() && max_domain_size < 1024) {
1231 const IntegerVariable prod = mapping->
Integer(
ct.int_prod().target());
1232 const std::vector<IntegerVariable> vars =
1233 mapping->Integers(
ct.int_prod().vars());
1234 CHECK_EQ(vars.size(), 2) <<
"General int_prod not supported yet.";
1240 const IntegerVariable div = mapping->
Integer(
ct.int_div().target());
1241 const std::vector<IntegerVariable> vars =
1242 mapping->Integers(
ct.int_div().vars());
1244 const IntegerValue denom(m->
Get(
Value(vars[1])));
1257 const IntegerVariable
min = mapping->Integer(
ct.int_min().target());
1258 const std::vector<IntegerVariable> vars =
1259 mapping->Integers(
ct.int_min().vars());
1267 for (
int j = 0; j < expr_proto.coeffs_size(); ++j) {
1268 expr.
coeffs.push_back(IntegerValue(expr_proto.coeffs(j)));
1270 expr.
offset = IntegerValue(expr_proto.offset());
1278 std::vector<LinearExpression> negated_exprs;
1279 negated_exprs.reserve(
ct.lin_max().exprs_size());
1280 for (
int i = 0; i <
ct.lin_max().exprs_size(); ++i) {
1281 negated_exprs.push_back(
1290 const IntegerVariable
max = mapping->Integer(
ct.int_max().target());
1291 const std::vector<IntegerVariable> vars =
1292 mapping->Integers(
ct.int_max().vars());
1302 if (
ct.no_overlap_2d().x_intervals().empty())
return;
1304 const std::vector<IntervalVariable> x_intervals =
1305 mapping->
Intervals(
ct.no_overlap_2d().x_intervals());
1306 const std::vector<IntervalVariable> y_intervals =
1307 mapping->Intervals(
ct.no_overlap_2d().y_intervals());
1309 x_intervals, y_intervals,
1310 !
ct.no_overlap_2d().boxes_with_null_area_can_overlap()));
1315 const std::vector<IntervalVariable> intervals =
1318 std::vector<AffineExpression> demands;
1319 for (
const IntegerVariable
var :
1320 mapping->Integers(
ct.cumulative().demands())) {
1335 const IntegerVariable
index = mapping->Integer(
ct.element().index());
1336 const IntegerVariable target = mapping->Integer(
ct.element().target());
1337 const std::vector<IntegerVariable> vars =
1338 mapping->Integers(
ct.element().vars());
1342 Domain union_of_non_constant_domains;
1343 std::map<IntegerValue, int> constant_to_num;
1345 const int i = literal_value.value.value();
1348 constant_to_num[
value]++;
1350 union_of_non_constant_domains = union_of_non_constant_domains.
UnionWith(
1351 integer_trail->InitialVariableDomain(vars[i]));
1356 for (
const auto entry : constant_to_num) {
1357 if (union_of_non_constant_domains.
Contains(entry.first.value())) {
1358 constant_to_num[entry.first]++;
1364 bool is_one_to_one_mapping =
true;
1366 const int i = literal_value.value.value();
1368 is_one_to_one_mapping =
false;
1373 if (constant_to_num[
value] == 1) {
1374 const Literal r = literal_value.literal;
1375 encoder->AssociateToIntegerEqualValue(r, target,
value);
1377 is_one_to_one_mapping =
false;
1381 return is_one_to_one_mapping;
1389 const IntegerVariable
index = mapping->Integer(
ct.element().index());
1390 const IntegerVariable target = mapping->Integer(
ct.element().target());
1391 const std::vector<IntegerVariable> vars =
1392 mapping->Integers(
ct.element().vars());
1397 std::vector<Literal> selectors;
1398 std::vector<IntegerVariable> possible_vars;
1399 for (
const auto literal_value : encoding) {
1400 const int i = literal_value.value.value();
1402 CHECK_LT(i, vars.size());
1403 possible_vars.push_back(vars[i]);
1404 selectors.push_back(literal_value.literal);
1405 const Literal r = literal_value.literal;
1407 if (vars[i] == target)
continue;
1441 const IntegerVariable
index = mapping->Integer(
ct.element().index());
1442 const IntegerVariable target = mapping->Integer(
ct.element().target());
1443 const std::vector<IntegerVariable> vars =
1444 mapping->Integers(
ct.element().vars());
1448 absl::flat_hash_map<IntegerValue, Literal> target_map;
1450 for (
const auto literal_value : target_encoding) {
1451 target_map[literal_value.value] = literal_value.literal;
1456 absl::flat_hash_map<IntegerValue, std::vector<Literal>> value_to_literals;
1459 for (
const auto literal_value : index_encoding) {
1460 const int i = literal_value.value.value();
1461 const Literal i_lit = literal_value.literal;
1465 value_to_literals[integer_trail->
LowerBound(vars[i])].push_back(i_lit);
1470 std::vector<Literal> var_selected_literals;
1471 for (
const auto var_literal_value : var_encoding) {
1472 const IntegerValue
value = var_literal_value.value;
1473 const Literal var_is_value = var_literal_value.literal;
1482 const Literal var_is_value_and_selected =
1485 value_to_literals[
value].push_back(var_is_value_and_selected);
1486 var_selected_literals.push_back(var_is_value_and_selected);
1493 for (
const auto& entry : target_map) {
1494 const IntegerValue
value = entry.first;
1495 const Literal target_is_value = entry.second;
1513 void LoadElementConstraintHalfAC(
const ConstraintProto&
ct, Model* m) {
1514 auto* mapping = m->GetOrCreate<CpModelMapping>();
1515 const IntegerVariable
index = mapping->Integer(
ct.element().index());
1516 const IntegerVariable target = mapping->Integer(
ct.element().target());
1517 const std::vector<IntegerVariable> vars =
1518 mapping->Integers(
ct.element().vars());
1520 CHECK(!m->Get(
IsFixed(target)));
1524 const int i = value_literal.value.value();
1526 LoadEquivalenceAC({value_literal.literal}, IntegerValue(1), vars[i],
1527 IntegerValue(-1), target, IntegerValue(0), m);
1531 void LoadBooleanElement(
const ConstraintProto&
ct, Model* m) {
1532 auto* mapping = m->GetOrCreate<CpModelMapping>();
1533 const IntegerVariable
index = mapping->Integer(
ct.element().index());
1534 const std::vector<Literal> literals = mapping->Literals(
ct.element().vars());
1535 const Literal target = mapping->Literal(
ct.element().target());
1542 std::vector<Literal> all_true;
1543 std::vector<Literal> all_false;
1545 const Literal a_lit = literals[value_literal.value.value()];
1546 const Literal i_lit = value_literal.literal;
1549 all_true.push_back(a_lit.Negated());
1550 all_false.push_back(a_lit);
1552 all_true.push_back(target);
1553 all_false.push_back(target.Negated());
1563 const IntegerVariable
index = mapping->Integer(
ct.element().index());
1565 bool boolean_array =
true;
1566 for (
const int ref :
ct.element().vars()) {
1567 if (!mapping->IsBoolean(ref)) {
1568 boolean_array =
false;
1572 if (boolean_array && !mapping->IsBoolean(
ct.element().target())) {
1574 VLOG(1) <<
"Fix boolean_element not propagated on target";
1575 boolean_array =
false;
1580 if (boolean_array) {
1581 LoadBooleanElement(
ct, m);
1585 const IntegerVariable target = mapping->Integer(
ct.element().target());
1586 const std::vector<IntegerVariable> vars =
1587 mapping->Integers(
ct.element().vars());
1616 int num_AC_variables = 0;
1617 const int num_vars =
ct.element().vars().size();
1618 for (
const int v :
ct.element().vars()) {
1619 IntegerVariable variable = mapping->Integer(v);
1620 const bool is_full =
1622 if (is_full) num_AC_variables++;
1625 const SatParameters& params = *m->
GetOrCreate<SatParameters>();
1626 if (params.boolean_encoding_level() > 0 &&
1627 (target_is_AC || num_AC_variables >= num_vars - 1)) {
1628 if (params.boolean_encoding_level() > 1) {
1631 LoadElementConstraintHalfAC(
ct, m);
1640 const std::vector<IntegerVariable> vars =
1641 mapping->Integers(
ct.table().vars());
1642 const std::vector<int64> values = ValuesFromProto(
ct.table().values());
1643 const int num_vars = vars.size();
1644 const int num_tuples = values.size() / num_vars;
1645 std::vector<std::vector<int64>> tuples(num_tuples);
1647 for (
int i = 0; i < num_tuples; ++i) {
1648 for (
int j = 0; j < num_vars; ++j) {
1649 tuples[i].push_back(values[count++]);
1652 if (
ct.table().negated()) {
1661 const std::vector<IntegerVariable> vars =
1662 mapping->Integers(
ct.automaton().vars());
1664 const int num_transitions =
ct.automaton().transition_tail_size();
1665 std::vector<std::vector<int64>> transitions;
1666 transitions.reserve(num_transitions);
1667 for (
int i = 0; i < num_transitions; ++i) {
1668 transitions.push_back({
ct.automaton().transition_tail(i),
1669 ct.automaton().transition_label(i),
1670 ct.automaton().transition_head(i)});
1673 const int64 starting_state =
ct.automaton().starting_state();
1674 const std::vector<int64> final_states =
1675 ValuesFromProto(
ct.automaton().final_states());
1682 const std::vector<IntegerVariable>& vars,
Model* m) {
1683 const int n = vars.size();
1688 std::vector<std::vector<Literal>> matrix(
1689 n, std::vector<Literal>(n, kFalseLiteral));
1690 for (
int i = 0; i < n; i++) {
1691 for (
int j = 0; j < n; j++) {
1694 DCHECK_LE(0,
value);
1695 DCHECK_LT(
value, n);
1696 matrix[i][
value] = kTrueLiteral;
1699 for (
const auto& entry : encoding) {
1700 const int value = entry.value.value();
1701 DCHECK_LE(0,
value);
1702 DCHECK_LT(
value, n);
1703 matrix[i][
value] = entry.literal;
1712 const auto& circuit =
ct.circuit();
1713 if (circuit.tails().empty())
return;
1715 std::vector<int> tails(circuit.tails().begin(), circuit.tails().end());
1716 std::vector<int> heads(circuit.heads().begin(), circuit.heads().end());
1717 std::vector<Literal> literals =
1719 const int num_nodes =
ReindexArcs(&tails, &heads, &literals);
1724 const auto& routes =
ct.routes();
1725 if (routes.tails().empty())
return;
1727 std::vector<int> tails(routes.tails().begin(), routes.tails().end());
1728 std::vector<int> heads(routes.heads().begin(), routes.heads().end());
1729 std::vector<Literal> literals =
1731 const int num_nodes =
ReindexArcs(&tails, &heads, &literals);
1738 const std::vector<IntegerVariable> nexts =
1739 mapping->
Integers(
ct.circuit_covering().nexts());
1740 const std::vector<std::vector<Literal>> graph =
1742 const std::vector<int> distinguished(
1743 ct.circuit_covering().distinguished_nodes().begin(),
1744 ct.circuit_covering().distinguished_nodes().end());
1750 switch (
ct.constraint_case()) {
1751 case ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET:
1753 case ConstraintProto::ConstraintCase::kBoolOr:
1756 case ConstraintProto::ConstraintCase::kBoolAnd:
1759 case ConstraintProto::ConstraintCase::kAtMostOne:
1762 case ConstraintProto::ConstraintCase::kBoolXor:
1765 case ConstraintProto::ConstraintProto::kLinear:
1768 case ConstraintProto::ConstraintProto::kAllDiff:
1771 case ConstraintProto::ConstraintProto::kIntProd:
1774 case ConstraintProto::ConstraintProto::kIntDiv:
1777 case ConstraintProto::ConstraintProto::kIntMin:
1780 case ConstraintProto::ConstraintProto::kLinMax:
1783 case ConstraintProto::ConstraintProto::kIntMax:
1786 case ConstraintProto::ConstraintProto::kInterval:
1789 case ConstraintProto::ConstraintProto::kNoOverlap:
1792 case ConstraintProto::ConstraintProto::kNoOverlap2D:
1795 case ConstraintProto::ConstraintProto::kCumulative:
1798 case ConstraintProto::ConstraintProto::kElement:
1801 case ConstraintProto::ConstraintProto::kTable:
1804 case ConstraintProto::ConstraintProto::kAutomaton:
1807 case ConstraintProto::ConstraintProto::kCircuit:
1810 case ConstraintProto::ConstraintProto::kRoutes:
1813 case ConstraintProto::ConstraintProto::kCircuitCovering: