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"
62bool 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;
142bool 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 if (num_true_literals % 2 == 0) {
179 return RemoveConstraint(
ct);
181 }
else if (new_size == 1) {
182 if (num_true_literals % 2 == 0) {
185 "bool_xor: cannot fix last literal");
190 "bool_xor: cannot fix last literal");
194 return RemoveConstraint(
ct);
195 }
else if (new_size == 2) {
196 const int a =
ct->bool_xor().literals(0);
197 const int b =
ct->bool_xor().literals(1);
198 if (num_true_literals % 2 == 0) {
205 return RemoveConstraint(
ct);
208 if (num_true_literals % 2 == 1) {
210 ct->mutable_bool_xor()->set_literals(new_size++, true_literal);
212 if (num_true_literals > 1) {
213 context_->
UpdateRuleStats(
"bool_xor: remove even number of true literals");
216 ct->mutable_bool_xor()->mutable_literals()->Truncate(new_size);
220bool CpModelPresolver::PresolveBoolOr(ConstraintProto*
ct) {
227 for (
const int literal :
ct->enforcement_literal()) {
230 ct->clear_enforcement_literal();
234 bool changed =
false;
237 for (
const int literal :
ct->bool_or().literals()) {
244 return RemoveConstraint(
ct);
252 return RemoveConstraint(
ct);
256 return RemoveConstraint(
ct);
275 return RemoveConstraint(
ct);
288 ct->mutable_bool_or()->mutable_literals()->Clear();
290 ct->mutable_bool_or()->add_literals(lit);
299ABSL_MUST_USE_RESULT
bool CpModelPresolver::MarkConstraintAsFalse(
300 ConstraintProto*
ct) {
303 ct->mutable_bool_or()->clear_literals();
304 for (
const int lit :
ct->enforcement_literal()) {
307 ct->clear_enforcement_literal();
315bool CpModelPresolver::PresolveBoolAnd(ConstraintProto*
ct) {
320 for (
const int literal :
ct->bool_and().literals()) {
323 return RemoveConstraint(
ct);
326 bool changed =
false;
328 for (
const int literal :
ct->bool_and().literals()) {
331 return MarkConstraintAsFalse(
ct);
351 ct->mutable_bool_and()->mutable_literals()->Clear();
353 ct->mutable_bool_and()->add_literals(lit);
362 if (
ct->enforcement_literal().size() == 1 &&
363 ct->bool_and().literals().size() == 1) {
364 const int enforcement =
ct->enforcement_literal(0);
374 ct->bool_and().literals(0));
382bool CpModelPresolver::PresolveAtMostOrExactlyOne(ConstraintProto*
ct) {
384 const std::string
name = is_at_most_one ?
"at_most_one: " :
"exactly_one: ";
385 auto* literals = is_at_most_one
386 ?
ct->mutable_at_most_one()->mutable_literals()
387 :
ct->mutable_exactly_one()->mutable_literals();
391 for (
const int literal : *literals) {
397 int num_positive = 0;
398 int num_negative = 0;
399 for (
const int other : *literals) {
420 return RemoveConstraint(
ct);
426 bool changed =
false;
427 bool transform_to_at_most_one =
false;
429 for (
const int literal : *literals) {
432 for (
const int other : *literals) {
437 return RemoveConstraint(
ct);
450 if (is_at_most_one && !is_removable &&
454 const int64_t coeff = it->second;
461 if (is_at_most_one) {
468 is_at_most_one =
true;
469 transform_to_at_most_one =
true;
480 if (!is_at_most_one && !transform_to_at_most_one &&
485 if (transform_to_at_most_one) {
488 literals =
ct->mutable_at_most_one()->mutable_literals();
500bool CpModelPresolver::PresolveAtMostOne(ConstraintProto*
ct) {
503 const bool changed = PresolveAtMostOrExactlyOne(
ct);
507 const auto& literals =
ct->at_most_one().literals();
508 if (literals.empty()) {
510 return RemoveConstraint(
ct);
514 if (literals.size() == 1) {
516 return RemoveConstraint(
ct);
522bool CpModelPresolver::PresolveExactlyOne(ConstraintProto*
ct) {
525 const bool changed = PresolveAtMostOrExactlyOne(
ct);
529 const auto& literals =
ct->exactly_one().literals();
530 if (literals.empty()) {
535 if (literals.size() == 1) {
538 return RemoveConstraint(
ct);
542 if (literals.size() == 2) {
546 return RemoveConstraint(
ct);
552bool CpModelPresolver::PresolveIntMax(ConstraintProto*
ct) {
554 if (
ct->int_max().vars().empty()) {
556 return MarkConstraintAsFalse(
ct);
558 const int target_ref =
ct->int_max().target();
563 bool contains_target_ref =
false;
564 bool contains_negated_target_ref =
false;
565 std::set<int> used_ref;
567 for (
const int ref :
ct->int_max().vars()) {
568 if (ref == target_ref) contains_target_ref =
true;
572 infered_min =
std::max(infered_min, int64_t{0});
578 contains_negated_target_ref =
true;
581 target_ref, {0, std::numeric_limits<int64_t>::max()})) {
585 used_ref.insert(ref);
586 ct->mutable_int_max()->set_vars(new_size++, ref);
590 if (new_size < ct->int_max().vars_size()) {
593 ct->mutable_int_max()->mutable_vars()->Truncate(new_size);
595 if (contains_target_ref) {
597 for (
const int ref :
ct->int_max().vars()) {
598 if (ref == target_ref)
continue;
601 auto* arg = new_ct->mutable_linear();
602 arg->add_vars(target_ref);
609 return RemoveConstraint(
ct);
613 Domain infered_domain;
614 for (
const int ref :
ct->int_max().vars()) {
615 infered_domain = infered_domain.UnionWith(
620 bool domain_reduced =
false;
634 !contains_negated_target_ref) {
635 const Domain& target_domain = context_->
DomainOf(target_ref);
638 target_domain.Max()))
639 .IsIncludedIn(target_domain)) {
640 if (infered_domain.Max() <= target_domain.Max()) {
643 }
else if (
ct->enforcement_literal().empty()) {
645 for (
const int ref :
ct->int_max().vars()) {
649 target_domain.Max()))) {
657 for (
const int ref :
ct->int_max().vars()) {
659 *(new_ct->mutable_enforcement_literal()) =
ct->enforcement_literal();
660 ct->mutable_linear()->add_vars(ref);
661 ct->mutable_linear()->add_coeffs(1);
663 ct->mutable_linear()->add_domain(target_domain.Max());
670 return RemoveConstraint(
ct);
676 const int size =
ct->int_max().vars_size();
677 const int64_t target_max = context_->
MaxOf(target_ref);
678 for (
const int ref :
ct->int_max().vars()) {
686 if (context_->
MaxOf(ref) >= infered_min) {
687 ct->mutable_int_max()->set_vars(new_size++, ref);
690 if (domain_reduced) {
694 bool modified =
false;
695 if (new_size < size) {
697 ct->mutable_int_max()->mutable_vars()->Truncate(new_size);
703 return MarkConstraintAsFalse(
ct);
711 auto* arg = new_ct->mutable_linear();
712 arg->add_vars(target_ref);
714 arg->add_vars(
ct->int_max().vars(0));
719 return RemoveConstraint(
ct);
725 const bool convert_result = ConvertIntMax(
ct);
726 return modified || convert_result;
733bool CpModelPresolver::PresolveLinMin(ConstraintProto*
ct) {
735 const auto copy =
ct->lin_min();
737 ct->mutable_lin_max()->mutable_target());
738 for (
const LinearExpressionProto& expr : copy.exprs()) {
739 LinearExpressionProto*
const new_expr =
ct->mutable_lin_max()->add_exprs();
742 return PresolveLinMax(
ct);
750bool CpModelPresolver::ConvertIntMax(ConstraintProto*
ct) {
752 const auto copy =
ct->int_max();
753 ct->mutable_lin_max()->mutable_target()->add_vars(copy.target());
754 ct->mutable_lin_max()->mutable_target()->add_coeffs(1);
755 for (
const int ref : copy.vars()) {
756 LinearExpressionProto* expr =
ct->mutable_lin_max()->add_exprs();
761 return PresolveLinMax(
ct);
765bool CpModelPresolver::PresolveLinMax(ConstraintProto*
ct) {
772 bool changed = CanonicalizeLinearExpression(
773 *
ct,
ct->mutable_lin_max()->mutable_target());
774 for (LinearExpressionProto& exp : *(
ct->mutable_lin_max()->mutable_exprs())) {
775 changed |= CanonicalizeLinearExpression(*
ct, &exp);
780 const LinearExpressionProto& target =
ct->lin_max().target();
782 int64_t infered_min = context_->
MinOf(target);
784 for (
const LinearExpressionProto& expr :
ct->lin_max().exprs()) {
789 if (target.vars().empty()) {
790 if (!Domain(infered_min, infered_max).Contains(target.offset())) {
792 return MarkConstraintAsFalse(
ct);
797 for (
const LinearExpressionProto& expr :
ct->lin_max().exprs()) {
798 rhs_domain = rhs_domain.UnionWith(
800 {infered_min, infered_max}));
802 bool reduced =
false;
813 const int64_t target_min = context_->
MinOf(target);
814 const int64_t target_max = context_->
MaxOf(target);
817 for (
int i = 0; i <
ct->lin_max().exprs_size(); ++i) {
818 const LinearExpressionProto& expr =
ct->lin_max().exprs(i);
819 if (context_->
MaxOf(expr) < target_min)
continue;
820 *
ct->mutable_lin_max()->mutable_exprs(new_size) = expr;
823 if (new_size < ct->lin_max().exprs_size()) {
825 ct->mutable_lin_max()->mutable_exprs()->DeleteSubrange(
826 new_size,
ct->lin_max().exprs_size() - new_size);
831 if (
ct->lin_max().exprs().empty()) {
833 return MarkConstraintAsFalse(
ct);
836 if (
ct->lin_max().exprs().size() == 1) {
842 auto* arg = new_ct->mutable_linear();
843 const LinearExpressionProto&
a =
ct->lin_max().target();
844 const LinearExpressionProto&
b =
ct->lin_max().exprs(0);
845 for (
int i = 0; i <
a.vars().size(); ++i) {
846 arg->add_vars(
a.vars(i));
847 arg->add_coeffs(
a.coeffs(i));
849 for (
int i = 0; i <
b.vars().size(); ++i) {
850 arg->add_vars(
b.vars(i));
851 arg->add_coeffs(-
b.coeffs(i));
853 arg->add_domain(
b.offset() -
a.offset());
854 arg->add_domain(
b.offset() -
a.offset());
856 return RemoveConstraint(
ct);
864 for (
const LinearExpressionProto& expr :
ct->lin_max().exprs()) {
865 const int64_t value_min = context_->
MinOf(expr);
866 bool modified =
false;
874 const int64_t value_max = context_->
MaxOf(expr);
875 if (value_max > target_max) {
876 context_->
UpdateRuleStats(
"TODO lin_max: linear expression above max.");
880 if (abort)
return changed;
884 if (target_min == target_max) {
885 bool all_booleans =
true;
886 std::vector<int> literals;
887 const int64_t fixed_target = target_min;
888 for (
const LinearExpressionProto& expr :
ct->lin_max().exprs()) {
889 const int64_t value_min = context_->
MinOf(expr);
890 const int64_t value_max = context_->
MaxOf(expr);
891 CHECK_LE(value_max, fixed_target) <<
"Presolved above";
892 if (value_max < fixed_target)
continue;
894 if (value_min == value_max && value_max == fixed_target) {
896 return RemoveConstraint(
ct);
902 all_booleans =
false;
906 if (literals.empty()) {
907 return MarkConstraintAsFalse(
ct);
912 for (
const int lit : literals) {
913 ct->mutable_bool_or()->add_literals(lit);
926 bool min_is_reachable =
false;
927 std::vector<int> min_literals;
928 std::vector<int> literals_above_min;
929 std::vector<int> max_literals;
931 for (
const LinearExpressionProto& expr :
ct->lin_max().exprs()) {
932 const int64_t value_min = context_->
MinOf(expr);
933 const int64_t value_max = context_->
MaxOf(expr);
936 if (value_min > target_min) {
944 if (value_min == value_max) {
945 if (value_min == target_min) min_is_reachable =
true;
956 if (value_min == target_min) {
961 if (value_max == target_max) {
962 max_literals.push_back(ref);
963 literals_above_min.push_back(ref);
964 }
else if (value_max > target_min) {
965 literals_above_min.push_back(ref);
966 }
else if (value_max == target_min) {
967 min_literals.push_back(ref);
976 clause->mutable_bool_or();
977 for (
const int lit : max_literals) {
978 clause->mutable_bool_or()->add_literals(lit);
982 for (
const int lit : literals_above_min) {
986 if (!min_is_reachable) {
990 clause->mutable_bool_or();
991 for (
const int lit : min_literals) {
992 clause->mutable_bool_or()->add_literals(lit);
997 return RemoveConstraint(
ct);
1004bool CpModelPresolver::PresolveIntAbs(ConstraintProto*
ct) {
1007 const int target_ref =
ct->int_max().target();
1011 const Domain var_domain = context_->
DomainOf(
var);
1012 const Domain new_target_domain =
1013 var_domain.
UnionWith(var_domain.Negation())
1023 const Domain target_domain = context_->
DomainOf(target_ref);
1024 const Domain new_var_domain =
1025 target_domain.
UnionWith(target_domain.Negation());
1037 auto* arg = new_ct->mutable_linear();
1038 arg->add_vars(target_ref);
1041 arg->add_coeffs(-1);
1045 return RemoveConstraint(
ct);
1052 auto* arg = new_ct->mutable_linear();
1053 arg->add_vars(target_ref);
1060 return RemoveConstraint(
ct);
1066 context_->
IsFixed(target_ref)) {
1067 if (!context_->
IsFixed(target_ref)) {
1072 return RemoveConstraint(
ct);
1082bool CpModelPresolver::PresolveIntMin(ConstraintProto*
ct) {
1085 const auto copy =
ct->int_min();
1086 ct->mutable_int_max()->set_target(
NegatedRef(copy.target()));
1087 for (
const int ref : copy.vars()) {
1090 return PresolveIntMax(
ct);
1093bool CpModelPresolver::PresolveIntProd(ConstraintProto*
ct) {
1099 int64_t constant_factor = 1;
1101 bool changed =
false;
1102 for (
int i = 0; i <
ct->int_prod().vars().size(); ++i) {
1103 const int ref =
ct->int_prod().vars(i);
1107 constant_factor =
CapProd(constant_factor, context_->
MinOf(ref));
1111 if (r.representative != ref && r.offset == 0) {
1113 ct->mutable_int_prod()->set_vars(new_size++, r.representative);
1114 constant_factor =
CapProd(constant_factor, r.coeff);
1116 ct->mutable_int_prod()->set_vars(new_size++, ref);
1119 ct->mutable_int_prod()->mutable_vars()->Truncate(new_size);
1121 if (constant_factor == 0) {
1126 return RemoveConstraint(
ct);
1133 constant_factor = 1;
1140 if (
ct->int_prod().vars().empty()) {
1142 Domain(constant_factor))) {
1146 return RemoveConstraint(
ct);
1150 if (
ct->int_prod().vars().size() == 1) {
1153 lin->mutable_linear()->add_coeffs(1);
1154 lin->mutable_linear()->add_vars(
ct->int_prod().vars(0));
1155 lin->mutable_linear()->add_coeffs(-constant_factor);
1156 lin->mutable_linear()->add_domain(0);
1157 lin->mutable_linear()->add_domain(0);
1159 context_->
UpdateRuleStats(
"int_prod: linearize product by constant.");
1160 return RemoveConstraint(
ct);
1170 if (constant_factor != 1) {
1171 context_->
UpdateRuleStats(
"int_prod: extracted product by constant.");
1173 const int old_target =
ct->int_prod().target();
1183 ct->mutable_int_prod()->set_target(new_target);
1184 if (context_->
IsFixed(new_target)) {
1187 old_target, context_->
DomainOf(new_target)
1193 constant_factor, 0)) {
1201 lin->add_vars(new_target);
1202 lin->add_coeffs(-constant_factor);
1212 for (
const int ref :
ct->int_prod().vars()) {
1213 implied = implied.ContinuousMultiplicationBy(context_->
DomainOf(ref));
1215 bool modified =
false;
1224 if (
ct->int_prod().vars_size() == 2) {
1225 int a =
ct->int_prod().vars(0);
1226 int b =
ct->int_prod().vars(1);
1227 const int product =
ct->int_prod().target();
1228 if (
a ==
b &&
a == product) {
1233 return RemoveConstraint(
ct);
1238 const int target_ref =
ct->int_prod().target();
1240 for (
const int var :
ct->int_prod().vars()) {
1242 if (context_->
MinOf(
var) < 0)
return changed;
1243 if (context_->
MaxOf(
var) > 1)
return changed;
1254 auto* arg = new_ct->mutable_bool_and();
1255 for (
const int var :
ct->int_prod().vars()) {
1256 arg->add_literals(
var);
1263 for (
const int var :
ct->int_prod().vars()) {
1268 return RemoveConstraint(
ct);
1271bool CpModelPresolver::PresolveIntDiv(ConstraintProto*
ct) {
1273 const int target =
ct->int_div().target();
1274 const int ref_x =
ct->int_div().vars(0);
1275 const int ref_div =
ct->int_div().vars(1);
1280 !context_->
IsFixed(ref_div)) {
1284 const int64_t divisor = context_->
MinOf(ref_div);
1286 LinearConstraintProto*
const lin =
1290 lin->add_vars(target);
1291 lin->add_coeffs(-1);
1296 return RemoveConstraint(
ct);
1298 bool domain_modified =
false;
1301 &domain_modified)) {
1302 if (domain_modified) {
1304 "int_div: updated domain of target in target = X / cte");
1315 if (context_->
MinOf(target) >= 0 && context_->
MinOf(ref_x) >= 0 &&
1317 LinearConstraintProto*
const lin =
1321 lin->add_vars(target);
1322 lin->add_coeffs(-divisor);
1324 lin->add_domain(divisor - 1);
1327 "int_div: linearize positive division with a constant divisor");
1328 return RemoveConstraint(
ct);
1336bool CpModelPresolver::PresolveIntMod(ConstraintProto*
ct) {
1339 const int target =
ct->int_mod().target();
1340 const int ref_mod =
ct->int_mod().vars(1);
1341 const int ref_x =
ct->int_mod().vars(0);
1343 bool changed =
false;
1359bool CpModelPresolver::ExploitEquivalenceRelations(
int c, ConstraintProto*
ct) {
1360 bool changed =
false;
1365 if (
ct->constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
1366 for (
int& ref : *
ct->mutable_enforcement_literal()) {
1378 bool work_to_do =
false;
1381 if (r.representative !=
var) {
1386 if (!work_to_do)
return false;
1390 [&changed,
this](
int* ref) {
1401 [&changed,
this](
int* ref) {
1412void CpModelPresolver::DivideLinearByGcd(ConstraintProto*
ct) {
1417 const int num_vars =
ct->linear().vars().size();
1418 for (
int i = 0; i < num_vars; ++i) {
1419 const int64_t magnitude = std::abs(
ct->linear().coeffs(i));
1421 if (gcd == 1)
break;
1425 for (
int i = 0; i < num_vars; ++i) {
1426 ct->mutable_linear()->set_coeffs(i,
ct->linear().coeffs(i) / gcd);
1430 if (
ct->linear().domain_size() == 0) {
1431 return (
void)MarkConstraintAsFalse(
ct);
1436void CpModelPresolver::PresolveLinearEqualityModuloTwo(ConstraintProto*
ct) {
1437 if (!
ct->enforcement_literal().empty())
return;
1438 if (
ct->linear().domain().size() != 2)
return;
1439 if (
ct->linear().domain(0) !=
ct->linear().domain(1))
return;
1444 std::vector<int> literals;
1445 for (
int i = 0; i <
ct->linear().vars().size(); ++i) {
1446 const int64_t coeff =
ct->linear().coeffs(i);
1447 const int ref =
ct->linear().vars(i);
1448 if (coeff % 2 == 0)
continue;
1451 if (literals.size() > 2)
return;
1453 if (literals.size() == 1) {
1454 const int64_t rhs = std::abs(
ct->linear().domain(0));
1455 context_->
UpdateRuleStats(
"linear: only one odd Boolean in equality");
1457 }
else if (literals.size() == 2) {
1458 const int64_t rhs = std::abs(
ct->linear().domain(0));
1459 context_->
UpdateRuleStats(
"linear: only two odd Booleans in equality");
1469template <
typename ProtoWithVarsAndCoeffs>
1470bool CpModelPresolver::CanonicalizeLinearExpressionInternal(
1471 const ConstraintProto&
ct, ProtoWithVarsAndCoeffs*
proto, int64_t* offset) {
1477 int64_t sum_of_fixed_terms = 0;
1478 bool remapped =
false;
1479 const int old_size =
proto->vars().size();
1481 for (
int i = 0; i < old_size; ++i) {
1482 const int ref =
proto->vars(i);
1484 const int64_t coeff =
1486 if (coeff == 0)
continue;
1489 sum_of_fixed_terms += coeff * context_->
MinOf(
var);
1495 bool removed =
false;
1496 for (
const int enf :
ct.enforcement_literal()) {
1500 sum_of_fixed_terms += coeff;
1509 context_->
UpdateRuleStats(
"linear: enforcement literal in expression");
1514 if (r.representative !=
var) {
1516 sum_of_fixed_terms += coeff * r.
offset;
1518 tmp_terms_.push_back({r.representative, coeff * r.coeff});
1520 proto->clear_vars();
1521 proto->clear_coeffs();
1522 std::sort(tmp_terms_.begin(), tmp_terms_.end());
1523 int current_var = 0;
1524 int64_t current_coeff = 0;
1525 for (
const auto entry : tmp_terms_) {
1527 if (entry.first == current_var) {
1528 current_coeff += entry.second;
1530 if (current_coeff != 0) {
1531 proto->add_vars(current_var);
1532 proto->add_coeffs(current_coeff);
1534 current_var = entry.first;
1535 current_coeff = entry.second;
1538 if (current_coeff != 0) {
1539 proto->add_vars(current_var);
1540 proto->add_coeffs(current_coeff);
1545 if (
proto->vars().size() < old_size) {
1548 *offset = sum_of_fixed_terms;
1549 return remapped ||
proto->vars().size() < old_size;
1552bool CpModelPresolver::CanonicalizeLinearExpression(
1553 const ConstraintProto&
ct, LinearExpressionProto* exp) {
1555 const bool result = CanonicalizeLinearExpressionInternal(
ct, exp, &offset);
1556 exp->set_offset(exp->offset() + offset);
1560bool CpModelPresolver::CanonicalizeLinear(ConstraintProto*
ct) {
1561 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
1566 if (
ct->linear().domain().empty()) {
1568 return MarkConstraintAsFalse(
ct);
1573 CanonicalizeLinearExpressionInternal(*
ct,
ct->mutable_linear(), &offset);
1577 ct->mutable_linear());
1579 DivideLinearByGcd(
ct);
1583bool CpModelPresolver::RemoveSingletonInLinear(ConstraintProto*
ct) {
1584 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
1589 std::set<int> index_to_erase;
1590 const int num_vars =
ct->linear().vars().size();
1596 for (
int i = 0; i < num_vars; ++i) {
1597 const int var =
ct->linear().vars(i);
1598 const int64_t coeff =
ct->linear().coeffs(i);
1602 const auto term_domain =
1604 if (!exact)
continue;
1608 if (new_rhs.NumIntervals() > 100)
continue;
1615 index_to_erase.insert(i);
1622 if (index_to_erase.empty()) {
1625 if (!
ct->enforcement_literal().empty())
return false;
1629 if (rhs.Min() != rhs.Max())
return false;
1631 for (
int i = 0; i < num_vars; ++i) {
1632 const int var =
ct->linear().vars(i);
1633 const int64_t coeff =
ct->linear().coeffs(i);
1651 const int64_t objective_coeff =
1654 if (objective_coeff % coeff != 0)
continue;
1658 const auto term_domain =
1660 if (!exact)
continue;
1662 if (new_rhs.NumIntervals() > 100)
continue;
1670 objective_coeff))) {
1687 LOG(
WARNING) <<
"This was not supposed to happen and the presolve "
1688 "could be improved.";
1696 context_->
UpdateRuleStats(
"linear: singleton column define objective.");
1699 return RemoveConstraint(
ct);
1711 "linear: singleton column in equality and in objective.");
1713 index_to_erase.insert(i);
1717 if (index_to_erase.empty())
return false;
1726 for (
int i = 0; i < num_vars; ++i) {
1727 if (index_to_erase.count(i)) {
1731 ct->mutable_linear()->set_coeffs(new_size,
ct->linear().coeffs(i));
1732 ct->mutable_linear()->set_vars(new_size,
ct->linear().vars(i));
1735 ct->mutable_linear()->mutable_vars()->Truncate(new_size);
1736 ct->mutable_linear()->mutable_coeffs()->Truncate(new_size);
1738 DivideLinearByGcd(
ct);
1742bool CpModelPresolver::PresolveSmallLinear(ConstraintProto*
ct) {
1743 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
1749 if (
ct->linear().vars().empty()) {
1752 if (rhs.Contains(0)) {
1753 return RemoveConstraint(
ct);
1755 return MarkConstraintAsFalse(
ct);
1762 if (
ct->linear().vars_size() == 1 &&
ct->enforcement_literal_size() > 0 &&
1763 ct->linear().coeffs(0) == 1 &&
1766 context_->
UpdateRuleStats(
"linear: remove abs from abs(x) in domain");
1767 const Domain implied_abs_target_domain =
1770 .IntersectionWith(context_->
DomainOf(
ct->linear().vars(0)));
1772 if (implied_abs_target_domain.IsEmpty()) {
1773 return MarkConstraintAsFalse(
ct);
1776 const Domain new_abs_var_domain =
1777 implied_abs_target_domain
1778 .UnionWith(implied_abs_target_domain.Negation())
1779 .IntersectionWith(context_->
DomainOf(abs_arg));
1781 if (new_abs_var_domain.IsEmpty()) {
1782 return MarkConstraintAsFalse(
ct);
1787 for (
const int literal :
ct->enforcement_literal()) {
1788 new_ct->add_enforcement_literal(
literal);
1790 auto* arg = new_ct->mutable_linear();
1791 arg->add_vars(abs_arg);
1795 return RemoveConstraint(
ct);
1800 if (
ct->enforcement_literal_size() != 1 ||
ct->linear().vars_size() != 1 ||
1801 (
ct->linear().coeffs(0) != 1 &&
ct->linear().coeffs(0) == -1)) {
1809 const int literal =
ct->enforcement_literal(0);
1810 const LinearConstraintProto& linear =
ct->linear();
1811 const int ref = linear.vars(0);
1813 const int64_t coeff =
1816 if (linear.domain_size() == 2 && linear.domain(0) == linear.domain(1)) {
1818 : -linear.domain(0) * coeff;
1829 if (complement.Size() != 1)
return false;
1831 : -complement.Min() * coeff;
1846 if (
ct->linear().vars().size() == 1) {
1848 ?
ct->linear().coeffs(0)
1849 : -
ct->linear().coeffs(0);
1854 rhs.InverseMultiplicationBy(coeff))) {
1857 return RemoveConstraint(
ct);
1864 const LinearConstraintProto& arg =
ct->linear();
1865 if (arg.vars_size() == 2) {
1867 const int64_t rhs_min = rhs.Min();
1868 const int64_t rhs_max = rhs.Max();
1869 if (rhs_min == rhs_max) {
1870 const int v1 = arg.vars(0);
1871 const int v2 = arg.vars(1);
1872 const int64_t coeff1 = arg.coeffs(0);
1873 const int64_t coeff2 = arg.coeffs(1);
1877 }
else if (coeff2 == 1) {
1879 }
else if (coeff1 == -1) {
1881 }
else if (coeff2 == -1) {
1884 if (added)
return RemoveConstraint(
ct);
1894bool IsLeConstraint(
const Domain& domain,
const Domain& all_values) {
1898 .IsIncludedIn(domain);
1902bool IsGeConstraint(
const Domain& domain,
const Domain& all_values) {
1906 .IsIncludedIn(domain);
1912bool RhsCanBeFixedToMin(int64_t coeff,
const Domain& var_domain,
1913 const Domain& terms,
const Domain& rhs) {
1914 if (var_domain.NumIntervals() != 1)
return false;
1915 if (std::abs(coeff) != 1)
return false;
1923 if (coeff == 1 && terms.Max() + var_domain.Min() <= rhs.Min()) {
1926 if (coeff == -1 && terms.Max() - var_domain.Max() <= rhs.Min()) {
1932bool RhsCanBeFixedToMax(int64_t coeff,
const Domain& var_domain,
1933 const Domain& terms,
const Domain& rhs) {
1934 if (var_domain.NumIntervals() != 1)
return false;
1935 if (std::abs(coeff) != 1)
return false;
1937 if (coeff == 1 && terms.Min() + var_domain.Max() >= rhs.Max()) {
1940 if (coeff == -1 && terms.Min() - var_domain.Min() >= rhs.Max()) {
1947void TakeIntersectionWith(
const absl::flat_hash_set<int>& current,
1948 absl::flat_hash_set<int>* to_clear) {
1949 std::vector<int> new_set;
1950 for (
const int c : *to_clear) {
1951 if (current.contains(c)) new_set.push_back(c);
1954 for (
const int c : new_set) to_clear->insert(c);
1959bool CpModelPresolver::PropagateDomainsInLinear(
int ct_index,
1960 ConstraintProto*
ct) {
1961 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
1969 const int num_vars =
ct->linear().vars_size();
1970 term_domains.resize(num_vars + 1);
1971 left_domains.resize(num_vars + 1);
1972 left_domains[0] = Domain(0);
1973 for (
int i = 0; i < num_vars; ++i) {
1974 const int var =
ct->linear().vars(i);
1975 const int64_t coeff =
ct->linear().coeffs(i);
1978 left_domains[i + 1] =
1981 const Domain& implied_rhs = left_domains[num_vars];
1985 if (implied_rhs.IsIncludedIn(old_rhs)) {
1987 return RemoveConstraint(
ct);
1991 Domain rhs = old_rhs.SimplifyUsingImpliedDomain(implied_rhs);
1992 if (rhs.IsEmpty()) {
1994 return MarkConstraintAsFalse(
ct);
1996 if (rhs != old_rhs) {
2004 bool is_le_constraint = IsLeConstraint(rhs, implied_rhs);
2005 bool is_ge_constraint = IsGeConstraint(rhs, implied_rhs);
2008 if (
ct->enforcement_literal().size() > 1)
return false;
2010 bool new_bounds =
false;
2011 bool recanonicalize =
false;
2012 Domain negated_rhs = rhs.Negation();
2013 Domain right_domain(0);
2015 Domain implied_term_domain;
2016 term_domains[num_vars] = Domain(0);
2017 for (
int i = num_vars - 1; i >= 0; --i) {
2018 const int var =
ct->linear().vars(i);
2019 const int64_t var_coeff =
ct->linear().coeffs(i);
2021 right_domain.AdditionWith(term_domains[i + 1]).RelaxIfTooComplex();
2022 implied_term_domain = left_domains[i].AdditionWith(right_domain);
2023 new_domain = implied_term_domain.AdditionWith(negated_rhs)
2024 .InverseMultiplicationBy(-var_coeff);
2026 if (
ct->enforcement_literal().empty()) {
2031 }
else if (
ct->enforcement_literal().size() == 1) {
2042 recanonicalize =
true;
2046 if (is_le_constraint || is_ge_constraint) {
2047 CHECK_NE(is_le_constraint, is_ge_constraint);
2048 if ((var_coeff > 0) == is_ge_constraint) {
2063 const bool is_in_objective =
2067 const int64_t obj_coeff =
2076 if (obj_coeff <= 0 &&
2086 recanonicalize =
true;
2090 if (obj_coeff >= 0 &&
2100 recanonicalize =
true;
2108 if (!
ct->enforcement_literal().empty())
continue;
2120 if (rhs.Min() != rhs.Max() &&
2123 const bool same_sign = (var_coeff > 0) == (obj_coeff > 0);
2125 if (same_sign && RhsCanBeFixedToMin(var_coeff, context_->
DomainOf(
var),
2126 implied_term_domain, rhs)) {
2127 rhs = Domain(rhs.Min());
2130 if (!same_sign && RhsCanBeFixedToMax(var_coeff, context_->
DomainOf(
var),
2131 implied_term_domain, rhs)) {
2132 rhs = Domain(rhs.Max());
2138 negated_rhs = rhs.Negation();
2142 right_domain = Domain(0);
2146 is_le_constraint =
false;
2147 is_ge_constraint =
false;
2148 for (
const int var :
ct->linear().vars()) {
2165 if (
ct->linear().vars().size() <= 2)
continue;
2170 if (rhs.Min() != rhs.Max())
continue;
2176 if (context_->
DomainOf(
var) != new_domain)
continue;
2177 if (std::abs(var_coeff) != 1)
continue;
2184 bool is_in_objective =
false;
2186 is_in_objective =
true;
2192 if (is_in_objective) col_size--;
2193 const int row_size =
ct->linear().vars_size();
2197 const int num_entries_added = (row_size - 1) * (col_size - 1);
2198 const int num_entries_removed = col_size + row_size - 1;
2200 if (num_entries_added > num_entries_removed) {
2206 std::vector<int> others;
2214 if (c == ct_index)
continue;
2216 ConstraintProto::ConstraintCase::kLinear) {
2220 for (
const int ref :
2227 others.push_back(c);
2229 if (abort)
continue;
2232 for (
const int c : others) {
2246 if (is_in_objective &&
2252 absl::StrCat(
"linear: variable substitution ", others.size()));
2265 LinearConstraintProto* mapping_linear_ct =
2268 std::swap(mapping_linear_ct->mutable_vars()->at(0),
2269 mapping_linear_ct->mutable_vars()->at(i));
2270 std::swap(mapping_linear_ct->mutable_coeffs()->at(0),
2271 mapping_linear_ct->mutable_coeffs()->at(i));
2272 return RemoveConstraint(
ct);
2277 if (recanonicalize)
return CanonicalizeLinear(
ct);
2288void CpModelPresolver::ExtractEnforcementLiteralFromLinearConstraint(
2289 int ct_index, ConstraintProto*
ct) {
2290 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
2295 const LinearConstraintProto& arg =
ct->linear();
2296 const int num_vars = arg.vars_size();
2300 if (num_vars <= 1)
return;
2302 int64_t min_sum = 0;
2303 int64_t max_sum = 0;
2304 int64_t max_coeff_magnitude = 0;
2305 for (
int i = 0; i < num_vars; ++i) {
2306 const int ref = arg.vars(i);
2307 const int64_t coeff = arg.coeffs(i);
2308 const int64_t term_a = coeff * context_->
MinOf(ref);
2309 const int64_t term_b = coeff * context_->
MaxOf(ref);
2310 max_coeff_magnitude =
std::max(max_coeff_magnitude, std::abs(coeff));
2311 min_sum +=
std::min(term_a, term_b);
2312 max_sum +=
std::max(term_a, term_b);
2321 const auto& domain =
ct->linear().domain();
2322 const int64_t ub_threshold = domain[domain.size() - 2] - min_sum;
2323 const int64_t lb_threshold = max_sum - domain[1];
2325 if (max_coeff_magnitude <
std::max(ub_threshold, lb_threshold))
return;
2344 const bool lower_bounded = min_sum < rhs_domain.Min();
2345 const bool upper_bounded = max_sum > rhs_domain.Max();
2346 if (!lower_bounded && !upper_bounded)
return;
2347 if (lower_bounded && upper_bounded) {
2351 if (!
ct->name().empty()) {
2352 new_ct1->set_name(absl::StrCat(
ct->name(),
" (part 1)"));
2355 new_ct1->mutable_linear());
2359 if (!
ct->name().empty()) {
2360 new_ct2->set_name(absl::StrCat(
ct->name(),
" (part 2)"));
2363 new_ct2->mutable_linear());
2366 return (
void)RemoveConstraint(
ct);
2372 const int64_t threshold = lower_bounded ? ub_threshold : lb_threshold;
2379 const bool only_booleans =
2386 int64_t rhs_offset = 0;
2387 bool some_integer_encoding_were_extracted =
false;
2388 LinearConstraintProto* mutable_arg =
ct->mutable_linear();
2389 for (
int i = 0; i < arg.vars_size(); ++i) {
2390 int ref = arg.vars(i);
2391 int64_t coeff = arg.coeffs(i);
2398 if (context_->
IsFixed(ref) || coeff < threshold ||
2399 (only_booleans && !is_boolean)) {
2401 mutable_arg->set_vars(new_size, mutable_arg->vars(i));
2402 mutable_arg->set_coeffs(new_size, mutable_arg->coeffs(i));
2410 some_integer_encoding_were_extracted =
true;
2412 "linear: extracted integer enforcement literal");
2414 if (lower_bounded) {
2415 ct->add_enforcement_literal(is_boolean
2418 ref, context_->
MinOf(ref)));
2419 rhs_offset -= coeff * context_->
MinOf(ref);
2421 ct->add_enforcement_literal(is_boolean
2424 ref, context_->
MaxOf(ref)));
2425 rhs_offset -= coeff * context_->
MaxOf(ref);
2428 mutable_arg->mutable_vars()->Truncate(new_size);
2429 mutable_arg->mutable_coeffs()->Truncate(new_size);
2431 if (some_integer_encoding_were_extracted) {
2437void CpModelPresolver::ExtractAtMostOneFromLinear(ConstraintProto*
ct) {
2442 const LinearConstraintProto& arg =
ct->linear();
2443 const int num_vars = arg.vars_size();
2444 int64_t min_sum = 0;
2445 int64_t max_sum = 0;
2446 for (
int i = 0; i < num_vars; ++i) {
2447 const int ref = arg.vars(i);
2448 const int64_t coeff = arg.coeffs(i);
2449 const int64_t term_a = coeff * context_->
MinOf(ref);
2450 const int64_t term_b = coeff * context_->
MaxOf(ref);
2451 min_sum +=
std::min(term_a, term_b);
2452 max_sum +=
std::max(term_a, term_b);
2454 for (
const int type : {0, 1}) {
2455 std::vector<int> at_most_one;
2456 for (
int i = 0; i < num_vars; ++i) {
2457 const int ref = arg.vars(i);
2458 const int64_t coeff = arg.coeffs(i);
2459 if (context_->
MinOf(ref) != 0)
continue;
2460 if (context_->
MaxOf(ref) != 1)
continue;
2465 if (min_sum + 2 * std::abs(coeff) > rhs.Max()) {
2466 at_most_one.push_back(coeff > 0 ? ref :
NegatedRef(ref));
2469 if (max_sum - 2 * std::abs(coeff) < rhs.Min()) {
2470 at_most_one.push_back(coeff > 0 ?
NegatedRef(ref) : ref);
2474 if (at_most_one.size() > 1) {
2482 for (
const int ref : at_most_one) {
2483 new_ct->mutable_at_most_one()->add_literals(ref);
2492bool CpModelPresolver::PresolveLinearOnBooleans(ConstraintProto*
ct) {
2493 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
2498 const LinearConstraintProto& arg =
ct->linear();
2499 const int num_vars = arg.vars_size();
2501 int64_t max_coeff = 0;
2502 int64_t min_sum = 0;
2503 int64_t max_sum = 0;
2504 for (
int i = 0; i < num_vars; ++i) {
2506 const int var = arg.vars(i);
2507 const int64_t coeff = arg.coeffs(i);
2510 if (context_->
MinOf(
var) != 0)
return false;
2511 if (context_->
MaxOf(
var) != 1)
return false;
2515 min_coeff =
std::min(min_coeff, coeff);
2516 max_coeff =
std::max(max_coeff, coeff);
2520 min_coeff =
std::min(min_coeff, -coeff);
2521 max_coeff =
std::max(max_coeff, -coeff);
2533 if ((!rhs_domain.Contains(min_sum) &&
2534 min_sum + min_coeff > rhs_domain.Max()) ||
2535 (!rhs_domain.Contains(max_sum) &&
2536 max_sum - min_coeff < rhs_domain.Min())) {
2537 context_->
UpdateRuleStats(
"linear: all booleans and trivially false");
2538 return MarkConstraintAsFalse(
ct);
2540 if (Domain(min_sum, max_sum).IsIncludedIn(rhs_domain)) {
2542 return RemoveConstraint(
ct);
2549 DCHECK(!rhs_domain.IsEmpty());
2550 if (min_sum + min_coeff > rhs_domain.Max()) {
2553 const auto copy = arg;
2554 ct->mutable_bool_and()->clear_literals();
2555 for (
int i = 0; i < num_vars; ++i) {
2556 ct->mutable_bool_and()->add_literals(
2557 copy.coeffs(i) > 0 ?
NegatedRef(copy.vars(i)) : copy.vars(i));
2559 PresolveBoolAnd(
ct);
2561 }
else if (max_sum - min_coeff < rhs_domain.Min()) {
2564 const auto copy = arg;
2565 ct->mutable_bool_and()->clear_literals();
2566 for (
int i = 0; i < num_vars; ++i) {
2567 ct->mutable_bool_and()->add_literals(
2568 copy.coeffs(i) > 0 ? copy.vars(i) :
NegatedRef(copy.vars(i)));
2570 PresolveBoolAnd(
ct);
2572 }
else if (min_sum + min_coeff >= rhs_domain.Min() &&
2573 rhs_domain.front().end >= max_sum) {
2576 const auto copy = arg;
2577 ct->mutable_bool_or()->clear_literals();
2578 for (
int i = 0; i < num_vars; ++i) {
2579 ct->mutable_bool_or()->add_literals(
2580 copy.coeffs(i) > 0 ? copy.vars(i) :
NegatedRef(copy.vars(i)));
2584 }
else if (max_sum - min_coeff <= rhs_domain.Max() &&
2585 rhs_domain.back().start <= min_sum) {
2588 const auto copy = arg;
2589 ct->mutable_bool_or()->clear_literals();
2590 for (
int i = 0; i < num_vars; ++i) {
2591 ct->mutable_bool_or()->add_literals(
2592 copy.coeffs(i) > 0 ?
NegatedRef(copy.vars(i)) : copy.vars(i));
2597 min_sum + max_coeff <= rhs_domain.Max() &&
2598 min_sum + 2 * min_coeff > rhs_domain.Max() &&
2599 rhs_domain.back().start <= min_sum) {
2602 const auto copy = arg;
2603 ct->mutable_at_most_one()->clear_literals();
2604 for (
int i = 0; i < num_vars; ++i) {
2605 ct->mutable_at_most_one()->add_literals(
2606 copy.coeffs(i) > 0 ? copy.vars(i) :
NegatedRef(copy.vars(i)));
2610 max_sum - max_coeff >= rhs_domain.Min() &&
2611 max_sum - 2 * min_coeff < rhs_domain.Min() &&
2612 rhs_domain.front().end >= max_sum) {
2615 const auto copy = arg;
2616 ct->mutable_at_most_one()->clear_literals();
2617 for (
int i = 0; i < num_vars; ++i) {
2618 ct->mutable_at_most_one()->add_literals(
2619 copy.coeffs(i) > 0 ?
NegatedRef(copy.vars(i)) : copy.vars(i));
2623 min_sum < rhs_domain.Min() &&
2624 min_sum + min_coeff >= rhs_domain.Min() &&
2625 min_sum + 2 * min_coeff > rhs_domain.Max() &&
2626 min_sum + max_coeff <= rhs_domain.Max()) {
2630 for (
int i = 0; i < num_vars; ++i) {
2631 exactly_one->mutable_exactly_one()->add_literals(
2632 arg.coeffs(i) > 0 ? arg.vars(i) :
NegatedRef(arg.vars(i)));
2635 return RemoveConstraint(
ct);
2637 max_sum > rhs_domain.Max() &&
2638 max_sum - min_coeff <= rhs_domain.Max() &&
2639 max_sum - 2 * min_coeff < rhs_domain.Min() &&
2640 max_sum - max_coeff >= rhs_domain.Min()) {
2644 for (
int i = 0; i < num_vars; ++i) {
2645 exactly_one->mutable_exactly_one()->add_literals(
2646 arg.coeffs(i) > 0 ?
NegatedRef(arg.vars(i)) : arg.vars(i));
2649 return RemoveConstraint(
ct);
2656 if (num_vars > 3)
return false;
2661 const int max_mask = (1 << arg.vars_size());
2662 for (
int mask = 0; mask < max_mask; ++mask) {
2664 for (
int i = 0; i < num_vars; ++i) {
2665 if ((mask >> i) & 1)
value += arg.coeffs(i);
2667 if (rhs_domain.Contains(
value))
continue;
2673 *new_ct->mutable_enforcement_literal() =
ct->enforcement_literal();
2675 for (
int i = 0; i < num_vars; ++i) {
2676 new_arg->add_literals(((mask >> i) & 1) ?
NegatedRef(arg.vars(i))
2682 return RemoveConstraint(
ct);
2685void CpModelPresolver::AddLinearConstraintFromInterval(
2686 const ConstraintProto&
ct) {
2687 const int start =
ct.interval().start();
2688 const int end =
ct.interval().end();
2689 const int size =
ct.interval().size();
2691 *(new_ct->mutable_enforcement_literal()) =
ct.enforcement_literal();
2692 new_ct->mutable_linear()->add_domain(0);
2693 new_ct->mutable_linear()->add_domain(0);
2694 new_ct->mutable_linear()->add_vars(start);
2695 new_ct->mutable_linear()->add_coeffs(1);
2696 new_ct->mutable_linear()->add_vars(size);
2697 new_ct->mutable_linear()->add_coeffs(1);
2698 new_ct->mutable_linear()->add_vars(end);
2699 new_ct->mutable_linear()->add_coeffs(-1);
2700 CanonicalizeLinear(new_ct);
2702 if (context_->
MinOf(size) < 0) {
2703 CHECK(!
ct.enforcement_literal().empty());
2705 *(positive->mutable_enforcement_literal()) =
ct.enforcement_literal();
2706 positive->mutable_linear()->add_domain(0);
2707 positive->mutable_linear()->add_domain(context_->
MaxOf(size));
2708 positive->mutable_linear()->add_vars(size);
2709 positive->mutable_linear()->add_coeffs(1);
2714bool CpModelPresolver::PresolveInterval(
int c, ConstraintProto*
ct) {
2717 if (
ct->enforcement_literal().empty() && !
ct->interval().has_start_view()) {
2718 bool changed =
false;
2719 const int start =
ct->interval().start();
2720 const int end =
ct->interval().end();
2721 const int size =
ct->interval().size();
2722 const Domain start_domain = context_->
DomainOf(start);
2723 const Domain end_domain = context_->
DomainOf(end);
2724 const Domain size_domain = context_->
DomainOf(size);
2731 end, start_domain.AdditionWith(size_domain), &changed)) {
2735 start, end_domain.AdditionWith(size_domain.Negation()), &changed)) {
2739 size, end_domain.AdditionWith(start_domain.Negation()), &changed)) {
2748 if (!
ct->interval().has_start_view()) {
2749 AddLinearConstraintFromInterval(*
ct);
2752 return RemoveConstraint(
ct);
2761 bool changed =
false;
2762 IntervalConstraintProto*
interval =
ct->mutable_interval();
2763 if (!
ct->interval().has_start_view()) {
2768 AddLinearConstraintFromInterval(*
ct);
2772 interval->mutable_start_view()->add_coeffs(1);
2773 interval->mutable_start_view()->set_offset(0);
2775 interval->mutable_size_view()->add_coeffs(1);
2776 interval->mutable_size_view()->set_offset(0);
2778 interval->mutable_end_view()->add_coeffs(1);
2779 interval->mutable_end_view()->set_offset(0);
2788 CanonicalizeLinearExpression(*
ct,
interval->mutable_start_view());
2789 changed |= CanonicalizeLinearExpression(*
ct,
interval->mutable_size_view());
2790 changed |= CanonicalizeLinearExpression(*
ct,
interval->mutable_end_view());
2799bool CpModelPresolver::PresolveInverse(ConstraintProto*
ct) {
2800 const int size =
ct->inverse().f_direct().size();
2801 bool changed =
false;
2804 for (
const int ref :
ct->inverse().f_direct()) {
2806 VLOG(1) <<
"Empty domain for a variable in ExpandInverse()";
2810 for (
const int ref :
ct->inverse().f_inverse()) {
2812 VLOG(1) <<
"Empty domain for a variable in ExpandInverse()";
2820 const auto filter_inverse_domain =
2821 [
this, size, &changed](
const auto& direct,
const auto& inverse) {
2823 std::vector<absl::flat_hash_set<int64_t>> inverse_values(size);
2824 for (
int i = 0; i < size; ++i) {
2825 const Domain domain = context_->
DomainOf(inverse[i]);
2826 for (
const int64_t j : domain.Values()) {
2827 inverse_values[i].insert(j);
2834 std::vector<int64_t> possible_values;
2835 for (
int i = 0; i < size; ++i) {
2836 possible_values.clear();
2837 const Domain domain = context_->
DomainOf(direct[i]);
2838 bool removed_value =
false;
2839 for (
const int64_t j : domain.Values()) {
2840 if (inverse_values[j].contains(i)) {
2841 possible_values.push_back(j);
2843 removed_value =
true;
2846 if (removed_value) {
2850 VLOG(1) <<
"Empty domain for a variable in ExpandInverse()";
2858 if (!filter_inverse_domain(
ct->inverse().f_direct(),
2859 ct->inverse().f_inverse())) {
2863 if (!filter_inverse_domain(
ct->inverse().f_inverse(),
2864 ct->inverse().f_direct())) {
2875bool CpModelPresolver::PresolveElement(ConstraintProto*
ct) {
2878 if (
ct->element().vars().empty()) {
2883 const int index_ref =
ct->element().index();
2884 const int target_ref =
ct->element().target();
2889 bool all_constants =
true;
2890 absl::flat_hash_set<int64_t> constant_set;
2891 bool all_included_in_target_domain =
true;
2894 bool reduced_index_domain =
false;
2896 Domain(0,
ct->element().vars_size() - 1),
2897 &reduced_index_domain)) {
2905 std::vector<int64_t> possible_indices;
2906 const Domain& index_domain = context_->
DomainOf(index_ref);
2907 for (
const int64_t index_value : index_domain.Values()) {
2908 const int ref =
ct->element().vars(index_value);
2909 const int64_t target_value =
2910 target_ref == index_ref ? index_value : -index_value;
2912 possible_indices.push_back(target_value);
2915 if (possible_indices.size() < index_domain.Size()) {
2921 "element: reduced index domain when target equals index");
2927 Domain infered_domain;
2928 const Domain& initial_index_domain = context_->
DomainOf(index_ref);
2929 const Domain& target_domain = context_->
DomainOf(target_ref);
2930 std::vector<int64_t> possible_indices;
2931 for (
const int64_t
value : initial_index_domain.Values()) {
2934 const int ref =
ct->element().vars(
value);
2935 const Domain& domain = context_->
DomainOf(ref);
2936 if (domain.IntersectionWith(target_domain).IsEmpty())
continue;
2937 possible_indices.push_back(
value);
2938 if (domain.IsFixed()) {
2939 constant_set.insert(domain.Min());
2941 all_constants =
false;
2943 if (!domain.IsIncludedIn(target_domain)) {
2944 all_included_in_target_domain =
false;
2946 infered_domain = infered_domain.
UnionWith(domain);
2948 if (possible_indices.size() < initial_index_domain.Size()) {
2955 bool domain_modified =
false;
2957 &domain_modified)) {
2960 if (domain_modified) {
2966 if (context_->
IsFixed(index_ref)) {
2967 const int var =
ct->element().vars(context_->
MinOf(index_ref));
2968 if (
var != target_ref) {
2969 LinearConstraintProto*
const lin =
2972 lin->add_coeffs(-1);
2973 lin->add_vars(target_ref);
2980 return RemoveConstraint(
ct);
2986 if (all_constants && constant_set.size() == 1) {
2989 return RemoveConstraint(
ct);
2994 if (context_->
MinOf(index_ref) == 0 && context_->
MaxOf(index_ref) == 1 &&
2996 const int64_t v0 = context_->
MinOf(
ct->element().vars(0));
2997 const int64_t v1 = context_->
MinOf(
ct->element().vars(1));
2999 LinearConstraintProto*
const lin =
3003 lin->add_vars(index_ref);
3004 lin->add_coeffs(v0 - v1);
3005 lin->add_domain(v0);
3006 lin->add_domain(v0);
3008 context_->
UpdateRuleStats(
"element: linearize constant element of size 2");
3009 return RemoveConstraint(
ct);
3013 const AffineRelation::Relation r_index =
3015 if (r_index.representative != index_ref) {
3017 if (context_->
DomainOf(r_index.representative).
Size() >
3023 const int64_t r_min = context_->
MinOf(r_ref);
3024 const int64_t r_max = context_->
MaxOf(r_ref);
3025 const int array_size =
ct->element().vars_size();
3027 context_->
UpdateRuleStats(
"TODO element: representative has bad domain");
3028 }
else if (r_index.offset >= 0 && r_index.offset < array_size &&
3029 r_index.offset + r_max * r_index.coeff >= 0 &&
3030 r_index.offset + r_max * r_index.coeff < array_size) {
3032 ElementConstraintProto*
const element =
3034 for (int64_t v = 0; v <= r_max; ++v) {
3035 const int64_t scaled_index = v * r_index.coeff + r_index.offset;
3037 CHECK_LT(scaled_index, array_size);
3038 element->add_vars(
ct->element().vars(scaled_index));
3040 element->set_index(r_ref);
3041 element->set_target(target_ref);
3043 if (r_index.coeff == 1) {
3049 return RemoveConstraint(
ct);
3060 absl::flat_hash_map<int, int> local_var_occurrence_counter;
3061 local_var_occurrence_counter[
PositiveRef(index_ref)]++;
3062 local_var_occurrence_counter[
PositiveRef(target_ref)]++;
3068 const int ref =
ct->element().vars(
value);
3074 local_var_occurrence_counter.at(
PositiveRef(index_ref)) == 1) {
3075 if (all_constants) {
3079 context_->
UpdateRuleStats(
"element: trivial target domain reduction");
3082 return RemoveConstraint(
ct);
3088 if (!context_->
IsFixed(target_ref) &&
3090 local_var_occurrence_counter.at(
PositiveRef(target_ref)) == 1) {
3091 if (all_included_in_target_domain) {
3095 return RemoveConstraint(
ct);
3104bool CpModelPresolver::PresolveTable(ConstraintProto*
ct) {
3107 if (
ct->table().vars().empty()) {
3109 return RemoveConstraint(
ct);
3112 const int initial_num_vars =
ct->table().vars_size();
3113 bool changed =
true;
3116 std::vector<AffineRelation::Relation> affine_relations;
3117 std::vector<int64_t> old_var_lb;
3118 std::vector<int64_t> old_var_ub;
3120 for (
int v = 0; v < initial_num_vars; ++v) {
3121 const int ref =
ct->table().vars(v);
3123 affine_relations.push_back(r);
3124 old_var_lb.push_back(context_->
MinOf(ref));
3125 old_var_ub.push_back(context_->
MaxOf(ref));
3126 if (r.representative != ref) {
3128 ct->mutable_table()->set_vars(v, r.representative);
3130 "table: replace variable by canonical affine one");
3139 std::vector<int> old_index_of_duplicate_to_new_index_of_first_occurrence(
3140 initial_num_vars, -1);
3142 std::vector<int> old_index_to_new_index(initial_num_vars, -1);
3145 absl::flat_hash_map<int, int> first_visit;
3146 for (
int p = 0; p < initial_num_vars; ++p) {
3147 const int ref =
ct->table().vars(p);
3149 const auto& it = first_visit.find(
var);
3150 if (it != first_visit.end()) {
3151 const int previous = it->second;
3152 old_index_of_duplicate_to_new_index_of_first_occurrence[p] = previous;
3156 ct->mutable_table()->set_vars(num_vars, ref);
3157 first_visit[
var] = num_vars;
3158 old_index_to_new_index[p] = num_vars;
3163 if (num_vars < initial_num_vars) {
3164 ct->mutable_table()->mutable_vars()->Truncate(num_vars);
3171 std::vector<std::vector<int64_t>> new_tuples;
3172 const int initial_num_tuples =
ct->table().values_size() / initial_num_vars;
3173 std::vector<absl::flat_hash_set<int64_t>> new_domains(num_vars);
3176 std::vector<int64_t> tuple(num_vars);
3177 new_tuples.reserve(initial_num_tuples);
3178 for (
int i = 0; i < initial_num_tuples; ++i) {
3179 bool delete_row =
false;
3181 for (
int j = 0; j < initial_num_vars; ++j) {
3182 const int64_t old_value =
ct->table().values(i * initial_num_vars + j);
3186 if (old_value < old_var_lb[j] || old_value > old_var_ub[j]) {
3192 const AffineRelation::Relation& r = affine_relations[j];
3193 const int64_t
value = (old_value - r.offset) / r.coeff;
3194 if (
value * r.coeff + r.offset != old_value) {
3199 const int mapped_position = old_index_to_new_index[j];
3200 if (mapped_position == -1) {
3201 const int new_index_of_first_occurrence =
3202 old_index_of_duplicate_to_new_index_of_first_occurrence[j];
3203 if (
value != tuple[new_index_of_first_occurrence]) {
3208 const int ref =
ct->table().vars(mapped_position);
3213 tuple[mapped_position] =
value;
3220 new_tuples.push_back(tuple);
3221 for (
int j = 0; j < num_vars; ++j) {
3222 new_domains[j].insert(tuple[j]);
3226 if (new_tuples.size() < initial_num_tuples) {
3233 ct->mutable_table()->clear_values();
3234 for (
const std::vector<int64_t>& t : new_tuples) {
3235 for (
const int64_t v : t) {
3236 ct->mutable_table()->add_values(v);
3242 if (
ct->table().negated())
return changed;
3245 for (
int j = 0; j < num_vars; ++j) {
3246 const int ref =
ct->table().vars(j);
3250 new_domains[j].end())),
3258 if (num_vars == 1) {
3261 return RemoveConstraint(
ct);
3266 for (
int j = 0; j < num_vars; ++j) prod *= new_domains[j].size();
3267 if (prod == new_tuples.size()) {
3269 return RemoveConstraint(
ct);
3275 if (new_tuples.size() > 0.7 * prod) {
3277 std::vector<std::vector<int64_t>> var_to_values(num_vars);
3278 for (
int j = 0; j < num_vars; ++j) {
3279 var_to_values[j].assign(new_domains[j].begin(), new_domains[j].end());
3281 std::vector<std::vector<int64_t>> all_tuples(prod);
3282 for (
int i = 0; i < prod; ++i) {
3283 all_tuples[i].resize(num_vars);
3285 for (
int j = 0; j < num_vars; ++j) {
3286 all_tuples[i][j] = var_to_values[j][
index % var_to_values[j].size()];
3287 index /= var_to_values[j].size();
3293 std::vector<std::vector<int64_t>> diff(prod - new_tuples.size());
3294 std::set_difference(all_tuples.begin(), all_tuples.end(),
3295 new_tuples.begin(), new_tuples.end(), diff.begin());
3298 ct->mutable_table()->set_negated(!
ct->table().negated());
3299 ct->mutable_table()->clear_values();
3300 for (
const std::vector<int64_t>& t : diff) {
3301 for (
const int64_t v : t)
ct->mutable_table()->add_values(v);
3308bool CpModelPresolver::PresolveAllDiff(ConstraintProto*
ct) {
3312 AllDifferentConstraintProto& all_diff = *
ct->mutable_all_diff();
3314 bool constraint_has_changed =
false;
3316 const int size = all_diff.vars_size();
3319 return RemoveConstraint(
ct);
3323 return RemoveConstraint(
ct);
3326 bool something_was_propagated =
false;
3327 std::vector<int> new_variables;
3328 for (
int i = 0; i < size; ++i) {
3329 if (!context_->
IsFixed(all_diff.vars(i))) {
3330 new_variables.push_back(all_diff.vars(i));
3334 const int64_t
value = context_->
MinOf(all_diff.vars(i));
3335 bool propagated =
false;
3336 for (
int j = 0; j < size; ++j) {
3337 if (i == j)
continue;
3340 Domain(
value).Complement())) {
3348 something_was_propagated =
true;
3352 std::sort(new_variables.begin(), new_variables.end(),
3353 [](
int ref_a,
int ref_b) {
3354 const int var_a = PositiveRef(ref_a);
3355 const int var_b = PositiveRef(ref_b);
3356 return std::tie(var_a, ref_a) < std::tie(var_b, ref_b);
3358 for (
int i = 1; i < new_variables.size(); ++i) {
3359 if (new_variables[i] == new_variables[i - 1]) {
3361 "Duplicate variable in all_diff");
3363 if (new_variables[i] ==
NegatedRef(new_variables[i - 1])) {
3364 bool domain_modified =
false;
3366 Domain(0).Complement(),
3367 &domain_modified)) {
3370 if (domain_modified) {
3372 "all_diff: remove 0 from variable appearing with its opposite.");
3377 if (new_variables.size() < all_diff.vars_size()) {
3378 all_diff.mutable_vars()->Clear();
3379 for (
const int var : new_variables) {
3380 all_diff.add_vars(
var);
3383 something_was_propagated =
true;
3384 constraint_has_changed =
true;
3385 if (new_variables.size() <= 1)
continue;
3390 Domain domain = context_->
DomainOf(all_diff.vars(0));
3391 for (
int i = 1; i < all_diff.vars_size(); ++i) {
3394 if (all_diff.vars_size() == domain.Size()) {
3395 absl::flat_hash_map<int64_t, std::vector<int>> value_to_refs;
3396 for (
const int ref : all_diff.vars()) {
3398 value_to_refs[v].push_back(ref);
3401 bool propagated =
false;
3402 for (
const auto& it : value_to_refs) {
3403 if (it.second.size() == 1 &&
3405 const int ref = it.second.
front();
3414 "all_diff: propagated mandatory values in permutation");
3415 something_was_propagated =
true;
3418 if (!something_was_propagated)
break;
3421 return constraint_has_changed;
3428std::vector<int> GetLiteralsFromSetPPCConstraint(
const ConstraintProto&
ct) {
3429 std::vector<int> sorted_literals;
3431 for (
const int literal :
ct.at_most_one().literals()) {
3432 sorted_literals.push_back(
literal);
3435 for (
const int literal :
ct.bool_or().literals()) {
3436 sorted_literals.push_back(
literal);
3439 for (
const int literal :
ct.exactly_one().literals()) {
3440 sorted_literals.push_back(
literal);
3443 std::sort(sorted_literals.begin(), sorted_literals.end());
3444 return sorted_literals;
3449void AddImplication(
int lhs,
int rhs, CpModelProto*
proto,
3450 absl::flat_hash_map<int, int>* ref_to_bool_and) {
3451 if (ref_to_bool_and->contains(lhs)) {
3452 const int ct_index = (*ref_to_bool_and)[lhs];
3454 }
else if (ref_to_bool_and->contains(
NegatedRef(rhs))) {
3455 const int ct_index = (*ref_to_bool_and)[
NegatedRef(rhs)];
3461 ct->add_enforcement_literal(lhs);
3462 ct->mutable_bool_and()->add_literals(rhs);
3466template <
typename ClauseContainer>
3467void ExtractClauses(
bool use_bool_and,
const ClauseContainer& container,
3468 CpModelProto*
proto) {
3475 absl::flat_hash_map<int, int> ref_to_bool_and;
3476 for (
int i = 0; i < container.NumClauses(); ++i) {
3477 const std::vector<Literal>& clause = container.Clause(i);
3478 if (clause.empty())
continue;
3481 if (use_bool_and && clause.size() == 2) {
3482 const int a = clause[0].IsPositive()
3483 ? clause[0].Variable().value()
3485 const int b = clause[1].IsPositive()
3486 ? clause[1].Variable().value()
3494 for (
const Literal l : clause) {
3495 if (l.IsPositive()) {
3496 ct->mutable_bool_or()->add_literals(l.Variable().value());
3498 ct->mutable_bool_or()->add_literals(
NegatedRef(l.Variable().value()));
3506bool CpModelPresolver::PresolveNoOverlap(ConstraintProto*
ct) {
3508 NoOverlapConstraintProto*
proto =
ct->mutable_no_overlap();
3509 bool changed =
false;
3513 const int initial_num_intervals =
proto->intervals_size();
3516 for (
int i = 0; i < initial_num_intervals; ++i) {
3517 const int interval_index =
proto->intervals(i);
3522 proto->set_intervals(new_size++, interval_index);
3525 if (new_size < initial_num_intervals) {
3526 proto->mutable_intervals()->Truncate(new_size);
3533 if (
proto->intervals_size() > 1) {
3534 std::vector<IndexedInterval> indexed_intervals;
3535 for (
int i = 0; i <
proto->intervals().size(); ++i) {
3537 indexed_intervals.push_back({
index,
3541 std::vector<std::vector<int>> components;
3544 if (components.size() > 1) {
3545 for (
const std::vector<int>& intervals : components) {
3546 if (intervals.size() <= 1)
continue;
3548 NoOverlapConstraintProto* new_no_overlap =
3552 for (
const int i : intervals) {
3557 context_->
UpdateRuleStats(
"no_overlap: split into disjoint components");
3558 return RemoveConstraint(
ct);
3562 std::vector<int> constant_intervals;
3563 int64_t size_min_of_non_constant_intervals =
3565 for (
int i = 0; i <
proto->intervals_size(); ++i) {
3566 const int interval_index =
proto->intervals(i);
3568 constant_intervals.push_back(interval_index);
3570 size_min_of_non_constant_intervals =
3571 std::min(size_min_of_non_constant_intervals,
3572 context_->
SizeMin(interval_index));
3576 if (!constant_intervals.empty()) {
3578 std::sort(constant_intervals.begin(), constant_intervals.end(),
3579 [
this](
int i1,
int i2) {
3580 return context_->StartMin(i1) < context_->StartMin(i2);
3586 for (
int i = 0; i + 1 < constant_intervals.size(); ++i) {
3587 if (context_->
EndMax(constant_intervals[i]) >
3588 context_->
StartMin(constant_intervals[i + 1])) {
3594 if (constant_intervals.size() ==
proto->intervals_size()) {
3596 return RemoveConstraint(
ct);
3599 absl::flat_hash_set<int> intervals_to_remove;
3603 for (
int i = 0; i + 1 < constant_intervals.size(); ++i) {
3604 const int start = i;
3605 while (i + 1 < constant_intervals.size() &&
3606 context_->
StartMin(constant_intervals[i + 1]) -
3607 context_->
EndMax(constant_intervals[i]) <
3608 size_min_of_non_constant_intervals) {
3611 if (i == start)
continue;
3612 for (
int j = start; j <= i; ++j) {
3613 intervals_to_remove.insert(constant_intervals[j]);
3615 const int64_t new_start = context_->
StartMin(constant_intervals[start]);
3616 const int64_t new_end = context_->
EndMax(constant_intervals[i]);
3618 IntervalConstraintProto* new_interval =
3621 new_interval->mutable_size_view()->set_offset(new_end - new_start);
3622 new_interval->mutable_end_view()->set_offset(new_end);
3626 if (!intervals_to_remove.empty()) {
3628 const int old_size =
proto->intervals_size();
3629 for (
int i = 0; i < old_size; ++i) {
3630 const int interval_index =
proto->intervals(i);
3631 if (intervals_to_remove.contains(interval_index)) {
3634 proto->set_intervals(new_size++, interval_index);
3637 proto->mutable_intervals()->Truncate(new_size);
3639 "no_overlap: merge constant contiguous intervals");
3640 intervals_to_remove.clear();
3641 constant_intervals.clear();
3647 if (
proto->intervals_size() == 1) {
3649 return RemoveConstraint(
ct);
3651 if (
proto->intervals().empty()) {
3653 return RemoveConstraint(
ct);
3659bool CpModelPresolver::PresolveNoOverlap2D(
int c, ConstraintProto*
ct) {
3664 const NoOverlap2DConstraintProto&
proto =
ct->no_overlap_2d();
3665 const int initial_num_boxes =
proto.x_intervals_size();
3667 bool has_zero_sizes =
false;
3668 bool x_constant =
true;
3669 bool y_constant =
true;
3673 std::vector<Rectangle> bounding_boxes;
3674 std::vector<int> active_boxes;
3675 for (
int i = 0; i <
proto.x_intervals_size(); ++i) {
3676 const int x_interval_index =
proto.x_intervals(i);
3677 const int y_interval_index =
proto.y_intervals(i);
3684 if (
proto.boxes_with_null_area_can_overlap() &&
3685 (context_->
SizeMax(x_interval_index) == 0 ||
3686 context_->
SizeMax(y_interval_index) == 0)) {
3687 if (
proto.boxes_with_null_area_can_overlap())
continue;
3688 has_zero_sizes =
true;
3690 ct->mutable_no_overlap_2d()->set_x_intervals(new_size, x_interval_index);
3691 ct->mutable_no_overlap_2d()->set_y_intervals(new_size, y_interval_index);
3692 bounding_boxes.push_back(
3693 {IntegerValue(context_->
StartMin(x_interval_index)),
3694 IntegerValue(context_->
EndMax(x_interval_index)),
3695 IntegerValue(context_->
StartMin(y_interval_index)),
3696 IntegerValue(context_->
EndMax(y_interval_index))});
3697 active_boxes.push_back(new_size);
3709 bounding_boxes, absl::MakeSpan(active_boxes));
3710 if (components.size() > 1) {
3711 for (
const absl::Span<int> boxes : components) {
3712 if (boxes.size() <= 1)
continue;
3714 NoOverlap2DConstraintProto* new_no_overlap_2d =
3716 for (
const int b : boxes) {
3718 new_no_overlap_2d->add_y_intervals(
proto.y_intervals(
b));
3722 context_->
UpdateRuleStats(
"no_overlap_2d: split into disjoint components");
3723 return RemoveConstraint(
ct);
3726 if (!has_zero_sizes && (x_constant || y_constant)) {
3728 "no_overlap_2d: a dimension is constant, splitting into many no "
3730 std::vector<IndexedInterval> indexed_intervals;
3731 for (
int i = 0; i < new_size; ++i) {
3732 int x =
proto.x_intervals(i);
3733 int y =
proto.y_intervals(i);
3735 indexed_intervals.push_back({x, IntegerValue(context_->
StartMin(y)),
3736 IntegerValue(context_->
EndMax(y))});
3738 std::vector<std::vector<int>> no_overlaps;
3741 for (
const std::vector<int>& no_overlap : no_overlaps) {
3745 for (
const int i : no_overlap) {
3750 return RemoveConstraint(
ct);
3753 if (new_size < initial_num_boxes) {
3755 ct->mutable_no_overlap_2d()->mutable_x_intervals()->Truncate(new_size);
3756 ct->mutable_no_overlap_2d()->mutable_y_intervals()->Truncate(new_size);
3759 if (new_size == 0) {
3761 return RemoveConstraint(
ct);
3764 if (new_size == 1) {
3766 return RemoveConstraint(
ct);
3769 return new_size < initial_num_boxes;
3772bool CpModelPresolver::PresolveCumulative(ConstraintProto*
ct) {
3775 CumulativeConstraintProto*
proto =
ct->mutable_cumulative();
3776 bool changed =
false;
3777 int num_fixed_demands = 0;
3778 const int64_t capacity_max = context_->
MaxOf(
proto->capacity());
3782 bool domain_changed =
false;
3784 proto->capacity(), Domain(0, capacity_max), &domain_changed)) {
3787 if (domain_changed) {
3796 int num_zero_demand_removed = 0;
3797 int num_zero_size_removed = 0;
3798 int num_incompatible_demands = 0;
3799 for (
int i = 0; i <
proto->intervals_size(); ++i) {
3802 const int demand_ref =
proto->demands(i);
3803 const int64_t demand_max = context_->
MaxOf(demand_ref);
3804 if (demand_max == 0) {
3805 num_zero_demand_removed++;
3808 if (context_->
IsFixed(demand_ref)) {
3809 num_fixed_demands++;
3814 num_zero_size_removed++;
3818 if (context_->
MinOf(demand_ref) > capacity_max) {
3820 ConstraintProto* interval_ct =
3822 DCHECK_EQ(interval_ct->enforcement_literal_size(), 1);
3823 const int literal = interval_ct->enforcement_literal(0);
3827 num_incompatible_demands++;
3831 "cumulative: performed demand exceeds capacity.");
3835 proto->set_intervals(new_size,
proto->intervals(i));
3836 proto->set_demands(new_size,
proto->demands(i));
3837 if (!
proto->energies().empty()) {
3838 *
proto->mutable_energies(new_size) =
proto->energies(i);
3843 if (new_size < proto->intervals_size()) {
3845 proto->mutable_intervals()->Truncate(new_size);
3846 proto->mutable_demands()->Truncate(new_size);
3847 if (!
proto->energies().empty()) {
3848 proto->mutable_energies()->erase(
3849 proto->mutable_energies()->begin() + new_size,
3850 proto->mutable_energies()->end());
3854 if (num_zero_demand_removed > 0) {
3856 "cumulative: removed intervals with no demands");
3858 if (num_zero_size_removed > 0) {
3860 "cumulative: removed intervals with a size of zero");
3862 if (num_incompatible_demands > 0) {
3864 "cumulative: removed intervals demands greater than the capacity");
3870 for (
int i = 0; i <
proto->demands_size(); ++i) {
3872 const int demand_ref =
proto->demands(i);
3874 bool domain_changed =
false;
3879 if (domain_changed) {
3881 "cumulative: fit demand in [0..capacity_max]");
3893 if (
proto->intervals_size() > 1) {
3894 std::vector<IndexedInterval> indexed_intervals;
3895 for (
int i = 0; i <
proto->intervals().size(); ++i) {
3897 indexed_intervals.push_back({i, IntegerValue(context_->
StartMin(
index)),
3900 std::vector<std::vector<int>> components;
3903 if (components.size() > 1) {
3904 for (
const std::vector<int>& component : components) {
3905 CumulativeConstraintProto* new_cumulative =
3907 for (
const int i : component) {
3909 new_cumulative->add_demands(
proto->demands(i));
3911 new_cumulative->set_capacity(
proto->capacity());
3914 context_->
UpdateRuleStats(
"cumulative: split into disjoint components");
3915 return RemoveConstraint(
ct);
3923 std::map<int64_t, int64_t> time_to_demand_deltas;
3924 const int64_t capacity_min = context_->
MinOf(
proto->capacity());
3925 for (
int i = 0; i <
proto->intervals_size(); ++i) {
3926 const int interval_index =
proto->intervals(i);
3927 const int64_t demand_max = context_->
MaxOf(
proto->demands(i));
3928 time_to_demand_deltas[context_->
StartMin(interval_index)] += demand_max;
3929 time_to_demand_deltas[context_->
EndMax(interval_index)] -= demand_max;
3938 int num_possible_overloads = 0;
3939 int64_t current_load = 0;
3940 absl::flat_hash_map<int64_t, int64_t> num_possible_overloads_before;
3941 for (
const auto& it : time_to_demand_deltas) {
3942 num_possible_overloads_before[it.first] = num_possible_overloads;
3943 current_load += it.second;
3944 if (current_load > capacity_min) {
3945 ++num_possible_overloads;
3951 if (num_possible_overloads == 0) {
3953 "cumulative: max profile is always under the min capacity");
3954 return RemoveConstraint(
ct);
3964 for (
int i = 0; i <
proto->intervals_size(); ++i) {
3982 const int num_diff = num_possible_overloads_before.at(
end_max) -
3983 num_possible_overloads_before.at(
start_min);
3984 if (num_diff == 0)
continue;
3986 proto->set_intervals(new_size,
proto->intervals(i));
3987 proto->set_demands(new_size,
proto->demands(i));
3991 if (new_size < proto->intervals_size()) {
3993 proto->mutable_intervals()->Truncate(new_size);
3994 proto->mutable_demands()->Truncate(new_size);
3996 "cumulative: remove never conflicting intervals.");
4000 if (
proto->intervals().empty()) {
4002 return RemoveConstraint(
ct);
4006 int64_t max_of_performed_demand_mins = 0;
4007 int64_t sum_of_max_demands = 0;
4008 for (
int i = 0; i <
proto->intervals_size(); ++i) {
4009 const ConstraintProto& interval_ct =
4012 const int demand_ref =
proto->demands(i);
4013 sum_of_max_demands += context_->
MaxOf(demand_ref);
4015 if (interval_ct.enforcement_literal().empty()) {
4016 max_of_performed_demand_mins =
4017 std::max(max_of_performed_demand_mins, context_->
MinOf(demand_ref));
4021 const int capacity_ref =
proto->capacity();
4022 if (max_of_performed_demand_mins > context_->
MinOf(capacity_ref)) {
4025 capacity_ref, Domain(max_of_performed_demand_mins,
4031 if (max_of_performed_demand_mins > context_->
MaxOf(capacity_ref)) {
4032 context_->
UpdateRuleStats(
"cumulative: cannot fit performed demands");
4036 if (sum_of_max_demands <= context_->MinOf(capacity_ref)) {
4037 context_->
UpdateRuleStats(
"cumulative: capacity exceeds sum of demands");
4038 return RemoveConstraint(
ct);
4042 if (num_fixed_demands ==
proto->intervals_size() &&
4045 for (
int i = 0; i <
ct->cumulative().demands_size(); ++i) {
4046 const int64_t
demand = context_->
MinOf(
ct->cumulative().demands(i));
4048 if (gcd == 1)
break;
4052 for (
int i = 0; i <
ct->cumulative().demands_size(); ++i) {
4053 const int64_t
demand = context_->
MinOf(
ct->cumulative().demands(i));
4059 ct->mutable_cumulative()->clear_energies();
4061 const int64_t old_capacity = context_->
MinOf(
proto->capacity());
4064 "cumulative: divide demands and capacity by gcd");
4069 for (LinearExpressionProto& exp :
4070 *(
ct->mutable_cumulative()->mutable_energies())) {
4071 changed |= CanonicalizeLinearExpression(*
ct, &exp);
4076 const int num_intervals =
proto->intervals_size();
4077 const int capacity_ref =
proto->capacity();
4079 bool with_start_view =
false;
4080 std::vector<int> start_refs(num_intervals, -1);
4082 int num_duration_one = 0;
4083 int num_greater_half_capacity = 0;
4085 bool has_optional_interval =
false;
4086 for (
int i = 0; i < num_intervals; ++i) {
4090 const ConstraintProto&
ct =
4092 const IntervalConstraintProto&
interval =
ct.interval();
4093 if (
interval.has_start_view()) with_start_view =
true;
4095 const int demand_ref =
proto->demands(i);
4104 const int64_t demand_min = context_->
MinOf(demand_ref);
4105 const int64_t demand_max = context_->
MaxOf(demand_ref);
4106 if (demand_min > capacity_max / 2) {
4107 num_greater_half_capacity++;
4109 if (demand_min > capacity_max) {
4110 context_->
UpdateRuleStats(
"cumulative: demand_min exceeds capacity max");
4114 CHECK_EQ(
ct.enforcement_literal().size(), 1);
4120 }
else if (demand_max > capacity_max) {
4121 if (
ct.enforcement_literal().empty()) {
4123 "cumulative: demand_max exceeds capacity max.");
4133 "cumulative: demand_max of optional interval exceeds capacity.");
4138 if (num_greater_half_capacity == num_intervals) {
4139 if (num_duration_one == num_intervals && !has_optional_interval &&
4146 return RemoveConstraint(
ct);
4152 for (
int i = 0; i <
proto->demands_size(); ++i) {
4153 const int demand_ref =
proto->demands(i);
4154 const int64_t demand_max = context_->
MaxOf(demand_ref);
4155 if (demand_max > context_->
MinOf(capacity_ref)) {
4156 ConstraintProto* capacity_gt =
4163 capacity_gt->mutable_linear()->add_vars(capacity_ref);
4164 capacity_gt->mutable_linear()->add_coeffs(1);
4165 capacity_gt->mutable_linear()->add_vars(demand_ref);
4166 capacity_gt->mutable_linear()->add_coeffs(-1);
4167 capacity_gt->mutable_linear()->add_domain(0);
4168 capacity_gt->mutable_linear()->add_domain(
4180 return RemoveConstraint(
ct);
4187bool CpModelPresolver::PresolveRoutes(ConstraintProto*
ct) {
4190 RoutesConstraintProto&
proto = *
ct->mutable_routes();
4192 const int old_size =
proto.literals_size();
4194 std::vector<bool> has_incoming_or_outgoing_arcs;
4195 const int num_arcs =
proto.literals_size();
4196 for (
int i = 0; i < num_arcs; ++i) {
4197 const int ref =
proto.literals(i);
4204 proto.set_literals(new_size, ref);
4208 if (
tail >= has_incoming_or_outgoing_arcs.size()) {
4209 has_incoming_or_outgoing_arcs.resize(
tail + 1,
false);
4211 if (
head >= has_incoming_or_outgoing_arcs.size()) {
4212 has_incoming_or_outgoing_arcs.resize(
head + 1,
false);
4214 has_incoming_or_outgoing_arcs[
tail] =
true;
4215 has_incoming_or_outgoing_arcs[
head] =
true;
4218 if (old_size > 0 && new_size == 0) {
4223 "routes: graph with nodes and no arcs");
4226 if (new_size < num_arcs) {
4227 proto.mutable_literals()->Truncate(new_size);
4228 proto.mutable_tails()->Truncate(new_size);
4229 proto.mutable_heads()->Truncate(new_size);
4235 for (
int n = 0; n < has_incoming_or_outgoing_arcs.size(); ++n) {
4236 if (!has_incoming_or_outgoing_arcs[n]) {
4238 "routes: node ", n,
" misses incoming or outgoing arcs"));
4245bool CpModelPresolver::PresolveCircuit(ConstraintProto*
ct) {
4248 CircuitConstraintProto&
proto = *
ct->mutable_circuit();
4252 ct->mutable_circuit()->mutable_heads());
4256 std::vector<std::vector<int>> incoming_arcs;
4257 std::vector<std::vector<int>> outgoing_arcs;
4259 const int num_arcs =
proto.literals_size();
4260 for (
int i = 0; i < num_arcs; ++i) {
4261 const int ref =
proto.literals(i);
4269 incoming_arcs[
head].push_back(ref);
4270 outgoing_arcs[
tail].push_back(ref);
4274 for (
int i = 0; i < num_nodes; ++i) {
4275 if (incoming_arcs[i].empty() || outgoing_arcs[i].empty()) {
4276 return MarkConstraintAsFalse(
ct);
4285 bool loop_again =
true;
4286 int num_fixed_at_true = 0;
4287 while (loop_again) {
4289 for (
const auto* node_to_refs : {&incoming_arcs, &outgoing_arcs}) {
4290 for (
const std::vector<int>& refs : *node_to_refs) {
4291 if (refs.size() == 1) {
4293 ++num_fixed_at_true;
4302 for (
const int ref : refs) {
4312 if (num_true == 1) {
4313 for (
const int ref : refs) {
4314 if (ref != true_ref) {
4315 if (!context_->
IsFixed(ref)) {
4326 if (num_fixed_at_true > 0) {
4333 int circuit_start = -1;
4334 std::vector<int>
next(num_nodes, -1);
4335 std::vector<int> new_in_degree(num_nodes, 0);
4336 std::vector<int> new_out_degree(num_nodes, 0);
4337 for (
int i = 0; i < num_arcs; ++i) {
4338 const int ref =
proto.literals(i);
4346 circuit_start =
proto.tails(i);
4350 ++new_out_degree[
proto.tails(i)];
4351 ++new_in_degree[
proto.heads(i)];
4354 proto.set_literals(new_size,
proto.literals(i));
4364 for (
int i = 0; i < num_nodes; ++i) {
4365 if (new_in_degree[i] == 0 || new_out_degree[i] == 0) {
4371 if (circuit_start != -1) {
4372 std::vector<bool> visited(num_nodes,
false);
4373 int current = circuit_start;
4374 while (current != -1 && !visited[current]) {
4375 visited[current] =
true;
4376 current =
next[current];
4378 if (current == circuit_start) {
4381 std::vector<bool> has_self_arc(num_nodes,
false);
4382 for (
int i = 0; i < num_arcs; ++i) {
4383 if (visited[
proto.tails(i)])
continue;
4385 has_self_arc[
proto.tails(i)] =
true;
4391 for (
int n = 0; n < num_nodes; ++n) {
4392 if (!visited[n] && !has_self_arc[n]) {
4394 return MarkConstraintAsFalse(
ct);
4398 return RemoveConstraint(
ct);
4402 if (num_true == new_size) {
4404 return RemoveConstraint(
ct);
4410 for (
int i = 0; i < num_nodes; ++i) {
4411 for (
const std::vector<int>* arc_literals :
4412 {&incoming_arcs[i], &outgoing_arcs[i]}) {
4413 std::vector<int> literals;
4414 for (
const int ref : *arc_literals) {
4420 literals.push_back(ref);
4422 if (literals.size() == 2 && literals[0] !=
NegatedRef(literals[1])) {
4431 if (new_size < num_arcs) {
4432 proto.mutable_tails()->Truncate(new_size);
4433 proto.mutable_heads()->Truncate(new_size);
4434 proto.mutable_literals()->Truncate(new_size);
4441bool CpModelPresolver::PresolveAutomaton(ConstraintProto*
ct) {
4444 AutomatonConstraintProto&
proto = *
ct->mutable_automaton();
4445 if (
proto.vars_size() == 0 ||
proto.transition_label_size() == 0) {
4449 bool all_have_same_affine_relation =
true;
4450 std::vector<AffineRelation::Relation> affine_relations;
4451 for (
int v = 0; v <
proto.vars_size(); ++v) {
4452 const int var =
ct->automaton().vars(v);
4454 affine_relations.push_back(r);
4455 if (r.representative ==
var) {
4456 all_have_same_affine_relation =
false;
4459 if (v > 0 && (r.coeff != affine_relations[v - 1].coeff ||
4460 r.offset != affine_relations[v - 1].offset)) {
4461 all_have_same_affine_relation =
false;
4466 if (all_have_same_affine_relation) {
4467 for (
int v = 0; v <
proto.vars_size(); ++v) {
4470 const AffineRelation::Relation rep = affine_relations.front();
4472 for (
int t = 0; t <
proto.transition_tail_size(); ++t) {
4473 const int64_t label =
proto.transition_label(t);
4474 int64_t inverse_label = (label - rep.offset) / rep.coeff;
4475 if (inverse_label * rep.coeff + rep.offset == label) {
4476 if (new_size != t) {
4477 proto.set_transition_tail(new_size,
proto.transition_tail(t));
4478 proto.set_transition_head(new_size,
proto.transition_head(t));
4480 proto.set_transition_label(new_size, inverse_label);
4484 if (new_size <
proto.transition_tail_size()) {
4485 proto.mutable_transition_tail()->Truncate(new_size);
4486 proto.mutable_transition_label()->Truncate(new_size);
4487 proto.mutable_transition_head()->Truncate(new_size);
4495 for (
int v = 1; v <
proto.vars_size(); ++v) {
4500 for (
int t = 0; t <
proto.transition_tail_size(); ++t) {
4501 const int64_t label =
proto.transition_label(t);
4502 if (hull.Contains(label)) {
4503 if (new_size != t) {
4504 proto.set_transition_tail(new_size,
proto.transition_tail(t));
4505 proto.set_transition_label(new_size, label);
4506 proto.set_transition_head(new_size,
proto.transition_head(t));
4511 if (new_size <
proto.transition_tail_size()) {
4512 proto.mutable_transition_tail()->Truncate(new_size);
4513 proto.mutable_transition_label()->Truncate(new_size);
4514 proto.mutable_transition_head()->Truncate(new_size);
4519 const int n =
proto.vars_size();
4520 const std::vector<int> vars = {
proto.vars().begin(),
proto.vars().end()};
4523 std::vector<std::set<int64_t>> reachable_states(n + 1);
4524 reachable_states[0].insert(
proto.starting_state());
4525 reachable_states[n] = {
proto.final_states().begin(),
4526 proto.final_states().end()};
4533 for (
int t = 0; t <
proto.transition_tail_size(); ++t) {
4534 const int64_t
tail =
proto.transition_tail(t);
4535 const int64_t label =
proto.transition_label(t);
4536 const int64_t
head =
proto.transition_head(t);
4539 reachable_states[
time + 1].insert(
head);
4543 std::vector<std::set<int64_t>> reached_values(n);
4547 std::set<int64_t> new_set;
4548 for (
int t = 0; t <
proto.transition_tail_size(); ++t) {
4549 const int64_t
tail =
proto.transition_tail(t);
4550 const int64_t label =
proto.transition_label(t);
4551 const int64_t
head =
proto.transition_head(t);
4556 new_set.insert(
tail);
4557 reached_values[
time].insert(label);
4559 reachable_states[
time].swap(new_set);
4562 bool removed_values =
false;
4567 {reached_values[time].begin(), reached_values[time].end()}),
4572 if (removed_values) {
4578bool CpModelPresolver::PresolveReservoir(ConstraintProto*
ct) {
4582 bool changed =
false;
4584 ReservoirConstraintProto& mutable_reservoir = *
ct->mutable_reservoir();
4585 if (mutable_reservoir.actives().empty()) {
4587 for (
int i = 0; i < mutable_reservoir.times_size(); ++i) {
4588 mutable_reservoir.add_actives(true_literal);
4593 const auto& demand_is_null = [&](
int i) {
4594 return mutable_reservoir.demands(i) == 0 ||
4600 for (
int i = 0; i < mutable_reservoir.demands_size(); ++i) {
4601 if (demand_is_null(i)) num_zeros++;
4604 if (num_zeros > 0) {
4607 for (
int i = 0; i < mutable_reservoir.demands_size(); ++i) {
4608 if (demand_is_null(i))
continue;
4609 mutable_reservoir.set_demands(new_size, mutable_reservoir.demands(i));
4610 mutable_reservoir.set_times(new_size, mutable_reservoir.times(i));
4611 mutable_reservoir.set_actives(new_size, mutable_reservoir.actives(i));
4615 mutable_reservoir.mutable_demands()->Truncate(new_size);
4616 mutable_reservoir.mutable_times()->Truncate(new_size);
4617 mutable_reservoir.mutable_actives()->Truncate(new_size);
4620 "reservoir: remove zero demands or inactive events.");
4623 const int num_events = mutable_reservoir.demands_size();
4624 int64_t gcd = mutable_reservoir.demands().empty()
4626 : std::abs(mutable_reservoir.demands(0));
4627 int num_positives = 0;
4628 int num_negatives = 0;
4629 int64_t max_sum_of_positive_demands = 0;
4630 int64_t min_sum_of_negative_demands = 0;
4631 for (
int i = 0; i < num_events; ++i) {
4632 const int64_t
demand = mutable_reservoir.demands(i);
4636 max_sum_of_positive_demands +=
demand;
4640 min_sum_of_negative_demands +=
demand;
4644 if (min_sum_of_negative_demands >= mutable_reservoir.min_level() &&
4645 max_sum_of_positive_demands <= mutable_reservoir.max_level()) {
4647 return RemoveConstraint(
ct);
4650 if (min_sum_of_negative_demands > mutable_reservoir.max_level() ||
4651 max_sum_of_positive_demands < mutable_reservoir.min_level()) {
4656 if (min_sum_of_negative_demands > mutable_reservoir.min_level()) {
4657 mutable_reservoir.set_min_level(min_sum_of_negative_demands);
4659 "reservoir: increase min_level to reachable value");
4662 if (max_sum_of_positive_demands < mutable_reservoir.max_level()) {
4663 mutable_reservoir.set_max_level(max_sum_of_positive_demands);
4664 context_->
UpdateRuleStats(
"reservoir: reduce max_level to reachable value");
4667 if (mutable_reservoir.min_level() <= 0 &&
4668 mutable_reservoir.max_level() >= 0 &&
4669 (num_positives == 0 || num_negatives == 0)) {
4674 int64_t fixed_contrib = 0;
4675 for (
int i = 0; i < mutable_reservoir.demands_size(); ++i) {
4676 const int64_t
demand = mutable_reservoir.demands(i);
4679 const int active = mutable_reservoir.actives(i);
4681 sum->add_vars(active);
4685 sum->add_coeffs(-
demand);
4689 sum->add_domain(mutable_reservoir.min_level() - fixed_contrib);
4690 sum->add_domain(mutable_reservoir.max_level() - fixed_contrib);
4692 return RemoveConstraint(
ct);
4696 for (
int i = 0; i < mutable_reservoir.demands_size(); ++i) {
4697 mutable_reservoir.set_demands(i, mutable_reservoir.demands(i) / gcd);
4703 const Domain reduced_domain =
4704 Domain({mutable_reservoir.min_level(), mutable_reservoir.max_level()})
4705 .InverseMultiplicationBy(gcd);
4706 mutable_reservoir.set_min_level(reduced_domain.Min());
4707 mutable_reservoir.set_max_level(reduced_domain.Max());
4708 context_->
UpdateRuleStats(
"reservoir: simplify demands and levels by gcd.");
4711 if (num_positives == 1 && num_negatives > 0) {
4713 "TODO reservoir: one producer, multiple consumers.");
4716 absl::flat_hash_set<std::pair<int, int>> time_active_set;
4717 for (
int i = 0; i < mutable_reservoir.demands_size(); ++i) {
4718 const std::pair<int, int> key = std::make_pair(
4719 mutable_reservoir.times(i), mutable_reservoir.actives(i));
4720 if (time_active_set.contains(key)) {
4721 context_->
UpdateRuleStats(
"TODO reservoir: merge synchronized events.");
4724 time_active_set.insert(key);
4734void CpModelPresolver::ExtractBoolAnd() {
4735 absl::flat_hash_map<int, int> ref_to_bool_and;
4737 std::vector<int> to_remove;
4738 for (
int c = 0; c < num_constraints; ++c) {
4742 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr &&
4743 ct.bool_or().literals().size() == 2) {
4747 to_remove.push_back(c);
4751 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kAtMostOne &&
4752 ct.at_most_one().literals().size() == 2) {
4753 AddImplication(
ct.at_most_one().literals(0),
4756 to_remove.push_back(c);
4762 for (
const int c : to_remove) {
4771void CpModelPresolver::Probe() {
4796 auto* local_param =
model.GetOrCreate<SatParameters>();
4797 *local_param = context_->
params();
4800 model.GetOrCreate<TimeLimit>()->MergeWithGlobalTimeLimit(
4802 model.Register<ModelRandomGenerator>(context_->
random());
4803 auto* encoder =
model.GetOrCreate<IntegerEncoder>();
4804 encoder->DisableImplicationBetweenLiteral();
4805 auto* mapping =
model.GetOrCreate<CpModelMapping>();
4812 auto* sat_solver =
model.GetOrCreate<SatSolver>();
4813 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
4814 if (mapping->ConstraintIsAlreadyLoaded(&
ct))
continue;
4816 if (sat_solver->IsModelUnsat()) {
4818 "after loading constraint during probing ",
ct.ShortDebugString()));
4821 encoder->AddAllImplicationsBetweenAssociatedLiterals();
4822 if (!sat_solver->Propagate()) {
4824 "during probing initial propagation");
4831 auto* implication_graph =
model.GetOrCreate<BinaryImplicationGraph>();
4832 auto* prober =
model.GetOrCreate<Prober>();
4833 prober->ProbeBooleanVariables(1.0);
4835 model.GetOrCreate<TimeLimit>()->GetElapsedDeterministicTime());
4836 if (sat_solver->IsModelUnsat() || !implication_graph->DetectEquivalences()) {
4841 CHECK_EQ(sat_solver->CurrentDecisionLevel(), 0);
4842 for (
int i = 0; i < sat_solver->LiteralTrail().
Index(); ++i) {
4843 const Literal l = sat_solver->LiteralTrail()[i];
4844 const int var = mapping->GetProtoVariableFromBooleanVariable(l.Variable());
4852 auto* integer_trail =
model.GetOrCreate<IntegerTrail>();
4853 for (
int var = 0;
var < num_variables; ++
var) {
4856 if (!mapping->IsBoolean(
var)) {
4859 integer_trail->InitialVariableDomain(mapping->Integer(
var)))) {
4866 const Literal l = mapping->Literal(
var);
4867 const Literal r = implication_graph->RepresentativeOf(l);
4870 mapping->GetProtoVariableFromBooleanVariable(r.Variable());
4880void CpModelPresolver::PresolvePureSatPart() {
4886 SatPostsolver sat_postsolver(num_variables);
4887 SatPresolver sat_presolver(&sat_postsolver, logger_);
4888 sat_presolver.SetNumVariables(num_variables);
4889 sat_presolver.SetTimeLimit(context_->
time_limit());
4891 SatParameters params = context_->
params();
4898 if (params.cp_model_postsolve_with_full_solver()) {
4905 params.set_presolve_use_bva(
false);
4906 sat_presolver.SetParameters(params);
4909 absl::flat_hash_set<int> used_variables;
4910 auto convert = [&used_variables](
int ref) {
4912 if (
RefIsPositive(ref))
return Literal(BooleanVariable(ref),
true);
4913 return Literal(BooleanVariable(
NegatedRef(ref)),
false);
4923 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr ||
4924 ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolAnd) {
4943 std::vector<Literal> clause;
4944 int num_removed_constraints = 0;
4948 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr) {
4949 ++num_removed_constraints;
4951 for (
const int ref :
ct.bool_or().literals()) {
4952 clause.push_back(convert(ref));
4954 for (
const int ref :
ct.enforcement_literal()) {
4955 clause.push_back(convert(ref).Negated());
4957 sat_presolver.AddClause(clause);
4964 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolAnd) {
4965 ++num_removed_constraints;
4966 std::vector<Literal> clause;
4967 for (
const int ref :
ct.enforcement_literal()) {
4968 clause.push_back(convert(ref).Negated());
4971 for (
const int ref :
ct.bool_and().literals()) {
4972 clause.back() = convert(ref);
4973 sat_presolver.AddClause(clause);
4983 if (num_removed_constraints == 0)
return;
4993 std::vector<bool> can_be_removed(num_variables,
false);
4994 for (
int i = 0; i < num_variables; ++i) {
4996 can_be_removed[i] =
true;
5002 if (used_variables.contains(i) && context_->
IsFixed(i)) {
5004 sat_presolver.AddClause({convert(i)});
5006 sat_presolver.AddClause({convert(
NegatedRef(i))});
5014 const int num_passes = params.presolve_use_bva() ? 4 : 1;
5015 for (
int i = 0; i < num_passes; ++i) {
5016 const int old_num_clause = sat_postsolver.NumClauses();
5017 if (!sat_presolver.Presolve(can_be_removed)) {
5018 VLOG(1) <<
"UNSAT during SAT presolve.";
5021 if (old_num_clause == sat_postsolver.NumClauses())
break;
5025 const int new_num_variables = sat_presolver.NumVariables();
5027 VLOG(1) <<
"New variables added by the SAT presolver.";
5029 i < new_num_variables; ++i) {
5030 IntegerVariableProto* var_proto =
5033 var_proto->add_domain(1);
5039 ExtractClauses(
true, sat_presolver, context_->
working_model);
5047 ExtractClauses(
false, sat_postsolver,
5055void CpModelPresolver::ExpandObjective() {
5074 int unique_expanded_constraint = -1;
5075 const bool objective_was_a_single_variable =
5082 absl::flat_hash_set<int> relevant_constraints;
5083 std::vector<int> var_to_num_relevant_constraints(num_variables, 0);
5084 for (
int ct_index = 0; ct_index < num_constraints; ++ct_index) {
5087 if (!
ct.enforcement_literal().empty() ||
5088 ct.constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
5089 ct.linear().domain().size() != 2 ||
5090 ct.linear().domain(0) !=
ct.linear().domain(1)) {
5094 relevant_constraints.insert(ct_index);
5095 const int num_terms =
ct.linear().vars_size();
5096 for (
int i = 0; i < num_terms; ++i) {
5097 var_to_num_relevant_constraints[
PositiveRef(
ct.linear().vars(i))]++;
5101 std::set<int> var_to_process;
5103 const int var = entry.first;
5105 if (var_to_num_relevant_constraints[
var] != 0) {
5106 var_to_process.insert(
var);
5111 int num_expansions = 0;
5112 int last_expanded_objective_var;
5113 absl::flat_hash_set<int> processed_vars;
5114 std::vector<int> new_vars_in_objective;
5115 while (!relevant_constraints.empty()) {
5117 int objective_var = -1;
5118 while (!var_to_process.empty()) {
5119 const int var = *var_to_process.begin();
5120 CHECK(!processed_vars.contains(
var));
5121 if (var_to_num_relevant_constraints[
var] == 0) {
5122 processed_vars.insert(
var);
5123 var_to_process.erase(
var);
5128 var_to_process.erase(
var);
5131 objective_var =
var;
5135 if (objective_var == -1)
break;
5137 processed_vars.insert(objective_var);
5138 var_to_process.erase(objective_var);
5140 int expanded_linear_index = -1;
5141 int64_t objective_coeff_in_expanded_constraint;
5142 int64_t size_of_expanded_constraint = 0;
5143 const auto& non_deterministic_list =
5145 std::vector<int> constraints_with_objective(non_deterministic_list.begin(),
5146 non_deterministic_list.end());
5147 std::sort(constraints_with_objective.begin(),
5148 constraints_with_objective.end());
5149 for (
const int ct_index : constraints_with_objective) {
5150 if (relevant_constraints.count(ct_index) == 0)
continue;
5151 const ConstraintProto&
ct =
5156 relevant_constraints.erase(ct_index);
5157 const int num_terms =
ct.linear().vars_size();
5158 for (
int i = 0; i < num_terms; ++i) {
5159 var_to_num_relevant_constraints[
PositiveRef(
ct.linear().vars(i))]--;
5171 bool is_present =
false;
5172 int64_t objective_coeff;
5173 for (
int i = 0; i < num_terms; ++i) {
5174 const int ref =
ct.linear().vars(i);
5175 const int64_t coeff =
ct.linear().coeffs(i);
5177 CHECK(!is_present) <<
"Duplicate variables not supported.";
5179 objective_coeff = (ref == objective_var) ? coeff : -coeff;
5192 if (std::abs(objective_coeff) == 1 &&
5193 num_terms > size_of_expanded_constraint) {
5194 expanded_linear_index = ct_index;
5195 size_of_expanded_constraint = num_terms;
5196 objective_coeff_in_expanded_constraint = objective_coeff;
5200 if (expanded_linear_index != -1) {
5203 CHECK_EQ(std::abs(objective_coeff_in_expanded_constraint), 1);
5204 const ConstraintProto&
ct =
5207 objective_var, objective_coeff_in_expanded_constraint,
ct,
5208 &new_vars_in_objective)) {
5213 context_->
UpdateRuleStats(
"objective: expanded objective constraint.");
5216 for (
const int var : new_vars_in_objective) {
5217 if (!processed_vars.contains(
var)) var_to_process.insert(
var);
5230 for (
int i = 0; i < size_of_expanded_constraint; ++i) {
5231 const int ref =
ct.linear().vars(i);
5236 -
ct.linear().coeffs(i)))
5237 .RelaxIfTooComplex();
5239 implied_domain = implied_domain.InverseMultiplicationBy(
5240 objective_coeff_in_expanded_constraint);
5244 if (implied_domain.IsIncludedIn(context_->
DomainOf(objective_var))) {
5246 context_->
UpdateRuleStats(
"objective: removed objective constraint.");
5252 unique_expanded_constraint = expanded_linear_index;
5257 last_expanded_objective_var = objective_var;
5263 if (num_expansions == 1 && objective_was_a_single_variable &&
5264 unique_expanded_constraint != -1) {
5266 "objective: removed unique objective constraint.");
5268 unique_expanded_constraint);
5270 mutable_ct->
Clear();
5283void CpModelPresolver::MergeNoOverlapConstraints() {
5287 int old_num_no_overlaps = 0;
5288 int old_num_intervals = 0;
5291 std::vector<int> disjunctive_index;
5292 std::vector<std::vector<Literal>> cliques;
5293 for (
int c = 0; c < num_constraints; ++c) {
5295 if (
ct.constraint_case() != ConstraintProto::ConstraintCase::kNoOverlap) {
5298 std::vector<Literal> clique;
5299 for (
const int i :
ct.no_overlap().intervals()) {
5300 clique.push_back(Literal(BooleanVariable(i),
true));
5302 cliques.push_back(clique);
5303 disjunctive_index.push_back(c);
5305 old_num_no_overlaps++;
5306 old_num_intervals += clique.size();
5308 if (old_num_no_overlaps == 0)
return;
5312 local_model.GetOrCreate<Trail>()->Resize(num_constraints);
5313 auto* graph = local_model.GetOrCreate<BinaryImplicationGraph>();
5314 graph->Resize(num_constraints);
5315 for (
const std::vector<Literal>& clique : cliques) {
5318 CHECK(graph->AddAtMostOne(clique));
5320 CHECK(graph->DetectEquivalences());
5321 graph->TransformIntoMaxCliques(
5325 int new_num_no_overlaps = 0;
5326 int new_num_intervals = 0;
5327 for (
int i = 0; i < cliques.size(); ++i) {
5328 const int ct_index = disjunctive_index[i];
5329 ConstraintProto*
ct =
5332 if (cliques[i].empty())
continue;
5333 for (
const Literal l : cliques[i]) {
5334 CHECK(l.IsPositive());
5335 ct->mutable_no_overlap()->add_intervals(l.Variable().value());
5337 new_num_no_overlaps++;
5338 new_num_intervals += cliques[i].size();
5340 if (old_num_intervals != new_num_intervals ||
5341 old_num_no_overlaps != new_num_no_overlaps) {
5342 VLOG(1) << absl::StrCat(
"Merged ", old_num_no_overlaps,
" no-overlaps (",
5343 old_num_intervals,
" intervals) into ",
5344 new_num_no_overlaps,
" no-overlaps (",
5345 new_num_intervals,
" intervals).");
5354void CpModelPresolver::TransformIntoMaxCliques() {
5357 auto convert = [](
int ref) {
5358 if (
RefIsPositive(ref))
return Literal(BooleanVariable(ref),
true);
5359 return Literal(BooleanVariable(
NegatedRef(ref)),
false);
5364 std::vector<std::vector<Literal>> cliques;
5366 for (
int c = 0; c < num_constraints; ++c) {
5368 if (
ct->constraint_case() == ConstraintProto::ConstraintCase::kAtMostOne) {
5369 std::vector<Literal> clique;
5370 for (
const int ref :
ct->at_most_one().literals()) {
5371 clique.push_back(convert(ref));
5373 cliques.push_back(clique);
5374 if (RemoveConstraint(
ct)) {
5377 }
else if (
ct->constraint_case() ==
5378 ConstraintProto::ConstraintCase::kBoolAnd) {
5379 if (
ct->enforcement_literal().size() != 1)
continue;
5380 const Literal enforcement = convert(
ct->enforcement_literal(0));
5381 for (
const int ref :
ct->bool_and().literals()) {
5382 if (ref ==
ct->enforcement_literal(0))
continue;
5383 cliques.push_back({enforcement, convert(ref).Negated()});
5385 if (RemoveConstraint(
ct)) {
5391 int64_t num_literals_before = 0;
5392 const int num_old_cliques = cliques.size();
5397 local_model.GetOrCreate<Trail>()->Resize(num_variables);
5398 auto* graph = local_model.GetOrCreate<BinaryImplicationGraph>();
5399 graph->Resize(num_variables);
5400 for (
const std::vector<Literal>& clique : cliques) {
5401 num_literals_before += clique.size();
5402 if (!graph->AddAtMostOne(clique)) {
5406 if (!graph->DetectEquivalences()) {
5409 graph->TransformIntoMaxCliques(
5415 for (
int var = 0;
var < num_variables; ++
var) {
5416 const Literal l = Literal(BooleanVariable(
var),
true);
5417 if (graph->RepresentativeOf(l) != l) {
5418 const Literal r = graph->RepresentativeOf(l);
5420 var, r.IsPositive() ? r.Variable().value()
5425 int num_new_cliques = 0;
5426 int64_t num_literals_after = 0;
5427 for (
const std::vector<Literal>& clique : cliques) {
5428 if (clique.empty())
continue;
5430 num_literals_after += clique.size();
5432 for (
const Literal
literal : clique) {
5434 ct->mutable_at_most_one()->add_literals(
literal.Variable().value());
5436 ct->mutable_at_most_one()->add_literals(
5442 PresolveAtMostOne(
ct);
5445 if (num_new_cliques != num_old_cliques) {
5446 context_->
UpdateRuleStats(
"at_most_one: transformed into max clique.");
5449 if (num_old_cliques != num_new_cliques ||
5450 num_literals_before != num_literals_after) {
5451 SOLVER_LOG(logger_,
"[MaxClique] Merged ", num_old_cliques,
"(",
5452 num_literals_before,
" literals) into ", num_new_cliques,
"(",
5453 num_literals_after,
" literals) at_most_ones.");
5462 if (ExploitEquivalenceRelations(c,
ct)) {
5467 if (PresolveEnforcementLiteral(
ct)) {
5472 switch (
ct->constraint_case()) {
5473 case ConstraintProto::ConstraintCase::kBoolOr:
5474 return PresolveBoolOr(
ct);
5475 case ConstraintProto::ConstraintCase::kBoolAnd:
5476 return PresolveBoolAnd(
ct);
5477 case ConstraintProto::ConstraintCase::kAtMostOne:
5478 return PresolveAtMostOne(
ct);
5479 case ConstraintProto::ConstraintCase::kExactlyOne:
5480 return PresolveExactlyOne(
ct);
5481 case ConstraintProto::ConstraintCase::kBoolXor:
5482 return PresolveBoolXor(
ct);
5483 case ConstraintProto::ConstraintCase::kIntMax:
5484 if (
ct->int_max().vars_size() == 2 &&
5486 return PresolveIntAbs(
ct);
5488 return PresolveIntMax(
ct);
5490 case ConstraintProto::ConstraintCase::kIntMin:
5491 return PresolveIntMin(
ct);
5492 case ConstraintProto::ConstraintCase::kLinMax:
5493 return PresolveLinMax(
ct);
5494 case ConstraintProto::ConstraintCase::kLinMin:
5495 return PresolveLinMin(
ct);
5496 case ConstraintProto::ConstraintCase::kIntProd:
5497 return PresolveIntProd(
ct);
5498 case ConstraintProto::ConstraintCase::kIntDiv:
5499 return PresolveIntDiv(
ct);
5500 case ConstraintProto::ConstraintCase::kIntMod:
5501 return PresolveIntMod(
ct);
5502 case ConstraintProto::ConstraintCase::kLinear: {
5511 for (
const int ref :
ct->linear().vars()) {
5517 if (CanonicalizeLinear(
ct)) {
5520 if (PresolveSmallLinear(
ct)) {
5523 if (PropagateDomainsInLinear(c,
ct)) {
5526 if (PresolveSmallLinear(
ct)) {
5530 if (RemoveSingletonInLinear(
ct)) {
5535 if (PresolveSmallLinear(
ct)) {
5539 if (PresolveLinearOnBooleans(
ct)) {
5543 const int old_num_enforcement_literals =
ct->enforcement_literal_size();
5544 ExtractEnforcementLiteralFromLinearConstraint(c,
ct);
5545 if (
ct->constraint_case() ==
5546 ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET) {
5550 if (
ct->enforcement_literal_size() > old_num_enforcement_literals &&
5551 PresolveSmallLinear(
ct)) {
5554 PresolveLinearEqualityModuloTwo(
ct);
5558 case ConstraintProto::ConstraintCase::kInterval:
5559 return PresolveInterval(c,
ct);
5560 case ConstraintProto::ConstraintCase::kInverse:
5561 return PresolveInverse(
ct);
5562 case ConstraintProto::ConstraintCase::kElement:
5563 return PresolveElement(
ct);
5564 case ConstraintProto::ConstraintCase::kTable:
5565 return PresolveTable(
ct);
5566 case ConstraintProto::ConstraintCase::kAllDiff:
5567 return PresolveAllDiff(
ct);
5568 case ConstraintProto::ConstraintCase::kNoOverlap:
5569 return PresolveNoOverlap(
ct);
5570 case ConstraintProto::ConstraintCase::kNoOverlap2D:
5571 return PresolveNoOverlap2D(c,
ct);
5572 case ConstraintProto::ConstraintCase::kCumulative:
5573 return PresolveCumulative(
ct);
5574 case ConstraintProto::ConstraintCase::kCircuit:
5575 return PresolveCircuit(
ct);
5576 case ConstraintProto::ConstraintCase::kRoutes:
5577 return PresolveRoutes(
ct);
5578 case ConstraintProto::ConstraintCase::kAutomaton:
5579 return PresolveAutomaton(
ct);
5580 case ConstraintProto::ConstraintCase::kReservoir:
5581 return PresolveReservoir(
ct);
5591bool CpModelPresolver::ProcessSetPPCSubset(
5592 int c1,
int c2,
const std::vector<int>& c2_minus_c1,
5593 const std::vector<int>& original_constraint_index,
5594 std::vector<bool>* marked_for_removal) {
5597 CHECK(!(*marked_for_removal)[c1]);
5598 CHECK(!(*marked_for_removal)[c2]);
5601 original_constraint_index[c1]);
5603 original_constraint_index[c2]);
5613 for (
const int literal : c2_minus_c1) {
5620 ConstraintProto copy = *ct2;
5626 (*marked_for_removal)[c1] =
true;
5637 (*marked_for_removal)[c2] =
true;
5647 (*marked_for_removal)[c1] =
true;
5664 int num_matches = 0;
5665 for (
int i = 0; i < ct2->
linear().vars().size(); ++i) {
5667 if (literals.contains(
var)) {
5675 if (num_matches != literals.size())
return true;
5681 for (
int i = 0; i < ct2->
linear().vars().size(); ++i) {
5684 if (literals.contains(
var)) {
5686 if (coeff == min_coeff)
continue;
5713bool CpModelPresolver::ProcessSetPPC() {
5718 std::vector<uint64_t> signatures;
5722 std::vector<std::vector<int>> constraint_literals;
5726 std::vector<std::vector<int>> literals_to_constraints;
5729 std::vector<bool> removed;
5733 std::vector<int> original_constraint_index;
5737 int num_setppc_constraints = 0;
5738 std::vector<int> temp_literals;
5740 for (
int c = 0; c < num_constraints; ++c) {
5752 constraint_literals.push_back(GetLiteralsFromSetPPCConstraint(*
ct));
5763 const int size =
ct->linear().vars().size();
5764 if (size <= 2)
continue;
5769 temp_literals.clear();
5770 for (
int i = 0; i < size; ++i) {
5771 const int var =
ct->linear().vars(i);
5772 const int64_t coeff =
ct->linear().coeffs(i);
5775 if (coeff < 0)
continue;
5776 temp_literals.push_back(
var);
5778 if (temp_literals.size() <= 2)
continue;
5779 constraint_literals.push_back(temp_literals);
5784 uint64_t signature = 0;
5785 for (
const int literal : constraint_literals.back()) {
5787 signature |= (int64_t{1} << (positive_literal % 64));
5789 if (positive_literal >= literals_to_constraints.size()) {
5790 literals_to_constraints.resize(positive_literal + 1);
5792 literals_to_constraints[positive_literal].push_back(
5793 num_setppc_constraints);
5795 signatures.push_back(signature);
5796 removed.push_back(
false);
5797 original_constraint_index.push_back(c);
5798 num_setppc_constraints++;
5800 VLOG(1) <<
"#setppc constraints: " << num_setppc_constraints;
5803 absl::flat_hash_set<std::pair<int, int>> compared_constraints;
5804 for (
const std::vector<int>& literal_to_constraints :
5805 literals_to_constraints) {
5806 for (
int index1 = 0; index1 < literal_to_constraints.size(); ++index1) {
5809 const int c1 = literal_to_constraints[index1];
5810 if (removed[c1])
continue;
5811 const std::vector<int>& c1_literals = constraint_literals[c1];
5813 for (
int index2 = index1 + 1; index2 < literal_to_constraints.size();
5815 const int c2 = literal_to_constraints[index2];
5816 if (removed[c2])
continue;
5817 if (removed[c1])
break;
5820 if (c1 == c2)
continue;
5824 std::pair<int, int>(c1, c2))) {
5827 compared_constraints.insert({c1, c2});
5831 if (compared_constraints.size() >= 50000)
return true;
5833 const bool smaller = (signatures[c1] & ~signatures[c2]) == 0;
5834 const bool larger = (signatures[c2] & ~signatures[c1]) == 0;
5835 if (!(smaller || larger))
continue;
5838 const std::vector<int>& c2_literals = constraint_literals[c2];
5842 std::vector<int> c1_minus_c2;
5844 std::vector<int> c2_minus_c1;
5847 if (c1_minus_c2.empty()) {
5848 if (!ProcessSetPPCSubset(c1, c2, c2_minus_c1,
5849 original_constraint_index, &removed)) {
5852 }
else if (c2_minus_c1.empty()) {
5853 if (!ProcessSetPPCSubset(c2, c1, c1_minus_c2,
5854 original_constraint_index, &removed)) {
5865void CpModelPresolver::TryToSimplifyDomain(
int var) {
5874 if (r.representative !=
var)
return;
5890 std::vector<int> literals;
5891 std::vector<int> equality_constraints;
5892 std::vector<int> other_constraints;
5893 absl::flat_hash_map<int64_t, int> value_to_equal_literal;
5894 absl::flat_hash_map<int64_t, int> value_to_not_equal_literal;
5897 if (c < 0)
continue;
5901 int64_t coeff =
ct.linear().coeffs(0);
5902 if (std::abs(coeff) != 1 ||
ct.enforcement_literal().size() != 1) {
5910 if (rhs.IsFixed()) {
5911 const int64_t
value = rhs.FixedValue();
5912 if (value_to_equal_literal.contains(
value)) {
5916 equality_constraints.push_back(c);
5917 literals.push_back(
ct.enforcement_literal(0));
5918 value_to_equal_literal[
value] =
ct.enforcement_literal(0);
5920 const Domain complement =
5922 if (complement.IsEmpty()) {
5927 if (complement.IsFixed()) {
5928 const int64_t
value = complement.FixedValue();
5929 if (value_to_not_equal_literal.contains(
value)) {
5933 other_constraints.push_back(c);
5934 value_to_not_equal_literal[
value] =
ct.enforcement_literal(0);
5943 if (value_to_equal_literal.size() != context_->
DomainOf(
var).
Size()) {
5947 if (!value_to_equal_literal.contains(
value)) {
5951 if (value_to_not_equal_literal.contains(
value) &&
5952 value_to_equal_literal[
value] !=
5966 ConstraintProto encoding_ct;
5967 LinearConstraintProto* linear = encoding_ct.mutable_linear();
5968 const int64_t coeff_in_equality = -1;
5969 linear->add_vars(
var);
5970 linear->add_coeffs(coeff_in_equality);
5972 std::vector<int64_t> all_values;
5973 for (
const auto entry : value_to_equal_literal) {
5974 all_values.push_back(entry.first);
5976 std::sort(all_values.begin(), all_values.end());
5983 CHECK(!all_values.empty());
5984 const int64_t min_value = all_values[0];
5985 linear->add_domain(-min_value);
5986 linear->add_domain(-min_value);
5987 for (
const int64_t
value : all_values) {
5988 if (
value == min_value)
continue;
5989 const int enf = value_to_equal_literal.at(
value);
5990 const int64_t coeff =
value - min_value;
5992 linear->add_vars(enf);
5993 linear->add_coeffs(coeff);
5996 linear->set_domain(0, encoding_ct.linear().domain(0) - coeff);
5997 linear->set_domain(1, encoding_ct.linear().domain(1) - coeff);
5999 linear->add_coeffs(-coeff);
6005 "TODO variables: only used in objective and in full encoding");
6009 "variables: only used in objective and in full encoding");
6017 for (
const int c : equality_constraints) {
6023 for (
const int c : other_constraints) {
6030 std::sort(literals.begin(), literals.end());
6031 for (
const int literal : literals) {
6032 new_ct->mutable_exactly_one()->add_literals(
literal);
6037 PresolveExactlyOne(new_ct);
6061 Domain union_of_domain;
6062 int num_positive = 0;
6063 std::vector<int> constraint_indices_to_remove;
6069 constraint_indices_to_remove.push_back(c);
6071 if (
ct.enforcement_literal().size() != 1 ||
6074 ct.linear().vars().size() != 1) {
6078 if (
ct.enforcement_literal(0) ==
var) ++num_positive;
6079 if (ct_var != -1 &&
PositiveRef(
ct.linear().vars(0)) != ct_var) {
6084 union_of_domain = union_of_domain.UnionWith(
6087 ?
ct.linear().coeffs(0)
6088 : -
ct.linear().coeffs(0)));
6090 if (!abort && num_positive == 1) {
6094 context_->
UpdateRuleStats(
"variables: removable enforcement literal");
6095 for (
const int c : constraint_indices_to_remove) {
6114 if (domain.Size() == 2 && (domain.Min() != 0 || domain.Max() != 1)) {
6119 if (domain.NumIntervals() != domain.Size())
return;
6121 const int64_t var_min = domain.Min();
6122 int64_t gcd = domain[1].start - var_min;
6124 const ClosedInterval& i = domain[
index];
6126 const int64_t shifted_value = i.start - var_min;
6130 if (gcd == 1)
break;
6132 if (gcd == 1)
return;
6136 std::vector<int64_t> scaled_values;
6137 scaled_values.reserve(domain.NumIntervals());
6138 for (
const ClosedInterval i : domain) {
6140 const int64_t shifted_value = i.start - var_min;
6141 scaled_values.push_back(shifted_value / gcd);
6153void CpModelPresolver::EncodeAllAffineRelations() {
6154 int64_t num_added = 0;
6159 if (r.representative ==
var)
continue;
6166 if (!PresolveAffineRelationIfAny(
var))
break;
6173 auto* arg =
ct->mutable_linear();
6176 arg->add_vars(r.representative);
6177 arg->add_coeffs(-r.coeff);
6178 arg->add_domain(r.offset);
6179 arg->add_domain(r.offset);
6187 if (num_added > 0) {
6188 SOLVER_LOG(logger_, num_added,
" affine relations still in the model.");
6193bool CpModelPresolver::PresolveAffineRelationIfAny(
int var) {
6197 if (r.representative ==
var)
return true;
6216 auto* arg =
ct->mutable_linear();
6219 arg->add_vars(r.representative);
6220 arg->add_coeffs(-r.coeff);
6221 arg->add_domain(r.offset);
6222 arg->add_domain(r.offset);
6228void CpModelPresolver::PresolveToFixPoint() {
6232 const int64_t max_num_operations =
6240 absl::flat_hash_set<std::pair<int, int>> var_constraint_pair_already_called;
6247 std::deque<int> queue;
6248 for (
int c = 0; c < in_queue.size(); ++c) {
6250 ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET) {
6261 std::shuffle(queue.begin(), queue.end(), *context_->
random());
6263 std::sort(queue.begin(), queue.end(), [
this](
int a,
int b) {
6264 const int score_a = context_->ConstraintToVars(a).size();
6265 const int score_b = context_->ConstraintToVars(b).size();
6266 return score_a < score_b || (score_a == score_b && a < b);
6276 const int c = queue.front();
6277 in_queue[c] =
false;
6280 const int old_num_constraint =
6284 SOLVER_LOG(logger_,
"Unsat after presolving constraint #", c,
6285 " (warning, dump might be inconsistent): ",
6290 const int new_num_constraints =
6292 if (new_num_constraints > old_num_constraint) {
6294 in_queue.resize(new_num_constraints,
true);
6295 for (
int c = old_num_constraint; c < new_num_constraints; ++c) {
6318 for (
int v = 0; v < current_num_variables; ++v) {
6320 if (!PresolveAffineRelationIfAny(v))
return;
6325 TryToSimplifyDomain(v);
6334 for (
int v = 0; v < num_vars; ++v) {
6336 if (constraints.size() != 1)
continue;
6337 const int c = *constraints.begin();
6338 if (c < 0)
continue;
6344 std::pair<int, int>(v, c))) {
6347 var_constraint_pair_already_called.insert({v, c});
6355 for (
int i = 0; i < 2; ++i) {
6366 if (c >= 0 && !in_queue[c]) {
6374 if (!queue.empty() || i == 1)
break;
6384 VarDomination var_dom;
6385 DualBoundStrengthening dual_bound_strengthening;
6387 &dual_bound_strengthening);
6388 if (!dual_bound_strengthening.Strengthen(context_))
return;
6400 std::sort(queue.begin(), queue.end());
6415 for (
int c = 0; c < num_constraints; ++c) {
6417 switch (
ct->constraint_case()) {
6418 case ConstraintProto::ConstraintCase::kNoOverlap:
6420 if (PresolveNoOverlap(
ct)) {
6424 case ConstraintProto::ConstraintCase::kNoOverlap2D:
6426 if (PresolveNoOverlap2D(c,
ct)) {
6430 case ConstraintProto::ConstraintCase::kCumulative:
6432 if (PresolveCumulative(
ct)) {
6436 case ConstraintProto::ConstraintCase::kBoolOr: {
6439 for (
const auto& pair :
6441 bool modified =
false;
6464 const CpModelProto& in_model,
const std::vector<int>& ignored_constraints) {
6465 const absl::flat_hash_set<int> ignored_constraints_set(
6466 ignored_constraints.begin(), ignored_constraints.end());
6471 if (ignored_constraints_set.contains(c))
continue;
6474 if (OneEnforcementLiteralIsFalse(
ct) &&
6478 switch (
ct.constraint_case()) {
6483 if (!CopyBoolOr(
ct))
return CreateUnsatModel();
6487 if (!CopyBoolAnd(
ct))
return CreateUnsatModel();
6491 if (!CopyLinear(
ct))
return CreateUnsatModel();
6495 if (!CopyAtMostOne(
ct))
return CreateUnsatModel();
6499 if (!CopyExactlyOne(
ct))
return CreateUnsatModel();
6503 if (!CopyInterval(
ct, c))
return CreateUnsatModel();
6514 for (
int c = starting_constraint_index_;
6519 const auto& it = interval_mapping_.find(*ref);
6520 if (it != interval_mapping_.end()) {
6532 temp_enforcement_literals_.clear();
6535 skipped_non_zero_++;
6538 temp_enforcement_literals_.push_back(lit);
6541 temp_enforcement_literals_.end());
6544bool ModelCopy::OneEnforcementLiteralIsFalse(
const ConstraintProto&
ct)
const {
6545 for (
const int lit :
ct.enforcement_literal()) {
6553bool ModelCopy::CopyBoolOr(
const ConstraintProto&
ct) {
6554 temp_literals_.clear();
6555 for (
const int lit :
ct.enforcement_literal()) {
6559 for (
const int lit :
ct.bool_or().literals()) {
6564 skipped_non_zero_++;
6566 temp_literals_.push_back(lit);
6573 ->Add(temp_literals_.begin(), temp_literals_.end());
6574 return !temp_literals_.empty();
6577bool ModelCopy::CopyBoolAnd(
const ConstraintProto&
ct) {
6578 bool at_least_one_false =
false;
6579 int num_non_fixed_literals = 0;
6580 for (
const int lit :
ct.bool_and().literals()) {
6582 at_least_one_false =
true;
6586 num_non_fixed_literals++;
6590 if (at_least_one_false) {
6595 for (
const int lit :
ct.enforcement_literal()) {
6597 skipped_non_zero_++;
6602 return !bool_or->literals().empty();
6603 }
else if (num_non_fixed_literals > 0) {
6605 CopyEnforcementLiterals(
ct, new_ct);
6606 BoolArgumentProto* bool_and = new_ct->mutable_bool_and();
6607 bool_and->mutable_literals()->Reserve(num_non_fixed_literals);
6608 for (
const int lit :
ct.bool_and().literals()) {
6610 skipped_non_zero_++;
6613 bool_and->add_literals(lit);
6619bool ModelCopy::CopyLinear(
const ConstraintProto&
ct) {
6620 non_fixed_variables_.clear();
6621 non_fixed_coefficients_.clear();
6623 for (
int i = 0; i <
ct.linear().vars_size(); ++i) {
6624 const int ref =
ct.linear().vars(i);
6625 const int64_t coeff =
ct.linear().coeffs(i);
6627 offset += coeff * context_->
MinOf(ref);
6628 skipped_non_zero_++;
6630 non_fixed_variables_.push_back(ref);
6631 non_fixed_coefficients_.push_back(coeff);
6635 const Domain new_domain =
6637 if (non_fixed_variables_.empty() && !new_domain.Contains(0)) {
6638 if (
ct.enforcement_literal().empty()) {
6641 temp_literals_.clear();
6642 for (
const int literal :
ct.enforcement_literal()) {
6644 skipped_non_zero_++;
6652 ->Add(temp_literals_.begin(), temp_literals_.end());
6653 return !temp_literals_.empty();
6657 CopyEnforcementLiterals(
ct, new_ct);
6658 LinearConstraintProto* linear = new_ct->mutable_linear();
6659 linear->mutable_vars()->Add(non_fixed_variables_.begin(),
6660 non_fixed_variables_.end());
6661 linear->mutable_coeffs()->Add(non_fixed_coefficients_.begin(),
6662 non_fixed_coefficients_.end());
6667bool ModelCopy::CopyAtMostOne(
const ConstraintProto&
ct) {
6669 temp_literals_.clear();
6670 for (
const int lit :
ct.at_most_one().literals()) {
6672 skipped_non_zero_++;
6675 temp_literals_.push_back(lit);
6679 if (temp_literals_.size() <= 1)
return true;
6680 if (num_true > 1)
return false;
6684 CopyEnforcementLiterals(
ct, new_ct);
6685 new_ct->mutable_at_most_one()->mutable_literals()->Add(temp_literals_.begin(),
6686 temp_literals_.end());
6690bool ModelCopy::CopyExactlyOne(
const ConstraintProto&
ct) {
6692 temp_literals_.clear();
6693 for (
const int lit :
ct.exactly_one().literals()) {
6695 skipped_non_zero_++;
6698 temp_literals_.push_back(lit);
6702 if (temp_literals_.empty() || num_true > 1)
return false;
6706 CopyEnforcementLiterals(
ct, new_ct);
6707 new_ct->mutable_exactly_one()->mutable_literals()->Add(temp_literals_.begin(),
6708 temp_literals_.end());
6712bool ModelCopy::CopyInterval(
const ConstraintProto&
ct,
int c) {
6714 CHECK_EQ(starting_constraint_index_, 0)
6715 <<
"Adding new interval constraints to partially filled model is not "
6722bool ModelCopy::CreateUnsatModel() {
6736 return context->NotifyThatModelIsUnsat();
6741 if (!in_model.
name().empty()) {
6742 context->working_model->set_name(in_model.
name());
6748 *
context->working_model->mutable_search_strategy() =
6767 std::vector<int>* postsolve_mapping) {
6773 std::vector<int>* postsolve_mapping)
6774 : postsolve_mapping_(postsolve_mapping),
6786 for (
const auto& decision_strategy :
6833 PresolveEnforcementLiteral(
ct);
6834 switch (
ct->constraint_case()) {
6835 case ConstraintProto::ConstraintCase::kBoolOr:
6838 case ConstraintProto::ConstraintCase::kBoolAnd:
6839 PresolveBoolAnd(
ct);
6841 case ConstraintProto::ConstraintCase::kAtMostOne:
6842 PresolveAtMostOne(
ct);
6844 case ConstraintProto::ConstraintCase::kExactlyOne:
6845 PresolveExactlyOne(
ct);
6847 case ConstraintProto::ConstraintCase::kLinear:
6848 CanonicalizeLinear(
ct);
6860 for (
int iter = 0; iter < context_->
params().max_presolve_iterations();
6865 int old_num_non_empty_constraints = 0;
6869 if (type == ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET)
continue;
6870 old_num_non_empty_constraints++;
6877 PresolveToFixPoint();
6887 PresolveToFixPoint();
6896 PresolvePureSatPart();
6910 for (
int c = 0; c < old_size; ++c) {
6912 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear) {
6915 ExtractAtMostOneFromLinear(
ct);
6920 if (iter == 0) TransformIntoMaxCliques();
6937 PresolveToFixPoint();
6943 old_num_non_empty_constraints)) {
6950 MergeNoOverlapConstraints();
6960 EncodeAllAffineRelations();
6968 const std::vector<std::pair<int, int>> duplicates =
6970 for (
const auto [dup, rep] : duplicates) {
6978 if (type == ConstraintProto::ConstraintCase::kInterval) {
6991 "duplicate: merged rhs of linear constraint");
6994 if (!MarkConstraintAsFalse(
6996 SOLVER_LOG(logger_,
"Unsat after merging two linear constraints");
7035 absl::flat_hash_set<int> used_variables;
7040 strategy.clear_transformations();
7041 for (
const int ref : copy.
variables()) {
7050 used_variables.insert(
var);
7058 if (strategy.variable_selection_strategy() !=
7061 strategy.add_transformations();
7062 t->
set_index(strategy.variables_size());
7066 strategy.add_variables(rep);
7073 strategy.add_variables(ref);
7090 postsolve_mapping_->clear();
7092 int num_free_variables = 0;
7102 ++num_free_variables;
7108 mapping[i] = postsolve_mapping_->size();
7109 postsolve_mapping_->push_back(i);
7111 context_->
UpdateRuleStats(absl::StrCat(
"presolve: ", num_free_variables,
7112 " free variables removed."));
7115 std::shuffle(postsolve_mapping_->begin(), postsolve_mapping_->end(),
7117 for (
int i = 0; i < postsolve_mapping_->size(); ++i) {
7118 mapping[(*postsolve_mapping_)[i]] = i;
7143 if (!error.empty()) {
7144 SOLVER_LOG(logger_,
"Error while validating postsolved model: ", error);
7150 if (!error.empty()) {
7152 "Error while validating mapping_model model: ", error);
7165 auto mapping_function = [&mapping](
int* ref) {
7178 mapping_function(&mutable_ref);
7184 mapping_function(&mutable_ref);
7192 std::vector<int> new_indices(copy.
variables().size(), -1);
7193 for (
int i = 0; i < copy.
variables().size(); ++i) {
7197 new_indices[i] = strategy.variables_size();
7201 strategy.clear_transformations();
7203 CHECK_LT(transform.index(), new_indices.size());
7204 const int new_index = new_indices[transform.index()];
7205 if (new_index == -1)
continue;
7206 auto* new_transform = strategy.add_transformations();
7207 *new_transform = transform;
7208 CHECK_LT(new_index, strategy.variables().size());
7209 new_transform->set_index(new_index);
7216 absl::flat_hash_set<int> used_vars;
7219 for (
int i = 0; i < mutable_hint->vars_size(); ++i) {
7220 const int old_ref = mutable_hint->
vars(i);
7221 int64_t old_value = mutable_hint->values(i);
7224 if (old_value <
context.MinOf(old_ref)) old_value =
context.MinOf(old_ref);
7225 if (old_value >
context.MaxOf(old_ref)) old_value =
context.MaxOf(old_ref);
7232 const int image = mapping[
var];
7234 if (!used_vars.insert(image).second)
continue;
7235 mutable_hint->set_vars(new_size, image);
7236 mutable_hint->set_values(new_size,
value);
7241 mutable_hint->mutable_vars()->Truncate(new_size);
7242 mutable_hint->mutable_values()->Truncate(new_size);
7249 std::vector<IntegerVariableProto> new_variables;
7250 for (
int i = 0; i < mapping.size(); ++i) {
7251 const int image = mapping[i];
7252 if (image < 0)
continue;
7253 if (image >= new_variables.size()) {
7270ConstraintProto CopyConstraintForDuplicateDetection(
const ConstraintProto&
ct) {
7271 ConstraintProto copy =
ct;
7274 copy.mutable_linear()->clear_domain();
7282 std::vector<std::pair<int, int>> result;
7286 absl::flat_hash_map<uint64_t, int> equiv_constraints;
7290 for (
int c = 0; c < num_constraints; ++c) {
7300 s = copy.SerializeAsString();
7302 const uint64_t
hash = absl::Hash<std::string>()(s);
7303 const auto [it, inserted] = equiv_constraints.insert({
hash, c});
7306 const int other_c_with_same_hash = it->second;
7307 copy = CopyConstraintForDuplicateDetection(
7309 if (s == copy.SerializeAsString()) {
7310 result.push_back({c, other_c_with_same_hash});
#define DCHECK_NE(val1, val2)
#define CHECK_LT(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define CHECK_GT(val1, val2)
#define DCHECK_GE(val1, val2)
#define CHECK_NE(val1, val2)
#define DCHECK_GT(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define CHECK_LE(val1, val2)
#define DCHECK_EQ(val1, val2)
#define VLOG(verboselevel)
We call domain any subset of Int64 = [kint64min, kint64max].
Domain InverseMultiplicationBy(const int64_t coeff) const
Returns {x ∈ Int64, ∃ e ∈ D, x * coeff = e}.
bool IsIncludedIn(const Domain &domain) const
Returns true iff D is included in the given domain.
bool Contains(int64_t value) const
Returns true iff value is in Domain.
Domain AdditionWith(const Domain &domain) const
Returns {x ∈ Int64, ∃ a ∈ D, ∃ b ∈ domain, x = a + b}.
ClosedInterval front() const
int64_t Size() const
Returns the number of elements in the domain.
Domain UnionWith(const Domain &domain) const
Returns the union of D and domain.
Domain MultiplicationBy(int64_t coeff, bool *exact=nullptr) const
Returns {x ∈ Int64, ∃ e ∈ D, x = e * coeff}.
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
bool IsEmpty() const
Returns true if this is the empty set.
int64_t SmallestValue() const
Returns the value closest to zero.
Domain RelaxIfTooComplex() const
If NumIntervals() is too large, this return a superset of the domain.
static Domain FromValues(std::vector< int64_t > values)
Creates a domain from the union of an unsorted list of integer values.
Domain DivisionBy(int64_t coeff) const
Returns {x ∈ Int64, ∃ e ∈ D, x = e / coeff}.
DomainIteratorBeginEnd Values() const &
Domain PositiveModuloBySuperset(const Domain &modulo) const
Returns a superset of {x ∈ Int64, ∃ e ∈ D, ∃ m ∈ modulo, x = e % m }.
static int64_t GCD64(int64_t x, int64_t y)
bool LoggingIsEnabled() const
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
void AdvanceDeterministicTime(double deterministic_duration)
Advances the deterministic time.
void add_vars(::PROTOBUF_NAMESPACE_ID::int32 value)
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int32 > * mutable_literals()
void add_literals(::PROTOBUF_NAMESPACE_ID::int32 value)
::PROTOBUF_NAMESPACE_ID::int32 literals(int index) const
const ::operations_research::sat::BoolArgumentProto & at_most_one() const
::operations_research::sat::IntervalConstraintProto * mutable_interval()
::operations_research::sat::NoOverlap2DConstraintProto * mutable_no_overlap_2d()
::operations_research::sat::ElementConstraintProto * mutable_element()
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int32 > * mutable_enforcement_literal()
::operations_research::sat::NoOverlapConstraintProto * mutable_no_overlap()
ConstraintCase constraint_case() const
const ::operations_research::sat::LinearConstraintProto & linear() const
void set_name(ArgT0 &&arg0, ArgT... args)
::operations_research::sat::BoolArgumentProto * mutable_bool_and()
const ::operations_research::sat::BoolArgumentProto & exactly_one() const
PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final
::operations_research::sat::AllDifferentConstraintProto * mutable_all_diff()
void Swap(ConstraintProto *other)
::PROTOBUF_NAMESPACE_ID::int32 enforcement_literal(int index) const
void add_enforcement_literal(::PROTOBUF_NAMESPACE_ID::int32 value)
::operations_research::sat::BoolArgumentProto * mutable_exactly_one()
::operations_research::sat::LinearConstraintProto * mutable_linear()
::operations_research::sat::BoolArgumentProto * mutable_bool_or()
::operations_research::sat::CumulativeConstraintProto * mutable_cumulative()
void RemoveEmptyConstraints()
CpModelPresolver(PresolveContext *context, std::vector< int > *postsolve_mapping)
bool PresolveOneConstraint(int c)
::operations_research::sat::DecisionStrategyProto * add_search_strategy()
const std::string & name() const
const ::operations_research::sat::CpObjectiveProto & objective() const
::operations_research::sat::DecisionStrategyProto * mutable_search_strategy(int index)
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int32 > * mutable_assumptions()
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
const ::operations_research::sat::DecisionStrategyProto & search_strategy(int index) const
const ::operations_research::sat::PartialVariableAssignment & solution_hint() const
bool has_objective() const
::operations_research::sat::PartialVariableAssignment * mutable_solution_hint()
::operations_research::sat::ConstraintProto * mutable_constraints(int index)
PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final
const ::operations_research::sat::SymmetryProto & symmetry() const
bool has_solution_hint() const
::operations_research::sat::IntegerVariableProto * mutable_variables(int index)
int variables_size() const
::operations_research::sat::ConstraintProto * add_constraints()
::operations_research::sat::IntegerVariableProto * add_variables()
::operations_research::sat::CpObjectiveProto * mutable_objective()
void clear_solution_hint()
::PROTOBUF_NAMESPACE_ID::int32 assumptions(int index) const
int constraints_size() const
bool has_symmetry() const
const ::operations_research::sat::ConstraintProto & constraints(int index) const
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int32 > * mutable_vars()
void add_intervals(::PROTOBUF_NAMESPACE_ID::int32 value)
::PROTOBUF_NAMESPACE_ID::int32 variables(int index) const
static constexpr VariableSelectionStrategy CHOOSE_FIRST
const ::operations_research::sat::DecisionStrategyProto_AffineTransformation & transformations(int index) const
std::vector< std::pair< int, Domain > > ProcessClause(absl::Span< const int > clause)
void MarkProcessingAsDoneForNow()
int NumDeductions() const
void AddDeduction(int literal_ref, int var, Domain domain)
void add_domain(::PROTOBUF_NAMESPACE_ID::int64 value)
void Swap(IntegerVariableProto *other)
void CopyFrom(const IntegerVariableProto &from)
::operations_research::sat::LinearExpressionProto * mutable_start_view()
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int32 > * mutable_vars()
::PROTOBUF_NAMESPACE_ID::int64 coeffs(int index) const
void set_vars(int index, ::PROTOBUF_NAMESPACE_ID::int32 value)
void set_coeffs(int index, ::PROTOBUF_NAMESPACE_ID::int64 value)
::PROTOBUF_NAMESPACE_ID::int32 vars(int index) const
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 > * mutable_coeffs()
void add_vars(::PROTOBUF_NAMESPACE_ID::int32 value)
void set_offset(::PROTOBUF_NAMESPACE_ID::int64 value)
ModelCopy(PresolveContext *context)
bool ImportAndSimplifyConstraints(const CpModelProto &in_model, const std::vector< int > &ignored_constraints)
void add_x_intervals(::PROTOBUF_NAMESPACE_ID::int32 value)
void add_intervals(::PROTOBUF_NAMESPACE_ID::int32 value)
::PROTOBUF_NAMESPACE_ID::int32 vars(int index) const
bool VariableIsRemovable(int ref) const
int64_t MaxOf(int ref) const
SparseBitset< int64_t > modified_domains
bool StoreAbsRelation(int target_ref, int ref)
bool ConstraintIsInactive(int ct_index) const
bool ConstraintVariableUsageIsConsistent()
const absl::flat_hash_set< int > & VarToConstraints(int var) const
bool ModelIsExpanded() const
void AddImplication(int a, int b)
bool ModelIsUnsat() const
ABSL_MUST_USE_RESULT bool IntersectDomainWith(int ref, const Domain &domain, bool *domain_modified=nullptr)
std::vector< absl::flat_hash_set< int > > var_to_lb_only_constraints
bool ConstraintVariableGraphIsUpToDate() const
bool StoreLiteralImpliesVarNEqValue(int literal, int var, int64_t value)
int64_t StartMin(int ct_ref) const
bool DomainOfVarIsIncludedIn(int var, const Domain &domain)
bool VariableWithCostIsUniqueAndRemovable(int ref) const
void WriteObjectiveToProto() const
int GetLiteralRepresentative(int ref) const
ABSL_MUST_USE_RESULT bool SetLiteralToTrue(int lit)
bool StoreAffineRelation(int ref_x, int ref_y, int64_t coeff, int64_t offset)
std::vector< int > tmp_literals
std::vector< absl::flat_hash_set< int > > var_to_ub_only_constraints
bool ObjectiveDomainIsConstraining() const
CpModelProto * mapping_model
ABSL_MUST_USE_RESULT bool SubstituteVariableInObjective(int var_in_equality, int64_t coeff_in_equality, const ConstraintProto &equality, std::vector< int > *new_vars_in_objective=nullptr)
int GetOrCreateVarValueEncoding(int ref, int64_t value)
void UpdateNewConstraintsVariableUsage()
bool VariableIsUniqueAndRemovable(int ref) const
void RemoveVariableFromAffineRelation(int var)
ABSL_MUST_USE_RESULT bool NotifyThatModelIsUnsat(const std::string &message="")
bool PropagateAffineRelation(int ref)
Domain DomainOf(int ref) const
int64_t num_presolve_operations
void InitializeNewDomains()
int GetVariableRepresentative(int ref) const
int NewIntVar(const Domain &domain)
void MarkVariableAsRemoved(int ref)
ModelRandomGenerator * random()
DomainDeductions deductions
std::vector< Domain > tmp_left_domains
bool DomainIsEmpty(int ref) const
void CanonicalizeDomainOfSizeTwo(int var)
const std::vector< int > & ConstraintToVars(int c) const
bool LiteralIsTrue(int lit) const
const absl::flat_hash_map< int, int64_t > & ObjectiveMap() const
void StoreBooleanEqualityRelation(int ref_a, int ref_b)
int IntervalUsage(int c) const
CpModelProto * working_model
bool IntervalIsConstant(int ct_ref) const
bool DomainContains(int ref, int64_t value) const
bool LiteralIsFalse(int lit) const
void UpdateRuleStats(const std::string &name, int num_times=1)
ABSL_MUST_USE_RESULT bool CanonicalizeObjective()
void RemoveAllVariablesFromAffineRelationConstraint()
AffineRelation::Relation GetAffineRelation(int ref) const
bool VariableIsNotUsedAnymore(int ref) const
void UpdateConstraintVariableUsage(int c)
bool keep_all_feasible_solutions
bool IsFixed(int ref) const
std::vector< Domain > tmp_term_domains
ABSL_MUST_USE_RESULT bool SetLiteralToFalse(int lit)
int LiteralForExpressionMax(const LinearExpressionProto &expr) const
int64_t EndMax(int ct_ref) const
bool ConstraintIsOptional(int ct_ref) const
int GetOrCreateConstantVar(int64_t cst)
const SatParameters & params() const
bool ExpressionIsAffineBoolean(const LinearExpressionProto &expr) const
int64_t SizeMax(int ct_ref) const
bool ExploitExactlyOneInObjective(absl::Span< const int > exactly_one)
SolverLogger * logger() const
Domain DomainSuperSetOf(const LinearExpressionProto &expr) const
absl::flat_hash_set< int > tmp_literal_set
void ReadObjectiveFromProto()
int64_t SizeMin(int ct_ref) const
bool CanBeUsedAsLiteral(int ref) const
bool VariableWasRemoved(int ref) const
void RegisterVariablesUsedInAssumptions()
void ExploitFixedDomain(int var)
int64_t MinOf(int ref) const
bool VariableIsOnlyUsedInEncodingAndMaybeInObjective(int ref) const
bool GetAbsRelation(int target_ref, int *ref)
bool StoreLiteralImpliesVarEqValue(int literal, int var, int64_t value)
const Domain & ObjectiveDomain() const
double merge_no_overlap_work_limit() const
::PROTOBUF_NAMESPACE_ID::int32 symmetry_level() const
bool enumerate_all_solutions() const
bool cp_model_use_sat_presolve() const
bool permute_presolve_constraint_order() const
::operations_research::sat::SatParameters_SearchBranching search_branching() const
::PROTOBUF_NAMESPACE_ID::int32 cp_model_max_num_presolve_operations() const
bool keep_all_feasible_solutions_in_presolve() const
::PROTOBUF_NAMESPACE_ID::int32 cp_model_probing_level() const
bool convert_intervals() const
::PROTOBUF_NAMESPACE_ID::int32 presolve_substitution_level() const
double merge_at_most_one_work_limit() const
bool cp_model_presolve() const
bool fill_tightened_domains_in_response() const
void set_presolve_blocked_clause(bool value)
void set_use_implied_bounds(bool value)
bool presolve_extract_integer_enforcement() const
bool permute_variable_randomly() const
static constexpr SearchBranching FIXED_SEARCH
CpModelProto const * model_proto
ModelSharedTimeLimit * time_limit
GurobiMPCallbackContext * context
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void STLSetDifference(const In1 &a, const In2 &b, Out *out, Compare compare)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
bool ContainsKey(const Collection &collection, const Key &key)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
void LoadVariables(const CpModelProto &model_proto, bool view_all_booleans_as_integers, Model *m)
bool DetectAndExploitSymmetriesInPresolve(PresolveContext *context)
bool LoadConstraint(const ConstraintProto &ct, Model *m)
bool RefIsPositive(int ref)
void SetToNegatedLinearExpression(const LinearExpressionProto &input_expr, LinearExpressionProto *output_negated_expr)
void GetOverlappingIntervalComponents(std::vector< IndexedInterval > *intervals, std::vector< std::vector< int > > *components)
const LiteralIndex kNoLiteralIndex(-1)
std::vector< absl::Span< int > > GetOverlappingRectangleComponents(const std::vector< Rectangle > &rectangles, absl::Span< int > active_rectangles)
bool HasEnforcementLiteral(const ConstraintProto &ct)
void ExpandCpModel(PresolveContext *context)
constexpr int kAffineRelationConstraint
bool PresolveCpModel(PresolveContext *context, std::vector< int > *postsolve_mapping)
void SubstituteVariable(int var, int64_t var_coeff_in_definition, const ConstraintProto &definition, ConstraintProto *ct)
void ApplyToAllLiteralIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
void DetectDominanceRelations(const PresolveContext &context, VarDomination *var_domination, DualBoundStrengthening *dual_bound_strengthening)
void ApplyToAllIntervalIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
void CopyEverythingExceptVariablesAndConstraintsFieldsIntoContext(const CpModelProto &in_model, PresolveContext *context)
int ReindexArcs(IntContainer *tails, IntContainer *heads)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
bool ImportConstraintsWithBasicPresolveIntoContext(const CpModelProto &in_model, PresolveContext *context)
std::vector< std::pair< int, int > > FindDuplicateConstraints(const CpModelProto &model_proto)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
void ApplyToAllVariableIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
void ConstructOverlappingSets(bool already_sorted, std::vector< IndexedInterval > *intervals, std::vector< std::vector< int > > *result)
constexpr int kObjectiveConstraint
void ExtractEncoding(const CpModelProto &model_proto, Model *m)
bool ExploitDominanceRelations(const VarDomination &var_domination, PresolveContext *context)
void ApplyVariableMapping(const std::vector< int > &mapping, const PresolveContext &context)
std::string ValidateCpModel(const CpModelProto &model)
Collection of objects used to extend the Constraint Solver library.
int64_t CapProd(int64_t x, int64_t y)
#define SOLVER_LOG(logger,...)