24#include "absl/container/btree_set.h"
25#include "absl/container/flat_hash_map.h"
26#include "absl/container/flat_hash_set.h"
27#include "absl/memory/memory.h"
28#include "absl/meta/type_traits.h"
29#include "absl/strings/str_cat.h"
30#include "absl/types/span.h"
39#include "ortools/sat/cp_model.pb.h"
53#include "ortools/sat/sat_parameters.pb.h"
65template <
typename Values>
66std::vector<int64_t> ValuesFromProto(
const Values& values) {
67 return std::vector<int64_t>(values.begin(), values.end());
70void ComputeLinearBounds(
const LinearConstraintProto&
proto,
71 CpModelMapping* mapping, IntegerTrail* integer_trail,
72 int64_t* sum_min, int64_t* sum_max) {
76 for (
int i = 0; i <
proto.vars_size(); ++i) {
78 const IntegerVariable
var = mapping->Integer(
proto.vars(i));
79 const int64_t lb = integer_trail->LowerBound(
var).value();
80 const int64_t ub = integer_trail->UpperBound(
var).value();
82 (*sum_min) +=
coeff * lb;
83 (*sum_max) +=
coeff * ub;
85 (*sum_min) +=
coeff * ub;
86 (*sum_max) +=
coeff * lb;
92bool ConstraintIsEq(
const LinearConstraintProto&
proto) {
97bool ConstraintIsNEq(
const LinearConstraintProto&
proto,
98 CpModelMapping* mapping, IntegerTrail* integer_trail,
99 int64_t* single_value) {
102 ComputeLinearBounds(
proto, mapping, integer_trail, &sum_min, &sum_max);
104 const Domain complement =
105 Domain(sum_min, sum_max)
107 if (complement.IsEmpty())
return false;
108 const int64_t
value = complement.Min();
110 if (complement.Size() == 1) {
111 if (single_value !=
nullptr) {
112 *single_value =
value;
122 bool view_all_booleans_as_integers,
Model* m) {
124 const int num_proto_variables =
model_proto.variables_size();
130 CHECK_EQ(sat_solver->NumVariables(), 0);
132 BooleanVariable new_var(0);
133 std::vector<BooleanVariable> false_variables;
134 std::vector<BooleanVariable> true_variables;
137 mapping->reverse_boolean_map_.resize(num_proto_variables, -1);
138 for (
int i = 0; i < num_proto_variables; ++i) {
139 const auto& domain =
model_proto.variables(i).domain();
140 if (domain.size() != 2)
continue;
141 if (domain[0] >= 0 && domain[1] <= 1) {
142 mapping->booleans_[i] = new_var;
143 mapping->reverse_boolean_map_[new_var] = i;
144 if (domain[1] == 0) {
145 false_variables.push_back(new_var);
146 }
else if (domain[0] == 1) {
147 true_variables.push_back(new_var);
153 sat_solver->SetNumVariables(new_var.value());
154 for (
const BooleanVariable
var : true_variables) {
157 for (
const BooleanVariable
var : false_variables) {
164 std::vector<int> var_to_instantiate_as_integer;
165 if (view_all_booleans_as_integers) {
166 var_to_instantiate_as_integer.resize(num_proto_variables);
167 for (
int i = 0; i < num_proto_variables; ++i) {
168 var_to_instantiate_as_integer[i] = i;
172 absl::flat_hash_set<int> used_variables;
175 for (
int c = 0; c <
model_proto.constraints_size(); ++c) {
186 for (
const int obj_var :
model_proto.objective().vars()) {
193 for (
int i = 0; i < num_proto_variables; ++i) {
195 used_variables.insert(i);
200 var_to_instantiate_as_integer.assign(used_variables.begin(),
201 used_variables.end());
208 var_to_instantiate_as_integer.size());
209 mapping->reverse_integer_map_.resize(2 * var_to_instantiate_as_integer.size(),
211 for (
const int i : var_to_instantiate_as_integer) {
213 mapping->integers_[i] =
215 DCHECK_LT(mapping->integers_[i], mapping->reverse_integer_map_.size());
216 mapping->reverse_integer_map_[mapping->integers_[i]] = i;
223 for (
int i = 0; i < num_proto_variables; ++i) {
228 encoder->AssociateToIntegerEqualValue(
229 sat::Literal(mapping->booleans_[i],
true), mapping->integers_[i],
234 mapping->intervals_.resize(
model_proto.constraints_size(),
236 for (
int c = 0; c <
model_proto.constraints_size(); ++c) {
238 if (
ct.constraint_case() != ConstraintProto::ConstraintCase::kInterval) {
243 mapping->
Literal(
ct.enforcement_literal(0));
247 mapping->intervals_[c] = intervals_repository->CreateInterval(
248 mapping->Affine(
ct.interval().start()),
249 mapping->Affine(
ct.interval().end()),
250 mapping->Affine(
ct.interval().size()), enforcement_literal.
Index(),
253 mapping->intervals_[c] = intervals_repository->CreateInterval(
254 mapping->Affine(
ct.interval().start()),
255 mapping->Affine(
ct.interval().end()),
259 mapping->already_loaded_ct_.insert(&
ct);
265 const SymmetryProto& symmetry =
model_proto.symmetry();
266 if (symmetry.permutations().empty())
return;
269 const int num_vars =
model_proto.variables().size();
270 std::vector<bool> can_be_used_in_symmetry(num_vars,
true);
273 for (
int v = 0; v < num_vars; ++v) {
274 if (!mapping->IsBoolean(v)) can_be_used_in_symmetry[v] =
false;
285 const int num_constraints =
model_proto.constraints().size();
286 for (
int c = 0; c < num_constraints; ++c) {
288 if (
ct.constraint_case() != ConstraintProto::kLinear)
continue;
289 if (
ct.linear().domain().size() <= 2)
continue;
294 for (
const int ref :
ct.linear().vars()) {
301 sat_solver->AddPropagator(symmetry_handler);
302 const int num_literals = 2 * sat_solver->NumVariables();
304 for (
const SparsePermutationProto& perm : symmetry.permutations()) {
305 bool can_be_used =
true;
306 for (
const int var : perm.support()) {
307 if (!can_be_used_in_symmetry[
var]) {
312 if (!can_be_used)
continue;
315 auto literal_permutation =
316 absl::make_unique<SparsePermutation>(num_literals);
317 int support_index = 0;
318 const int num_cycle = perm.cycle_sizes().size();
319 for (
int i = 0; i < num_cycle; ++i) {
320 const int size = perm.cycle_sizes(i);
321 const int saved_support_index = support_index;
322 for (
int j = 0; j < size; ++j) {
323 const int var = perm.support(support_index++);
324 literal_permutation->AddToCurrentCycle(
325 mapping->Literal(
var).Index().value());
327 literal_permutation->CloseCurrentCycle();
331 support_index = saved_support_index;
332 for (
int j = 0; j < size; ++j) {
333 const int var = perm.support(support_index++);
334 literal_permutation->AddToCurrentCycle(
335 mapping->Literal(
var).NegatedIndex().value());
337 literal_permutation->CloseCurrentCycle();
339 symmetry_handler->AddSymmetry(std::move(literal_permutation));
343 symmetry_handler->num_permutations(),
344 " symmetry to the SAT solver.");
360 if (sat_solver->IsModelUnsat())
return;
365 struct EqualityDetectionHelper {
366 const ConstraintProto*
ct;
371 bool operator<(
const EqualityDetectionHelper& o)
const {
372 if (
literal.Variable() == o.literal.Variable()) {
373 if (
value == o.value)
return is_equality && !o.is_equality;
374 return value < o.value;
376 return literal.Variable() < o.literal.Variable();
379 std::vector<std::vector<EqualityDetectionHelper>> var_to_equalities(
391 struct InequalityDetectionHelper {
392 const ConstraintProto*
ct;
396 bool operator<(
const InequalityDetectionHelper& o)
const {
397 if (
literal.Variable() == o.literal.Variable()) {
398 return i_lit.
var < o.i_lit.var;
400 return literal.Variable() < o.literal.Variable();
403 std::vector<InequalityDetectionHelper> inequalities;
406 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
407 if (
ct.constraint_case() != ConstraintProto::ConstraintCase::kLinear) {
410 if (
ct.enforcement_literal().size() != 1)
continue;
411 if (
ct.linear().vars_size() != 1)
continue;
415 mapping->
Literal(
ct.enforcement_literal(0));
416 if (sat_solver->Assignment().LiteralIsFalse(enforcement_literal))
continue;
418 const int ref =
ct.linear().vars(0);
422 const Domain domain_if_enforced =
427 if (domain_if_enforced.
IsEmpty()) {
428 if (!sat_solver->AddUnitClause(enforcement_literal.
Negated()))
return;
434 if (domain_if_enforced.
Max() >= domain.
Max() &&
435 domain_if_enforced.
Min() > domain.
Min()) {
436 inequalities.push_back({&
ct, enforcement_literal,
438 mapping->Integer(
var),
439 IntegerValue(domain_if_enforced.
Min()))});
440 }
else if (domain_if_enforced.
Min() <= domain.
Min() &&
441 domain_if_enforced.
Max() < domain.
Max()) {
442 inequalities.push_back({&
ct, enforcement_literal,
444 mapping->Integer(
var),
445 IntegerValue(domain_if_enforced.
Max()))});
451 if (domain_if_enforced.
Min() > domain.
Min()) {
455 mapping->Integer(
var), IntegerValue(domain_if_enforced.
Min())));
457 if (domain_if_enforced.
Max() < domain.
Max()) {
461 IntegerValue(domain_if_enforced.
Max())));
472 var_to_equalities[
var].push_back(
473 {&
ct, enforcement_literal, inter.
Min(),
true});
475 mapping->variables_to_encoded_values_[
var].insert(inter.
Min());
483 var_to_equalities[
var].push_back(
484 {&
ct, enforcement_literal, inter.
Min(),
false});
486 mapping->variables_to_encoded_values_[
var].insert(inter.
Min());
493 int num_inequalities = 0;
494 std::sort(inequalities.begin(), inequalities.end());
495 for (
int i = 0; i + 1 < inequalities.size(); i++) {
503 if (integer_trail->IntegerLiteralIsTrue(inequalities[i].i_lit) ||
504 integer_trail->IntegerLiteralIsFalse(inequalities[i].i_lit)) {
507 if (integer_trail->IntegerLiteralIsTrue(inequalities[i + 1].i_lit) ||
508 integer_trail->IntegerLiteralIsFalse(inequalities[i + 1].i_lit)) {
512 const auto pair_a = encoder->Canonicalize(inequalities[i].i_lit);
513 const auto pair_b = encoder->Canonicalize(inequalities[i + 1].i_lit);
514 if (pair_a.first == pair_b.second) {
516 encoder->AssociateToIntegerLiteral(inequalities[i].
literal,
517 inequalities[i].i_lit);
518 mapping->already_loaded_ct_.insert(inequalities[i].
ct);
519 mapping->already_loaded_ct_.insert(inequalities[i + 1].
ct);
524 int num_half_inequalities = 0;
525 for (
const auto inequality : inequalities) {
526 if (mapping->ConstraintIsAlreadyLoaded(inequality.ct))
continue;
529 encoder->GetOrCreateAssociatedLiteral(inequality.i_lit)));
530 if (sat_solver->IsModelUnsat())
return;
532 ++num_half_inequalities;
533 mapping->already_loaded_ct_.insert(inequality.ct);
534 mapping->is_half_encoding_ct_.insert(inequality.ct);
537 if (!inequalities.empty()) {
538 VLOG(1) << num_inequalities <<
" literals associated to VAR >= value, and "
539 << num_half_inequalities <<
" half-associations.";
545 int num_constraints = 0;
546 int num_equalities = 0;
547 int num_half_equalities = 0;
548 int num_fully_encoded = 0;
549 int num_partially_encoded = 0;
550 for (
int i = 0; i < var_to_equalities.size(); ++i) {
551 std::vector<EqualityDetectionHelper>& encoding = var_to_equalities[i];
552 std::sort(encoding.begin(), encoding.end());
553 if (encoding.empty())
continue;
554 num_constraints += encoding.size();
556 absl::flat_hash_set<int64_t> values;
557 for (
int j = 0; j + 1 < encoding.size(); j++) {
558 if ((encoding[j].
value != encoding[j + 1].
value) ||
560 (encoding[j].is_equality !=
true) ||
561 (encoding[j + 1].is_equality !=
false)) {
566 encoder->AssociateToIntegerEqualValue(encoding[j].
literal,
567 mapping->integers_[i],
568 IntegerValue(encoding[j].
value));
569 mapping->already_loaded_ct_.insert(encoding[j].
ct);
570 mapping->already_loaded_ct_.insert(encoding[j + 1].
ct);
571 values.insert(encoding[j].
value);
577 if (sat_solver->IsModelUnsat())
return;
585 for (
const auto equality : encoding) {
586 if (mapping->ConstraintIsAlreadyLoaded(equality.ct))
continue;
587 const class Literal eq = encoder->GetOrCreateLiteralAssociatedToEquality(
588 mapping->integers_[i], IntegerValue(equality.value));
589 if (equality.is_equality) {
595 ++num_half_equalities;
596 mapping->already_loaded_ct_.insert(equality.ct);
597 mapping->is_half_encoding_ct_.insert(equality.ct);
602 if (encoder->VariableIsFullyEncoded(mapping->integers_[i])) {
605 ++num_partially_encoded;
610 if (num_constraints > 0) {
611 VLOG(1) << num_equalities <<
" literals associated to VAR == value, and "
612 << num_half_equalities <<
" half-associations.";
614 if (num_fully_encoded > 0) {
615 VLOG(1) <<
"num_fully_encoded_variables: " << num_fully_encoded;
617 if (num_partially_encoded > 0) {
618 VLOG(1) <<
"num_partially_encoded_variables: " << num_partially_encoded;
623 int num_element_encoded = 0;
629 for (
int c = 0; c <
model_proto.constraints_size(); ++c) {
632 if (
ct.constraint_case() != ConstraintProto::kExactlyOne)
continue;
635 absl::flat_hash_map<IntegerVariable, std::vector<ValueLiteralPair>>
636 var_to_value_literal_list;
637 for (
const int l :
ct.exactly_one().literals()) {
639 for (
const auto& var_value : implied_bounds->GetImpliedValues(
literal)) {
640 var_to_value_literal_list[var_value.first].push_back(
646 std::vector<IntegerVariable> encoded_variables;
647 std::string encoded_variables_str;
650 for (
const auto& [
var, literal_value_list] : var_to_value_literal_list) {
651 if (literal_value_list.size() <
ct.exactly_one().literals_size()) {
652 VLOG(2) <<
"X" <<
var.value() <<
" has " << literal_value_list.size()
653 <<
" implied values, and a domain of size "
655 ->InitialVariableDomain(
var)
661 implied_bounds->AddElementEncoding(
var, literal_value_list, c);
663 encoded_variables.push_back(
var);
664 absl::StrAppend(&encoded_variables_str,
" X",
var.value());
665 num_element_encoded++;
668 if (encoded_variables.size() > 1 &&
VLOG_IS_ON(1)) {
669 VLOG(1) <<
"exactly_one(" << c <<
") encodes " << encoded_variables.size()
670 <<
" variables at the same time: " << encoded_variables_str;
674 if (num_element_encoded > 0) {
675 VLOG(1) <<
"num_element_encoded: " << num_element_encoded;
686 int64_t num_associations = 0;
687 int64_t num_set_to_false = 0;
688 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
689 if (!
ct.enforcement_literal().empty())
continue;
690 if (
ct.constraint_case() != ConstraintProto::kLinear)
continue;
691 if (
ct.linear().vars_size() != 2)
continue;
692 if (!ConstraintIsEq(
ct.linear()))
continue;
694 const IntegerValue rhs(
ct.linear().domain(0));
697 IntegerVariable var1 = mapping->Integer(
ct.linear().vars(0));
698 IntegerVariable var2 = mapping->Integer(
ct.linear().vars(1));
699 IntegerValue coeff1(
ct.linear().coeffs(0));
700 IntegerValue coeff2(
ct.linear().coeffs(1));
712 if (coeff1 == 0 || coeff2 == 0)
continue;
717 for (
int i = 0; i < 2; ++i) {
718 for (
const auto value_literal :
719 encoder->PartialGreaterThanEncoding(var1)) {
720 const IntegerValue value1 = value_literal.first;
721 const IntegerValue bound2 =
FloorRatio(rhs - value1 * coeff1, coeff2);
723 encoder->AssociateToIntegerLiteral(
735 for (
int i = 0; i < 2; ++i) {
736 for (
const auto value_literal : encoder->PartialDomainEncoding(var1)) {
737 const IntegerValue value1 = value_literal.value;
738 const IntegerValue intermediate = rhs - value1 * coeff1;
739 if (intermediate % coeff2 != 0) {
742 sat_solver->AddUnitClause(value_literal.literal.Negated());
746 encoder->AssociateToIntegerEqualValue(value_literal.literal, var2,
747 intermediate / coeff2);
754 if (num_associations > 0) {
755 VLOG(1) <<
"Num associations from equivalences = " << num_associations;
757 if (num_set_to_false > 0) {
758 VLOG(1) <<
"Num literals set to false from equivalences = "
766 if (!
parameters.use_optional_variables())
return;
767 if (
parameters.enumerate_all_solutions())
return;
770 const int num_proto_variables =
model_proto.variables_size();
771 std::vector<bool> already_seen(num_proto_variables,
false);
773 for (
const int ref :
model_proto.objective().vars()) {
786 std::vector<std::vector<int>> enforcement_intersection(num_proto_variables);
787 absl::btree_set<int> literals_set;
788 for (
int c = 0; c <
model_proto.constraints_size(); ++c) {
790 if (
ct.enforcement_literal().empty()) {
792 already_seen[
var] =
true;
793 enforcement_intersection[
var].clear();
796 literals_set.clear();
797 literals_set.insert(
ct.enforcement_literal().begin(),
798 ct.enforcement_literal().end());
800 if (!already_seen[
var]) {
801 enforcement_intersection[
var].assign(
ct.enforcement_literal().begin(),
802 ct.enforcement_literal().end());
805 std::vector<int>& vector_ref = enforcement_intersection[
var];
807 for (
const int literal : vector_ref) {
809 vector_ref[new_size++] =
literal;
812 vector_ref.resize(new_size);
814 already_seen[
var] =
true;
820 int num_optionals = 0;
822 for (
int var = 0;
var < num_proto_variables; ++
var) {
823 const IntegerVariableProto& var_proto =
model_proto.variables(
var);
824 const int64_t
min = var_proto.domain(0);
825 const int64_t
max = var_proto.domain(var_proto.domain().size() - 1);
827 if (
min == 0 &&
max == 1)
continue;
828 if (enforcement_intersection[
var].empty())
continue;
831 integer_trail->MarkIntegerVariableAsOptional(
832 mapping->Integer(
var),
833 mapping->Literal(enforcement_intersection[
var].front()));
836 if (num_optionals > 0) {
838 " optional variables.");
848 for (
const DecisionStrategyProto& strategy :
model_proto.search_strategy()) {
849 if (strategy.domain_reduction_strategy() ==
850 DecisionStrategyProto::SELECT_MEDIAN_VALUE) {
851 for (
const int ref : strategy.variables()) {
852 if (!mapping->IsInteger(ref))
return;
853 const IntegerVariable variable = mapping->Integer(
PositiveRef(ref));
854 if (!integer_trail->IsFixed(variable)) {
868 std::vector<Literal> literals = mapping->
Literals(
ct.bool_or().literals());
869 for (
const int ref :
ct.enforcement_literal()) {
870 literals.push_back(mapping->Literal(ref).Negated());
877 std::vector<Literal> literals;
878 for (
const int ref :
ct.enforcement_literal()) {
879 literals.push_back(mapping->Literal(ref).Negated());
911void LoadEquivalenceAC(
const std::vector<Literal> enforcement_literal,
912 IntegerValue coeff1, IntegerVariable var1,
913 IntegerValue coeff2, IntegerVariable var2,
914 const IntegerValue rhs, Model* m) {
915 auto* encoder = m->GetOrCreate<IntegerEncoder>();
916 CHECK(encoder->VariableIsFullyEncoded(var1));
917 CHECK(encoder->VariableIsFullyEncoded(var2));
918 absl::flat_hash_map<IntegerValue, Literal> term1_value_to_literal;
919 for (
const auto value_literal : encoder->FullDomainEncoding(var1)) {
920 term1_value_to_literal[coeff1 * value_literal.value] =
921 value_literal.literal;
923 for (
const auto value_literal : encoder->FullDomainEncoding(var2)) {
924 const IntegerValue target = rhs - value_literal.value * coeff2;
925 if (!term1_value_to_literal.contains(target)) {
927 {value_literal.literal.Negated()}));
929 const Literal target_literal = term1_value_to_literal[target];
931 {value_literal.literal.Negated(), target_literal}));
933 {value_literal.literal, target_literal.Negated()}));
937 term1_value_to_literal.erase(target);
943 std::vector<Literal> implied_false;
944 for (
const auto entry : term1_value_to_literal) {
945 implied_false.push_back(entry.second);
947 std::sort(implied_false.begin(), implied_false.end());
948 for (
const Literal l : implied_false) {
955void LoadEquivalenceNeqAC(
const std::vector<Literal> enforcement_literal,
956 IntegerValue coeff1, IntegerVariable var1,
957 IntegerValue coeff2, IntegerVariable var2,
958 const IntegerValue rhs, Model* m) {
959 auto* encoder = m->GetOrCreate<IntegerEncoder>();
960 CHECK(encoder->VariableIsFullyEncoded(var1));
961 CHECK(encoder->VariableIsFullyEncoded(var2));
962 absl::flat_hash_map<IntegerValue, Literal> term1_value_to_literal;
963 for (
const auto value_literal : encoder->FullDomainEncoding(var1)) {
964 term1_value_to_literal[coeff1 * value_literal.value] =
965 value_literal.literal;
967 for (
const auto value_literal : encoder->FullDomainEncoding(var2)) {
968 const IntegerValue target_value = rhs - value_literal.value * coeff2;
969 const auto& it = term1_value_to_literal.find(target_value);
970 if (it != term1_value_to_literal.end()) {
971 const Literal target_literal = it->second;
974 {value_literal.literal.Negated(), target_literal.Negated()}));
983 if (
ct.linear().vars().empty()) {
987 std::vector<Literal> clause;
988 for (
const int ref :
ct.enforcement_literal()) {
989 clause.push_back(mapping->Literal(ref).Negated());
993 VLOG(1) <<
"Trivially UNSAT constraint: " <<
ct.DebugString();
1000 const std::vector<IntegerVariable> vars =
1001 mapping->Integers(
ct.linear().vars());
1002 const std::vector<int64_t> coeffs = ValuesFromProto(
ct.linear().coeffs());
1008 IntegerValue min_sum(0);
1009 IntegerValue max_sum(0);
1010 IntegerValue max_domain_size(0);
1011 bool all_booleans =
true;
1012 for (
int i = 0; i < vars.size(); ++i) {
1013 if (all_booleans && !mapping->IsBoolean(
ct.linear().vars(i))) {
1014 all_booleans =
false;
1016 const IntegerValue lb = integer_trail->LowerBound(vars[i]);
1017 const IntegerValue ub = integer_trail->UpperBound(vars[i]);
1018 max_domain_size =
std::max(max_domain_size, ub - lb + 1);
1019 const IntegerValue term_a = coeffs[i] * lb;
1020 const IntegerValue term_b = coeffs[i] * ub;
1021 min_sum +=
std::min(term_a, term_b);
1022 max_sum +=
std::max(term_a, term_b);
1025 const SatParameters& params = *m->
GetOrCreate<SatParameters>();
1026 const IntegerValue domain_size_limit(
1027 params.max_domain_size_when_encoding_eq_neq_constraints());
1028 if (
ct.linear().vars_size() == 2 && !integer_trail->IsFixed(vars[0]) &&
1029 !integer_trail->IsFixed(vars[1]) &&
1030 max_domain_size <= domain_size_limit) {
1032 if (params.boolean_encoding_level() > 0 && ConstraintIsEq(
ct.linear()) &&
1033 ct.linear().domain(0) != min_sum &&
ct.linear().domain(0) != max_sum &&
1034 encoder->VariableIsFullyEncoded(vars[0]) &&
1035 encoder->VariableIsFullyEncoded(vars[1])) {
1036 VLOG(3) <<
"Load AC version of " <<
ct.DebugString() <<
", var0 domain = "
1037 << integer_trail->InitialVariableDomain(vars[0])
1038 <<
", var1 domain = "
1039 << integer_trail->InitialVariableDomain(vars[1]);
1040 return LoadEquivalenceAC(mapping->Literals(
ct.enforcement_literal()),
1041 IntegerValue(coeffs[0]), vars[0],
1042 IntegerValue(coeffs[1]), vars[1],
1043 IntegerValue(
ct.linear().domain(0)), m);
1046 int64_t single_value = 0;
1047 if (params.boolean_encoding_level() > 0 &&
1048 ConstraintIsNEq(
ct.linear(), mapping, integer_trail, &single_value) &&
1049 single_value != min_sum && single_value != max_sum &&
1050 encoder->VariableIsFullyEncoded(vars[0]) &&
1051 encoder->VariableIsFullyEncoded(vars[1])) {
1052 VLOG(3) <<
"Load NAC version of " <<
ct.DebugString()
1053 <<
", var0 domain = "
1054 << integer_trail->InitialVariableDomain(vars[0])
1055 <<
", var1 domain = "
1056 << integer_trail->InitialVariableDomain(vars[1])
1057 <<
", value = " << single_value;
1058 return LoadEquivalenceNeqAC(mapping->Literals(
ct.enforcement_literal()),
1059 IntegerValue(coeffs[0]), vars[0],
1060 IntegerValue(coeffs[1]), vars[1],
1061 IntegerValue(single_value), m);
1065 if (
ct.linear().domain_size() == 2) {
1066 int64_t lb =
ct.linear().domain(0);
1067 int64_t ub =
ct.linear().domain(1);
1075 std::vector<LiteralWithCoeff> cst;
1076 for (
int i = 0; i < vars.size(); ++i) {
1077 const int ref =
ct.linear().vars(i);
1078 cst.push_back({mapping->Literal(ref), coeffs[i]});
1090 const std::vector<Literal> enforcement_literals =
1091 mapping->Literals(
ct.enforcement_literal());
1104 const bool special_case =
1105 ct.enforcement_literal().empty() &&
ct.linear().domain_size() == 4;
1107 std::vector<Literal> clause;
1108 for (
int i = 0; i <
ct.linear().domain_size(); i += 2) {
1109 int64_t lb =
ct.linear().domain(i);
1110 int64_t ub =
ct.linear().domain(i + 1);
1114 const Literal subdomain_literal(
1115 special_case && i > 0 ? clause.back().Negated()
1117 clause.push_back(subdomain_literal);
1128 for (
const int ref :
ct.enforcement_literal()) {
1129 clause.push_back(mapping->Literal(ref).Negated());
1137 const std::vector<AffineExpression> expressions =
1138 mapping->
Affines(
ct.all_diff().exprs());
1146 <<
"General int_prod not supported yet.";
1153 VLOG(1) <<
"Product " <<
ct.DebugString() <<
" can be linearized";
1165 if (integer_trail->IsFixed(denom)) {
1171 VLOG(1) <<
"Division " <<
ct.DebugString() <<
" can be linearized";
1185 CHECK(integer_trail->IsFixed(mod));
1186 const IntegerValue fixed_modulo = integer_trail->FixedValue(mod);
1191 if (
ct.lin_max().exprs().empty()) {
1198 std::vector<LinearExpression> negated_exprs;
1199 negated_exprs.reserve(
ct.lin_max().exprs_size());
1200 for (
int i = 0; i <
ct.lin_max().exprs_size(); ++i) {
1201 negated_exprs.push_back(
1202 NegationOf(mapping->GetExprFromProto(
ct.lin_max().exprs(i))));
1214 if (
ct.no_overlap_2d().x_intervals().empty())
return;
1216 const std::vector<IntervalVariable> x_intervals =
1217 mapping->
Intervals(
ct.no_overlap_2d().x_intervals());
1218 const std::vector<IntervalVariable> y_intervals =
1219 mapping->Intervals(
ct.no_overlap_2d().y_intervals());
1221 x_intervals, y_intervals,
1222 !
ct.no_overlap_2d().boxes_with_null_area_can_overlap(),
1223 m->
GetOrCreate<SatParameters>()->use_cumulative_in_no_overlap_2d()));
1228 const std::vector<IntervalVariable> intervals =
1231 const std::vector<AffineExpression> demands =
1232 mapping->Affines(
ct.cumulative().demands());
1237 const auto& circuit =
ct.circuit();
1238 if (circuit.tails().empty())
return;
1240 std::vector<int> tails(circuit.tails().begin(), circuit.tails().end());
1241 std::vector<int> heads(circuit.heads().begin(), circuit.heads().end());
1242 std::vector<Literal> literals =
1244 const int num_nodes =
ReindexArcs(&tails, &heads);
1249 const auto& routes =
ct.routes();
1250 if (routes.tails().empty())
return;
1252 std::vector<int> tails(routes.tails().begin(), routes.tails().end());
1253 std::vector<int> heads(routes.heads().begin(), routes.heads().end());
1254 std::vector<Literal> literals =
1256 const int num_nodes =
ReindexArcs(&tails, &heads);
1262 switch (
ct.constraint_case()) {
1263 case ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET:
1265 case ConstraintProto::ConstraintCase::kBoolOr:
1268 case ConstraintProto::ConstraintCase::kBoolAnd:
1271 case ConstraintProto::ConstraintCase::kAtMostOne:
1274 case ConstraintProto::ConstraintCase::kExactlyOne:
1277 case ConstraintProto::ConstraintCase::kBoolXor:
1280 case ConstraintProto::ConstraintProto::kLinear:
1283 case ConstraintProto::ConstraintProto::kAllDiff:
1286 case ConstraintProto::ConstraintProto::kIntProd:
1289 case ConstraintProto::ConstraintProto::kIntDiv:
1292 case ConstraintProto::ConstraintProto::kIntMod:
1295 case ConstraintProto::ConstraintProto::kLinMax:
1298 case ConstraintProto::ConstraintProto::kInterval:
1301 case ConstraintProto::ConstraintProto::kNoOverlap:
1304 case ConstraintProto::ConstraintProto::kNoOverlap2D:
1307 case ConstraintProto::ConstraintProto::kCumulative:
1310 case ConstraintProto::ConstraintProto::kCircuit:
1313 case ConstraintProto::ConstraintProto::kRoutes:
#define CHECK_EQ(val1, val2)
#define DCHECK_LT(val1, val2)
#define VLOG(verboselevel)
We call domain any subset of Int64 = [kint64min, kint64max].
Domain InverseMultiplicationBy(const int64_t coeff) const
Returns {x ∈ Int64, ∃ e ∈ D, x * coeff = e}.
Domain Complement() const
Returns the set Int64 ∖ D.
bool Contains(int64_t value) const
Returns true iff value is in Domain.
int NumIntervals() const
Basic read-only std::vector<> wrapping to view a Domain as a sorted list of non-adjacent intervals.
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
int64_t Min() const
Returns the min value of the domain.
bool IsEmpty() const
Returns true if this is the empty set.
int64_t Max() const
Returns the max value of the domain.
std::vector< IntervalVariable > Intervals(const ProtoIndices &indices) const
std::vector< AffineExpression > Affines(const List &list) const
std::vector< sat::Literal > Literals(const ProtoIndices &indices) const
void ReserveSpaceForNumVariables(int num_vars)
Literal(int signed_value)
LiteralIndex Index() const
Class that owns everything related to a particular optimization model.
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:
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
bool AddProblemClause(absl::Span< const Literal > literals)
CpModelProto const * model_proto
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
bool ContainsKey(const Collection &collection, const Key &key)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
std::function< std::vector< ValueLiteralPair >(Model *)> FullyEncodeVariable(IntegerVariable var)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
std::function< void(Model *)> WeightedSumGreaterOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
std::function< void(Model *)> LiteralXorIs(const std::vector< Literal > &literals, bool value)
void LoadExactlyOneConstraint(const ConstraintProto &ct, Model *m)
void LoadVariables(const CpModelProto &model_proto, bool view_all_booleans_as_integers, Model *m)
void LoadIntProdConstraint(const ConstraintProto &ct, Model *m)
bool LoadConstraint(const ConstraintProto &ct, Model *m)
std::vector< int > UsedVariables(const ConstraintProto &ct)
void LoadBoolOrConstraint(const ConstraintProto &ct, Model *m)
bool RefIsPositive(int ref)
void ExtractElementEncoding(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> WeightedSumLowerOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
const LiteralIndex kNoLiteralIndex(-1)
std::function< void(Model *)> ProductConstraint(AffineExpression a, AffineExpression b, AffineExpression p)
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
std::function< void(Model *)> EnforcedClause(absl::Span< const Literal > enforcement_literals, absl::Span< const Literal > clause)
std::function< void(Model *)> SubcircuitConstraint(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, bool multiple_subcircuit_through_zero)
std::function< BooleanVariable(Model *)> NewBooleanVariable()
std::function< void(Model *)> FixedDivisionConstraint(AffineExpression a, IntegerValue b, AffineExpression c)
bool HasEnforcementLiteral(const ConstraintProto &ct)
void LoadBooleanSymmetries(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> ConditionalWeightedSumGreaterOrEqual(const std::vector< Literal > &enforcement_literals, const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
void LoadCumulativeConstraint(const ConstraintProto &ct, Model *m)
void LoadRoutesConstraint(const ConstraintProto &ct, Model *m)
void LoadBoolAndConstraint(const ConstraintProto &ct, Model *m)
void LoadLinMaxConstraint(const ConstraintProto &ct, Model *m)
void LoadBoolXorConstraint(const ConstraintProto &ct, Model *m)
void LoadIntModConstraint(const ConstraintProto &ct, Model *m)
const IntegerVariable kNoIntegerVariable(-1)
const IntervalVariable kNoIntervalVariable(-1)
std::function< void(Model *)> ConditionalWeightedSumLowerOrEqual(const std::vector< Literal > &enforcement_literals, const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
std::function< void(Model *)> Cumulative(const std::vector< IntervalVariable > &vars, const std::vector< AffineExpression > &demands, AffineExpression capacity, SchedulingConstraintHelper *helper)
void LoadIntDivConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> DivisionConstraint(AffineExpression num, AffineExpression denom, AffineExpression div)
std::function< void(Model *)> Implication(const std::vector< Literal > &enforcement_literals, IntegerLiteral i)
void LoadLinearConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> AtMostOneConstraint(const std::vector< Literal > &literals)
bool DetectLinearEncodingOfProducts(const AffineExpression &left, const AffineExpression &right, Model *model, LinearConstraintBuilder *builder)
int ReindexArcs(IntContainer *tails, IntContainer *heads)
std::function< void(Model *)> Disjunctive(const std::vector< IntervalVariable > &vars)
void LoadAtMostOneConstraint(const ConstraintProto &ct, Model *m)
void LoadCircuitConstraint(const ConstraintProto &ct, Model *m)
void LoadNoOverlapConstraint(const ConstraintProto &ct, Model *m)
void DetectOptionalVariables(const CpModelProto &model_proto, Model *m)
void LoadAllDiffConstraint(const ConstraintProto &ct, Model *m)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
std::function< void(Model *)> IsEqualToMinOf(IntegerVariable min_var, const std::vector< IntegerVariable > &vars)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
void LoadNoOverlap2dConstraint(const ConstraintProto &ct, Model *m)
IndexReferences GetReferencesUsedByConstraint(const ConstraintProto &ct)
std::function< void(Model *)> NonOverlappingRectangles(const std::vector< IntervalVariable > &x, const std::vector< IntervalVariable > &y, bool is_strict, bool add_cumulative_relaxation=true)
std::function< void(Model *)> BooleanLinearConstraint(int64_t lower_bound, int64_t upper_bound, std::vector< LiteralWithCoeff > *cst)
void AddFullEncodingFromSearchBranching(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
void ExtractEncoding(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> FixedModuloConstraint(AffineExpression a, IntegerValue b, AffineExpression c)
const BooleanVariable kNoBooleanVariable(-1)
void PropagateEncodingFromEquivalenceRelations(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> AllDifferentOnBounds(const std::vector< AffineExpression > &expressions)
Collection of objects used to extend the Constraint Solver library.
std::vector< int > variables
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
#define SOLVER_LOG(logger,...)
#define VLOG_IS_ON(verboselevel)