31 #include "absl/container/flat_hash_map.h" 32 #include "absl/container/flat_hash_set.h" 33 #include "absl/hash/hash.h" 34 #include "absl/random/random.h" 35 #include "absl/strings/str_join.h" 62 bool CpModelPresolver::RemoveConstraint(ConstraintProto*
ct) {
72 int new_num_constraints = 0;
73 const int old_num_non_empty_constraints =
75 for (
int c = 0; c < old_num_non_empty_constraints; ++c) {
77 if (type == ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET)
continue;
78 if (type == ConstraintProto::ConstraintCase::kInterval) {
79 interval_mapping[c] = new_num_constraints;
85 new_num_constraints, old_num_non_empty_constraints - new_num_constraints);
89 [&interval_mapping](
int* ref) {
90 *ref = interval_mapping[*ref];
102 const int old_size =
ct->enforcement_literal().size();
103 for (
const int literal :
ct->enforcement_literal()) {
112 return RemoveConstraint(
ct);
119 return RemoveConstraint(
ct);
126 const int64_t obj_coeff =
130 context_->
UpdateRuleStats(
"enforcement literal with unique direction");
132 return RemoveConstraint(
ct);
136 ct->set_enforcement_literal(new_size++,
literal);
138 ct->mutable_enforcement_literal()->Truncate(new_size);
139 return new_size != old_size;
142 bool CpModelPresolver::PresolveBoolXor(ConstraintProto*
ct) {
147 bool changed =
false;
148 int num_true_literals = 0;
150 for (
const int literal :
ct->bool_xor().literals()) {
171 ct->mutable_bool_xor()->set_literals(new_size++,
literal);
175 }
else if (new_size == 2) {
178 if (num_true_literals % 2 == 1) {
180 ct->mutable_bool_xor()->set_literals(new_size++, true_literal);
182 if (num_true_literals > 1) {
183 context_->
UpdateRuleStats(
"bool_xor: remove even number of true literals");
186 ct->mutable_bool_xor()->mutable_literals()->Truncate(new_size);
190 bool CpModelPresolver::PresolveBoolOr(ConstraintProto*
ct) {
197 for (
const int literal :
ct->enforcement_literal()) {
200 ct->clear_enforcement_literal();
204 bool changed =
false;
207 for (
const int literal :
ct->bool_or().literals()) {
214 return RemoveConstraint(
ct);
222 return RemoveConstraint(
ct);
226 return RemoveConstraint(
ct);
245 return RemoveConstraint(
ct);
258 ct->mutable_bool_or()->mutable_literals()->Clear();
260 ct->mutable_bool_or()->add_literals(lit);
269 ABSL_MUST_USE_RESULT
bool CpModelPresolver::MarkConstraintAsFalse(
270 ConstraintProto*
ct) {
273 ct->mutable_bool_or()->clear_literals();
274 for (
const int lit :
ct->enforcement_literal()) {
277 ct->clear_enforcement_literal();
285 bool CpModelPresolver::PresolveBoolAnd(ConstraintProto*
ct) {
290 for (
const int literal :
ct->bool_and().literals()) {
293 return RemoveConstraint(
ct);
296 bool changed =
false;
298 for (
const int literal :
ct->bool_and().literals()) {
301 return MarkConstraintAsFalse(
ct);
321 ct->mutable_bool_and()->mutable_literals()->Clear();
323 ct->mutable_bool_and()->add_literals(lit);
332 if (
ct->enforcement_literal().size() == 1 &&
333 ct->bool_and().literals().size() == 1) {
334 const int enforcement =
ct->enforcement_literal(0);
344 ct->bool_and().literals(0));
352 bool CpModelPresolver::PresolveAtMostOrExactlyOne(ConstraintProto*
ct) {
354 const std::string
name = is_at_most_one ?
"at_most_one: " :
"exactly_one: ";
355 auto* literals = is_at_most_one
356 ?
ct->mutable_at_most_one()->mutable_literals()
357 :
ct->mutable_exactly_one()->mutable_literals();
361 for (
const int literal : *literals) {
367 int num_positive = 0;
368 int num_negative = 0;
369 for (
const int other : *literals) {
390 return RemoveConstraint(
ct);
396 bool changed =
false;
397 bool transform_to_at_most_one =
false;
399 for (
const int literal : *literals) {
402 for (
const int other : *literals) {
407 return RemoveConstraint(
ct);
420 if (is_at_most_one && !is_removable &&
424 const int64_t coeff = it->second;
431 if (is_at_most_one) {
438 is_at_most_one =
true;
439 transform_to_at_most_one =
true;
450 if (!is_at_most_one && !transform_to_at_most_one &&
455 if (transform_to_at_most_one) {
458 literals =
ct->mutable_at_most_one()->mutable_literals();
470 bool CpModelPresolver::PresolveAtMostOne(ConstraintProto*
ct) {
473 const bool changed = PresolveAtMostOrExactlyOne(
ct);
477 const auto& literals =
ct->at_most_one().literals();
478 if (literals.empty()) {
480 return RemoveConstraint(
ct);
484 if (literals.size() == 1) {
486 return RemoveConstraint(
ct);
492 bool CpModelPresolver::PresolveExactlyOne(ConstraintProto*
ct) {
495 const bool changed = PresolveAtMostOrExactlyOne(
ct);
499 const auto& literals =
ct->exactly_one().literals();
500 if (literals.empty()) {
505 if (literals.size() == 1) {
508 return RemoveConstraint(
ct);
512 if (literals.size() == 2) {
516 return RemoveConstraint(
ct);
522 bool CpModelPresolver::PresolveIntMax(ConstraintProto*
ct) {
524 if (
ct->int_max().vars().empty()) {
526 return MarkConstraintAsFalse(
ct);
528 const int target_ref =
ct->int_max().target();
533 bool contains_target_ref =
false;
534 bool contains_negated_target_ref =
false;
535 std::set<int> used_ref;
537 for (
const int ref :
ct->int_max().vars()) {
538 if (ref == target_ref) contains_target_ref =
true;
542 infered_min =
std::max(infered_min, int64_t{0});
548 contains_negated_target_ref =
true;
551 target_ref, {0, std::numeric_limits<int64_t>::max()})) {
555 used_ref.insert(ref);
556 ct->mutable_int_max()->set_vars(new_size++, ref);
560 if (new_size < ct->int_max().vars_size()) {
563 ct->mutable_int_max()->mutable_vars()->Truncate(new_size);
565 if (contains_target_ref) {
567 for (
const int ref :
ct->int_max().vars()) {
568 if (ref == target_ref)
continue;
571 auto* arg = new_ct->mutable_linear();
572 arg->add_vars(target_ref);
579 return RemoveConstraint(
ct);
583 Domain infered_domain;
584 for (
const int ref :
ct->int_max().vars()) {
585 infered_domain = infered_domain.UnionWith(
590 bool domain_reduced =
false;
604 !contains_negated_target_ref) {
605 const Domain& target_domain = context_->
DomainOf(target_ref);
608 target_domain.Max()))
609 .IsIncludedIn(target_domain)) {
610 if (infered_domain.Max() <= target_domain.Max()) {
613 }
else if (
ct->enforcement_literal().empty()) {
615 for (
const int ref :
ct->int_max().vars()) {
619 target_domain.Max()))) {
627 for (
const int ref :
ct->int_max().vars()) {
629 *(new_ct->mutable_enforcement_literal()) =
ct->enforcement_literal();
630 ct->mutable_linear()->add_vars(ref);
631 ct->mutable_linear()->add_coeffs(1);
633 ct->mutable_linear()->add_domain(target_domain.Max());
640 return RemoveConstraint(
ct);
646 const int size =
ct->int_max().vars_size();
647 const int64_t target_max = context_->
MaxOf(target_ref);
648 for (
const int ref :
ct->int_max().vars()) {
656 if (context_->
MaxOf(ref) >= infered_min) {
657 ct->mutable_int_max()->set_vars(new_size++, ref);
660 if (domain_reduced) {
664 bool modified =
false;
665 if (new_size < size) {
667 ct->mutable_int_max()->mutable_vars()->Truncate(new_size);
673 return MarkConstraintAsFalse(
ct);
681 auto* arg = new_ct->mutable_linear();
682 arg->add_vars(target_ref);
684 arg->add_vars(
ct->int_max().vars(0));
689 return RemoveConstraint(
ct);
695 const bool convert_result = ConvertIntMax(
ct);
696 return modified || convert_result;
703 bool CpModelPresolver::PresolveLinMin(ConstraintProto*
ct) {
705 const auto copy =
ct->lin_min();
707 ct->mutable_lin_max()->mutable_target());
708 for (
const LinearExpressionProto& expr : copy.exprs()) {
709 LinearExpressionProto*
const new_expr =
ct->mutable_lin_max()->add_exprs();
712 return PresolveLinMax(
ct);
720 bool CpModelPresolver::ConvertIntMax(ConstraintProto*
ct) {
722 const auto copy =
ct->int_max();
723 ct->mutable_lin_max()->mutable_target()->add_vars(copy.target());
724 ct->mutable_lin_max()->mutable_target()->add_coeffs(1);
725 for (
const int ref : copy.vars()) {
726 LinearExpressionProto* expr =
ct->mutable_lin_max()->add_exprs();
731 return PresolveLinMax(
ct);
735 bool CpModelPresolver::PresolveLinMax(ConstraintProto*
ct) {
742 bool changed = CanonicalizeLinearExpression(
743 *
ct,
ct->mutable_lin_max()->mutable_target());
744 for (LinearExpressionProto& exp : *(
ct->mutable_lin_max()->mutable_exprs())) {
745 changed |= CanonicalizeLinearExpression(*
ct, &exp);
750 const LinearExpressionProto& target =
ct->lin_max().target();
752 int64_t infered_min = context_->
MinOf(target);
754 for (
const LinearExpressionProto& expr :
ct->lin_max().exprs()) {
759 if (target.vars().empty()) {
760 if (!Domain(infered_min, infered_max).Contains(target.offset())) {
762 return MarkConstraintAsFalse(
ct);
767 for (
const LinearExpressionProto& expr :
ct->lin_max().exprs()) {
768 rhs_domain = rhs_domain.UnionWith(
770 {infered_min, infered_max}));
772 bool reduced =
false;
783 const int64_t target_min = context_->
MinOf(target);
784 const int64_t target_max = context_->
MaxOf(target);
787 for (
int i = 0; i <
ct->lin_max().exprs_size(); ++i) {
788 const LinearExpressionProto& expr =
ct->lin_max().exprs(i);
789 if (context_->
MaxOf(expr) < target_min)
continue;
790 *
ct->mutable_lin_max()->mutable_exprs(new_size) = expr;
793 if (new_size < ct->lin_max().exprs_size()) {
795 ct->mutable_lin_max()->mutable_exprs()->DeleteSubrange(
796 new_size,
ct->lin_max().exprs_size() - new_size);
801 if (
ct->lin_max().exprs().empty()) {
803 return MarkConstraintAsFalse(
ct);
806 if (
ct->lin_max().exprs().size() == 1) {
812 auto* arg = new_ct->mutable_linear();
813 const LinearExpressionProto&
a =
ct->lin_max().target();
814 const LinearExpressionProto&
b =
ct->lin_max().exprs(0);
815 for (
int i = 0; i <
a.vars().size(); ++i) {
816 arg->add_vars(
a.vars(i));
817 arg->add_coeffs(
a.coeffs(i));
819 for (
int i = 0; i <
b.vars().size(); ++i) {
820 arg->add_vars(
b.vars(i));
821 arg->add_coeffs(-
b.coeffs(i));
823 arg->add_domain(
b.offset() -
a.offset());
824 arg->add_domain(
b.offset() -
a.offset());
826 return RemoveConstraint(
ct);
834 for (
const LinearExpressionProto& expr :
ct->lin_max().exprs()) {
835 const int64_t value_min = context_->
MinOf(expr);
836 bool modified =
false;
844 const int64_t value_max = context_->
MaxOf(expr);
845 if (value_max > target_max) {
846 context_->
UpdateRuleStats(
"TODO lin_max: linear expression above max.");
850 if (abort)
return changed;
854 if (target_min == target_max) {
855 bool all_booleans =
true;
856 std::vector<int> literals;
857 const int64_t fixed_target = target_min;
858 for (
const LinearExpressionProto& expr :
ct->lin_max().exprs()) {
859 const int64_t value_min = context_->
MinOf(expr);
860 const int64_t value_max = context_->
MaxOf(expr);
861 CHECK_LE(value_max, fixed_target) <<
"Presolved above";
862 if (value_max < fixed_target)
continue;
864 if (value_min == value_max && value_max == fixed_target) {
866 return RemoveConstraint(
ct);
872 all_booleans =
false;
876 if (literals.empty()) {
877 return MarkConstraintAsFalse(
ct);
882 for (
const int lit : literals) {
883 ct->mutable_bool_or()->add_literals(lit);
896 bool min_is_reachable =
false;
897 std::vector<int> min_literals;
898 std::vector<int> literals_above_min;
899 std::vector<int> max_literals;
901 for (
const LinearExpressionProto& expr :
ct->lin_max().exprs()) {
902 const int64_t value_min = context_->
MinOf(expr);
903 const int64_t value_max = context_->
MaxOf(expr);
906 if (value_min > target_min) {
914 if (value_min == value_max) {
915 if (value_min == target_min) min_is_reachable =
true;
926 if (value_min == target_min) {
931 if (value_max == target_max) {
932 max_literals.push_back(ref);
933 literals_above_min.push_back(ref);
934 }
else if (value_max > target_min) {
935 literals_above_min.push_back(ref);
936 }
else if (value_max == target_min) {
937 min_literals.push_back(ref);
946 clause->mutable_bool_or();
947 for (
const int lit : max_literals) {
948 clause->mutable_bool_or()->add_literals(lit);
952 for (
const int lit : literals_above_min) {
956 if (!min_is_reachable) {
960 clause->mutable_bool_or();
961 for (
const int lit : min_literals) {
962 clause->mutable_bool_or()->add_literals(lit);
967 return RemoveConstraint(
ct);
974 bool CpModelPresolver::PresolveIntAbs(ConstraintProto*
ct) {
977 const int target_ref =
ct->int_max().target();
982 const Domain new_target_domain =
983 var_domain.
UnionWith(var_domain.Negation())
993 const Domain target_domain = context_->
DomainOf(target_ref);
994 const Domain new_var_domain =
995 target_domain.
UnionWith(target_domain.Negation());
1007 auto* arg = new_ct->mutable_linear();
1008 arg->add_vars(target_ref);
1011 arg->add_coeffs(-1);
1015 return RemoveConstraint(
ct);
1022 auto* arg = new_ct->mutable_linear();
1023 arg->add_vars(target_ref);
1030 return RemoveConstraint(
ct);
1036 context_->
IsFixed(target_ref)) {
1037 if (!context_->
IsFixed(target_ref)) {
1042 return RemoveConstraint(
ct);
1052 bool CpModelPresolver::PresolveIntMin(ConstraintProto*
ct) {
1055 const auto copy =
ct->int_min();
1056 ct->mutable_int_max()->set_target(
NegatedRef(copy.target()));
1057 for (
const int ref : copy.vars()) {
1060 return PresolveIntMax(
ct);
1063 bool CpModelPresolver::PresolveIntProd(ConstraintProto*
ct) {
1069 int64_t constant_factor = 1;
1071 bool changed =
false;
1072 for (
int i = 0; i <
ct->int_prod().vars().size(); ++i) {
1073 const int ref =
ct->int_prod().vars(i);
1077 constant_factor =
CapProd(constant_factor, context_->
MinOf(ref));
1081 if (r.representative != ref && r.offset == 0) {
1083 ct->mutable_int_prod()->set_vars(new_size++, r.representative);
1084 constant_factor =
CapProd(constant_factor, r.coeff);
1086 ct->mutable_int_prod()->set_vars(new_size++, ref);
1089 ct->mutable_int_prod()->mutable_vars()->Truncate(new_size);
1091 if (constant_factor == 0) {
1096 return RemoveConstraint(
ct);
1103 constant_factor = 1;
1110 if (
ct->int_prod().vars().empty()) {
1112 Domain(constant_factor))) {
1116 return RemoveConstraint(
ct);
1120 if (
ct->int_prod().vars().size() == 1) {
1123 lin->mutable_linear()->add_coeffs(1);
1124 lin->mutable_linear()->add_vars(
ct->int_prod().vars(0));
1125 lin->mutable_linear()->add_coeffs(-constant_factor);
1126 lin->mutable_linear()->add_domain(0);
1127 lin->mutable_linear()->add_domain(0);
1129 context_->
UpdateRuleStats(
"int_prod: linearize product by constant.");
1130 return RemoveConstraint(
ct);
1140 if (constant_factor != 1) {
1141 context_->
UpdateRuleStats(
"int_prod: extracted product by constant.");
1143 const int old_target =
ct->int_prod().target();
1153 ct->mutable_int_prod()->set_target(new_target);
1154 if (context_->
IsFixed(new_target)) {
1157 old_target, context_->
DomainOf(new_target)
1163 constant_factor, 0)) {
1171 lin->add_vars(new_target);
1172 lin->add_coeffs(-constant_factor);
1182 for (
const int ref :
ct->int_prod().vars()) {
1183 implied = implied.ContinuousMultiplicationBy(context_->
DomainOf(ref));
1185 bool modified =
false;
1194 if (
ct->int_prod().vars_size() == 2) {
1195 int a =
ct->int_prod().vars(0);
1196 int b =
ct->int_prod().vars(1);
1197 const int product =
ct->int_prod().target();
1198 if (
a ==
b &&
a == product) {
1203 return RemoveConstraint(
ct);
1208 const int target_ref =
ct->int_prod().target();
1210 for (
const int var :
ct->int_prod().vars()) {
1212 if (context_->
MinOf(
var) < 0)
return changed;
1213 if (context_->
MaxOf(
var) > 1)
return changed;
1224 auto* arg = new_ct->mutable_bool_and();
1225 for (
const int var :
ct->int_prod().vars()) {
1226 arg->add_literals(
var);
1233 for (
const int var :
ct->int_prod().vars()) {
1238 return RemoveConstraint(
ct);
1241 bool CpModelPresolver::PresolveIntDiv(ConstraintProto*
ct) {
1243 const int target =
ct->int_div().target();
1244 const int ref_x =
ct->int_div().vars(0);
1245 const int ref_div =
ct->int_div().vars(1);
1250 !context_->
IsFixed(ref_div)) {
1254 const int64_t divisor = context_->
MinOf(ref_div);
1256 LinearConstraintProto*
const lin =
1260 lin->add_vars(target);
1261 lin->add_coeffs(-1);
1266 return RemoveConstraint(
ct);
1268 bool domain_modified =
false;
1271 &domain_modified)) {
1272 if (domain_modified) {
1274 "int_div: updated domain of target in target = X / cte");
1285 if (context_->
MinOf(target) >= 0 && context_->
MinOf(ref_x) >= 0 &&
1287 LinearConstraintProto*
const lin =
1291 lin->add_vars(target);
1292 lin->add_coeffs(-divisor);
1294 lin->add_domain(divisor - 1);
1297 "int_div: linearize positive division with a constant divisor");
1298 return RemoveConstraint(
ct);
1306 bool CpModelPresolver::PresolveIntMod(ConstraintProto*
ct) {
1309 const int target =
ct->int_mod().target();
1310 const int ref_mod =
ct->int_mod().vars(1);
1311 const int ref_x =
ct->int_mod().vars(0);
1313 bool changed =
false;
1329 bool CpModelPresolver::ExploitEquivalenceRelations(
int c, ConstraintProto*
ct) {
1330 bool changed =
false;
1335 if (
ct->constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
1336 for (
int& ref : *
ct->mutable_enforcement_literal()) {
1348 bool work_to_do =
false;
1351 if (r.representative !=
var) {
1356 if (!work_to_do)
return false;
1360 [&changed,
this](
int* ref) {
1371 [&changed,
this](
int* ref) {
1382 void CpModelPresolver::DivideLinearByGcd(ConstraintProto*
ct) {
1387 const int num_vars =
ct->linear().vars().size();
1388 for (
int i = 0; i < num_vars; ++i) {
1389 const int64_t magnitude = std::abs(
ct->linear().coeffs(i));
1391 if (gcd == 1)
break;
1395 for (
int i = 0; i < num_vars; ++i) {
1396 ct->mutable_linear()->set_coeffs(i,
ct->linear().coeffs(i) / gcd);
1400 if (
ct->linear().domain_size() == 0) {
1401 return (
void)MarkConstraintAsFalse(
ct);
1406 void CpModelPresolver::PresolveLinearEqualityModuloTwo(ConstraintProto*
ct) {
1407 if (!
ct->enforcement_literal().empty())
return;
1408 if (
ct->linear().domain().size() != 2)
return;
1409 if (
ct->linear().domain(0) !=
ct->linear().domain(1))
return;
1414 std::vector<int> literals;
1415 for (
int i = 0; i <
ct->linear().vars().size(); ++i) {
1416 const int64_t coeff =
ct->linear().coeffs(i);
1417 const int ref =
ct->linear().vars(i);
1418 if (coeff % 2 == 0)
continue;
1421 if (literals.size() > 2)
return;
1423 if (literals.size() == 1) {
1424 const int64_t rhs = std::abs(
ct->linear().domain(0));
1425 context_->
UpdateRuleStats(
"linear: only one odd Boolean in equality");
1427 }
else if (literals.size() == 2) {
1428 const int64_t rhs = std::abs(
ct->linear().domain(0));
1429 context_->
UpdateRuleStats(
"linear: only two odd Booleans in equality");
1439 template <
typename ProtoWithVarsAndCoeffs>
1440 bool CpModelPresolver::CanonicalizeLinearExpressionInternal(
1441 const ConstraintProto&
ct, ProtoWithVarsAndCoeffs*
proto, int64_t* offset) {
1447 int64_t sum_of_fixed_terms = 0;
1448 bool remapped =
false;
1449 const int old_size =
proto->vars().size();
1451 for (
int i = 0; i < old_size; ++i) {
1452 const int ref =
proto->vars(i);
1454 const int64_t coeff =
1456 if (coeff == 0)
continue;
1459 sum_of_fixed_terms += coeff * context_->
MinOf(
var);
1465 bool removed =
false;
1466 for (
const int enf :
ct.enforcement_literal()) {
1470 sum_of_fixed_terms += coeff;
1479 context_->
UpdateRuleStats(
"linear: enforcement literal in expression");
1484 if (r.representative !=
var) {
1486 sum_of_fixed_terms += coeff * r.
offset;
1488 tmp_terms_.push_back({r.representative, coeff * r.coeff});
1490 proto->clear_vars();
1491 proto->clear_coeffs();
1492 std::sort(tmp_terms_.begin(), tmp_terms_.end());
1493 int current_var = 0;
1494 int64_t current_coeff = 0;
1495 for (
const auto entry : tmp_terms_) {
1497 if (entry.first == current_var) {
1498 current_coeff += entry.second;
1500 if (current_coeff != 0) {
1501 proto->add_vars(current_var);
1502 proto->add_coeffs(current_coeff);
1504 current_var = entry.first;
1505 current_coeff = entry.second;
1508 if (current_coeff != 0) {
1509 proto->add_vars(current_var);
1510 proto->add_coeffs(current_coeff);
1515 if (
proto->vars().size() < old_size) {
1518 *offset = sum_of_fixed_terms;
1519 return remapped ||
proto->vars().size() < old_size;
1522 bool CpModelPresolver::CanonicalizeLinearExpression(
1523 const ConstraintProto&
ct, LinearExpressionProto* exp) {
1525 const bool result = CanonicalizeLinearExpressionInternal(
ct, exp, &offset);
1526 exp->set_offset(exp->offset() + offset);
1530 bool CpModelPresolver::CanonicalizeLinear(ConstraintProto*
ct) {
1531 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
1536 if (
ct->linear().domain().empty()) {
1538 return MarkConstraintAsFalse(
ct);
1543 CanonicalizeLinearExpressionInternal(*
ct,
ct->mutable_linear(), &offset);
1547 ct->mutable_linear());
1549 DivideLinearByGcd(
ct);
1553 bool CpModelPresolver::RemoveSingletonInLinear(ConstraintProto*
ct) {
1554 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
1559 std::set<int> index_to_erase;
1560 const int num_vars =
ct->linear().vars().size();
1566 for (
int i = 0; i < num_vars; ++i) {
1567 const int var =
ct->linear().vars(i);
1568 const int64_t coeff =
ct->linear().coeffs(i);
1572 const auto term_domain =
1574 if (!exact)
continue;
1578 if (new_rhs.NumIntervals() > 100)
continue;
1585 index_to_erase.insert(i);
1592 if (index_to_erase.empty()) {
1595 if (!
ct->enforcement_literal().empty())
return false;
1599 if (rhs.Min() != rhs.Max())
return false;
1601 for (
int i = 0; i < num_vars; ++i) {
1602 const int var =
ct->linear().vars(i);
1603 const int64_t coeff =
ct->linear().coeffs(i);
1621 const int64_t objective_coeff =
1624 if (objective_coeff % coeff != 0)
continue;
1628 const auto term_domain =
1630 if (!exact)
continue;
1632 if (new_rhs.NumIntervals() > 100)
continue;
1640 objective_coeff))) {
1657 LOG(
WARNING) <<
"This was not supposed to happen and the presolve " 1658 "could be improved.";
1661 context_->
UpdateRuleStats(
"linear: singleton column define objective.");
1665 return RemoveConstraint(
ct);
1674 "linear: singleton column in equality and in objective.");
1676 index_to_erase.insert(i);
1680 if (index_to_erase.empty())
return false;
1689 for (
int i = 0; i < num_vars; ++i) {
1690 if (index_to_erase.count(i)) {
1694 ct->mutable_linear()->set_coeffs(new_size,
ct->linear().coeffs(i));
1695 ct->mutable_linear()->set_vars(new_size,
ct->linear().vars(i));
1698 ct->mutable_linear()->mutable_vars()->Truncate(new_size);
1699 ct->mutable_linear()->mutable_coeffs()->Truncate(new_size);
1701 DivideLinearByGcd(
ct);
1705 bool CpModelPresolver::PresolveSmallLinear(ConstraintProto*
ct) {
1706 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
1712 if (
ct->linear().vars().empty()) {
1715 if (rhs.Contains(0)) {
1716 return RemoveConstraint(
ct);
1718 return MarkConstraintAsFalse(
ct);
1725 if (
ct->linear().vars_size() == 1 &&
ct->enforcement_literal_size() > 0 &&
1726 ct->linear().coeffs(0) == 1 &&
1729 context_->
UpdateRuleStats(
"linear: remove abs from abs(x) in domain");
1730 const Domain implied_abs_target_domain =
1733 .IntersectionWith(context_->
DomainOf(
ct->linear().vars(0)));
1735 if (implied_abs_target_domain.IsEmpty()) {
1736 return MarkConstraintAsFalse(
ct);
1739 const Domain new_abs_var_domain =
1740 implied_abs_target_domain
1741 .UnionWith(implied_abs_target_domain.Negation())
1742 .IntersectionWith(context_->
DomainOf(abs_arg));
1744 if (new_abs_var_domain.IsEmpty()) {
1745 return MarkConstraintAsFalse(
ct);
1750 for (
const int literal :
ct->enforcement_literal()) {
1751 new_ct->add_enforcement_literal(
literal);
1753 auto* arg = new_ct->mutable_linear();
1754 arg->add_vars(abs_arg);
1758 return RemoveConstraint(
ct);
1763 if (
ct->enforcement_literal_size() != 1 ||
ct->linear().vars_size() != 1 ||
1764 (
ct->linear().coeffs(0) != 1 &&
ct->linear().coeffs(0) == -1)) {
1772 const int literal =
ct->enforcement_literal(0);
1773 const LinearConstraintProto& linear =
ct->linear();
1774 const int ref = linear.vars(0);
1776 const int64_t coeff =
1779 if (linear.domain_size() == 2 && linear.domain(0) == linear.domain(1)) {
1781 : -linear.domain(0) * coeff;
1790 if (complement.Size() != 1)
return false;
1792 : -complement.Min() * coeff;
1807 if (
ct->linear().vars().size() == 1) {
1809 ?
ct->linear().coeffs(0)
1810 : -
ct->linear().coeffs(0);
1815 rhs.InverseMultiplicationBy(coeff))) {
1818 return RemoveConstraint(
ct);
1825 const LinearConstraintProto& arg =
ct->linear();
1826 if (arg.vars_size() == 2) {
1828 const int64_t rhs_min = rhs.
Min();
1829 const int64_t rhs_max = rhs.Max();
1830 if (rhs_min == rhs_max) {
1831 const int v1 = arg.vars(0);
1832 const int v2 = arg.vars(1);
1833 const int64_t coeff1 = arg.coeffs(0);
1834 const int64_t coeff2 = arg.coeffs(1);
1838 }
else if (coeff2 == 1) {
1840 }
else if (coeff1 == -1) {
1842 }
else if (coeff2 == -1) {
1845 if (added)
return RemoveConstraint(
ct);
1855 bool IsLeConstraint(
const Domain& domain,
const Domain& all_values) {
1859 .IsIncludedIn(domain);
1863 bool IsGeConstraint(
const Domain& domain,
const Domain& all_values) {
1867 .IsIncludedIn(domain);
1873 bool RhsCanBeFixedToMin(int64_t coeff,
const Domain& var_domain,
1874 const Domain& terms,
const Domain& rhs) {
1875 if (var_domain.NumIntervals() != 1)
return false;
1876 if (std::abs(coeff) != 1)
return false;
1884 if (coeff == 1 && terms.Max() + var_domain.Min() <= rhs.Min()) {
1887 if (coeff == -1 && terms.Max() - var_domain.Max() <= rhs.Min()) {
1893 bool RhsCanBeFixedToMax(int64_t coeff,
const Domain& var_domain,
1894 const Domain& terms,
const Domain& rhs) {
1895 if (var_domain.NumIntervals() != 1)
return false;
1896 if (std::abs(coeff) != 1)
return false;
1898 if (coeff == 1 && terms.Min() + var_domain.Max() >= rhs.Max()) {
1901 if (coeff == -1 && terms.Min() - var_domain.Min() >= rhs.Max()) {
1908 void TakeIntersectionWith(
const absl::flat_hash_set<int>& current,
1909 absl::flat_hash_set<int>* to_clear) {
1910 std::vector<int> new_set;
1911 for (
const int c : *to_clear) {
1912 if (current.contains(c)) new_set.push_back(c);
1915 for (
const int c : new_set) to_clear->insert(c);
1920 bool CpModelPresolver::PropagateDomainsInLinear(
int ct_index,
1921 ConstraintProto*
ct) {
1922 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
1930 const int num_vars =
ct->linear().vars_size();
1931 term_domains.resize(num_vars + 1);
1932 left_domains.resize(num_vars + 1);
1933 left_domains[0] = Domain(0);
1934 for (
int i = 0; i < num_vars; ++i) {
1935 const int var =
ct->linear().vars(i);
1936 const int64_t coeff =
ct->linear().coeffs(i);
1939 left_domains[i + 1] =
1942 const Domain& implied_rhs = left_domains[num_vars];
1946 if (implied_rhs.IsIncludedIn(old_rhs)) {
1948 return RemoveConstraint(
ct);
1952 Domain rhs = old_rhs.SimplifyUsingImpliedDomain(implied_rhs);
1953 if (rhs.IsEmpty()) {
1955 return MarkConstraintAsFalse(
ct);
1957 if (rhs != old_rhs) {
1965 bool is_le_constraint = IsLeConstraint(rhs, implied_rhs);
1966 bool is_ge_constraint = IsGeConstraint(rhs, implied_rhs);
1969 if (
ct->enforcement_literal().size() > 1)
return false;
1971 bool new_bounds =
false;
1972 bool recanonicalize =
false;
1973 Domain negated_rhs = rhs.Negation();
1974 Domain right_domain(0);
1976 Domain implied_term_domain;
1977 term_domains[num_vars] = Domain(0);
1978 for (
int i = num_vars - 1; i >= 0; --i) {
1979 const int var =
ct->linear().vars(i);
1980 const int64_t var_coeff =
ct->linear().coeffs(i);
1982 right_domain.AdditionWith(term_domains[i + 1]).RelaxIfTooComplex();
1983 implied_term_domain = left_domains[i].AdditionWith(right_domain);
1984 new_domain = implied_term_domain.AdditionWith(negated_rhs)
1985 .InverseMultiplicationBy(-var_coeff);
1987 if (
ct->enforcement_literal().empty()) {
1992 }
else if (
ct->enforcement_literal().size() == 1) {
2003 recanonicalize =
true;
2007 if (is_le_constraint || is_ge_constraint) {
2008 CHECK_NE(is_le_constraint, is_ge_constraint);
2009 if ((var_coeff > 0) == is_ge_constraint) {
2024 const bool is_in_objective =
2028 const int64_t obj_coeff =
2037 if (obj_coeff <= 0 &&
2047 recanonicalize =
true;
2051 if (obj_coeff >= 0 &&
2061 recanonicalize =
true;
2069 if (!
ct->enforcement_literal().empty())
continue;
2081 if (rhs.Min() != rhs.Max() &&
2084 const bool same_sign = (var_coeff > 0) == (obj_coeff > 0);
2086 if (same_sign && RhsCanBeFixedToMin(var_coeff, context_->
DomainOf(
var),
2087 implied_term_domain, rhs)) {
2088 rhs = Domain(rhs.Min());
2091 if (!same_sign && RhsCanBeFixedToMax(var_coeff, context_->
DomainOf(
var),
2092 implied_term_domain, rhs)) {
2093 rhs = Domain(rhs.Max());
2099 negated_rhs = rhs.Negation();
2103 right_domain = Domain(0);
2107 is_le_constraint =
false;
2108 is_ge_constraint =
false;
2109 for (
const int var :
ct->linear().vars()) {
2126 if (
ct->linear().vars().size() <= 2)
continue;
2131 if (rhs.Min() != rhs.Max())
continue;
2137 if (context_->
DomainOf(
var) != new_domain)
continue;
2138 if (std::abs(var_coeff) != 1)
continue;
2145 bool is_in_objective =
false;
2147 is_in_objective =
true;
2153 if (is_in_objective) col_size--;
2154 const int row_size =
ct->linear().vars_size();
2158 const int num_entries_added = (row_size - 1) * (col_size - 1);
2159 const int num_entries_removed = col_size + row_size - 1;
2161 if (num_entries_added > num_entries_removed) {
2167 std::vector<int> others;
2175 if (c == ct_index)
continue;
2177 ConstraintProto::ConstraintCase::kLinear) {
2181 for (
const int ref :
2188 others.push_back(c);
2190 if (abort)
continue;
2193 for (
const int c : others) {
2207 if (is_in_objective &&
2213 absl::StrCat(
"linear: variable substitution ", others.size()));
2226 LinearConstraintProto* mapping_linear_ct =
2229 std::swap(mapping_linear_ct->mutable_vars()->at(0),
2230 mapping_linear_ct->mutable_vars()->at(i));
2231 std::swap(mapping_linear_ct->mutable_coeffs()->at(0),
2232 mapping_linear_ct->mutable_coeffs()->at(i));
2233 return RemoveConstraint(
ct);
2238 if (recanonicalize)
return CanonicalizeLinear(
ct);
2249 void CpModelPresolver::ExtractEnforcementLiteralFromLinearConstraint(
2250 int ct_index, ConstraintProto*
ct) {
2251 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
2256 const LinearConstraintProto& arg =
ct->linear();
2257 const int num_vars = arg.vars_size();
2261 if (num_vars <= 1)
return;
2263 int64_t min_sum = 0;
2264 int64_t max_sum = 0;
2265 int64_t max_coeff_magnitude = 0;
2266 for (
int i = 0; i < num_vars; ++i) {
2267 const int ref = arg.vars(i);
2268 const int64_t coeff = arg.coeffs(i);
2269 const int64_t term_a = coeff * context_->
MinOf(ref);
2270 const int64_t term_b = coeff * context_->
MaxOf(ref);
2271 max_coeff_magnitude =
std::max(max_coeff_magnitude, std::abs(coeff));
2272 min_sum +=
std::min(term_a, term_b);
2273 max_sum +=
std::max(term_a, term_b);
2282 const auto& domain =
ct->linear().domain();
2283 const int64_t ub_threshold = domain[domain.size() - 2] - min_sum;
2284 const int64_t lb_threshold = max_sum - domain[1];
2286 if (max_coeff_magnitude <
std::max(ub_threshold, lb_threshold))
return;
2305 const bool lower_bounded = min_sum < rhs_domain.
Min();
2306 const bool upper_bounded = max_sum > rhs_domain.Max();
2307 if (!lower_bounded && !upper_bounded)
return;
2308 if (lower_bounded && upper_bounded) {
2312 if (!
ct->name().empty()) {
2313 new_ct1->set_name(absl::StrCat(
ct->name(),
" (part 1)"));
2316 new_ct1->mutable_linear());
2320 if (!
ct->name().empty()) {
2321 new_ct2->set_name(absl::StrCat(
ct->name(),
" (part 2)"));
2324 new_ct2->mutable_linear());
2327 return (
void)RemoveConstraint(
ct);
2333 const int64_t threshold = lower_bounded ? ub_threshold : lb_threshold;
2340 const bool only_booleans =
2347 int64_t rhs_offset = 0;
2348 bool some_integer_encoding_were_extracted =
false;
2349 LinearConstraintProto* mutable_arg =
ct->mutable_linear();
2350 for (
int i = 0; i < arg.vars_size(); ++i) {
2351 int ref = arg.vars(i);
2352 int64_t coeff = arg.coeffs(i);
2359 if (context_->
IsFixed(ref) || coeff < threshold ||
2360 (only_booleans && !is_boolean)) {
2362 mutable_arg->set_vars(new_size, mutable_arg->vars(i));
2363 mutable_arg->set_coeffs(new_size, mutable_arg->coeffs(i));
2371 some_integer_encoding_were_extracted =
true;
2373 "linear: extracted integer enforcement literal");
2375 if (lower_bounded) {
2376 ct->add_enforcement_literal(is_boolean
2379 ref, context_->
MinOf(ref)));
2380 rhs_offset -= coeff * context_->
MinOf(ref);
2382 ct->add_enforcement_literal(is_boolean
2385 ref, context_->
MaxOf(ref)));
2386 rhs_offset -= coeff * context_->
MaxOf(ref);
2389 mutable_arg->mutable_vars()->Truncate(new_size);
2390 mutable_arg->mutable_coeffs()->Truncate(new_size);
2392 if (some_integer_encoding_were_extracted) {
2398 void CpModelPresolver::ExtractAtMostOneFromLinear(ConstraintProto*
ct) {
2403 const LinearConstraintProto& arg =
ct->linear();
2404 const int num_vars = arg.vars_size();
2405 int64_t min_sum = 0;
2406 int64_t max_sum = 0;
2407 for (
int i = 0; i < num_vars; ++i) {
2408 const int ref = arg.vars(i);
2409 const int64_t coeff = arg.coeffs(i);
2410 const int64_t term_a = coeff * context_->
MinOf(ref);
2411 const int64_t term_b = coeff * context_->
MaxOf(ref);
2412 min_sum +=
std::min(term_a, term_b);
2413 max_sum +=
std::max(term_a, term_b);
2415 for (
const int type : {0, 1}) {
2416 std::vector<int> at_most_one;
2417 for (
int i = 0; i < num_vars; ++i) {
2418 const int ref = arg.vars(i);
2419 const int64_t coeff = arg.coeffs(i);
2420 if (context_->
MinOf(ref) != 0)
continue;
2421 if (context_->
MaxOf(ref) != 1)
continue;
2426 if (min_sum + 2 * std::abs(coeff) > rhs.Max()) {
2427 at_most_one.push_back(coeff > 0 ? ref :
NegatedRef(ref));
2430 if (max_sum - 2 * std::abs(coeff) < rhs.Min()) {
2431 at_most_one.push_back(coeff > 0 ?
NegatedRef(ref) : ref);
2435 if (at_most_one.size() > 1) {
2443 for (
const int ref : at_most_one) {
2444 new_ct->mutable_at_most_one()->add_literals(ref);
2453 bool CpModelPresolver::PresolveLinearOnBooleans(ConstraintProto*
ct) {
2454 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
2459 const LinearConstraintProto& arg =
ct->linear();
2460 const int num_vars = arg.vars_size();
2462 int64_t max_coeff = 0;
2463 int64_t min_sum = 0;
2464 int64_t max_sum = 0;
2465 for (
int i = 0; i < num_vars; ++i) {
2467 const int var = arg.vars(i);
2468 const int64_t coeff = arg.coeffs(i);
2471 if (context_->
MinOf(
var) != 0)
return false;
2472 if (context_->
MaxOf(
var) != 1)
return false;
2476 min_coeff =
std::min(min_coeff, coeff);
2477 max_coeff =
std::max(max_coeff, coeff);
2481 min_coeff =
std::min(min_coeff, -coeff);
2482 max_coeff =
std::max(max_coeff, -coeff);
2494 if ((!rhs_domain.Contains(min_sum) &&
2495 min_sum + min_coeff > rhs_domain.Max()) ||
2496 (!rhs_domain.Contains(max_sum) &&
2497 max_sum - min_coeff < rhs_domain.Min())) {
2498 context_->
UpdateRuleStats(
"linear: all booleans and trivially false");
2499 return MarkConstraintAsFalse(
ct);
2501 if (Domain(min_sum, max_sum).IsIncludedIn(rhs_domain)) {
2503 return RemoveConstraint(
ct);
2510 DCHECK(!rhs_domain.IsEmpty());
2511 if (min_sum + min_coeff > rhs_domain.Max()) {
2514 const auto copy = arg;
2515 ct->mutable_bool_and()->clear_literals();
2516 for (
int i = 0; i < num_vars; ++i) {
2517 ct->mutable_bool_and()->add_literals(
2518 copy.coeffs(i) > 0 ?
NegatedRef(copy.vars(i)) : copy.vars(i));
2520 PresolveBoolAnd(
ct);
2522 }
else if (max_sum - min_coeff < rhs_domain.Min()) {
2525 const auto copy = arg;
2526 ct->mutable_bool_and()->clear_literals();
2527 for (
int i = 0; i < num_vars; ++i) {
2528 ct->mutable_bool_and()->add_literals(
2529 copy.coeffs(i) > 0 ? copy.vars(i) :
NegatedRef(copy.vars(i)));
2531 PresolveBoolAnd(
ct);
2533 }
else if (min_sum + min_coeff >= rhs_domain.Min() &&
2534 rhs_domain.front().end >= max_sum) {
2537 const auto copy = arg;
2538 ct->mutable_bool_or()->clear_literals();
2539 for (
int i = 0; i < num_vars; ++i) {
2540 ct->mutable_bool_or()->add_literals(
2541 copy.coeffs(i) > 0 ? copy.vars(i) :
NegatedRef(copy.vars(i)));
2545 }
else if (max_sum - min_coeff <= rhs_domain.Max() &&
2546 rhs_domain.back().start <= min_sum) {
2549 const auto copy = arg;
2550 ct->mutable_bool_or()->clear_literals();
2551 for (
int i = 0; i < num_vars; ++i) {
2552 ct->mutable_bool_or()->add_literals(
2553 copy.coeffs(i) > 0 ?
NegatedRef(copy.vars(i)) : copy.vars(i));
2558 min_sum + max_coeff <= rhs_domain.Max() &&
2559 min_sum + 2 * min_coeff > rhs_domain.Max() &&
2560 rhs_domain.back().start <= min_sum) {
2563 const auto copy = arg;
2564 ct->mutable_at_most_one()->clear_literals();
2565 for (
int i = 0; i < num_vars; ++i) {
2566 ct->mutable_at_most_one()->add_literals(
2567 copy.coeffs(i) > 0 ? copy.vars(i) :
NegatedRef(copy.vars(i)));
2571 max_sum - max_coeff >= rhs_domain.Min() &&
2572 max_sum - 2 * min_coeff < rhs_domain.Min() &&
2573 rhs_domain.front().end >= max_sum) {
2576 const auto copy = arg;
2577 ct->mutable_at_most_one()->clear_literals();
2578 for (
int i = 0; i < num_vars; ++i) {
2579 ct->mutable_at_most_one()->add_literals(
2580 copy.coeffs(i) > 0 ?
NegatedRef(copy.vars(i)) : copy.vars(i));
2584 min_sum < rhs_domain.Min() &&
2585 min_sum + min_coeff >= rhs_domain.Min() &&
2586 min_sum + 2 * min_coeff > rhs_domain.Max() &&
2587 min_sum + max_coeff <= rhs_domain.Max()) {
2591 for (
int i = 0; i < num_vars; ++i) {
2592 exactly_one->mutable_exactly_one()->add_literals(
2593 arg.coeffs(i) > 0 ? arg.vars(i) :
NegatedRef(arg.vars(i)));
2596 return RemoveConstraint(
ct);
2598 max_sum > rhs_domain.Max() &&
2599 max_sum - min_coeff <= rhs_domain.Max() &&
2600 max_sum - 2 * min_coeff < rhs_domain.Min() &&
2601 max_sum - max_coeff >= rhs_domain.Min()) {
2605 for (
int i = 0; i < num_vars; ++i) {
2606 exactly_one->mutable_exactly_one()->add_literals(
2607 arg.coeffs(i) > 0 ?
NegatedRef(arg.vars(i)) : arg.vars(i));
2610 return RemoveConstraint(
ct);
2617 if (num_vars > 3)
return false;
2622 const int max_mask = (1 << arg.vars_size());
2623 for (
int mask = 0; mask < max_mask; ++mask) {
2625 for (
int i = 0; i < num_vars; ++i) {
2626 if ((mask >> i) & 1)
value += arg.coeffs(i);
2628 if (rhs_domain.Contains(
value))
continue;
2634 *new_ct->mutable_enforcement_literal() =
ct->enforcement_literal();
2636 for (
int i = 0; i < num_vars; ++i) {
2637 new_arg->add_literals(((mask >> i) & 1) ?
NegatedRef(arg.vars(i))
2643 return RemoveConstraint(
ct);
2648 void AddLinearConstraintFromInterval(
const ConstraintProto&
ct,
2650 const int start =
ct.interval().start();
2651 const int end =
ct.interval().end();
2652 const int size =
ct.interval().size();
2653 ConstraintProto* new_ct =
context->working_model->add_constraints();
2654 *(new_ct->mutable_enforcement_literal()) =
ct.enforcement_literal();
2655 new_ct->mutable_linear()->add_domain(0);
2656 new_ct->mutable_linear()->add_domain(0);
2657 new_ct->mutable_linear()->add_vars(start);
2658 new_ct->mutable_linear()->add_coeffs(1);
2659 new_ct->mutable_linear()->add_vars(size);
2660 new_ct->mutable_linear()->add_coeffs(1);
2661 new_ct->mutable_linear()->add_vars(end);
2662 new_ct->mutable_linear()->add_coeffs(-1);
2663 context->UpdateNewConstraintsVariableUsage();
2668 bool CpModelPresolver::PresolveInterval(
int c, ConstraintProto*
ct) {
2671 if (
ct->enforcement_literal().empty() && !
ct->interval().has_start_view()) {
2672 bool changed =
false;
2673 const int start =
ct->interval().start();
2674 const int end =
ct->interval().end();
2675 const int size =
ct->interval().size();
2676 const Domain start_domain = context_->
DomainOf(start);
2677 const Domain end_domain = context_->
DomainOf(end);
2678 const Domain size_domain = context_->
DomainOf(size);
2685 end, start_domain.AdditionWith(size_domain), &changed)) {
2689 start, end_domain.AdditionWith(size_domain.Negation()), &changed)) {
2693 size, end_domain.AdditionWith(start_domain.Negation()), &changed)) {
2702 if (!
ct->interval().has_start_view()) {
2703 AddLinearConstraintFromInterval(*
ct, context_);
2706 return RemoveConstraint(
ct);
2715 bool changed =
false;
2716 IntervalConstraintProto*
interval =
ct->mutable_interval();
2717 if (!
ct->interval().has_start_view()) {
2722 AddLinearConstraintFromInterval(*
ct, context_);
2726 interval->mutable_start_view()->add_coeffs(1);
2727 interval->mutable_start_view()->set_offset(0);
2729 interval->mutable_size_view()->add_coeffs(1);
2730 interval->mutable_size_view()->set_offset(0);
2732 interval->mutable_end_view()->add_coeffs(1);
2733 interval->mutable_end_view()->set_offset(0);
2742 CanonicalizeLinearExpression(*
ct,
interval->mutable_start_view());
2743 changed |= CanonicalizeLinearExpression(*
ct,
interval->mutable_size_view());
2744 changed |= CanonicalizeLinearExpression(*
ct,
interval->mutable_end_view());
2753 bool CpModelPresolver::PresolveInverse(ConstraintProto*
ct) {
2754 const int size =
ct->inverse().f_direct().size();
2755 bool changed =
false;
2758 for (
const int ref :
ct->inverse().f_direct()) {
2760 VLOG(1) <<
"Empty domain for a variable in ExpandInverse()";
2764 for (
const int ref :
ct->inverse().f_inverse()) {
2766 VLOG(1) <<
"Empty domain for a variable in ExpandInverse()";
2774 const auto filter_inverse_domain =
2775 [
this, size, &changed](
const auto& direct,
const auto& inverse) {
2777 std::vector<absl::flat_hash_set<int64_t>> inverse_values(size);
2778 for (
int i = 0; i < size; ++i) {
2779 const Domain domain = context_->
DomainOf(inverse[i]);
2780 for (
const int64_t j : domain.Values()) {
2781 inverse_values[i].insert(j);
2788 std::vector<int64_t> possible_values;
2789 for (
int i = 0; i < size; ++i) {
2790 possible_values.clear();
2791 const Domain domain = context_->
DomainOf(direct[i]);
2792 bool removed_value =
false;
2793 for (
const int64_t j : domain.Values()) {
2794 if (inverse_values[j].contains(i)) {
2795 possible_values.push_back(j);
2797 removed_value =
true;
2800 if (removed_value) {
2804 VLOG(1) <<
"Empty domain for a variable in ExpandInverse()";
2812 if (!filter_inverse_domain(
ct->inverse().f_direct(),
2813 ct->inverse().f_inverse())) {
2817 if (!filter_inverse_domain(
ct->inverse().f_inverse(),
2818 ct->inverse().f_direct())) {
2829 bool CpModelPresolver::PresolveElement(ConstraintProto*
ct) {
2832 if (
ct->element().vars().empty()) {
2837 const int index_ref =
ct->element().index();
2838 const int target_ref =
ct->element().target();
2843 bool all_constants =
true;
2844 absl::flat_hash_set<int64_t> constant_set;
2845 bool all_included_in_target_domain =
true;
2848 bool reduced_index_domain =
false;
2850 Domain(0,
ct->element().vars_size() - 1),
2851 &reduced_index_domain)) {
2859 std::vector<int64_t> possible_indices;
2860 const Domain& index_domain = context_->
DomainOf(index_ref);
2861 for (
const int64_t index_value : index_domain.Values()) {
2862 const int ref =
ct->element().vars(index_value);
2863 const int64_t target_value =
2864 target_ref == index_ref ? index_value : -index_value;
2866 possible_indices.push_back(target_value);
2869 if (possible_indices.size() < index_domain.Size()) {
2875 "element: reduced index domain when target equals index");
2881 Domain infered_domain;
2882 const Domain& initial_index_domain = context_->
DomainOf(index_ref);
2883 const Domain& target_domain = context_->
DomainOf(target_ref);
2884 std::vector<int64_t> possible_indices;
2885 for (
const int64_t
value : initial_index_domain.Values()) {
2888 const int ref =
ct->element().vars(
value);
2889 const Domain& domain = context_->
DomainOf(ref);
2890 if (domain.IntersectionWith(target_domain).IsEmpty())
continue;
2891 possible_indices.push_back(
value);
2892 if (domain.IsFixed()) {
2893 constant_set.insert(domain.Min());
2895 all_constants =
false;
2897 if (!domain.IsIncludedIn(target_domain)) {
2898 all_included_in_target_domain =
false;
2900 infered_domain = infered_domain.UnionWith(domain);
2902 if (possible_indices.size() < initial_index_domain.Size()) {
2909 bool domain_modified =
false;
2911 &domain_modified)) {
2914 if (domain_modified) {
2920 if (context_->
IsFixed(index_ref)) {
2921 const int var =
ct->element().vars(context_->
MinOf(index_ref));
2922 if (
var != target_ref) {
2923 LinearConstraintProto*
const lin =
2926 lin->add_coeffs(-1);
2927 lin->add_vars(target_ref);
2934 return RemoveConstraint(
ct);
2940 if (all_constants && constant_set.size() == 1) {
2943 return RemoveConstraint(
ct);
2948 if (context_->
MinOf(index_ref) == 0 && context_->
MaxOf(index_ref) == 1 &&
2950 const int64_t v0 = context_->
MinOf(
ct->element().vars(0));
2951 const int64_t v1 = context_->
MinOf(
ct->element().vars(1));
2953 LinearConstraintProto*
const lin =
2957 lin->add_vars(index_ref);
2958 lin->add_coeffs(v0 - v1);
2959 lin->add_domain(v0);
2960 lin->add_domain(v0);
2962 context_->
UpdateRuleStats(
"element: linearize constant element of size 2");
2963 return RemoveConstraint(
ct);
2967 const AffineRelation::Relation r_index =
2969 if (r_index.representative != index_ref) {
2971 if (context_->
DomainOf(r_index.representative).
Size() >
2977 const int64_t r_min = context_->
MinOf(r_ref);
2978 const int64_t r_max = context_->
MaxOf(r_ref);
2979 const int array_size =
ct->element().vars_size();
2981 context_->
UpdateRuleStats(
"TODO element: representative has bad domain");
2982 }
else if (r_index.offset >= 0 && r_index.offset < array_size &&
2983 r_index.offset + r_max * r_index.coeff >= 0 &&
2984 r_index.offset + r_max * r_index.coeff < array_size) {
2986 ElementConstraintProto*
const element =
2988 for (int64_t v = 0; v <= r_max; ++v) {
2989 const int64_t scaled_index = v * r_index.coeff + r_index.offset;
2991 CHECK_LT(scaled_index, array_size);
2992 element->add_vars(
ct->element().vars(scaled_index));
2994 element->set_index(r_ref);
2995 element->set_target(target_ref);
2997 if (r_index.coeff == 1) {
3003 return RemoveConstraint(
ct);
3014 absl::flat_hash_map<int, int> local_var_occurrence_counter;
3015 local_var_occurrence_counter[
PositiveRef(index_ref)]++;
3016 local_var_occurrence_counter[
PositiveRef(target_ref)]++;
3022 const int ref =
ct->element().vars(
value);
3028 local_var_occurrence_counter.at(
PositiveRef(index_ref)) == 1) {
3029 if (all_constants) {
3033 context_->
UpdateRuleStats(
"element: trivial target domain reduction");
3036 return RemoveConstraint(
ct);
3042 if (!context_->
IsFixed(target_ref) &&
3044 local_var_occurrence_counter.at(
PositiveRef(target_ref)) == 1) {
3045 if (all_included_in_target_domain) {
3049 return RemoveConstraint(
ct);
3058 bool CpModelPresolver::PresolveTable(ConstraintProto*
ct) {
3061 if (
ct->table().vars().empty()) {
3063 return RemoveConstraint(
ct);
3066 const int initial_num_vars =
ct->table().vars_size();
3067 bool changed =
true;
3070 std::vector<AffineRelation::Relation> affine_relations;
3071 std::vector<int64_t> old_var_lb;
3072 std::vector<int64_t> old_var_ub;
3074 for (
int v = 0; v < initial_num_vars; ++v) {
3075 const int ref =
ct->table().vars(v);
3077 affine_relations.push_back(r);
3078 old_var_lb.push_back(context_->
MinOf(ref));
3079 old_var_ub.push_back(context_->
MaxOf(ref));
3080 if (r.representative != ref) {
3082 ct->mutable_table()->set_vars(v, r.representative);
3084 "table: replace variable by canonical affine one");
3093 std::vector<int> old_index_of_duplicate_to_new_index_of_first_occurrence(
3094 initial_num_vars, -1);
3096 std::vector<int> old_index_to_new_index(initial_num_vars, -1);
3099 absl::flat_hash_map<int, int> first_visit;
3100 for (
int p = 0; p < initial_num_vars; ++p) {
3101 const int ref =
ct->table().vars(p);
3103 const auto& it = first_visit.find(
var);
3104 if (it != first_visit.end()) {
3105 const int previous = it->second;
3106 old_index_of_duplicate_to_new_index_of_first_occurrence[p] = previous;
3110 ct->mutable_table()->set_vars(num_vars, ref);
3111 first_visit[
var] = num_vars;
3112 old_index_to_new_index[p] = num_vars;
3117 if (num_vars < initial_num_vars) {
3118 ct->mutable_table()->mutable_vars()->Truncate(num_vars);
3125 std::vector<std::vector<int64_t>> new_tuples;
3126 const int initial_num_tuples =
ct->table().values_size() / initial_num_vars;
3127 std::vector<absl::flat_hash_set<int64_t>> new_domains(num_vars);
3130 std::vector<int64_t> tuple(num_vars);
3131 new_tuples.reserve(initial_num_tuples);
3132 for (
int i = 0; i < initial_num_tuples; ++i) {
3133 bool delete_row =
false;
3135 for (
int j = 0; j < initial_num_vars; ++j) {
3136 const int64_t old_value =
ct->table().values(i * initial_num_vars + j);
3140 if (old_value < old_var_lb[j] || old_value > old_var_ub[j]) {
3146 const AffineRelation::Relation& r = affine_relations[j];
3147 const int64_t
value = (old_value - r.offset) / r.coeff;
3148 if (
value * r.coeff + r.offset != old_value) {
3153 const int mapped_position = old_index_to_new_index[j];
3154 if (mapped_position == -1) {
3155 const int new_index_of_first_occurrence =
3156 old_index_of_duplicate_to_new_index_of_first_occurrence[j];
3157 if (
value != tuple[new_index_of_first_occurrence]) {
3162 const int ref =
ct->table().vars(mapped_position);
3167 tuple[mapped_position] =
value;
3174 new_tuples.push_back(tuple);
3175 for (
int j = 0; j < num_vars; ++j) {
3176 new_domains[j].insert(tuple[j]);
3180 if (new_tuples.size() < initial_num_tuples) {
3187 ct->mutable_table()->clear_values();
3188 for (
const std::vector<int64_t>& t : new_tuples) {
3189 for (
const int64_t v : t) {
3190 ct->mutable_table()->add_values(v);
3196 if (
ct->table().negated())
return changed;
3199 for (
int j = 0; j < num_vars; ++j) {
3200 const int ref =
ct->table().vars(j);
3204 new_domains[j].end())),
3212 if (num_vars == 1) {
3215 return RemoveConstraint(
ct);
3220 for (
int j = 0; j < num_vars; ++j) prod *= new_domains[j].size();
3221 if (prod == new_tuples.size()) {
3223 return RemoveConstraint(
ct);
3229 if (new_tuples.size() > 0.7 * prod) {
3231 std::vector<std::vector<int64_t>> var_to_values(num_vars);
3232 for (
int j = 0; j < num_vars; ++j) {
3233 var_to_values[j].assign(new_domains[j].begin(), new_domains[j].end());
3235 std::vector<std::vector<int64_t>> all_tuples(prod);
3236 for (
int i = 0; i < prod; ++i) {
3237 all_tuples[i].resize(num_vars);
3239 for (
int j = 0; j < num_vars; ++j) {
3240 all_tuples[i][j] = var_to_values[j][
index % var_to_values[j].size()];
3241 index /= var_to_values[j].size();
3247 std::vector<std::vector<int64_t>> diff(prod - new_tuples.size());
3248 std::set_difference(all_tuples.begin(), all_tuples.end(),
3249 new_tuples.begin(), new_tuples.end(), diff.begin());
3252 ct->mutable_table()->set_negated(!
ct->table().negated());
3253 ct->mutable_table()->clear_values();
3254 for (
const std::vector<int64_t>& t : diff) {
3255 for (
const int64_t v : t)
ct->mutable_table()->add_values(v);
3262 bool CpModelPresolver::PresolveAllDiff(ConstraintProto*
ct) {
3266 AllDifferentConstraintProto& all_diff = *
ct->mutable_all_diff();
3268 bool constraint_has_changed =
false;
3270 const int size = all_diff.vars_size();
3273 return RemoveConstraint(
ct);
3277 return RemoveConstraint(
ct);
3280 bool something_was_propagated =
false;
3281 std::vector<int> new_variables;
3282 for (
int i = 0; i < size; ++i) {
3283 if (!context_->
IsFixed(all_diff.vars(i))) {
3284 new_variables.push_back(all_diff.vars(i));
3288 const int64_t
value = context_->
MinOf(all_diff.vars(i));
3289 bool propagated =
false;
3290 for (
int j = 0; j < size; ++j) {
3291 if (i == j)
continue;
3294 Domain(
value).Complement())) {
3302 something_was_propagated =
true;
3306 std::sort(new_variables.begin(), new_variables.end());
3307 for (
int i = 1; i < new_variables.size(); ++i) {
3308 if (new_variables[i] == new_variables[i - 1]) {
3310 "Duplicate variable in all_diff");
3314 if (new_variables.size() < all_diff.vars_size()) {
3315 all_diff.mutable_vars()->Clear();
3316 for (
const int var : new_variables) {
3317 all_diff.add_vars(
var);
3320 something_was_propagated =
true;
3321 constraint_has_changed =
true;
3322 if (new_variables.size() <= 1)
continue;
3327 Domain domain = context_->
DomainOf(all_diff.vars(0));
3328 for (
int i = 1; i < all_diff.vars_size(); ++i) {
3331 if (all_diff.vars_size() == domain.Size()) {
3332 absl::flat_hash_map<int64_t, std::vector<int>> value_to_refs;
3333 for (
const int ref : all_diff.vars()) {
3335 value_to_refs[v].push_back(ref);
3338 bool propagated =
false;
3339 for (
const auto& it : value_to_refs) {
3340 if (it.second.size() == 1 &&
3341 context_->
DomainOf(it.second.front()).Size() > 1) {
3342 const int ref = it.second.
front();
3351 "all_diff: propagated mandatory values in permutation");
3352 something_was_propagated =
true;
3355 if (!something_was_propagated)
break;
3358 return constraint_has_changed;
3365 std::vector<int> GetLiteralsFromSetPPCConstraint(
const ConstraintProto&
ct) {
3366 std::vector<int> sorted_literals;
3368 for (
const int literal :
ct.at_most_one().literals()) {
3369 sorted_literals.push_back(
literal);
3372 for (
const int literal :
ct.bool_or().literals()) {
3373 sorted_literals.push_back(
literal);
3376 for (
const int literal :
ct.exactly_one().literals()) {
3377 sorted_literals.push_back(
literal);
3380 std::sort(sorted_literals.begin(), sorted_literals.end());
3381 return sorted_literals;
3386 void AddImplication(
int lhs,
int rhs, CpModelProto*
proto,
3387 absl::flat_hash_map<int, int>* ref_to_bool_and) {
3388 if (ref_to_bool_and->contains(lhs)) {
3389 const int ct_index = (*ref_to_bool_and)[lhs];
3391 }
else if (ref_to_bool_and->contains(
NegatedRef(rhs))) {
3392 const int ct_index = (*ref_to_bool_and)[
NegatedRef(rhs)];
3398 ct->add_enforcement_literal(lhs);
3399 ct->mutable_bool_and()->add_literals(rhs);
3403 template <
typename ClauseContainer>
3404 void ExtractClauses(
bool use_bool_and,
const ClauseContainer& container,
3405 CpModelProto*
proto) {
3412 absl::flat_hash_map<int, int> ref_to_bool_and;
3413 for (
int i = 0; i < container.NumClauses(); ++i) {
3414 const std::vector<Literal>& clause = container.Clause(i);
3415 if (clause.empty())
continue;
3418 if (use_bool_and && clause.size() == 2) {
3419 const int a = clause[0].IsPositive()
3420 ? clause[0].Variable().value()
3422 const int b = clause[1].IsPositive()
3423 ? clause[1].Variable().value()
3431 for (
const Literal l : clause) {
3432 if (l.IsPositive()) {
3433 ct->mutable_bool_or()->add_literals(l.Variable().value());
3435 ct->mutable_bool_or()->add_literals(
NegatedRef(l.Variable().value()));
3443 bool CpModelPresolver::PresolveNoOverlap(ConstraintProto*
ct) {
3445 NoOverlapConstraintProto*
proto =
ct->mutable_no_overlap();
3446 bool changed =
false;
3450 const int initial_num_intervals =
proto->intervals_size();
3453 for (
int i = 0; i < initial_num_intervals; ++i) {
3454 const int interval_index =
proto->intervals(i);
3459 proto->set_intervals(new_size++, interval_index);
3462 if (new_size < initial_num_intervals) {
3463 proto->mutable_intervals()->Truncate(new_size);
3470 if (
proto->intervals_size() > 1) {
3471 std::vector<IndexedInterval> indexed_intervals;
3472 for (
int i = 0; i <
proto->intervals().size(); ++i) {
3474 indexed_intervals.push_back({
index,
3478 std::vector<std::vector<int>> components;
3481 if (components.size() > 1) {
3482 for (
const std::vector<int>& intervals : components) {
3483 if (intervals.size() <= 1)
continue;
3485 NoOverlapConstraintProto* new_no_overlap =
3489 for (
const int i : intervals) {
3494 context_->
UpdateRuleStats(
"no_overlap: split into disjoint components");
3495 return RemoveConstraint(
ct);
3499 std::vector<int> constant_intervals;
3500 int64_t size_min_of_non_constant_intervals =
3502 for (
int i = 0; i <
proto->intervals_size(); ++i) {
3503 const int interval_index =
proto->intervals(i);
3505 constant_intervals.push_back(interval_index);
3507 size_min_of_non_constant_intervals =
3508 std::min(size_min_of_non_constant_intervals,
3509 context_->
SizeMin(interval_index));
3513 if (!constant_intervals.empty()) {
3515 std::sort(constant_intervals.begin(), constant_intervals.end(),
3516 [
this](
int i1,
int i2) {
3523 for (
int i = 0; i + 1 < constant_intervals.size(); ++i) {
3524 if (context_->
EndMax(constant_intervals[i]) >
3525 context_->
StartMin(constant_intervals[i + 1])) {
3531 if (constant_intervals.size() ==
proto->intervals_size()) {
3533 return RemoveConstraint(
ct);
3536 absl::flat_hash_set<int> intervals_to_remove;
3540 for (
int i = 0; i + 1 < constant_intervals.size(); ++i) {
3541 const int start = i;
3542 while (i + 1 < constant_intervals.size() &&
3543 context_->
StartMin(constant_intervals[i + 1]) -
3544 context_->
EndMax(constant_intervals[i]) <
3545 size_min_of_non_constant_intervals) {
3548 if (i == start)
continue;
3549 for (
int j = start; j <= i; ++j) {
3550 intervals_to_remove.insert(constant_intervals[j]);
3552 const int64_t new_start = context_->
StartMin(constant_intervals[start]);
3553 const int64_t new_end = context_->
EndMax(constant_intervals[i]);
3555 IntervalConstraintProto* new_interval =
3558 new_interval->mutable_size_view()->set_offset(new_end - new_start);
3559 new_interval->mutable_end_view()->set_offset(new_end);
3563 if (!intervals_to_remove.empty()) {
3565 const int old_size =
proto->intervals_size();
3566 for (
int i = 0; i < old_size; ++i) {
3567 const int interval_index =
proto->intervals(i);
3568 if (intervals_to_remove.contains(interval_index)) {
3571 proto->set_intervals(new_size++, interval_index);
3574 proto->mutable_intervals()->Truncate(new_size);
3576 "no_overlap: merge constant contiguous intervals");
3577 intervals_to_remove.clear();
3578 constant_intervals.clear();
3584 if (
proto->intervals_size() == 1) {
3586 return RemoveConstraint(
ct);
3588 if (
proto->intervals().empty()) {
3590 return RemoveConstraint(
ct);
3596 bool CpModelPresolver::PresolveNoOverlap2D(
int c, ConstraintProto*
ct) {
3601 const NoOverlap2DConstraintProto&
proto =
ct->no_overlap_2d();
3602 const int initial_num_boxes =
proto.x_intervals_size();
3604 bool has_zero_sizes =
false;
3605 bool x_constant =
true;
3606 bool y_constant =
true;
3610 std::vector<Rectangle> bounding_boxes;
3611 std::vector<int> active_boxes;
3612 for (
int i = 0; i <
proto.x_intervals_size(); ++i) {
3613 const int x_interval_index =
proto.x_intervals(i);
3614 const int y_interval_index =
proto.y_intervals(i);
3621 if (
proto.boxes_with_null_area_can_overlap() &&
3622 (context_->
SizeMax(x_interval_index) == 0 ||
3623 context_->
SizeMax(y_interval_index) == 0)) {
3624 if (
proto.boxes_with_null_area_can_overlap())
continue;
3625 has_zero_sizes =
true;
3627 ct->mutable_no_overlap_2d()->set_x_intervals(new_size, x_interval_index);
3628 ct->mutable_no_overlap_2d()->set_y_intervals(new_size, y_interval_index);
3629 bounding_boxes.push_back(
3630 {IntegerValue(context_->
StartMin(x_interval_index)),
3631 IntegerValue(context_->
EndMax(x_interval_index)),
3632 IntegerValue(context_->
StartMin(y_interval_index)),
3633 IntegerValue(context_->
EndMax(y_interval_index))});
3634 active_boxes.push_back(new_size);
3646 bounding_boxes, absl::MakeSpan(active_boxes));
3647 if (components.size() > 1) {
3648 for (
const absl::Span<int> boxes : components) {
3649 if (boxes.size() <= 1)
continue;
3651 NoOverlap2DConstraintProto* new_no_overlap_2d =
3653 for (
const int b : boxes) {
3655 new_no_overlap_2d->add_y_intervals(
proto.y_intervals(
b));
3659 context_->
UpdateRuleStats(
"no_overlap_2d: split into disjoint components");
3660 return RemoveConstraint(
ct);
3663 if (!has_zero_sizes && (x_constant || y_constant)) {
3665 "no_overlap_2d: a dimension is constant, splitting into many no " 3667 std::vector<IndexedInterval> indexed_intervals;
3668 for (
int i = 0; i < new_size; ++i) {
3669 int x =
proto.x_intervals(i);
3670 int y =
proto.y_intervals(i);
3672 indexed_intervals.push_back({x, IntegerValue(context_->
StartMin(y)),
3673 IntegerValue(context_->
EndMax(y))});
3675 std::vector<std::vector<int>> no_overlaps;
3678 for (
const std::vector<int>& no_overlap : no_overlaps) {
3682 for (
const int i : no_overlap) {
3687 return RemoveConstraint(
ct);
3690 if (new_size < initial_num_boxes) {
3692 ct->mutable_no_overlap_2d()->mutable_x_intervals()->Truncate(new_size);
3693 ct->mutable_no_overlap_2d()->mutable_y_intervals()->Truncate(new_size);
3696 if (new_size == 0) {
3698 return RemoveConstraint(
ct);
3701 if (new_size == 1) {
3703 return RemoveConstraint(
ct);
3706 return new_size < initial_num_boxes;
3709 bool CpModelPresolver::PresolveCumulative(ConstraintProto*
ct) {
3712 CumulativeConstraintProto*
proto =
ct->mutable_cumulative();
3713 bool changed =
false;
3714 int num_fixed_demands = 0;
3715 const int64_t capacity_max = context_->
MaxOf(
proto->capacity());
3719 bool domain_changed =
false;
3721 proto->capacity(), Domain(0, capacity_max), &domain_changed)) {
3724 if (domain_changed) {
3733 int num_zero_demand_removed = 0;
3734 int num_zero_size_removed = 0;
3735 int num_incompatible_demands = 0;
3736 for (
int i = 0; i <
proto->intervals_size(); ++i) {
3739 const int demand_ref =
proto->demands(i);
3740 const int64_t demand_max = context_->
MaxOf(demand_ref);
3741 if (demand_max == 0) {
3742 num_zero_demand_removed++;
3745 if (context_->
IsFixed(demand_ref)) {
3746 num_fixed_demands++;
3751 num_zero_size_removed++;
3755 if (context_->
MinOf(demand_ref) > capacity_max) {
3757 ConstraintProto* interval_ct =
3759 DCHECK_EQ(interval_ct->enforcement_literal_size(), 1);
3760 const int literal = interval_ct->enforcement_literal(0);
3764 num_incompatible_demands++;
3768 "cumulative: performed demand exceeds capacity.");
3772 proto->set_intervals(new_size,
proto->intervals(i));
3773 proto->set_demands(new_size,
proto->demands(i));
3774 if (!
proto->energies().empty()) {
3775 *
proto->mutable_energies(new_size) =
proto->energies(i);
3780 if (new_size < proto->intervals_size()) {
3782 proto->mutable_intervals()->Truncate(new_size);
3783 proto->mutable_demands()->Truncate(new_size);
3784 if (!
proto->energies().empty()) {
3785 proto->mutable_energies()->erase(
3786 proto->mutable_energies()->begin() + new_size,
3787 proto->mutable_energies()->end());
3791 if (num_zero_demand_removed > 0) {
3793 "cumulative: removed intervals with no demands");
3795 if (num_zero_size_removed > 0) {
3797 "cumulative: removed intervals with a size of zero");
3799 if (num_incompatible_demands > 0) {
3801 "cumulative: removed intervals demands greater than the capacity");
3807 for (
int i = 0; i <
proto->demands_size(); ++i) {
3809 const int demand_ref =
proto->demands(i);
3811 bool domain_changed =
false;
3816 if (domain_changed) {
3818 "cumulative: fit demand in [0..capacity_max]");
3830 if (
proto->intervals_size() > 1) {
3831 std::vector<IndexedInterval> indexed_intervals;
3832 for (
int i = 0; i <
proto->intervals().size(); ++i) {
3834 indexed_intervals.push_back({i, IntegerValue(context_->
StartMin(
index)),
3837 std::vector<std::vector<int>> components;
3840 if (components.size() > 1) {
3841 for (
const std::vector<int>& component : components) {
3842 CumulativeConstraintProto* new_cumulative =
3844 for (
const int i : component) {
3846 new_cumulative->add_demands(
proto->demands(i));
3848 new_cumulative->set_capacity(
proto->capacity());
3851 context_->
UpdateRuleStats(
"cumulative: split into disjoint components");
3852 return RemoveConstraint(
ct);
3860 std::map<int64_t, int64_t> time_to_demand_deltas;
3861 const int64_t capacity_min = context_->
MinOf(
proto->capacity());
3862 for (
int i = 0; i <
proto->intervals_size(); ++i) {
3863 const int interval_index =
proto->intervals(i);
3864 const int64_t demand_max = context_->
MaxOf(
proto->demands(i));
3865 time_to_demand_deltas[context_->
StartMin(interval_index)] += demand_max;
3866 time_to_demand_deltas[context_->
EndMax(interval_index)] -= demand_max;
3875 int num_possible_overloads = 0;
3876 int64_t current_load = 0;
3877 absl::flat_hash_map<int64_t, int64_t> num_possible_overloads_before;
3878 for (
const auto& it : time_to_demand_deltas) {
3879 num_possible_overloads_before[it.first] = num_possible_overloads;
3880 current_load += it.second;
3881 if (current_load > capacity_min) {
3882 ++num_possible_overloads;
3888 if (num_possible_overloads == 0) {
3890 "cumulative: max profile is always under the min capacity");
3891 return RemoveConstraint(
ct);
3901 for (
int i = 0; i <
proto->intervals_size(); ++i) {
3919 const int num_diff = num_possible_overloads_before.at(
end_max) -
3920 num_possible_overloads_before.at(
start_min);
3921 if (num_diff == 0)
continue;
3923 proto->set_intervals(new_size,
proto->intervals(i));
3924 proto->set_demands(new_size,
proto->demands(i));
3928 if (new_size < proto->intervals_size()) {
3930 proto->mutable_intervals()->Truncate(new_size);
3931 proto->mutable_demands()->Truncate(new_size);
3933 "cumulative: remove never conflicting intervals.");
3937 if (
proto->intervals().empty()) {
3939 return RemoveConstraint(
ct);
3943 int64_t max_of_performed_demand_mins = 0;
3944 int64_t sum_of_max_demands = 0;
3945 for (
int i = 0; i <
proto->intervals_size(); ++i) {
3946 const ConstraintProto& interval_ct =
3949 const int demand_ref =
proto->demands(i);
3950 sum_of_max_demands += context_->
MaxOf(demand_ref);
3952 if (interval_ct.enforcement_literal().empty()) {
3953 max_of_performed_demand_mins =
3954 std::max(max_of_performed_demand_mins, context_->
MinOf(demand_ref));
3958 const int capacity_ref =
proto->capacity();
3959 if (max_of_performed_demand_mins > context_->
MinOf(capacity_ref)) {
3962 capacity_ref, Domain(max_of_performed_demand_mins,
3968 if (max_of_performed_demand_mins > context_->
MaxOf(capacity_ref)) {
3969 context_->
UpdateRuleStats(
"cumulative: cannot fit performed demands");
3973 if (sum_of_max_demands <= context_->MinOf(capacity_ref)) {
3974 context_->
UpdateRuleStats(
"cumulative: capacity exceeds sum of demands");
3975 return RemoveConstraint(
ct);
3979 if (num_fixed_demands ==
proto->intervals_size() &&
3982 for (
int i = 0; i <
ct->cumulative().demands_size(); ++i) {
3983 const int64_t
demand = context_->
MinOf(
ct->cumulative().demands(i));
3985 if (gcd == 1)
break;
3989 for (
int i = 0; i <
ct->cumulative().demands_size(); ++i) {
3990 const int64_t
demand = context_->
MinOf(
ct->cumulative().demands(i));
3996 ct->mutable_cumulative()->clear_energies();
3998 const int64_t old_capacity = context_->
MinOf(
proto->capacity());
4001 "cumulative: divide demands and capacity by gcd");
4006 for (LinearExpressionProto& exp :
4007 *(
ct->mutable_cumulative()->mutable_energies())) {
4008 changed |= CanonicalizeLinearExpression(*
ct, &exp);
4013 const int num_intervals =
proto->intervals_size();
4014 const int capacity_ref =
proto->capacity();
4016 bool with_start_view =
false;
4017 std::vector<int> start_refs(num_intervals, -1);
4019 int num_duration_one = 0;
4020 int num_greater_half_capacity = 0;
4022 bool has_optional_interval =
false;
4023 for (
int i = 0; i < num_intervals; ++i) {
4027 const ConstraintProto&
ct =
4029 const IntervalConstraintProto&
interval =
ct.interval();
4030 if (
interval.has_start_view()) with_start_view =
true;
4032 const int demand_ref =
proto->demands(i);
4041 const int64_t demand_min = context_->
MinOf(demand_ref);
4042 const int64_t demand_max = context_->
MaxOf(demand_ref);
4043 if (demand_min > capacity_max / 2) {
4044 num_greater_half_capacity++;
4046 if (demand_min > capacity_max) {
4047 context_->
UpdateRuleStats(
"cumulative: demand_min exceeds capacity max");
4051 CHECK_EQ(
ct.enforcement_literal().size(), 1);
4057 }
else if (demand_max > capacity_max) {
4058 if (
ct.enforcement_literal().empty()) {
4060 "cumulative: demand_max exceeds capacity max.");
4070 "cumulative: demand_max of optional interval exceeds capacity.");
4075 if (num_greater_half_capacity == num_intervals) {
4076 if (num_duration_one == num_intervals && !has_optional_interval &&
4083 return RemoveConstraint(
ct);
4089 for (
int i = 0; i <
proto->demands_size(); ++i) {
4090 const int demand_ref =
proto->demands(i);
4091 const int64_t demand_max = context_->
MaxOf(demand_ref);
4092 if (demand_max > context_->
MinOf(capacity_ref)) {
4093 ConstraintProto* capacity_gt =
4097 .enforcement_literal()) {
4100 capacity_gt->mutable_linear()->add_vars(capacity_ref);
4101 capacity_gt->mutable_linear()->add_coeffs(1);
4102 capacity_gt->mutable_linear()->add_vars(demand_ref);
4103 capacity_gt->mutable_linear()->add_coeffs(-1);
4104 capacity_gt->mutable_linear()->add_domain(0);
4105 capacity_gt->mutable_linear()->add_domain(
4117 return RemoveConstraint(
ct);
4124 bool CpModelPresolver::PresolveRoutes(ConstraintProto*
ct) {
4127 RoutesConstraintProto&
proto = *
ct->mutable_routes();
4130 const int num_arcs =
proto.literals_size();
4131 for (
int i = 0; i < num_arcs; ++i) {
4132 const int ref =
proto.literals(i);
4139 proto.set_literals(new_size, ref);
4144 if (new_size < num_arcs) {
4145 proto.mutable_literals()->Truncate(new_size);
4146 proto.mutable_tails()->Truncate(new_size);
4147 proto.mutable_heads()->Truncate(new_size);
4153 bool CpModelPresolver::PresolveCircuit(ConstraintProto*
ct) {
4156 CircuitConstraintProto&
proto = *
ct->mutable_circuit();
4160 ct->mutable_circuit()->mutable_heads());
4164 std::vector<std::vector<int>> incoming_arcs;
4165 std::vector<std::vector<int>> outgoing_arcs;
4167 const int num_arcs =
proto.literals_size();
4168 for (
int i = 0; i < num_arcs; ++i) {
4169 const int ref =
proto.literals(i);
4177 incoming_arcs[
head].push_back(ref);
4178 outgoing_arcs[
tail].push_back(ref);
4182 for (
int i = 0; i < num_nodes; ++i) {
4183 if (incoming_arcs[i].empty() || outgoing_arcs[i].empty()) {
4184 return MarkConstraintAsFalse(
ct);
4193 bool loop_again =
true;
4194 int num_fixed_at_true = 0;
4195 while (loop_again) {
4197 for (
const auto* node_to_refs : {&incoming_arcs, &outgoing_arcs}) {
4198 for (
const std::vector<int>& refs : *node_to_refs) {
4199 if (refs.size() == 1) {
4201 ++num_fixed_at_true;
4210 for (
const int ref : refs) {
4220 if (num_true == 1) {
4221 for (
const int ref : refs) {
4222 if (ref != true_ref) {
4223 if (!context_->
IsFixed(ref)) {
4234 if (num_fixed_at_true > 0) {
4241 int circuit_start = -1;
4242 std::vector<int>
next(num_nodes, -1);
4243 std::vector<int> new_in_degree(num_nodes, 0);
4244 std::vector<int> new_out_degree(num_nodes, 0);
4245 for (
int i = 0; i < num_arcs; ++i) {
4246 const int ref =
proto.literals(i);
4254 circuit_start =
proto.tails(i);
4258 ++new_out_degree[
proto.tails(i)];
4259 ++new_in_degree[
proto.heads(i)];
4262 proto.set_literals(new_size,
proto.literals(i));
4272 for (
int i = 0; i < num_nodes; ++i) {
4273 if (new_in_degree[i] == 0 || new_out_degree[i] == 0) {
4279 if (circuit_start != -1) {
4280 std::vector<bool> visited(num_nodes,
false);
4281 int current = circuit_start;
4282 while (current != -1 && !visited[current]) {
4283 visited[current] =
true;
4284 current =
next[current];
4286 if (current == circuit_start) {
4289 std::vector<bool> has_self_arc(num_nodes,
false);
4290 for (
int i = 0; i < num_arcs; ++i) {
4291 if (visited[
proto.tails(i)])
continue;
4293 has_self_arc[
proto.tails(i)] =
true;
4299 for (
int n = 0; n < num_nodes; ++n) {
4300 if (!visited[n] && !has_self_arc[n]) {
4302 return MarkConstraintAsFalse(
ct);
4306 return RemoveConstraint(
ct);
4310 if (num_true == new_size) {
4312 return RemoveConstraint(
ct);
4318 for (
int i = 0; i < num_nodes; ++i) {
4319 for (
const std::vector<int>* arc_literals :
4320 {&incoming_arcs[i], &outgoing_arcs[i]}) {
4321 std::vector<int> literals;
4322 for (
const int ref : *arc_literals) {
4328 literals.push_back(ref);
4330 if (literals.size() == 2 && literals[0] !=
NegatedRef(literals[1])) {
4339 if (new_size < num_arcs) {
4340 proto.mutable_tails()->Truncate(new_size);
4341 proto.mutable_heads()->Truncate(new_size);
4342 proto.mutable_literals()->Truncate(new_size);
4349 bool CpModelPresolver::PresolveAutomaton(ConstraintProto*
ct) {
4352 AutomatonConstraintProto&
proto = *
ct->mutable_automaton();
4353 if (
proto.vars_size() == 0 ||
proto.transition_label_size() == 0) {
4357 bool all_have_same_affine_relation =
true;
4358 std::vector<AffineRelation::Relation> affine_relations;
4359 for (
int v = 0; v <
proto.vars_size(); ++v) {
4360 const int var =
ct->automaton().vars(v);
4362 affine_relations.push_back(r);
4363 if (r.representative ==
var) {
4364 all_have_same_affine_relation =
false;
4367 if (v > 0 && (r.coeff != affine_relations[v - 1].coeff ||
4368 r.offset != affine_relations[v - 1].offset)) {
4369 all_have_same_affine_relation =
false;
4374 if (all_have_same_affine_relation) {
4375 for (
int v = 0; v <
proto.vars_size(); ++v) {
4378 const AffineRelation::Relation rep = affine_relations.front();
4380 for (
int t = 0; t <
proto.transition_tail_size(); ++t) {
4381 const int64_t label =
proto.transition_label(t);
4382 int64_t inverse_label = (label - rep.offset) / rep.coeff;
4383 if (inverse_label * rep.coeff + rep.offset == label) {
4384 if (new_size != t) {
4385 proto.set_transition_tail(new_size,
proto.transition_tail(t));
4386 proto.set_transition_head(new_size,
proto.transition_head(t));
4388 proto.set_transition_label(new_size, inverse_label);
4392 if (new_size <
proto.transition_tail_size()) {
4393 proto.mutable_transition_tail()->Truncate(new_size);
4394 proto.mutable_transition_label()->Truncate(new_size);
4395 proto.mutable_transition_head()->Truncate(new_size);
4403 for (
int v = 1; v <
proto.vars_size(); ++v) {
4408 for (
int t = 0; t <
proto.transition_tail_size(); ++t) {
4409 const int64_t label =
proto.transition_label(t);
4410 if (hull.Contains(label)) {
4411 if (new_size != t) {
4412 proto.set_transition_tail(new_size,
proto.transition_tail(t));
4413 proto.set_transition_label(new_size, label);
4414 proto.set_transition_head(new_size,
proto.transition_head(t));
4419 if (new_size <
proto.transition_tail_size()) {
4420 proto.mutable_transition_tail()->Truncate(new_size);
4421 proto.mutable_transition_label()->Truncate(new_size);
4422 proto.mutable_transition_head()->Truncate(new_size);
4427 const int n =
proto.vars_size();
4428 const std::vector<int> vars = {
proto.vars().begin(),
proto.vars().end()};
4431 std::vector<std::set<int64_t>> reachable_states(n + 1);
4432 reachable_states[0].insert(
proto.starting_state());
4433 reachable_states[n] = {
proto.final_states().begin(),
4434 proto.final_states().end()};
4441 for (
int t = 0; t <
proto.transition_tail_size(); ++t) {
4442 const int64_t
tail =
proto.transition_tail(t);
4443 const int64_t label =
proto.transition_label(t);
4444 const int64_t
head =
proto.transition_head(t);
4447 reachable_states[
time + 1].insert(
head);
4451 std::vector<std::set<int64_t>> reached_values(n);
4455 std::set<int64_t> new_set;
4456 for (
int t = 0; t <
proto.transition_tail_size(); ++t) {
4457 const int64_t
tail =
proto.transition_tail(t);
4458 const int64_t label =
proto.transition_label(t);
4459 const int64_t
head =
proto.transition_head(t);
4464 new_set.insert(
tail);
4465 reached_values[
time].insert(label);
4467 reachable_states[
time].swap(new_set);
4470 bool removed_values =
false;
4475 {reached_values[time].begin(), reached_values[time].end()}),
4480 if (removed_values) {
4486 bool CpModelPresolver::PresolveReservoir(ConstraintProto*
ct) {
4490 bool changed =
false;
4492 ReservoirConstraintProto& mutable_reservoir = *
ct->mutable_reservoir();
4493 if (mutable_reservoir.actives().empty()) {
4495 for (
int i = 0; i < mutable_reservoir.times_size(); ++i) {
4496 mutable_reservoir.add_actives(true_literal);
4501 const auto& demand_is_null = [&](
int i) {
4502 return mutable_reservoir.demands(i) == 0 ||
4508 for (
int i = 0; i < mutable_reservoir.demands_size(); ++i) {
4509 if (demand_is_null(i)) num_zeros++;
4512 if (num_zeros > 0) {
4515 for (
int i = 0; i < mutable_reservoir.demands_size(); ++i) {
4516 if (demand_is_null(i))
continue;
4517 mutable_reservoir.set_demands(new_size, mutable_reservoir.demands(i));
4518 mutable_reservoir.set_times(new_size, mutable_reservoir.times(i));
4519 mutable_reservoir.set_actives(new_size, mutable_reservoir.actives(i));
4523 mutable_reservoir.mutable_demands()->Truncate(new_size);
4524 mutable_reservoir.mutable_times()->Truncate(new_size);
4525 mutable_reservoir.mutable_actives()->Truncate(new_size);
4528 "reservoir: remove zero demands or inactive events.");
4531 const int num_events = mutable_reservoir.demands_size();
4532 int64_t gcd = mutable_reservoir.demands().empty()
4534 : std::abs(mutable_reservoir.demands(0));
4535 int num_positives = 0;
4536 int num_negatives = 0;
4537 int64_t max_sum_of_positive_demands = 0;
4538 int64_t min_sum_of_negative_demands = 0;
4539 for (
int i = 0; i < num_events; ++i) {
4540 const int64_t
demand = mutable_reservoir.demands(i);
4544 max_sum_of_positive_demands +=
demand;
4548 min_sum_of_negative_demands +=
demand;
4552 if (min_sum_of_negative_demands >= mutable_reservoir.min_level() &&
4553 max_sum_of_positive_demands <= mutable_reservoir.max_level()) {
4555 return RemoveConstraint(
ct);
4558 if (min_sum_of_negative_demands > mutable_reservoir.max_level() ||
4559 max_sum_of_positive_demands < mutable_reservoir.min_level()) {
4564 if (min_sum_of_negative_demands > mutable_reservoir.min_level()) {
4565 mutable_reservoir.set_min_level(min_sum_of_negative_demands);
4567 "reservoir: increase min_level to reachable value");
4570 if (max_sum_of_positive_demands < mutable_reservoir.max_level()) {
4571 mutable_reservoir.set_max_level(max_sum_of_positive_demands);
4572 context_->
UpdateRuleStats(
"reservoir: reduce max_level to reachable value");
4575 if (mutable_reservoir.min_level() <= 0 &&
4576 mutable_reservoir.max_level() >= 0 &&
4577 (num_positives == 0 || num_negatives == 0)) {
4582 int64_t fixed_contrib = 0;
4583 for (
int i = 0; i < mutable_reservoir.demands_size(); ++i) {
4584 const int64_t
demand = mutable_reservoir.demands(i);
4587 const int active = mutable_reservoir.actives(i);
4589 sum->add_vars(active);
4593 sum->add_coeffs(-
demand);
4597 sum->add_domain(mutable_reservoir.min_level() - fixed_contrib);
4598 sum->add_domain(mutable_reservoir.max_level() - fixed_contrib);
4600 return RemoveConstraint(
ct);
4604 for (
int i = 0; i < mutable_reservoir.demands_size(); ++i) {
4605 mutable_reservoir.set_demands(i, mutable_reservoir.demands(i) / gcd);
4611 const Domain reduced_domain =
4612 Domain({mutable_reservoir.min_level(), mutable_reservoir.max_level()})
4613 .InverseMultiplicationBy(gcd);
4614 mutable_reservoir.set_min_level(reduced_domain.Min());
4615 mutable_reservoir.set_max_level(reduced_domain.Max());
4616 context_->
UpdateRuleStats(
"reservoir: simplify demands and levels by gcd.");
4619 if (num_positives == 1 && num_negatives > 0) {
4621 "TODO reservoir: one producer, multiple consumers.");
4624 absl::flat_hash_set<std::pair<int, int>> time_active_set;
4625 for (
int i = 0; i < mutable_reservoir.demands_size(); ++i) {
4626 const std::pair<int, int> key = std::make_pair(
4627 mutable_reservoir.times(i), mutable_reservoir.actives(i));
4628 if (time_active_set.contains(key)) {
4629 context_->
UpdateRuleStats(
"TODO reservoir: merge synchronized events.");
4632 time_active_set.insert(key);
4642 void CpModelPresolver::ExtractBoolAnd() {
4643 absl::flat_hash_map<int, int> ref_to_bool_and;
4645 std::vector<int> to_remove;
4646 for (
int c = 0; c < num_constraints; ++c) {
4650 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr &&
4651 ct.bool_or().literals().size() == 2) {
4655 to_remove.push_back(c);
4659 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kAtMostOne &&
4660 ct.at_most_one().literals().size() == 2) {
4661 AddImplication(
ct.at_most_one().literals(0),
4664 to_remove.push_back(c);
4670 for (
const int c : to_remove) {
4677 void CpModelPresolver::Probe() {
4702 auto* local_param =
model.GetOrCreate<SatParameters>();
4703 *local_param = context_->
params();
4706 model.GetOrCreate<TimeLimit>()->MergeWithGlobalTimeLimit(
4708 model.Register<ModelRandomGenerator>(context_->
random());
4709 auto* encoder =
model.GetOrCreate<IntegerEncoder>();
4710 encoder->DisableImplicationBetweenLiteral();
4711 auto* mapping =
model.GetOrCreate<CpModelMapping>();
4718 auto* sat_solver =
model.GetOrCreate<SatSolver>();
4719 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
4720 if (mapping->ConstraintIsAlreadyLoaded(&
ct))
continue;
4722 if (sat_solver->IsModelUnsat()) {
4724 "after loading constraint during probing ",
ct.ShortDebugString()));
4727 encoder->AddAllImplicationsBetweenAssociatedLiterals();
4728 if (!sat_solver->Propagate()) {
4730 "during probing initial propagation");
4737 auto* implication_graph =
model.GetOrCreate<BinaryImplicationGraph>();
4738 auto* prober =
model.GetOrCreate<Prober>();
4739 prober->ProbeBooleanVariables(1.0);
4741 model.GetOrCreate<TimeLimit>()->GetElapsedDeterministicTime());
4742 if (sat_solver->IsModelUnsat() || !implication_graph->DetectEquivalences()) {
4747 CHECK_EQ(sat_solver->CurrentDecisionLevel(), 0);
4748 for (
int i = 0; i < sat_solver->LiteralTrail().Index(); ++i) {
4749 const Literal l = sat_solver->LiteralTrail()[i];
4750 const int var = mapping->GetProtoVariableFromBooleanVariable(l.Variable());
4758 auto* integer_trail =
model.GetOrCreate<IntegerTrail>();
4759 for (
int var = 0;
var < num_variables; ++
var) {
4762 if (!mapping->IsBoolean(
var)) {
4765 integer_trail->InitialVariableDomain(mapping->Integer(
var)))) {
4772 const Literal l = mapping->Literal(
var);
4773 const Literal r = implication_graph->RepresentativeOf(l);
4776 mapping->GetProtoVariableFromBooleanVariable(r.Variable());
4786 void CpModelPresolver::PresolvePureSatPart() {
4792 SatPostsolver sat_postsolver(num_variables);
4793 SatPresolver sat_presolver(&sat_postsolver, logger_);
4794 sat_presolver.SetNumVariables(num_variables);
4795 sat_presolver.SetTimeLimit(context_->
time_limit());
4797 SatParameters params = context_->
params();
4804 if (params.cp_model_postsolve_with_full_solver()) {
4811 params.set_presolve_use_bva(
false);
4812 sat_presolver.SetParameters(params);
4815 absl::flat_hash_set<int> used_variables;
4816 auto convert = [&used_variables](
int ref) {
4818 if (
RefIsPositive(ref))
return Literal(BooleanVariable(ref),
true);
4819 return Literal(BooleanVariable(
NegatedRef(ref)),
false);
4829 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr ||
4830 ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolAnd) {
4849 std::vector<Literal> clause;
4850 int num_removed_constraints = 0;
4854 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr) {
4855 ++num_removed_constraints;
4857 for (
const int ref :
ct.bool_or().literals()) {
4858 clause.push_back(convert(ref));
4860 for (
const int ref :
ct.enforcement_literal()) {
4861 clause.push_back(convert(ref).Negated());
4863 sat_presolver.AddClause(clause);
4870 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolAnd) {
4871 ++num_removed_constraints;
4872 std::vector<Literal> clause;
4873 for (
const int ref :
ct.enforcement_literal()) {
4874 clause.push_back(convert(ref).Negated());
4877 for (
const int ref :
ct.bool_and().literals()) {
4878 clause.back() = convert(ref);
4879 sat_presolver.AddClause(clause);
4889 if (num_removed_constraints == 0)
return;
4899 std::vector<bool> can_be_removed(num_variables,
false);
4900 for (
int i = 0; i < num_variables; ++i) {
4902 can_be_removed[i] =
true;
4908 if (used_variables.contains(i) && context_->
IsFixed(i)) {
4910 sat_presolver.AddClause({convert(i)});
4912 sat_presolver.AddClause({convert(
NegatedRef(i))});
4920 const int num_passes = params.presolve_use_bva() ? 4 : 1;
4921 for (
int i = 0; i < num_passes; ++i) {
4922 const int old_num_clause = sat_postsolver.NumClauses();
4923 if (!sat_presolver.Presolve(can_be_removed)) {
4924 VLOG(1) <<
"UNSAT during SAT presolve.";
4927 if (old_num_clause == sat_postsolver.NumClauses())
break;
4931 const int new_num_variables = sat_presolver.NumVariables();
4933 VLOG(1) <<
"New variables added by the SAT presolver.";
4935 i < new_num_variables; ++i) {
4936 IntegerVariableProto* var_proto =
4939 var_proto->add_domain(1);
4945 ExtractClauses(
true, sat_presolver, context_->
working_model);
4953 ExtractClauses(
false, sat_postsolver,
4961 void CpModelPresolver::ExpandObjective() {
4980 int unique_expanded_constraint = -1;
4981 const bool objective_was_a_single_variable =
4988 absl::flat_hash_set<int> relevant_constraints;
4989 std::vector<int> var_to_num_relevant_constraints(num_variables, 0);
4990 for (
int ct_index = 0; ct_index < num_constraints; ++ct_index) {
4993 if (!
ct.enforcement_literal().empty() ||
4994 ct.constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
4995 ct.linear().domain().size() != 2 ||
4996 ct.linear().domain(0) !=
ct.linear().domain(1)) {
5000 relevant_constraints.insert(ct_index);
5001 const int num_terms =
ct.linear().vars_size();
5002 for (
int i = 0; i < num_terms; ++i) {
5003 var_to_num_relevant_constraints[
PositiveRef(
ct.linear().vars(i))]++;
5007 std::set<int> var_to_process;
5009 const int var = entry.first;
5011 if (var_to_num_relevant_constraints[
var] != 0) {
5012 var_to_process.insert(
var);
5017 int num_expansions = 0;
5018 int last_expanded_objective_var;
5019 absl::flat_hash_set<int> processed_vars;
5020 std::vector<int> new_vars_in_objective;
5021 while (!relevant_constraints.empty()) {
5023 int objective_var = -1;
5024 while (!var_to_process.empty()) {
5025 const int var = *var_to_process.begin();
5026 CHECK(!processed_vars.contains(
var));
5027 if (var_to_num_relevant_constraints[
var] == 0) {
5028 processed_vars.insert(
var);
5029 var_to_process.erase(
var);
5034 var_to_process.erase(
var);
5037 objective_var =
var;
5041 if (objective_var == -1)
break;
5043 processed_vars.insert(objective_var);
5044 var_to_process.erase(objective_var);
5046 int expanded_linear_index = -1;
5047 int64_t objective_coeff_in_expanded_constraint;
5048 int64_t size_of_expanded_constraint = 0;
5049 const auto& non_deterministic_list =
5051 std::vector<int> constraints_with_objective(non_deterministic_list.begin(),
5052 non_deterministic_list.end());
5053 std::sort(constraints_with_objective.begin(),
5054 constraints_with_objective.end());
5055 for (
const int ct_index : constraints_with_objective) {
5056 if (relevant_constraints.count(ct_index) == 0)
continue;
5057 const ConstraintProto&
ct =
5062 relevant_constraints.erase(ct_index);
5063 const int num_terms =
ct.linear().vars_size();
5064 for (
int i = 0; i < num_terms; ++i) {
5065 var_to_num_relevant_constraints[
PositiveRef(
ct.linear().vars(i))]--;
5077 bool is_present =
false;
5078 int64_t objective_coeff;
5079 for (
int i = 0; i < num_terms; ++i) {
5080 const int ref =
ct.linear().vars(i);
5081 const int64_t coeff =
ct.linear().coeffs(i);
5083 CHECK(!is_present) <<
"Duplicate variables not supported.";
5085 objective_coeff = (ref == objective_var) ? coeff : -coeff;
5098 if (std::abs(objective_coeff) == 1 &&
5099 num_terms > size_of_expanded_constraint) {
5100 expanded_linear_index = ct_index;
5101 size_of_expanded_constraint = num_terms;
5102 objective_coeff_in_expanded_constraint = objective_coeff;
5106 if (expanded_linear_index != -1) {
5107 context_->
UpdateRuleStats(
"objective: expanded objective constraint.");
5111 CHECK_EQ(std::abs(objective_coeff_in_expanded_constraint), 1);
5112 const ConstraintProto&
ct =
5115 objective_var, objective_coeff_in_expanded_constraint,
ct,
5116 &new_vars_in_objective);
5119 for (
const int var : new_vars_in_objective) {
5120 if (!processed_vars.contains(
var)) var_to_process.insert(
var);
5133 for (
int i = 0; i < size_of_expanded_constraint; ++i) {
5134 const int ref =
ct.linear().vars(i);
5139 -
ct.linear().coeffs(i)))
5140 .RelaxIfTooComplex();
5142 implied_domain = implied_domain.InverseMultiplicationBy(
5143 objective_coeff_in_expanded_constraint);
5147 if (implied_domain.IsIncludedIn(context_->
DomainOf(objective_var))) {
5149 context_->
UpdateRuleStats(
"objective: removed objective constraint.");
5155 unique_expanded_constraint = expanded_linear_index;
5160 last_expanded_objective_var = objective_var;
5166 if (num_expansions == 1 && objective_was_a_single_variable &&
5167 unique_expanded_constraint != -1) {
5169 "objective: removed unique objective constraint.");
5171 unique_expanded_constraint);
5173 mutable_ct->
Clear();
5186 void CpModelPresolver::MergeNoOverlapConstraints() {
5190 int old_num_no_overlaps = 0;
5191 int old_num_intervals = 0;
5194 std::vector<int> disjunctive_index;
5195 std::vector<std::vector<Literal>> cliques;
5196 for (
int c = 0; c < num_constraints; ++c) {
5198 if (
ct.constraint_case() != ConstraintProto::ConstraintCase::kNoOverlap) {
5201 std::vector<Literal> clique;
5202 for (
const int i :
ct.no_overlap().intervals()) {
5203 clique.push_back(Literal(BooleanVariable(i),
true));
5205 cliques.push_back(clique);
5206 disjunctive_index.push_back(c);
5208 old_num_no_overlaps++;
5209 old_num_intervals += clique.size();
5211 if (old_num_no_overlaps == 0)
return;
5215 local_model.GetOrCreate<Trail>()->Resize(num_constraints);
5216 auto* graph = local_model.GetOrCreate<BinaryImplicationGraph>();
5217 graph->Resize(num_constraints);
5218 for (
const std::vector<Literal>& clique : cliques) {
5221 CHECK(graph->AddAtMostOne(clique));
5223 CHECK(graph->DetectEquivalences());
5224 graph->TransformIntoMaxCliques(
5228 int new_num_no_overlaps = 0;
5229 int new_num_intervals = 0;
5230 for (
int i = 0; i < cliques.size(); ++i) {
5231 const int ct_index = disjunctive_index[i];
5232 ConstraintProto*
ct =
5235 if (cliques[i].empty())
continue;
5236 for (
const Literal l : cliques[i]) {
5237 CHECK(l.IsPositive());
5238 ct->mutable_no_overlap()->add_intervals(l.Variable().value());
5240 new_num_no_overlaps++;
5241 new_num_intervals += cliques[i].size();
5243 if (old_num_intervals != new_num_intervals ||
5244 old_num_no_overlaps != new_num_no_overlaps) {
5245 VLOG(1) << absl::StrCat(
"Merged ", old_num_no_overlaps,
" no-overlaps (",
5246 old_num_intervals,
" intervals) into ",
5247 new_num_no_overlaps,
" no-overlaps (",
5248 new_num_intervals,
" intervals).");
5257 void CpModelPresolver::TransformIntoMaxCliques() {
5260 auto convert = [](
int ref) {
5261 if (
RefIsPositive(ref))
return Literal(BooleanVariable(ref),
true);
5262 return Literal(BooleanVariable(
NegatedRef(ref)),
false);
5267 std::vector<std::vector<Literal>> cliques;
5269 for (
int c = 0; c < num_constraints; ++c) {
5271 if (
ct->constraint_case() == ConstraintProto::ConstraintCase::kAtMostOne) {
5272 std::vector<Literal> clique;
5273 for (
const int ref :
ct->at_most_one().literals()) {
5274 clique.push_back(convert(ref));
5276 cliques.push_back(clique);
5277 if (RemoveConstraint(
ct)) {
5280 }
else if (
ct->constraint_case() ==
5281 ConstraintProto::ConstraintCase::kBoolAnd) {
5282 if (
ct->enforcement_literal().size() != 1)
continue;
5283 const Literal enforcement = convert(
ct->enforcement_literal(0));
5284 for (
const int ref :
ct->bool_and().literals()) {
5285 if (ref ==
ct->enforcement_literal(0))
continue;
5286 cliques.push_back({enforcement, convert(ref).Negated()});
5288 if (RemoveConstraint(
ct)) {
5294 int64_t num_literals_before = 0;
5295 const int num_old_cliques = cliques.size();
5300 local_model.GetOrCreate<Trail>()->Resize(num_variables);
5301 auto* graph = local_model.GetOrCreate<BinaryImplicationGraph>();
5302 graph->Resize(num_variables);
5303 for (
const std::vector<Literal>& clique : cliques) {
5304 num_literals_before += clique.size();
5305 if (!graph->AddAtMostOne(clique)) {
5309 if (!graph->DetectEquivalences()) {
5312 graph->TransformIntoMaxCliques(
5318 for (
int var = 0;
var < num_variables; ++
var) {
5319 const Literal l = Literal(BooleanVariable(
var),
true);
5320 if (graph->RepresentativeOf(l) != l) {
5321 const Literal r = graph->RepresentativeOf(l);
5323 var, r.IsPositive() ? r.Variable().value()
5328 int num_new_cliques = 0;
5329 int64_t num_literals_after = 0;
5330 for (
const std::vector<Literal>& clique : cliques) {
5331 if (clique.empty())
continue;
5333 num_literals_after += clique.size();
5335 for (
const Literal
literal : clique) {
5337 ct->mutable_at_most_one()->add_literals(
literal.Variable().value());
5339 ct->mutable_at_most_one()->add_literals(
5345 PresolveAtMostOne(
ct);
5348 if (num_new_cliques != num_old_cliques) {
5349 context_->
UpdateRuleStats(
"at_most_one: transformed into max clique.");
5352 if (num_old_cliques != num_new_cliques ||
5353 num_literals_before != num_literals_after) {
5354 SOLVER_LOG(logger_,
"[MaxClique] Merged ", num_old_cliques,
"(",
5355 num_literals_before,
" literals) into ", num_new_cliques,
"(",
5356 num_literals_after,
" literals) at_most_ones.");
5365 if (ExploitEquivalenceRelations(c,
ct)) {
5370 if (PresolveEnforcementLiteral(
ct)) {
5375 switch (
ct->constraint_case()) {
5376 case ConstraintProto::ConstraintCase::kBoolOr:
5377 return PresolveBoolOr(
ct);
5378 case ConstraintProto::ConstraintCase::kBoolAnd:
5379 return PresolveBoolAnd(
ct);
5380 case ConstraintProto::ConstraintCase::kAtMostOne:
5381 return PresolveAtMostOne(
ct);
5382 case ConstraintProto::ConstraintCase::kExactlyOne:
5383 return PresolveExactlyOne(
ct);
5384 case ConstraintProto::ConstraintCase::kBoolXor:
5385 return PresolveBoolXor(
ct);
5386 case ConstraintProto::ConstraintCase::kIntMax:
5387 if (
ct->int_max().vars_size() == 2 &&
5389 return PresolveIntAbs(
ct);
5391 return PresolveIntMax(
ct);
5393 case ConstraintProto::ConstraintCase::kIntMin:
5394 return PresolveIntMin(
ct);
5395 case ConstraintProto::ConstraintCase::kLinMax:
5396 return PresolveLinMax(
ct);
5397 case ConstraintProto::ConstraintCase::kLinMin:
5398 return PresolveLinMin(
ct);
5399 case ConstraintProto::ConstraintCase::kIntProd:
5400 return PresolveIntProd(
ct);
5401 case ConstraintProto::ConstraintCase::kIntDiv:
5402 return PresolveIntDiv(
ct);
5403 case ConstraintProto::ConstraintCase::kIntMod:
5404 return PresolveIntMod(
ct);
5405 case ConstraintProto::ConstraintCase::kLinear: {
5415 for (
const int ref :
ct->linear().vars()) {
5420 if (CanonicalizeLinear(
ct)) {
5423 if (PresolveSmallLinear(
ct)) {
5426 if (PropagateDomainsInLinear(c,
ct)) {
5429 if (PresolveSmallLinear(
ct)) {
5433 if (RemoveSingletonInLinear(
ct)) {
5438 if (PresolveSmallLinear(
ct)) {
5442 if (PresolveLinearOnBooleans(
ct)) {
5445 if (
ct->constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
5446 const int old_num_enforcement_literals =
ct->enforcement_literal_size();
5447 ExtractEnforcementLiteralFromLinearConstraint(c,
ct);
5448 if (
ct->constraint_case() ==
5449 ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET) {
5453 if (
ct->enforcement_literal_size() > old_num_enforcement_literals &&
5454 PresolveSmallLinear(
ct)) {
5457 PresolveLinearEqualityModuloTwo(
ct);
5461 case ConstraintProto::ConstraintCase::kInterval:
5462 return PresolveInterval(c,
ct);
5463 case ConstraintProto::ConstraintCase::kInverse:
5464 return PresolveInverse(
ct);
5465 case ConstraintProto::ConstraintCase::kElement:
5466 return PresolveElement(
ct);
5467 case ConstraintProto::ConstraintCase::kTable:
5468 return PresolveTable(
ct);
5469 case ConstraintProto::ConstraintCase::kAllDiff:
5470 return PresolveAllDiff(
ct);
5471 case ConstraintProto::ConstraintCase::kNoOverlap:
5472 return PresolveNoOverlap(
ct);
5473 case ConstraintProto::ConstraintCase::kNoOverlap2D:
5474 return PresolveNoOverlap2D(c,
ct);
5475 case ConstraintProto::ConstraintCase::kCumulative:
5476 return PresolveCumulative(
ct);
5477 case ConstraintProto::ConstraintCase::kCircuit:
5478 return PresolveCircuit(
ct);
5479 case ConstraintProto::ConstraintCase::kRoutes:
5480 return PresolveRoutes(
ct);
5481 case ConstraintProto::ConstraintCase::kAutomaton:
5482 return PresolveAutomaton(
ct);
5483 case ConstraintProto::ConstraintCase::kReservoir:
5484 return PresolveReservoir(
ct);
5494 bool CpModelPresolver::ProcessSetPPCSubset(
5495 int c1,
int c2,
const std::vector<int>& c2_minus_c1,
5496 const std::vector<int>& original_constraint_index,
5497 std::vector<bool>* marked_for_removal) {
5500 CHECK(!(*marked_for_removal)[c1]);
5501 CHECK(!(*marked_for_removal)[c2]);
5504 original_constraint_index[c1]);
5506 original_constraint_index[c2]);
5516 for (
const int literal : c2_minus_c1) {
5523 ConstraintProto copy = *ct2;
5529 (*marked_for_removal)[c1] =
true;
5540 (*marked_for_removal)[c2] =
true;
5550 (*marked_for_removal)[c1] =
true;
5567 int num_matches = 0;
5568 for (
int i = 0; i < ct2->
linear().
vars().size(); ++i) {
5570 if (literals.contains(
var)) {
5578 if (num_matches != literals.size())
return true;
5584 for (
int i = 0; i < ct2->
linear().
vars().size(); ++i) {
5587 if (literals.contains(
var)) {
5589 if (coeff == min_coeff)
continue;
5616 bool CpModelPresolver::ProcessSetPPC() {
5621 std::vector<uint64_t> signatures;
5625 std::vector<std::vector<int>> constraint_literals;
5629 std::vector<std::vector<int>> literals_to_constraints;
5632 std::vector<bool> removed;
5636 std::vector<int> original_constraint_index;
5640 int num_setppc_constraints = 0;
5641 std::vector<int> temp_literals;
5643 for (
int c = 0; c < num_constraints; ++c) {
5655 constraint_literals.push_back(GetLiteralsFromSetPPCConstraint(*
ct));
5666 const int size =
ct->linear().vars().size();
5667 if (size <= 2)
continue;
5672 temp_literals.clear();
5673 for (
int i = 0; i < size; ++i) {
5674 const int var =
ct->linear().vars(i);
5675 const int64_t coeff =
ct->linear().coeffs(i);
5678 if (coeff < 0)
continue;
5679 temp_literals.push_back(
var);
5681 if (temp_literals.size() <= 2)
continue;
5682 constraint_literals.push_back(temp_literals);
5687 uint64_t signature = 0;
5688 for (
const int literal : constraint_literals.back()) {
5690 signature |= (int64_t{1} << (positive_literal % 64));
5692 if (positive_literal >= literals_to_constraints.size()) {
5693 literals_to_constraints.resize(positive_literal + 1);
5695 literals_to_constraints[positive_literal].push_back(
5696 num_setppc_constraints);
5698 signatures.push_back(signature);
5699 removed.push_back(
false);
5700 original_constraint_index.push_back(c);
5701 num_setppc_constraints++;
5703 VLOG(1) <<
"#setppc constraints: " << num_setppc_constraints;
5706 absl::flat_hash_set<std::pair<int, int>> compared_constraints;
5707 for (
const std::vector<int>& literal_to_constraints :
5708 literals_to_constraints) {
5709 for (
int index1 = 0; index1 < literal_to_constraints.size(); ++index1) {
5712 const int c1 = literal_to_constraints[index1];
5713 if (removed[c1])
continue;
5714 const std::vector<int>& c1_literals = constraint_literals[c1];
5716 for (
int index2 = index1 + 1; index2 < literal_to_constraints.size();
5718 const int c2 = literal_to_constraints[index2];
5719 if (removed[c2])
continue;
5720 if (removed[c1])
break;
5723 if (c1 == c2)
continue;
5727 std::pair<int, int>(c1, c2))) {
5730 compared_constraints.insert({c1, c2});
5734 if (compared_constraints.size() >= 50000)
return true;
5736 const bool smaller = (signatures[c1] & ~signatures[c2]) == 0;
5737 const bool larger = (signatures[c2] & ~signatures[c1]) == 0;
5738 if (!(smaller || larger))
continue;
5741 const std::vector<int>& c2_literals = constraint_literals[c2];
5745 std::vector<int> c1_minus_c2;
5747 std::vector<int> c2_minus_c1;
5750 if (c1_minus_c2.empty()) {
5751 if (!ProcessSetPPCSubset(c1, c2, c2_minus_c1,
5752 original_constraint_index, &removed)) {
5755 }
else if (c2_minus_c1.empty()) {
5756 if (!ProcessSetPPCSubset(c2, c1, c1_minus_c2,
5757 original_constraint_index, &removed)) {
5768 void CpModelPresolver::TryToSimplifyDomain(
int var) {
5777 if (r.representative !=
var)
return;
5793 std::vector<int> literals;
5794 std::vector<int> equality_constraints;
5795 std::vector<int> other_constraints;
5796 absl::flat_hash_map<int64_t, int> value_to_equal_literal;
5797 absl::flat_hash_map<int64_t, int> value_to_not_equal_literal;
5800 if (c < 0)
continue;
5804 int64_t coeff =
ct.linear().coeffs(0);
5805 if (std::abs(coeff) != 1 ||
ct.enforcement_literal().size() != 1) {
5813 if (rhs.IsFixed()) {
5814 const int64_t
value = rhs.FixedValue();
5815 if (value_to_equal_literal.contains(
value)) {
5819 equality_constraints.push_back(c);
5820 literals.push_back(
ct.enforcement_literal(0));
5821 value_to_equal_literal[
value] =
ct.enforcement_literal(0);
5823 const Domain complement =
5825 if (complement.IsEmpty()) {
5830 if (complement.IsFixed()) {
5831 const int64_t
value = complement.FixedValue();
5832 if (value_to_not_equal_literal.contains(
value)) {
5836 other_constraints.push_back(c);
5837 value_to_not_equal_literal[
value] =
ct.enforcement_literal(0);
5846 if (value_to_equal_literal.size() != context_->
DomainOf(
var).
Size()) {
5850 if (!value_to_equal_literal.contains(
value)) {
5854 if (value_to_not_equal_literal.contains(
value) &&
5855 value_to_equal_literal[
value] !=
5869 ConstraintProto encoding_ct;
5870 LinearConstraintProto* linear = encoding_ct.mutable_linear();
5871 const int64_t coeff_in_equality = -1;
5872 linear->add_vars(
var);
5873 linear->add_coeffs(coeff_in_equality);
5875 std::vector<int64_t> all_values;
5876 for (
const auto entry : value_to_equal_literal) {
5877 all_values.push_back(entry.first);
5879 std::sort(all_values.begin(), all_values.end());
5886 CHECK(!all_values.empty());
5887 const int64_t min_value = all_values[0];
5888 linear->add_domain(-min_value);
5889 linear->add_domain(-min_value);
5890 for (
const int64_t
value : all_values) {
5891 if (
value == min_value)
continue;
5892 const int enf = value_to_equal_literal.at(
value);
5893 const int64_t coeff =
value - min_value;
5895 linear->add_vars(enf);
5896 linear->add_coeffs(coeff);
5899 linear->set_domain(0, encoding_ct.linear().domain(0) - coeff);
5900 linear->set_domain(1, encoding_ct.linear().domain(1) - coeff);
5902 linear->add_coeffs(-coeff);
5908 "TODO variables: only used in objective and in full encoding");
5912 "variables: only used in objective and in full encoding");
5920 for (
const int c : equality_constraints) {
5926 for (
const int c : other_constraints) {
5933 std::sort(literals.begin(), literals.end());
5934 for (
const int literal : literals) {
5940 PresolveExactlyOne(new_ct);
5964 Domain union_of_domain;
5965 int num_positive = 0;
5966 std::vector<int> constraint_indices_to_remove;
5972 constraint_indices_to_remove.push_back(c);
5974 if (
ct.enforcement_literal().size() != 1 ||
5977 ct.linear().vars().size() != 1) {
5981 if (
ct.enforcement_literal(0) ==
var) ++num_positive;
5982 if (ct_var != -1 &&
PositiveRef(
ct.linear().vars(0)) != ct_var) {
5987 union_of_domain = union_of_domain.UnionWith(
5990 ?
ct.linear().coeffs(0)
5991 : -
ct.linear().coeffs(0)));
5993 if (!abort && num_positive == 1) {
5997 context_->
UpdateRuleStats(
"variables: removable enforcement literal");
5998 for (
const int c : constraint_indices_to_remove) {
6017 if (domain.Size() == 2 && (domain.Min() != 0 || domain.Max() != 1)) {
6022 if (domain.NumIntervals() != domain.Size())
return;
6024 const int64_t var_min = domain.Min();
6025 int64_t gcd = domain[1].start - var_min;
6027 const ClosedInterval& i = domain[
index];
6029 const int64_t shifted_value = i.start - var_min;
6033 if (gcd == 1)
break;
6035 if (gcd == 1)
return;
6039 std::vector<int64_t> scaled_values;
6040 scaled_values.reserve(domain.NumIntervals());
6041 for (
const ClosedInterval i : domain) {
6043 const int64_t shifted_value = i.start - var_min;
6044 scaled_values.push_back(shifted_value / gcd);
6056 void CpModelPresolver::EncodeAllAffineRelations() {
6057 int64_t num_added = 0;
6062 if (r.representative ==
var)
continue;
6069 if (!PresolveAffineRelationIfAny(
var))
break;
6076 auto* arg =
ct->mutable_linear();
6079 arg->add_vars(r.representative);
6080 arg->add_coeffs(-r.coeff);
6081 arg->add_domain(r.offset);
6082 arg->add_domain(r.offset);
6090 if (num_added > 0) {
6091 SOLVER_LOG(logger_, num_added,
" affine relations still in the model.");
6096 bool CpModelPresolver::PresolveAffineRelationIfAny(
int var) {
6100 if (r.representative ==
var)
return true;
6119 auto* arg =
ct->mutable_linear();
6122 arg->add_vars(r.representative);
6123 arg->add_coeffs(-r.coeff);
6124 arg->add_domain(r.offset);
6125 arg->add_domain(r.offset);
6131 void CpModelPresolver::PresolveToFixPoint() {
6135 const int64_t max_num_operations =
6143 absl::flat_hash_set<std::pair<int, int>> var_constraint_pair_already_called;
6150 std::deque<int> queue;
6151 for (
int c = 0; c < in_queue.size(); ++c) {
6153 ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET) {
6164 std::shuffle(queue.begin(), queue.end(), *context_->
random());
6166 std::sort(queue.begin(), queue.end(), [
this](
int a,
int b) {
6169 return score_a < score_b || (score_a == score_b &&
a <
b);
6179 const int c = queue.front();
6180 in_queue[c] =
false;
6183 const int old_num_constraint =
6187 SOLVER_LOG(logger_,
"Unsat after presolving constraint #", c,
6188 " (warning, dump might be inconsistent): ",
6193 const int new_num_constraints =
6195 if (new_num_constraints > old_num_constraint) {
6197 in_queue.resize(new_num_constraints,
true);
6198 for (
int c = old_num_constraint; c < new_num_constraints; ++c) {
6221 for (
int v = 0; v < current_num_variables; ++v) {
6223 if (!PresolveAffineRelationIfAny(v))
return;
6228 TryToSimplifyDomain(v);
6237 for (
int v = 0; v < num_vars; ++v) {
6239 if (constraints.size() != 1)
continue;
6240 const int c = *constraints.begin();
6241 if (c < 0)
continue;
6247 std::pair<int, int>(v, c))) {
6250 var_constraint_pair_already_called.insert({v, c});
6258 for (
int i = 0; i < 2; ++i) {
6269 if (c >= 0 && !in_queue[c]) {
6277 if (!queue.empty() || i == 1)
break;
6287 VarDomination var_dom;
6288 DualBoundStrengthening dual_bound_strengthening;
6290 &dual_bound_strengthening);
6291 if (!dual_bound_strengthening.Strengthen(context_))
return;
6303 std::sort(queue.begin(), queue.end());
6318 for (
int c = 0; c < num_constraints; ++c) {
6320 switch (
ct->constraint_case()) {
6321 case ConstraintProto::ConstraintCase::kNoOverlap:
6323 if (PresolveNoOverlap(
ct)) {
6327 case ConstraintProto::ConstraintCase::kNoOverlap2D:
6329 if (PresolveNoOverlap2D(c,
ct)) {
6333 case ConstraintProto::ConstraintCase::kCumulative:
6335 if (PresolveCumulative(
ct)) {
6339 case ConstraintProto::ConstraintCase::kBoolOr: {
6342 for (
const auto& pair :
6344 bool modified =
false;
6367 const CpModelProto& in_model,
const std::vector<int>& ignored_constraints) {
6368 const absl::flat_hash_set<int> ignored_constraints_set(
6369 ignored_constraints.begin(), ignored_constraints.end());
6374 if (ignored_constraints_set.contains(c))
continue;
6377 if (OneEnforcementLiteralIsFalse(
ct) &&
6381 switch (
ct.constraint_case()) {
6386 if (!CopyBoolOr(
ct))
return CreateUnsatModel();
6390 if (!CopyBoolAnd(
ct))
return CreateUnsatModel();
6394 if (!CopyLinear(
ct))
return CreateUnsatModel();
6398 if (!CopyAtMostOne(
ct))
return CreateUnsatModel();
6402 if (!CopyExactlyOne(
ct))
return CreateUnsatModel();
6406 if (!CopyInterval(
ct, c))
return CreateUnsatModel();
6417 for (
int c = starting_constraint_index_;
6422 const auto& it = interval_mapping_.find(*ref);
6423 if (it != interval_mapping_.end()) {
6435 temp_enforcement_literals_.clear();
6438 skipped_non_zero_++;
6441 temp_enforcement_literals_.push_back(lit);
6444 temp_enforcement_literals_.end());
6447 bool ModelCopy::OneEnforcementLiteralIsFalse(
const ConstraintProto&
ct)
const {
6448 for (
const int lit :
ct.enforcement_literal()) {
6456 bool ModelCopy::CopyBoolOr(
const ConstraintProto&
ct) {
6457 temp_literals_.clear();
6458 for (
const int lit :
ct.enforcement_literal()) {
6462 for (
const int lit :
ct.bool_or().literals()) {
6467 skipped_non_zero_++;
6469 temp_literals_.push_back(lit);
6476 ->Add(temp_literals_.begin(), temp_literals_.end());
6477 return !temp_literals_.empty();
6480 bool ModelCopy::CopyBoolAnd(
const ConstraintProto&
ct) {
6481 bool at_least_one_false =
false;
6482 int num_non_fixed_literals = 0;
6483 for (
const int lit :
ct.bool_and().literals()) {
6485 at_least_one_false =
true;
6489 num_non_fixed_literals++;
6493 if (at_least_one_false) {
6498 for (
const int lit :
ct.enforcement_literal()) {
6500 skipped_non_zero_++;
6505 return !bool_or->literals().empty();
6506 }
else if (num_non_fixed_literals > 0) {
6508 CopyEnforcementLiterals(
ct, new_ct);
6509 BoolArgumentProto* bool_and = new_ct->mutable_bool_and();
6510 bool_and->mutable_literals()->Reserve(num_non_fixed_literals);
6511 for (
const int lit :
ct.bool_and().literals()) {
6513 skipped_non_zero_++;
6516 bool_and->add_literals(lit);
6522 bool ModelCopy::CopyLinear(
const ConstraintProto&
ct) {
6523 non_fixed_variables_.clear();
6524 non_fixed_coefficients_.clear();
6526 for (
int i = 0; i <
ct.linear().vars_size(); ++i) {
6527 const int ref =
ct.linear().vars(i);
6528 const int64_t coeff =
ct.linear().coeffs(i);
6530 offset += coeff * context_->
MinOf(ref);
6531 skipped_non_zero_++;
6533 non_fixed_variables_.push_back(ref);
6534 non_fixed_coefficients_.push_back(coeff);
6538 const Domain new_domain =
6540 if (non_fixed_variables_.empty() && !new_domain.Contains(0)) {
6541 if (
ct.enforcement_literal().empty()) {
6544 temp_literals_.clear();
6545 for (
const int literal :
ct.enforcement_literal()) {
6547 skipped_non_zero_++;
6555 ->Add(temp_literals_.begin(), temp_literals_.end());
6556 return !temp_literals_.empty();
6560 CopyEnforcementLiterals(
ct, new_ct);
6561 LinearConstraintProto* linear = new_ct->mutable_linear();
6562 linear->mutable_vars()->Add(non_fixed_variables_.begin(),
6563 non_fixed_variables_.end());
6564 linear->mutable_coeffs()->Add(non_fixed_coefficients_.begin(),
6565 non_fixed_coefficients_.end());
6570 bool ModelCopy::CopyAtMostOne(
const ConstraintProto&
ct) {
6572 temp_literals_.clear();
6573 for (
const int lit :
ct.at_most_one().literals()) {
6575 skipped_non_zero_++;
6578 temp_literals_.push_back(lit);
6582 if (temp_literals_.size() <= 1)
return true;
6583 if (num_true > 1)
return false;
6587 CopyEnforcementLiterals(
ct, new_ct);
6588 new_ct->mutable_at_most_one()->mutable_literals()->Add(temp_literals_.begin(),
6589 temp_literals_.end());
6593 bool ModelCopy::CopyExactlyOne(
const ConstraintProto&
ct) {
6595 temp_literals_.clear();
6596 for (
const int lit :
ct.exactly_one().literals()) {
6598 skipped_non_zero_++;
6601 temp_literals_.push_back(lit);
6605 if (temp_literals_.empty() || num_true > 1)
return false;
6609 CopyEnforcementLiterals(
ct, new_ct);
6610 new_ct->mutable_exactly_one()->mutable_literals()->Add(temp_literals_.begin(),
6611 temp_literals_.end());
6615 bool ModelCopy::CopyInterval(
const ConstraintProto&
ct,
int c) {
6617 CHECK_EQ(starting_constraint_index_, 0)
6618 <<
"Adding new interval constraints to partially filled model is not " 6625 bool ModelCopy::CreateUnsatModel() {
6639 return context->NotifyThatModelIsUnsat();
6644 if (!in_model.
name().empty()) {
6645 context->working_model->set_name(in_model.
name());
6651 *
context->working_model->mutable_search_strategy() =
6670 std::vector<int>* postsolve_mapping) {
6676 std::vector<int>* postsolve_mapping)
6677 : postsolve_mapping_(postsolve_mapping),
6689 for (
const auto& decision_strategy :
6736 PresolveEnforcementLiteral(
ct);
6737 switch (
ct->constraint_case()) {
6738 case ConstraintProto::ConstraintCase::kBoolOr:
6741 case ConstraintProto::ConstraintCase::kBoolAnd:
6742 PresolveBoolAnd(
ct);
6744 case ConstraintProto::ConstraintCase::kAtMostOne:
6745 PresolveAtMostOne(
ct);
6747 case ConstraintProto::ConstraintCase::kExactlyOne:
6748 PresolveExactlyOne(
ct);
6750 case ConstraintProto::ConstraintCase::kLinear:
6751 CanonicalizeLinear(
ct);
6768 int old_num_non_empty_constraints = 0;
6772 if (type == ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET)
continue;
6773 old_num_non_empty_constraints++;
6780 PresolveToFixPoint();
6790 PresolveToFixPoint();
6799 PresolvePureSatPart();
6813 for (
int c = 0; c < old_size; ++c) {
6815 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear) {
6818 ExtractAtMostOneFromLinear(
ct);
6823 if (iter == 0) TransformIntoMaxCliques();
6840 PresolveToFixPoint();
6846 old_num_non_empty_constraints)) {
6853 MergeNoOverlapConstraints();
6863 EncodeAllAffineRelations();
6870 const std::vector<std::pair<int, int>> duplicates =
6872 for (
const auto [dup, rep] : duplicates) {
6875 if (type == ConstraintProto::ConstraintCase::kInterval) {
6887 context_->
UpdateRuleStats(
"duplicate: merged rhs of linear constraint");
6916 absl::flat_hash_set<int> used_variables;
6921 strategy.clear_transformations();
6922 for (
const int ref : copy.
variables()) {
6931 used_variables.insert(
var);
6939 if (strategy.variable_selection_strategy() !=
6942 strategy.add_transformations();
6943 t->
set_index(strategy.variables_size());
6947 strategy.add_variables(rep);
6954 strategy.add_variables(ref);
6971 postsolve_mapping_->clear();
6973 int num_free_variables = 0;
6983 ++num_free_variables;
6989 mapping[i] = postsolve_mapping_->size();
6990 postsolve_mapping_->push_back(i);
6992 context_->
UpdateRuleStats(absl::StrCat(
"presolve: ", num_free_variables,
6993 " free variables removed."));
6996 std::shuffle(postsolve_mapping_->begin(), postsolve_mapping_->end(),
6998 for (
int i = 0; i < postsolve_mapping_->size(); ++i) {
6999 mapping[(*postsolve_mapping_)[i]] = i;
7024 if (!error.empty()) {
7025 SOLVER_LOG(logger_,
"Error while validating postsolved model: ", error);
7031 if (!error.empty()) {
7033 "Error while validating mapping_model model: ", error);
7046 auto mapping_function = [&mapping](
int* ref) {
7059 mapping_function(&mutable_ref);
7065 mapping_function(&mutable_ref);
7073 std::vector<int> new_indices(copy.
variables().size(), -1);
7074 for (
int i = 0; i < copy.
variables().size(); ++i) {
7078 new_indices[i] = strategy.variables_size();
7082 strategy.clear_transformations();
7084 CHECK_LT(transform.index(), new_indices.size());
7085 const int new_index = new_indices[transform.index()];
7086 if (new_index == -1)
continue;
7087 auto* new_transform = strategy.add_transformations();
7088 *new_transform = transform;
7089 CHECK_LT(new_index, strategy.variables().size());
7090 new_transform->set_index(new_index);
7097 absl::flat_hash_set<int> used_vars;
7100 for (
int i = 0; i < mutable_hint->vars_size(); ++i) {
7101 const int old_ref = mutable_hint->
vars(i);
7102 const int64_t old_value = mutable_hint->values(i);
7110 const int image = mapping[
var];
7112 if (!used_vars.insert(image).second)
continue;
7113 mutable_hint->set_vars(new_size, image);
7114 mutable_hint->set_values(new_size,
value);
7119 mutable_hint->mutable_vars()->Truncate(new_size);
7120 mutable_hint->mutable_values()->Truncate(new_size);
7127 std::vector<IntegerVariableProto> new_variables;
7128 for (
int i = 0; i < mapping.size(); ++i) {
7129 const int image = mapping[i];
7130 if (image < 0)
continue;
7131 if (image >= new_variables.size()) {
7148 ConstraintProto CopyConstraintForDuplicateDetection(
const ConstraintProto&
ct) {
7149 ConstraintProto copy =
ct;
7152 copy.mutable_linear()->clear_domain();
7160 std::vector<std::pair<int, int>> result;
7164 absl::flat_hash_map<int64_t, int> equiv_constraints;
7168 for (
int c = 0; c < num_constraints; ++c) {
7178 s = copy.SerializeAsString();
7180 const int64_t
hash = absl::Hash<std::string>()(s);
7181 const auto [it, inserted] = equiv_constraints.insert({
hash, c});
7184 const int other_c_with_same_hash = it->second;
7185 copy = CopyConstraintForDuplicateDetection(
7187 if (s == copy.SerializeAsString()) {
7188 result.push_back({c, other_c_with_same_hash});
int64_t SmallestValue() const
Returns the value closest to zero.
bool PresolveCpModel(PresolveContext *context, std::vector< int > *postsolve_mapping)
::operations_research::sat::DecisionStrategyProto * mutable_search_strategy(int index)
::operations_research::sat::BoolArgumentProto * mutable_exactly_one()
void set_presolve_blocked_clause(bool value)
int64_t MinOf(int ref) const
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int32 > * mutable_vars()
std::vector< int > tmp_literals
void InitializeNewDomains()
void add_vars(::PROTOBUF_NAMESPACE_ID::int32 value)
bool VariableIsUniqueAndRemovable(int ref) const
ConstraintCase constraint_case() const
::operations_research::sat::AllDifferentConstraintProto * mutable_all_diff()
void Set(IntegerType index)
bool ModelIsUnsat() const
void ReadObjectiveFromProto()
#define SOLVER_LOG(logger,...)
void add_enforcement_literal(::PROTOBUF_NAMESPACE_ID::int32 value)
ModelRandomGenerator * random()
#define CHECK_GE(val1, val2)
::operations_research::sat::ElementConstraintProto * mutable_element()
const ::operations_research::sat::BoolArgumentProto & at_most_one() const
Domain InverseMultiplicationBy(const int64_t coeff) const
Returns {x ∈ Int64, ∃ e ∈ D, x * coeff = e}.
void RemoveVariableFromAffineRelation(int var)
bool ConstraintVariableGraphIsUpToDate() const
bool ConstraintVariableUsageIsConsistent()
SparseBitset< int64_t > modified_domains
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int32 > * mutable_assumptions()
void UpdateRuleStats(const std::string &name, int num_times=1)
void GetOverlappingIntervalComponents(std::vector< IndexedInterval > *intervals, std::vector< std::vector< int >> *components)
void RemoveEmptyConstraints()
void StoreBooleanEqualityRelation(int ref_a, int ref_b)
std::vector< std::pair< int, int > > FindDuplicateConstraints(const CpModelProto &model_proto)
bool StoreLiteralImpliesVarNEqValue(int literal, int var, int64_t value)
ModelSharedTimeLimit * time_limit
const absl::flat_hash_set< int > & VarToConstraints(int var) const
bool ConstraintIsOptional(int ct_ref) const
int GetOrCreateConstantVar(int64_t cst)
void CanonicalizeDomainOfSizeTwo(int var)
void ApplyToAllIntervalIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
int64_t StartMin(int ct_ref) const
::PROTOBUF_NAMESPACE_ID::int32 max_presolve_iterations() const
#define CHECK_GT(val1, val2)
bool permute_variable_randomly() const
void add_intervals(::PROTOBUF_NAMESPACE_ID::int32 value)
#define VLOG(verboselevel)
bool ConstraintIsInactive(int ct_index) const
bool GetAbsRelation(int target_ref, int *ref)
const Domain & ObjectiveDomain() const
bool StoreAffineRelation(int ref_x, int ref_y, int64_t coeff, int64_t offset)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
void ApplyToAllVariableIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
::operations_research::sat::NoOverlapConstraintProto * mutable_no_overlap()
constexpr int kAffineRelationConstraint
DomainIteratorBeginEnd Values() const &
bool VariableIsNotUsedAnymore(int ref) const
::PROTOBUF_NAMESPACE_ID::int64 coeffs(int index) const
bool keep_all_feasible_solutions
::PROTOBUF_NAMESPACE_ID::int32 cp_model_max_num_presolve_operations() const
bool StoreAbsRelation(int target_ref, int ref)
int LiteralForExpressionMax(const LinearExpressionProto &expr) const
void add_literals(::PROTOBUF_NAMESPACE_ID::int32 value)
int64_t CapProd(int64_t x, int64_t y)
::operations_research::sat::CumulativeConstraintProto * mutable_cumulative()
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
::operations_research::sat::SatParameters_SearchBranching search_branching() const
::operations_research::sat::NoOverlap2DConstraintProto * mutable_no_overlap_2d()
#define DCHECK_GT(val1, val2)
const std::vector< int > & ConstraintToVars(int c) const
bool ExpressionIsAffineBoolean(const LinearExpressionProto &expr) const
CpModelProto * mapping_model
int variables_size() const
bool cp_model_use_sat_presolve() const
void ExtractEncoding(const CpModelProto &model_proto, Model *m)
bool LoadConstraint(const ConstraintProto &ct, Model *m)
bool permute_presolve_constraint_order() const
::operations_research::sat::IntegerVariableProto * add_variables()
void set_use_implied_bounds(bool value)
bool VariableIsOnlyUsedInEncodingAndMaybeInObjective(int ref) const
void add_vars(::PROTOBUF_NAMESPACE_ID::int32 value)
ModelCopy(PresolveContext *context)
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int32 > * mutable_literals()
int64_t num_presolve_operations
void UpdateConstraintVariableUsage(int c)
static constexpr VariableSelectionStrategy CHOOSE_FIRST
CpModelProto * working_model
bool ObjectiveDomainIsConstraining() const
void ExploitFixedDomain(int var)
bool CanBeUsedAsLiteral(int ref) const
Domain UnionWith(const Domain &domain) const
Returns the union of D and domain.
#define CHECK_LT(val1, val2)
int IntervalUsage(int c) const
PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final
const ::operations_research::sat::PartialVariableAssignment & solution_hint() const
ABSL_MUST_USE_RESULT bool SetLiteralToFalse(int lit)
bool has_symmetry() const
std::string ValidateCpModel(const CpModelProto &model)
const ::operations_research::sat::BoolArgumentProto & exactly_one() const
Domain DomainOf(int ref) const
void CopyFrom(const IntegerVariableProto &from)
int NewIntVar(const Domain &domain)
const ::operations_research::sat::SymmetryProto & symmetry() const
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int32 > * mutable_enforcement_literal()
::PROTOBUF_NAMESPACE_ID::int32 enforcement_literal(int index) const
int ReindexArcs(IntContainer *tails, IntContainer *heads)
::operations_research::sat::BoolArgumentProto * mutable_bool_or()
const ::operations_research::sat::LinearConstraintProto & linear() const
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 > * mutable_coeffs()
const SatParameters & params() const
const ::operations_research::sat::DecisionStrategyProto_AffineTransformation & transformations(int index) const
bool VariableIsRemovable(int ref) const
bool fill_tightened_domains_in_response() const
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
int64_t EndMax(int ct_ref) const
int64_t Min() const
Returns the min value of the domain.
::PROTOBUF_NAMESPACE_ID::int32 assumptions(int index) const
bool ImportConstraintsWithBasicPresolveIntoContext(const CpModelProto &in_model, PresolveContext *context)
::operations_research::sat::BoolArgumentProto * mutable_bool_and()
ClosedInterval front() const
::PROTOBUF_NAMESPACE_ID::int32 vars(int index) const
void CopyEverythingExceptVariablesAndConstraintsFieldsIntoContext(const CpModelProto &in_model, PresolveContext *context)
#define DCHECK_NE(val1, val2)
int64_t SizeMax(int ct_ref) const
bool VariableWasRemoved(int ref) const
int64_t MaxOf(int ref) const
void RegisterVariablesUsedInAssumptions()
void WriteObjectiveToProto() const
static Domain FromValues(std::vector< int64_t > values)
Creates a domain from the union of an unsorted list of integer values.
CpModelPresolver(PresolveContext *context, std::vector< int > *postsolve_mapping)
bool PropagateAffineRelation(int ref)
#define CHECK_LE(val1, val2)
::PROTOBUF_NAMESPACE_ID::int32 variables(int index) const
::operations_research::sat::LinearConstraintProto * mutable_linear()
void SubstituteVariable(int var, int64_t var_coeff_in_definition, const ConstraintProto &definition, ConstraintProto *ct)
void ExpandCpModel(PresolveContext *context)
const ::operations_research::sat::ConstraintProto & constraints(int index) const
static int64_t GCD64(int64_t x, int64_t y)
Domain PositiveModuloBySuperset(const Domain &modulo) const
Returns a superset of {x ∈ Int64, ∃ e ∈ D, ∃ m ∈ modulo, x = e % m }.
::operations_research::sat::ConstraintProto * add_constraints()
bool ContainsKey(const Collection &collection, const Key &key)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
const ::operations_research::sat::CpObjectiveProto & objective() const
void add_domain(::PROTOBUF_NAMESPACE_ID::int64 value)
void set_coeffs(int index, ::PROTOBUF_NAMESPACE_ID::int64 value)
void MarkVariableAsRemoved(int ref)
std::vector< absl::flat_hash_set< int > > var_to_ub_only_constraints
void ConstructOverlappingSets(bool already_sorted, std::vector< IndexedInterval > *intervals, std::vector< std::vector< int >> *result)
bool DomainIsEmpty(int ref) const
int64_t Size() const
Returns the number of elements in the domain.
Domain RelaxIfTooComplex() const
If NumIntervals() is too large, this return a superset of the domain.
::operations_research::sat::IntervalConstraintProto * mutable_interval()
ABSL_MUST_USE_RESULT bool IntersectDomainWith(int ref, const Domain &domain, bool *domain_modified=nullptr)
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
const std::string & name() const
#define DCHECK_GE(val1, val2)
Domain MultiplicationBy(int64_t coeff, bool *exact=nullptr) const
Returns {x ∈ Int64, ∃ e ∈ D, x = e * coeff}.
bool DetectAndExploitSymmetriesInPresolve(PresolveContext *context)
void set_vars(int index, ::PROTOBUF_NAMESPACE_ID::int32 value)
int constraints_size() const
PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final
absl::flat_hash_set< int > tmp_literal_set
bool VariableWithCostIsUniqueAndRemovable(int ref) const
ABSL_MUST_USE_RESULT bool NotifyThatModelIsUnsat(const std::string &message="")
#define CHECK_EQ(val1, val2)
std::vector< absl::Span< int > > GetOverlappingRectangleComponents(const std::vector< Rectangle > &rectangles, absl::Span< int > active_rectangles)
bool SubstituteVariableInObjective(int var_in_equality, int64_t coeff_in_equality, const ConstraintProto &equality, std::vector< int > *new_vars_in_objective=nullptr)
Domain AdditionWith(const Domain &domain) const
Returns {x ∈ Int64, ∃ a ∈ D, ∃ b ∈ domain, x = a + b}.
bool has_solution_hint() const
::operations_research::sat::LinearExpressionProto * mutable_start_view()
bool has_objective() const
::operations_research::sat::DecisionStrategyProto * add_search_strategy()
bool IsIncludedIn(const Domain &domain) const
Returns true iff D is included in the given domain.
::PROTOBUF_NAMESPACE_ID::int32 cp_model_probing_level() const
::operations_research::sat::ConstraintProto * mutable_constraints(int index)
bool ModelIsExpanded() const
CpModelProto const * model_proto
SolverLogger * logger() const
bool DomainContains(int ref, int64_t value) const
Domain DivisionBy(int64_t coeff) const
Returns {x ∈ Int64, ∃ e ∈ D, x = e / coeff}.
bool ImportAndSimplifyConstraints(const CpModelProto &in_model, const std::vector< int > &ignored_constraints)
bool enumerate_all_solutions() const
void add_x_intervals(::PROTOBUF_NAMESPACE_ID::int32 value)
#define DCHECK(condition)
void set_name(ArgT0 &&arg0, ArgT... args)
We call domain any subset of Int64 = [kint64min, kint64max].
bool LiteralIsTrue(int lit) const
void AddImplication(int a, int b)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
void Swap(IntegerVariableProto *other)
void UpdateNewConstraintsVariableUsage()
bool convert_intervals() const
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
double merge_at_most_one_work_limit() const
bool LoggingIsEnabled() const
#define DCHECK_EQ(val1, val2)
Domain DomainSuperSetOf(const LinearExpressionProto &expr) const
DomainDeductions deductions
bool HasEnforcementLiteral(const ConstraintProto &ct)
std::vector< absl::flat_hash_set< int > > var_to_lb_only_constraints
void AdvanceDeterministicTime(double deterministic_duration)
Advances the deterministic time.
void AddDeduction(int literal_ref, int var, Domain domain)
bool DomainOfVarIsIncludedIn(int var, const Domain &domain)
const ::operations_research::sat::DecisionStrategyProto & search_strategy(int index) const
std::vector< Domain > tmp_term_domains
void set_offset(::PROTOBUF_NAMESPACE_ID::int64 value)
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
void clear_solution_hint()
int GetVariableRepresentative(int ref) const
bool StoreLiteralImpliesVarEqValue(int literal, int var, int64_t value)
static constexpr SearchBranching FIXED_SEARCH
Collection of objects used to extend the Constraint Solver library.
bool PresolveOneConstraint(int c)
bool IsFixed(int ref) const
::operations_research::sat::CpObjectiveProto * mutable_objective()
int64_t SizeMin(int ct_ref) const
bool ExploitExactlyOneInObjective(absl::Span< const int > exactly_one)
bool IntervalIsConstant(int ct_ref) const
void DetectDominanceRelations(const PresolveContext &context, VarDomination *var_domination, DualBoundStrengthening *dual_bound_strengthening)
::operations_research::sat::PartialVariableAssignment * mutable_solution_hint()
const absl::flat_hash_map< int, int64_t > & ObjectiveMap() const
void RemoveAllVariablesFromAffineRelationConstraint()
std::vector< Domain > tmp_left_domains
bool ExploitDominanceRelations(const VarDomination &var_domination, PresolveContext *context)
bool RefIsPositive(int ref)
constexpr int kObjectiveConstraint
const LiteralIndex kNoLiteralIndex(-1)
void ApplyVariableMapping(const std::vector< int > &mapping, const PresolveContext &context)
void Swap(ConstraintProto *other)
double merge_no_overlap_work_limit() const
bool LiteralIsFalse(int lit) const
ABSL_MUST_USE_RESULT bool SetLiteralToTrue(int lit)
int GetOrCreateVarValueEncoding(int ref, int64_t value)
::PROTOBUF_NAMESPACE_ID::int32 vars(int index) const
GurobiMPCallbackContext * context
::PROTOBUF_NAMESPACE_ID::int32 literals(int index) const
::PROTOBUF_NAMESPACE_ID::int32 symmetry_level() const
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
::operations_research::sat::IntegerVariableProto * mutable_variables(int index)
void STLSetDifference(const In1 &a, const In2 &b, Out *out, Compare compare)
int NumDeductions() const
void ApplyToAllLiteralIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
AffineRelation::Relation GetAffineRelation(int ref) const
bool cp_model_presolve() const
#define CHECK_NE(val1, val2)
std::vector< std::pair< int, Domain > > ProcessClause(absl::Span< const int > clause)
int GetLiteralRepresentative(int ref) const
void add_intervals(::PROTOBUF_NAMESPACE_ID::int32 value)
#define DCHECK_LT(val1, val2)
bool presolve_extract_integer_enforcement() const
void MarkProcessingAsDoneForNow()
ABSL_MUST_USE_RESULT bool CanonicalizeObjective()
void LoadVariables(const CpModelProto &model_proto, bool view_all_booleans_as_integers, Model *m)
void SetToNegatedLinearExpression(const LinearExpressionProto &input_expr, LinearExpressionProto *output_negated_expr)
bool keep_all_feasible_solutions_in_presolve() const
::PROTOBUF_NAMESPACE_ID::int32 presolve_substitution_level() const
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int32 > * mutable_vars()