29 #include "absl/container/flat_hash_map.h"
30 #include "absl/container/flat_hash_set.h"
31 #include "absl/random/random.h"
32 #include "absl/strings/str_join.h"
57 bool CpModelPresolver::RemoveConstraint(ConstraintProto*
ct) {
65 std::vector<int> interval_mapping(context_->
working_model->constraints_size(),
67 int new_num_constraints = 0;
68 const int old_num_non_empty_constraints =
70 for (
int c = 0; c < old_num_non_empty_constraints; ++c) {
71 const auto type = context_->
working_model->constraints(c).constraint_case();
72 if (type == ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET)
continue;
73 if (type == ConstraintProto::ConstraintCase::kInterval) {
74 interval_mapping[c] = new_num_constraints;
76 context_->
working_model->mutable_constraints(new_num_constraints++)
79 context_->
working_model->mutable_constraints()->DeleteSubrange(
80 new_num_constraints, old_num_non_empty_constraints - new_num_constraints);
81 for (ConstraintProto& ct_ref :
84 [&interval_mapping](
int* ref) {
85 *ref = interval_mapping[*ref];
92 bool CpModelPresolver::PresolveEnforcementLiteral(ConstraintProto*
ct) {
97 const int old_size =
ct->enforcement_literal().size();
98 for (
const int literal :
ct->enforcement_literal()) {
107 return RemoveConstraint(
ct);
114 return RemoveConstraint(
ct);
121 const int64 obj_coeff =
125 context_->
UpdateRuleStats(
"enforcement literal with unique direction");
127 return RemoveConstraint(
ct);
131 ct->set_enforcement_literal(new_size++,
literal);
133 ct->mutable_enforcement_literal()->Truncate(new_size);
134 return new_size != old_size;
137 bool CpModelPresolver::PresolveBoolXor(ConstraintProto*
ct) {
142 bool changed =
false;
143 int num_true_literals = 0;
145 for (
const int literal :
ct->bool_xor().literals()) {
166 ct->mutable_bool_xor()->set_literals(new_size++,
literal);
170 }
else if (new_size == 2) {
173 if (num_true_literals % 2 == 1) {
175 ct->mutable_bool_xor()->set_literals(new_size++, true_literal);
177 if (num_true_literals > 1) {
178 context_->
UpdateRuleStats(
"bool_xor: remove even number of true literals");
181 ct->mutable_bool_xor()->mutable_literals()->Truncate(new_size);
185 bool CpModelPresolver::PresolveBoolOr(ConstraintProto*
ct) {
192 for (
const int literal :
ct->enforcement_literal()) {
195 ct->clear_enforcement_literal();
199 bool changed =
false;
202 for (
const int literal :
ct->bool_or().literals()) {
209 return RemoveConstraint(
ct);
217 return RemoveConstraint(
ct);
221 return RemoveConstraint(
ct);
240 return RemoveConstraint(
ct);
253 ct->mutable_bool_or()->mutable_literals()->Clear();
255 ct->mutable_bool_or()->add_literals(lit);
261 ABSL_MUST_USE_RESULT
bool CpModelPresolver::MarkConstraintAsFalse(
262 ConstraintProto*
ct) {
265 ct->mutable_bool_or()->clear_literals();
266 for (
const int lit :
ct->enforcement_literal()) {
269 ct->clear_enforcement_literal();
277 bool CpModelPresolver::PresolveBoolAnd(ConstraintProto*
ct) {
282 for (
const int literal :
ct->bool_and().literals()) {
285 return RemoveConstraint(
ct);
288 bool changed =
false;
290 for (
const int literal :
ct->bool_and().literals()) {
293 return MarkConstraintAsFalse(
ct);
313 ct->mutable_bool_and()->mutable_literals()->Clear();
315 ct->mutable_bool_and()->add_literals(lit);
324 if (
ct->enforcement_literal().size() == 1 &&
325 ct->bool_and().literals().size() == 1) {
326 const int enforcement =
ct->enforcement_literal(0);
336 ct->bool_and().literals(0));
344 bool CpModelPresolver::PresolveAtMostOne(ConstraintProto*
ct) {
349 if (
ct->at_most_one().literals_size() == 1) {
351 return RemoveConstraint(
ct);
355 std::sort(
ct->mutable_at_most_one()->mutable_literals()->begin(),
356 ct->mutable_at_most_one()->mutable_literals()->end());
358 for (
const int literal :
ct->at_most_one().literals()) {
366 bool changed =
false;
368 for (
const int literal :
ct->at_most_one().literals()) {
371 for (
const int other :
ct->at_most_one().literals()) {
376 return RemoveConstraint(
ct);
395 return RemoveConstraint(
ct);
399 ct->mutable_at_most_one()->mutable_literals()->Clear();
401 ct->mutable_at_most_one()->add_literals(lit);
410 bool CpModelPresolver::PresolveExactlyOne(ConstraintProto*
ct) {
416 const auto& literals =
ct->exactly_one().literals();
417 if (literals.size() == 1) {
420 return RemoveConstraint(
ct);
424 if (literals.size() == 2) {
428 return RemoveConstraint(
ct);
433 std::sort(
ct->mutable_exactly_one()->mutable_literals()->begin(),
434 ct->mutable_exactly_one()->mutable_literals()->end());
436 for (
const int literal :
ct->exactly_one().literals()) {
445 bool changed =
false;
447 for (
const int literal :
ct->exactly_one().literals()) {
450 for (
const int other :
ct->exactly_one().literals()) {
455 return RemoveConstraint(
ct);
478 ct->mutable_exactly_one()->mutable_literals()->Clear();
480 ct->mutable_exactly_one()->add_literals(lit);
487 bool CpModelPresolver::PresolveIntMax(ConstraintProto*
ct) {
489 if (
ct->int_max().vars().empty()) {
491 return MarkConstraintAsFalse(
ct);
493 const int target_ref =
ct->int_max().target();
498 bool contains_target_ref =
false;
499 std::set<int> used_ref;
501 for (
const int ref :
ct->int_max().vars()) {
502 if (ref == target_ref) contains_target_ref =
true;
508 used_ref.insert(ref);
509 ct->mutable_int_max()->set_vars(new_size++, ref);
513 if (new_size < ct->int_max().vars_size()) {
516 ct->mutable_int_max()->mutable_vars()->Truncate(new_size);
517 if (contains_target_ref) {
519 for (
const int ref :
ct->int_max().vars()) {
520 if (ref == target_ref)
continue;
521 ConstraintProto* new_ct = context_->
working_model->add_constraints();
522 *new_ct->mutable_enforcement_literal() =
ct->enforcement_literal();
523 auto* arg = new_ct->mutable_linear();
524 arg->add_vars(target_ref);
531 return RemoveConstraint(
ct);
535 Domain infered_domain;
536 for (
const int ref :
ct->int_max().vars()) {
537 infered_domain = infered_domain.UnionWith(
542 bool domain_reduced =
false;
554 const Domain& target_domain = context_->
DomainOf(target_ref);
555 if (infered_domain.IntersectionWith(Domain(
kint64min, target_domain.Max()))
556 .IsIncludedIn(target_domain)) {
557 if (infered_domain.Max() <= target_domain.Max()) {
560 }
else if (
ct->enforcement_literal().empty()) {
562 for (
const int ref :
ct->int_max().vars()) {
565 ref, Domain(
kint64min, target_domain.Max()))) {
573 for (
const int ref :
ct->int_max().vars()) {
574 ConstraintProto* new_ct = context_->
working_model->add_constraints();
575 *(new_ct->mutable_enforcement_literal()) =
ct->enforcement_literal();
576 ct->mutable_linear()->add_vars(ref);
577 ct->mutable_linear()->add_coeffs(1);
579 ct->mutable_linear()->add_domain(target_domain.Max());
586 return RemoveConstraint(
ct);
592 const int size =
ct->int_max().vars_size();
593 const int64 target_max = context_->
MaxOf(target_ref);
594 for (
const int ref :
ct->int_max().vars()) {
601 if (context_->
MaxOf(ref) >= infered_min) {
602 ct->mutable_int_max()->set_vars(new_size++, ref);
605 if (domain_reduced) {
609 bool modified =
false;
610 if (new_size < size) {
612 ct->mutable_int_max()->mutable_vars()->Truncate(new_size);
618 return MarkConstraintAsFalse(
ct);
624 ConstraintProto* new_ct = context_->
working_model->add_constraints();
626 auto* arg = new_ct->mutable_linear();
627 arg->add_vars(target_ref);
629 arg->add_vars(
ct->int_max().vars(0));
634 return RemoveConstraint(
ct);
639 bool CpModelPresolver::PresolveLinMin(ConstraintProto*
ct) {
642 const auto copy =
ct->lin_min();
644 ct->mutable_lin_max()->mutable_target());
645 for (
const LinearExpressionProto& expr : copy.exprs()) {
646 LinearExpressionProto*
const new_expr =
ct->mutable_lin_max()->add_exprs();
649 return PresolveLinMax(
ct);
652 bool CpModelPresolver::PresolveLinMax(ConstraintProto*
ct) {
654 if (
ct->lin_max().exprs().empty()) {
656 return MarkConstraintAsFalse(
ct);
663 bool changed = CanonicalizeLinearExpression(
664 *
ct,
ct->mutable_lin_max()->mutable_target());
665 for (LinearExpressionProto& exp : *(
ct->mutable_lin_max()->mutable_exprs())) {
666 changed |= CanonicalizeLinearExpression(*
ct, &exp);
672 int64 infered_min = context_->
MinOf(
ct->lin_max().target());
673 for (
const LinearExpressionProto& expr :
ct->lin_max().exprs()) {
684 for (
int i = 0; i <
ct->lin_max().exprs_size(); ++i) {
685 const LinearExpressionProto& expr =
ct->lin_max().exprs(i);
686 if (context_->
MaxOf(expr) >= infered_min) {
687 *
ct->mutable_lin_max()->mutable_exprs(new_size) = expr;
692 if (new_size < ct->lin_max().exprs_size()) {
694 ct->mutable_lin_max()->mutable_exprs()->DeleteSubrange(
695 new_size,
ct->lin_max().exprs_size() - new_size);
702 bool CpModelPresolver::PresolveIntAbs(ConstraintProto*
ct) {
705 const int target_ref =
ct->int_max().target();
710 const Domain new_target_domain = var_domain.
UnionWith(var_domain.Negation())
720 const Domain target_domain = context_->
DomainOf(target_ref);
721 const Domain new_var_domain =
722 target_domain.
UnionWith(target_domain.Negation());
732 ConstraintProto* new_ct = context_->
working_model->add_constraints();
733 new_ct->set_name(
ct->name());
734 auto* arg = new_ct->mutable_linear();
735 arg->add_vars(target_ref);
742 return RemoveConstraint(
ct);
747 ConstraintProto* new_ct = context_->
working_model->add_constraints();
748 new_ct->set_name(
ct->name());
749 auto* arg = new_ct->mutable_linear();
750 arg->add_vars(target_ref);
757 return RemoveConstraint(
ct);
763 context_->
IsFixed(target_ref)) {
764 if (!context_->
IsFixed(target_ref)) {
769 return RemoveConstraint(
ct);
779 bool CpModelPresolver::PresolveIntMin(ConstraintProto*
ct) {
782 const auto copy =
ct->int_min();
783 ct->mutable_int_max()->set_target(
NegatedRef(copy.target()));
784 for (
const int ref : copy.vars()) {
787 return PresolveIntMax(
ct);
790 bool CpModelPresolver::PresolveIntProd(ConstraintProto*
ct) {
794 if (
ct->int_prod().vars().empty()) {
799 return RemoveConstraint(
ct);
801 bool changed =
false;
806 for (
int i = 0; i <
ct->int_prod().vars().size(); ++i) {
807 const int ref =
ct->int_prod().vars(i);
809 if (r.representative != ref && r.offset == 0) {
811 ct->mutable_int_prod()->set_vars(i, r.representative);
826 const int old_target =
ct->int_prod().target();
827 const int new_target = context_->
working_model->variables_size();
829 IntegerVariableProto* var_proto = context_->
working_model->add_variables();
836 ct->mutable_int_prod()->set_target(new_target);
837 if (context_->
IsFixed(new_target)) {
849 ConstraintProto* new_ct = context_->
working_model->add_constraints();
850 LinearConstraintProto* lin = new_ct->mutable_linear();
851 lin->add_vars(old_target);
853 lin->add_vars(new_target);
854 lin->add_coeffs(-constant);
864 for (
const int ref :
ct->int_prod().vars()) {
865 implied = implied.ContinuousMultiplicationBy(context_->
DomainOf(ref));
867 bool modified =
false;
876 if (
ct->int_prod().vars_size() == 2) {
877 int a =
ct->int_prod().vars(0);
878 int b =
ct->int_prod().vars(1);
879 const int product =
ct->int_prod().target();
889 return RemoveConstraint(
ct);
890 }
else if (
b != product) {
891 ConstraintProto*
const lin = context_->
working_model->add_constraints();
892 lin->mutable_linear()->add_vars(
b);
893 lin->mutable_linear()->add_coeffs(value_a);
894 lin->mutable_linear()->add_vars(product);
895 lin->mutable_linear()->add_coeffs(-1);
896 lin->mutable_linear()->add_domain(0);
897 lin->mutable_linear()->add_domain(0);
900 return RemoveConstraint(
ct);
901 }
else if (value_a != 1) {
906 return RemoveConstraint(
ct);
909 return RemoveConstraint(
ct);
911 }
else if (
a ==
b &&
a == product) {
916 return RemoveConstraint(
ct);
921 const int target_ref =
ct->int_prod().target();
923 for (
const int var :
ct->int_prod().vars()) {
925 if (context_->
MinOf(
var) < 0)
return changed;
926 if (context_->
MaxOf(
var) > 1)
return changed;
935 ConstraintProto* new_ct = context_->
working_model->add_constraints();
936 new_ct->add_enforcement_literal(target_ref);
937 auto* arg = new_ct->mutable_bool_and();
938 for (
const int var :
ct->int_prod().vars()) {
939 arg->add_literals(
var);
943 ConstraintProto* new_ct = context_->
working_model->add_constraints();
944 auto* arg = new_ct->mutable_bool_or();
945 arg->add_literals(target_ref);
946 for (
const int var :
ct->int_prod().vars()) {
951 return RemoveConstraint(
ct);
954 bool CpModelPresolver::PresolveIntDiv(ConstraintProto*
ct) {
958 const int target =
ct->int_div().target();
959 const int ref_x =
ct->int_div().vars(0);
960 const int ref_div =
ct->int_div().vars(1);
967 const int64 divisor = context_->
MinOf(ref_div);
969 LinearConstraintProto*
const lin =
970 context_->
working_model->add_constraints()->mutable_linear();
971 lin->add_vars(ref_x);
973 lin->add_vars(target);
979 return RemoveConstraint(
ct);
981 bool domain_modified =
false;
985 if (domain_modified) {
987 "int_div: updated domain of target in target = X / cte");
998 if (context_->
MinOf(target) >= 0 && context_->
MinOf(ref_x) >= 0 &&
1000 LinearConstraintProto*
const lin =
1001 context_->
working_model->add_constraints()->mutable_linear();
1002 lin->add_vars(ref_x);
1004 lin->add_vars(target);
1005 lin->add_coeffs(-divisor);
1007 lin->add_domain(divisor - 1);
1010 "int_div: linearize positive division with a constant divisor");
1011 return RemoveConstraint(
ct);
1019 bool CpModelPresolver::ExploitEquivalenceRelations(
int c, ConstraintProto*
ct) {
1020 bool changed =
false;
1025 if (
ct->constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
1026 for (
int& ref : *
ct->mutable_enforcement_literal()) {
1038 bool work_to_do =
false;
1041 if (r.representative !=
var) {
1046 if (!work_to_do)
return false;
1050 [&changed,
this](
int* ref) {
1061 [&changed,
this](
int* ref) {
1072 void CpModelPresolver::DivideLinearByGcd(ConstraintProto*
ct) {
1077 const int num_vars =
ct->linear().vars().size();
1078 for (
int i = 0; i < num_vars; ++i) {
1079 const int64 magnitude = std::abs(
ct->linear().coeffs(i));
1081 if (gcd == 1)
break;
1085 for (
int i = 0; i < num_vars; ++i) {
1086 ct->mutable_linear()->set_coeffs(i,
ct->linear().coeffs(i) / gcd);
1090 if (
ct->linear().domain_size() == 0) {
1091 return (
void)MarkConstraintAsFalse(
ct);
1096 void CpModelPresolver::PresolveLinearEqualityModuloTwo(ConstraintProto*
ct) {
1097 if (!
ct->enforcement_literal().empty())
return;
1098 if (
ct->linear().domain().size() != 2)
return;
1099 if (
ct->linear().domain(0) !=
ct->linear().domain(1))
return;
1104 std::vector<int> literals;
1105 for (
int i = 0; i <
ct->linear().vars().size(); ++i) {
1106 const int64 coeff =
ct->linear().coeffs(i);
1107 const int ref =
ct->linear().vars(i);
1108 if (coeff % 2 == 0)
continue;
1111 if (literals.size() > 2)
return;
1113 if (literals.size() == 1) {
1114 const int64 rhs = std::abs(
ct->linear().domain(0));
1115 context_->
UpdateRuleStats(
"linear: only one odd Boolean in equality");
1117 }
else if (literals.size() == 2) {
1118 const int64 rhs = std::abs(
ct->linear().domain(0));
1119 context_->
UpdateRuleStats(
"linear: only two odd Booleans in equality");
1129 template <
typename ProtoWithVarsAndCoeffs>
1130 bool CpModelPresolver::CanonicalizeLinearExpressionInternal(
1131 const ConstraintProto&
ct, ProtoWithVarsAndCoeffs*
proto,
int64* offset) {
1137 int64 sum_of_fixed_terms = 0;
1138 bool remapped =
false;
1139 const int old_size =
proto->vars().size();
1141 for (
int i = 0; i < old_size; ++i) {
1142 const int ref =
proto->vars(i);
1146 if (coeff == 0)
continue;
1149 sum_of_fixed_terms += coeff * context_->
MinOf(
var);
1155 bool removed =
false;
1156 for (
const int enf :
ct.enforcement_literal()) {
1160 sum_of_fixed_terms += coeff;
1169 context_->
UpdateRuleStats(
"linear: enforcement literal in expression");
1174 if (r.representative !=
var) {
1176 sum_of_fixed_terms += coeff * r.
offset;
1178 tmp_terms_.push_back({r.representative, coeff * r.coeff});
1180 proto->clear_vars();
1181 proto->clear_coeffs();
1182 std::sort(tmp_terms_.begin(), tmp_terms_.end());
1183 int current_var = 0;
1184 int64 current_coeff = 0;
1185 for (
const auto entry : tmp_terms_) {
1187 if (entry.first == current_var) {
1188 current_coeff += entry.second;
1190 if (current_coeff != 0) {
1191 proto->add_vars(current_var);
1192 proto->add_coeffs(current_coeff);
1194 current_var = entry.first;
1195 current_coeff = entry.second;
1198 if (current_coeff != 0) {
1199 proto->add_vars(current_var);
1200 proto->add_coeffs(current_coeff);
1205 if (
proto->vars().size() < old_size) {
1208 *offset = sum_of_fixed_terms;
1209 return remapped ||
proto->vars().size() < old_size;
1212 bool CpModelPresolver::CanonicalizeLinearExpression(
1213 const ConstraintProto&
ct, LinearExpressionProto* exp) {
1215 const bool result = CanonicalizeLinearExpressionInternal(
ct, exp, &offset);
1216 exp->set_offset(exp->offset() + offset);
1220 bool CpModelPresolver::CanonicalizeLinear(ConstraintProto*
ct) {
1221 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
1227 CanonicalizeLinearExpressionInternal(*
ct,
ct->mutable_linear(), &offset);
1231 ct->mutable_linear());
1233 DivideLinearByGcd(
ct);
1237 bool CpModelPresolver::RemoveSingletonInLinear(ConstraintProto*
ct) {
1238 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
1243 std::set<int> index_to_erase;
1244 const int num_vars =
ct->linear().vars().size();
1248 for (
int i = 0; i < num_vars; ++i) {
1249 const int var =
ct->linear().vars(i);
1250 const int64 coeff =
ct->linear().coeffs(i);
1254 const auto term_domain =
1256 if (!exact)
continue;
1260 if (new_rhs.NumIntervals() > 100)
continue;
1267 index_to_erase.insert(i);
1274 if (index_to_erase.empty()) {
1276 if (context_->
params().presolve_substitution_level() <= 0)
return false;
1277 if (!
ct->enforcement_literal().empty())
return false;
1281 if (rhs.Min() != rhs.Max())
return false;
1283 for (
int i = 0; i < num_vars; ++i) {
1284 const int var =
ct->linear().vars(i);
1285 const int64 coeff =
ct->linear().coeffs(i);
1303 const int64 objective_coeff =
1306 if (objective_coeff % coeff != 0)
continue;
1310 const auto term_domain =
1312 if (!exact)
continue;
1314 if (new_rhs.NumIntervals() > 100)
continue;
1322 objective_coeff))) {
1339 LOG(
WARNING) <<
"This was not supposed to happen and the presolve "
1340 "could be improved.";
1343 context_->
UpdateRuleStats(
"linear: singleton column define objective.");
1347 return RemoveConstraint(
ct);
1356 "linear: singleton column in equality and in objective.");
1358 index_to_erase.insert(i);
1362 if (index_to_erase.empty())
return false;
1371 for (
int i = 0; i < num_vars; ++i) {
1372 if (index_to_erase.count(i)) {
1376 ct->mutable_linear()->set_coeffs(new_size,
ct->linear().coeffs(i));
1377 ct->mutable_linear()->set_vars(new_size,
ct->linear().vars(i));
1380 ct->mutable_linear()->mutable_vars()->Truncate(new_size);
1381 ct->mutable_linear()->mutable_coeffs()->Truncate(new_size);
1383 DivideLinearByGcd(
ct);
1387 bool CpModelPresolver::PresolveSmallLinear(ConstraintProto*
ct) {
1388 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
1394 if (
ct->linear().vars().empty()) {
1397 if (rhs.Contains(0)) {
1398 return RemoveConstraint(
ct);
1400 return MarkConstraintAsFalse(
ct);
1407 if (
ct->linear().vars_size() == 1 &&
ct->enforcement_literal_size() > 0 &&
1408 ct->linear().coeffs(0) == 1 &&
1411 context_->
UpdateRuleStats(
"linear: remove abs from abs(x) in domain");
1412 const Domain implied_abs_target_domain =
1415 .IntersectionWith(context_->
DomainOf(
ct->linear().vars(0)));
1417 if (implied_abs_target_domain.IsEmpty()) {
1418 return MarkConstraintAsFalse(
ct);
1421 const Domain new_abs_var_domain =
1422 implied_abs_target_domain
1423 .UnionWith(implied_abs_target_domain.Negation())
1424 .IntersectionWith(context_->
DomainOf(abs_arg));
1426 if (new_abs_var_domain.IsEmpty()) {
1427 return MarkConstraintAsFalse(
ct);
1430 ConstraintProto* new_ct = context_->
working_model->add_constraints();
1431 new_ct->set_name(
ct->name());
1432 for (
const int literal :
ct->enforcement_literal()) {
1433 new_ct->add_enforcement_literal(
literal);
1435 auto* arg = new_ct->mutable_linear();
1436 arg->add_vars(abs_arg);
1440 return RemoveConstraint(
ct);
1444 if (
ct->enforcement_literal_size() != 1 ||
ct->linear().vars_size() != 1 ||
1445 (
ct->linear().coeffs(0) != 1 &&
ct->linear().coeffs(0) == -1)) {
1449 const int literal =
ct->enforcement_literal(0);
1450 const LinearConstraintProto& linear =
ct->linear();
1451 const int ref = linear.vars(0);
1456 if (linear.domain_size() == 2 && linear.domain(0) == linear.domain(1)) {
1458 : -linear.domain(0) * coeff;
1467 if (complement.Size() != 1)
return false;
1469 : -complement.Min() * coeff;
1484 if (
ct->linear().vars().size() == 1) {
1486 ?
ct->linear().coeffs(0)
1487 : -
ct->linear().coeffs(0);
1492 rhs.InverseMultiplicationBy(coeff))) {
1495 return RemoveConstraint(
ct);
1502 const LinearConstraintProto& arg =
ct->linear();
1503 if (arg.vars_size() == 2) {
1505 const int64 rhs_min = rhs.Min();
1506 const int64 rhs_max = rhs.Max();
1507 if (rhs_min == rhs_max) {
1508 const int v1 = arg.vars(0);
1509 const int v2 = arg.vars(1);
1510 const int64 coeff1 = arg.coeffs(0);
1511 const int64 coeff2 = arg.coeffs(1);
1515 }
else if (coeff2 == 1) {
1517 }
else if (coeff1 == -1) {
1519 }
else if (coeff2 == -1) {
1522 if (added)
return RemoveConstraint(
ct);
1532 bool IsLeConstraint(
const Domain& domain,
const Domain& all_values) {
1533 return all_values.IntersectionWith(Domain(
kint64min, domain.Max()))
1534 .IsIncludedIn(domain);
1538 bool IsGeConstraint(
const Domain& domain,
const Domain& all_values) {
1539 return all_values.IntersectionWith(Domain(domain.Min(),
kint64max))
1540 .IsIncludedIn(domain);
1546 bool RhsCanBeFixedToMin(
int64 coeff,
const Domain& var_domain,
1547 const Domain& terms,
const Domain& rhs) {
1548 if (var_domain.NumIntervals() != 1)
return false;
1549 if (std::abs(coeff) != 1)
return false;
1557 if (coeff == 1 && terms.Max() + var_domain.Min() <= rhs.Min()) {
1560 if (coeff == -1 && terms.Max() - var_domain.Max() <= rhs.Min()) {
1566 bool RhsCanBeFixedToMax(
int64 coeff,
const Domain& var_domain,
1567 const Domain& terms,
const Domain& rhs) {
1568 if (var_domain.NumIntervals() != 1)
return false;
1569 if (std::abs(coeff) != 1)
return false;
1571 if (coeff == 1 && terms.Min() + var_domain.Max() >= rhs.Max()) {
1574 if (coeff == -1 && terms.Min() - var_domain.Min() >= rhs.Max()) {
1581 void TakeIntersectionWith(
const absl::flat_hash_set<int>& current,
1582 absl::flat_hash_set<int>* to_clear) {
1583 std::vector<int> new_set;
1584 for (
const int c : *to_clear) {
1585 if (current.contains(c)) new_set.push_back(c);
1588 for (
const int c : new_set) to_clear->insert(c);
1593 bool CpModelPresolver::PropagateDomainsInLinear(
int c, ConstraintProto*
ct) {
1594 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
1602 const int num_vars =
ct->linear().vars_size();
1603 term_domains.resize(num_vars + 1);
1604 left_domains.resize(num_vars + 1);
1605 left_domains[0] = Domain(0);
1606 for (
int i = 0; i < num_vars; ++i) {
1607 const int var =
ct->linear().vars(i);
1608 const int64 coeff =
ct->linear().coeffs(i);
1611 left_domains[i + 1] =
1614 const Domain& implied_rhs = left_domains[num_vars];
1618 if (implied_rhs.IsIncludedIn(old_rhs)) {
1620 return RemoveConstraint(
ct);
1624 Domain rhs = old_rhs.SimplifyUsingImpliedDomain(implied_rhs);
1625 if (rhs.IsEmpty()) {
1627 return MarkConstraintAsFalse(
ct);
1629 if (rhs != old_rhs) {
1637 bool is_le_constraint = IsLeConstraint(rhs, implied_rhs);
1638 bool is_ge_constraint = IsGeConstraint(rhs, implied_rhs);
1641 if (
ct->enforcement_literal().size() > 1)
return false;
1643 bool new_bounds =
false;
1644 bool recanonicalize =
false;
1645 Domain negated_rhs = rhs.Negation();
1646 Domain right_domain(0);
1648 Domain implied_term_domain;
1649 term_domains[num_vars] = Domain(0);
1650 for (
int i = num_vars - 1; i >= 0; --i) {
1651 const int var =
ct->linear().vars(i);
1652 const int64 var_coeff =
ct->linear().coeffs(i);
1654 right_domain.AdditionWith(term_domains[i + 1]).RelaxIfTooComplex();
1655 implied_term_domain = left_domains[i].AdditionWith(right_domain);
1656 new_domain = implied_term_domain.AdditionWith(negated_rhs)
1657 .InverseMultiplicationBy(-var_coeff);
1659 if (
ct->enforcement_literal().empty()) {
1664 }
else if (
ct->enforcement_literal().size() == 1) {
1675 recanonicalize =
true;
1679 if (is_le_constraint || is_ge_constraint) {
1680 CHECK_NE(is_le_constraint, is_ge_constraint);
1681 if ((var_coeff > 0) == is_ge_constraint) {
1696 const bool is_in_objective =
1700 const int64 obj_coeff =
1709 if (obj_coeff <= 0 &&
1719 recanonicalize =
true;
1723 if (obj_coeff >= 0 &&
1733 recanonicalize =
true;
1741 if (!
ct->enforcement_literal().empty())
continue;
1753 if (rhs.Min() != rhs.Max() &&
1756 const bool same_sign = (var_coeff > 0) == (obj_coeff > 0);
1758 if (same_sign && RhsCanBeFixedToMin(var_coeff, context_->
DomainOf(
var),
1759 implied_term_domain, rhs)) {
1760 rhs = Domain(rhs.Min());
1763 if (!same_sign && RhsCanBeFixedToMax(var_coeff, context_->
DomainOf(
var),
1764 implied_term_domain, rhs)) {
1765 rhs = Domain(rhs.Max());
1771 negated_rhs = rhs.Negation();
1775 right_domain = Domain(0);
1779 is_le_constraint =
false;
1780 is_ge_constraint =
false;
1781 for (
const int var :
ct->linear().vars()) {
1798 if (
ct->linear().vars().size() <= 2)
continue;
1803 if (rhs.Min() != rhs.Max())
continue;
1809 if (context_->
DomainOf(
var) != new_domain)
continue;
1810 if (std::abs(var_coeff) != 1)
continue;
1811 if (context_->
params().presolve_substitution_level() <= 0)
continue;
1817 bool is_in_objective =
false;
1819 is_in_objective =
true;
1825 if (is_in_objective) col_size--;
1826 const int row_size =
ct->linear().vars_size();
1830 const int num_entries_added = (row_size - 1) * (col_size - 1);
1831 const int num_entries_removed = col_size + row_size - 1;
1833 if (num_entries_added > num_entries_removed) {
1839 std::vector<int> others;
1848 if (context_->
working_model->constraints(c).constraint_case() !=
1849 ConstraintProto::ConstraintCase::kLinear) {
1853 for (
const int ref :
1854 context_->
working_model->constraints(c).enforcement_literal()) {
1860 others.push_back(c);
1862 if (abort)
continue;
1865 for (
const int c : others) {
1878 if (is_in_objective) {
1883 absl::StrCat(
"linear: variable substitution ", others.size()));
1894 const int ct_index = context_->
mapping_model->constraints().size();
1896 LinearConstraintProto* mapping_linear_ct =
1899 std::swap(mapping_linear_ct->mutable_vars()->at(0),
1900 mapping_linear_ct->mutable_vars()->at(i));
1901 std::swap(mapping_linear_ct->mutable_coeffs()->at(0),
1902 mapping_linear_ct->mutable_coeffs()->at(i));
1903 return RemoveConstraint(
ct);
1908 if (recanonicalize)
return CanonicalizeLinear(
ct);
1919 void CpModelPresolver::ExtractEnforcementLiteralFromLinearConstraint(
1920 int ct_index, ConstraintProto*
ct) {
1921 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
1926 const LinearConstraintProto& arg =
ct->linear();
1927 const int num_vars = arg.vars_size();
1931 if (num_vars <= 1)
return;
1935 int64 max_coeff_magnitude = 0;
1936 for (
int i = 0; i < num_vars; ++i) {
1937 const int ref = arg.vars(i);
1938 const int64 coeff = arg.coeffs(i);
1939 const int64 term_a = coeff * context_->
MinOf(ref);
1940 const int64 term_b = coeff * context_->
MaxOf(ref);
1941 max_coeff_magnitude =
std::max(max_coeff_magnitude, std::abs(coeff));
1942 min_sum +=
std::min(term_a, term_b);
1943 max_sum +=
std::max(term_a, term_b);
1952 const auto& domain =
ct->linear().domain();
1953 const int64 ub_threshold = domain[domain.size() - 2] - min_sum;
1954 const int64 lb_threshold = max_sum - domain[1];
1956 if (max_coeff_magnitude <
std::max(ub_threshold, lb_threshold))
return;
1975 const bool lower_bounded = min_sum < rhs_domain.Min();
1976 const bool upper_bounded = max_sum > rhs_domain.Max();
1977 if (!lower_bounded && !upper_bounded)
return;
1978 if (lower_bounded && upper_bounded) {
1980 ConstraintProto* new_ct1 = context_->
working_model->add_constraints();
1982 if (!
ct->name().empty()) {
1983 new_ct1->set_name(absl::StrCat(
ct->name(),
" (part 1)"));
1986 new_ct1->mutable_linear());
1988 ConstraintProto* new_ct2 = context_->
working_model->add_constraints();
1990 if (!
ct->name().empty()) {
1991 new_ct2->set_name(absl::StrCat(
ct->name(),
" (part 2)"));
1994 new_ct2->mutable_linear());
1997 return (
void)RemoveConstraint(
ct);
2003 const int64 threshold = lower_bounded ? ub_threshold : lb_threshold;
2006 const bool only_booleans =
2007 !context_->
params().presolve_extract_integer_enforcement();
2012 int64 rhs_offset = 0;
2013 bool some_integer_encoding_were_extracted =
false;
2014 LinearConstraintProto* mutable_arg =
ct->mutable_linear();
2015 for (
int i = 0; i < arg.vars_size(); ++i) {
2016 int ref = arg.vars(i);
2017 int64 coeff = arg.coeffs(i);
2024 if (context_->
IsFixed(ref) || coeff < threshold ||
2025 (only_booleans && !is_boolean)) {
2027 mutable_arg->set_vars(new_size, mutable_arg->vars(i));
2028 mutable_arg->set_coeffs(new_size, mutable_arg->coeffs(i));
2036 some_integer_encoding_were_extracted =
true;
2038 "linear: extracted integer enforcement literal");
2040 if (lower_bounded) {
2041 ct->add_enforcement_literal(is_boolean
2044 ref, context_->
MinOf(ref)));
2045 rhs_offset -= coeff * context_->
MinOf(ref);
2047 ct->add_enforcement_literal(is_boolean
2050 ref, context_->
MaxOf(ref)));
2051 rhs_offset -= coeff * context_->
MaxOf(ref);
2054 mutable_arg->mutable_vars()->Truncate(new_size);
2055 mutable_arg->mutable_coeffs()->Truncate(new_size);
2057 if (some_integer_encoding_were_extracted) {
2063 void CpModelPresolver::ExtractAtMostOneFromLinear(ConstraintProto*
ct) {
2068 const LinearConstraintProto& arg =
ct->linear();
2069 const int num_vars = arg.vars_size();
2072 for (
int i = 0; i < num_vars; ++i) {
2073 const int ref = arg.vars(i);
2074 const int64 coeff = arg.coeffs(i);
2075 const int64 term_a = coeff * context_->
MinOf(ref);
2076 const int64 term_b = coeff * context_->
MaxOf(ref);
2077 min_sum +=
std::min(term_a, term_b);
2078 max_sum +=
std::max(term_a, term_b);
2080 for (
const int type : {0, 1}) {
2081 std::vector<int> at_most_one;
2082 for (
int i = 0; i < num_vars; ++i) {
2083 const int ref = arg.vars(i);
2084 const int64 coeff = arg.coeffs(i);
2085 if (context_->
MinOf(ref) != 0)
continue;
2086 if (context_->
MaxOf(ref) != 1)
continue;
2091 if (min_sum + 2 * std::abs(coeff) > rhs.Max()) {
2092 at_most_one.push_back(coeff > 0 ? ref :
NegatedRef(ref));
2095 if (max_sum - 2 * std::abs(coeff) < rhs.Min()) {
2096 at_most_one.push_back(coeff > 0 ?
NegatedRef(ref) : ref);
2100 if (at_most_one.size() > 1) {
2106 ConstraintProto* new_ct = context_->
working_model->add_constraints();
2107 new_ct->set_name(
ct->name());
2108 for (
const int ref : at_most_one) {
2109 new_ct->mutable_at_most_one()->add_literals(ref);
2118 bool CpModelPresolver::PresolveLinearOnBooleans(ConstraintProto*
ct) {
2119 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
2124 const LinearConstraintProto& arg =
ct->linear();
2125 const int num_vars = arg.vars_size();
2127 int64 max_coeff = 0;
2130 for (
int i = 0; i < num_vars; ++i) {
2132 const int var = arg.vars(i);
2133 const int64 coeff = arg.coeffs(i);
2136 if (context_->
MinOf(
var) != 0)
return false;
2137 if (context_->
MaxOf(
var) != 1)
return false;
2141 min_coeff =
std::min(min_coeff, coeff);
2142 max_coeff =
std::max(max_coeff, coeff);
2146 min_coeff =
std::min(min_coeff, -coeff);
2147 max_coeff =
std::max(max_coeff, -coeff);
2159 if ((!rhs_domain.Contains(min_sum) &&
2160 min_sum + min_coeff > rhs_domain.Max()) ||
2161 (!rhs_domain.Contains(max_sum) &&
2162 max_sum - min_coeff < rhs_domain.Min())) {
2163 context_->
UpdateRuleStats(
"linear: all booleans and trivially false");
2164 return MarkConstraintAsFalse(
ct);
2166 if (Domain(min_sum, max_sum).IsIncludedIn(rhs_domain)) {
2168 return RemoveConstraint(
ct);
2175 DCHECK(!rhs_domain.IsEmpty());
2176 if (min_sum + min_coeff > rhs_domain.Max()) {
2179 const auto copy = arg;
2180 ct->mutable_bool_and()->clear_literals();
2181 for (
int i = 0; i < num_vars; ++i) {
2182 ct->mutable_bool_and()->add_literals(
2183 copy.coeffs(i) > 0 ?
NegatedRef(copy.vars(i)) : copy.vars(i));
2185 return PresolveBoolAnd(
ct);
2186 }
else if (max_sum - min_coeff < rhs_domain.Min()) {
2189 const auto copy = arg;
2190 ct->mutable_bool_and()->clear_literals();
2191 for (
int i = 0; i < num_vars; ++i) {
2192 ct->mutable_bool_and()->add_literals(
2193 copy.coeffs(i) > 0 ? copy.vars(i) :
NegatedRef(copy.vars(i)));
2195 return PresolveBoolAnd(
ct);
2196 }
else if (min_sum + min_coeff >= rhs_domain.Min() &&
2197 rhs_domain.front().end >= max_sum) {
2200 const auto copy = arg;
2201 ct->mutable_bool_or()->clear_literals();
2202 for (
int i = 0; i < num_vars; ++i) {
2203 ct->mutable_bool_or()->add_literals(
2204 copy.coeffs(i) > 0 ? copy.vars(i) :
NegatedRef(copy.vars(i)));
2206 return PresolveBoolOr(
ct);
2207 }
else if (max_sum - min_coeff <= rhs_domain.Max() &&
2208 rhs_domain.back().start <= min_sum) {
2211 const auto copy = arg;
2212 ct->mutable_bool_or()->clear_literals();
2213 for (
int i = 0; i < num_vars; ++i) {
2214 ct->mutable_bool_or()->add_literals(
2215 copy.coeffs(i) > 0 ?
NegatedRef(copy.vars(i)) : copy.vars(i));
2217 return PresolveBoolOr(
ct);
2219 min_sum + max_coeff <= rhs_domain.Max() &&
2220 min_sum + 2 * min_coeff > rhs_domain.Max() &&
2221 rhs_domain.back().start <= min_sum) {
2224 const auto copy = arg;
2225 ct->mutable_at_most_one()->clear_literals();
2226 for (
int i = 0; i < num_vars; ++i) {
2227 ct->mutable_at_most_one()->add_literals(
2228 copy.coeffs(i) > 0 ? copy.vars(i) :
NegatedRef(copy.vars(i)));
2232 max_sum - max_coeff >= rhs_domain.Min() &&
2233 max_sum - 2 * min_coeff < rhs_domain.Min() &&
2234 rhs_domain.front().end >= max_sum) {
2237 const auto copy = arg;
2238 ct->mutable_at_most_one()->clear_literals();
2239 for (
int i = 0; i < num_vars; ++i) {
2240 ct->mutable_at_most_one()->add_literals(
2241 copy.coeffs(i) > 0 ?
NegatedRef(copy.vars(i)) : copy.vars(i));
2245 min_sum < rhs_domain.Min() &&
2246 min_sum + min_coeff >= rhs_domain.Min() &&
2247 min_sum + 2 * min_coeff > rhs_domain.Max() &&
2248 min_sum + max_coeff <= rhs_domain.Max()) {
2250 ConstraintProto* exactly_one = context_->
working_model->add_constraints();
2251 exactly_one->set_name(
ct->name());
2252 for (
int i = 0; i < num_vars; ++i) {
2253 exactly_one->mutable_exactly_one()->add_literals(
2254 arg.coeffs(i) > 0 ? arg.vars(i) :
NegatedRef(arg.vars(i)));
2257 return RemoveConstraint(
ct);
2259 max_sum > rhs_domain.Max() &&
2260 max_sum - min_coeff <= rhs_domain.Max() &&
2261 max_sum - 2 * min_coeff < rhs_domain.Min() &&
2262 max_sum - max_coeff >= rhs_domain.Min()) {
2264 ConstraintProto* exactly_one = context_->
working_model->add_constraints();
2265 exactly_one->set_name(
ct->name());
2266 for (
int i = 0; i < num_vars; ++i) {
2267 exactly_one->mutable_exactly_one()->add_literals(
2268 arg.coeffs(i) > 0 ?
NegatedRef(arg.vars(i)) : arg.vars(i));
2271 return RemoveConstraint(
ct);
2278 if (num_vars > 3)
return false;
2283 const int max_mask = (1 << arg.vars_size());
2284 for (
int mask = 0; mask < max_mask; ++mask) {
2286 for (
int i = 0; i < num_vars; ++i) {
2287 if ((mask >> i) & 1)
value += arg.coeffs(i);
2289 if (rhs_domain.Contains(
value))
continue;
2292 ConstraintProto* new_ct = context_->
working_model->add_constraints();
2293 auto* new_arg = new_ct->mutable_bool_or();
2295 *new_ct->mutable_enforcement_literal() =
ct->enforcement_literal();
2297 for (
int i = 0; i < num_vars; ++i) {
2298 new_arg->add_literals(((mask >> i) & 1) ?
NegatedRef(arg.vars(i))
2304 return RemoveConstraint(
ct);
2309 void AddLinearExpression(
int64 coeff,
const LinearExpressionProto& exp,
2310 LinearConstraintProto* linear_ct) {
2311 const int size = exp.vars().size();
2312 for (
int i = 0; i < size; ++i) {
2313 linear_ct->add_vars(exp.vars(i));
2314 linear_ct->add_coeffs(coeff * exp.coeffs(i));
2316 if (exp.offset() != 0) {
2318 .AdditionWith(Domain(-coeff * exp.offset())),
2325 bool CpModelPresolver::PresolveInterval(
int c, ConstraintProto*
ct) {
2328 if (
ct->enforcement_literal().empty() && !
ct->interval().has_start_view()) {
2329 bool changed =
false;
2330 const int start =
ct->interval().start();
2331 const int end =
ct->interval().end();
2332 const int size =
ct->interval().size();
2333 const Domain start_domain = context_->
DomainOf(start);
2334 const Domain end_domain = context_->
DomainOf(end);
2335 const Domain size_domain = context_->
DomainOf(size);
2342 end, start_domain.AdditionWith(size_domain), &changed)) {
2346 start, end_domain.AdditionWith(size_domain.Negation()), &changed)) {
2350 size, end_domain.AdditionWith(start_domain.Negation()), &changed)) {
2359 if (!
ct->interval().has_start_view()) {
2361 const int start =
ct->interval().start();
2362 const int end =
ct->interval().end();
2363 const int size =
ct->interval().size();
2364 ConstraintProto* new_ct = context_->
working_model->add_constraints();
2365 *(new_ct->mutable_enforcement_literal()) =
ct->enforcement_literal();
2366 new_ct->mutable_linear()->add_domain(0);
2367 new_ct->mutable_linear()->add_domain(0);
2368 new_ct->mutable_linear()->add_vars(start);
2369 new_ct->mutable_linear()->add_coeffs(1);
2370 new_ct->mutable_linear()->add_vars(size);
2371 new_ct->mutable_linear()->add_coeffs(1);
2372 new_ct->mutable_linear()->add_vars(end);
2373 new_ct->mutable_linear()->add_coeffs(-1);
2377 return RemoveConstraint(
ct);
2385 if (context_->
params().convert_intervals()) {
2386 bool changed =
false;
2387 IntervalConstraintProto*
interval =
ct->mutable_interval();
2388 if (!
ct->interval().has_start_view()) {
2393 interval->mutable_start_view()->add_coeffs(1);
2394 interval->mutable_start_view()->set_offset(0);
2396 interval->mutable_size_view()->add_coeffs(1);
2397 interval->mutable_size_view()->set_offset(0);
2399 interval->mutable_end_view()->add_coeffs(1);
2400 interval->mutable_end_view()->set_offset(0);
2407 ConstraintProto* new_ct = context_->
working_model->add_constraints();
2408 *(new_ct->mutable_enforcement_literal()) =
ct->enforcement_literal();
2409 new_ct->mutable_linear()->add_domain(0);
2410 new_ct->mutable_linear()->add_domain(0);
2411 AddLinearExpression(1,
interval->start_view(), new_ct->mutable_linear());
2412 AddLinearExpression(1,
interval->size_view(), new_ct->mutable_linear());
2413 AddLinearExpression(-1,
interval->end_view(), new_ct->mutable_linear());
2423 CanonicalizeLinearExpression(*
ct,
interval->mutable_start_view());
2424 changed |= CanonicalizeLinearExpression(*
ct,
interval->mutable_size_view());
2425 changed |= CanonicalizeLinearExpression(*
ct,
interval->mutable_end_view());
2434 if ( (
false) &&
ct->enforcement_literal().empty() &&
2435 context_->
IsFixed(
ct->interval().size())) {
2437 1, context_->
MinOf(
ct->interval().size()));
2444 bool CpModelPresolver::PresolveElement(ConstraintProto*
ct) {
2447 const int index_ref =
ct->element().index();
2448 const int target_ref =
ct->element().target();
2453 bool all_constants =
true;
2454 absl::flat_hash_set<int64> constant_set;
2455 bool all_included_in_target_domain =
true;
2458 bool reduced_index_domain =
false;
2460 Domain(0,
ct->element().vars_size() - 1),
2461 &reduced_index_domain)) {
2469 std::vector<int64> possible_indices;
2470 const Domain& index_domain = context_->
DomainOf(index_ref);
2471 for (
const ClosedInterval&
interval : index_domain) {
2474 const int ref =
ct->element().vars(index_value);
2475 const int64 target_value =
2476 target_ref == index_ref ? index_value : -index_value;
2478 possible_indices.push_back(target_value);
2482 if (possible_indices.size() < index_domain.Size()) {
2488 "element: reduced index domain when target equals index");
2494 Domain infered_domain;
2495 const Domain& initial_index_domain = context_->
DomainOf(index_ref);
2496 const Domain& target_domain = context_->
DomainOf(target_ref);
2497 std::vector<int64> possible_indices;
2498 for (
const ClosedInterval
interval : initial_index_domain) {
2502 const int ref =
ct->element().vars(
value);
2503 const Domain& domain = context_->
DomainOf(ref);
2504 if (domain.IntersectionWith(target_domain).IsEmpty())
continue;
2505 possible_indices.push_back(
value);
2506 if (domain.IsFixed()) {
2507 constant_set.insert(domain.Min());
2509 all_constants =
false;
2511 if (!domain.IsIncludedIn(target_domain)) {
2512 all_included_in_target_domain =
false;
2514 infered_domain = infered_domain.
UnionWith(domain);
2517 if (possible_indices.size() < initial_index_domain.Size()) {
2524 bool domain_modified =
false;
2526 &domain_modified)) {
2529 if (domain_modified) {
2535 if (context_->
IsFixed(index_ref)) {
2536 const int var =
ct->element().vars(context_->
MinOf(index_ref));
2537 if (
var != target_ref) {
2538 LinearConstraintProto*
const lin =
2539 context_->
working_model->add_constraints()->mutable_linear();
2541 lin->add_coeffs(-1);
2542 lin->add_vars(target_ref);
2549 return RemoveConstraint(
ct);
2555 if (all_constants && constant_set.size() == 1) {
2558 return RemoveConstraint(
ct);
2563 if (context_->
MinOf(index_ref) == 0 && context_->
MaxOf(index_ref) == 1 &&
2565 const int64 v0 = context_->
MinOf(
ct->element().vars(0));
2566 const int64 v1 = context_->
MinOf(
ct->element().vars(1));
2568 LinearConstraintProto*
const lin =
2569 context_->
working_model->add_constraints()->mutable_linear();
2570 lin->add_vars(target_ref);
2572 lin->add_vars(index_ref);
2573 lin->add_coeffs(v0 - v1);
2574 lin->add_domain(v0);
2575 lin->add_domain(v0);
2577 context_->
UpdateRuleStats(
"element: linearize constant element of size 2");
2578 return RemoveConstraint(
ct);
2582 const AffineRelation::Relation r_index =
2584 if (r_index.representative != index_ref) {
2586 if (context_->
DomainOf(r_index.representative).
Size() >
2594 const int array_size =
ct->element().vars_size();
2596 context_->
UpdateRuleStats(
"TODO element: representative has bad domain");
2597 }
else if (r_index.offset >= 0 && r_index.offset < array_size &&
2598 r_index.offset + r_max * r_index.coeff >= 0 &&
2599 r_index.offset + r_max * r_index.coeff < array_size) {
2601 ElementConstraintProto*
const element =
2602 context_->
working_model->add_constraints()->mutable_element();
2603 for (
int64 v = 0; v <= r_max; ++v) {
2604 const int64 scaled_index = v * r_index.coeff + r_index.offset;
2606 CHECK_LT(scaled_index, array_size);
2607 element->add_vars(
ct->element().vars(scaled_index));
2609 element->set_index(r_ref);
2610 element->set_target(target_ref);
2612 if (r_index.coeff == 1) {
2618 return RemoveConstraint(
ct);
2624 if (all_constants && unique_index) {
2628 context_->
UpdateRuleStats(
"element: trivial target domain reduction");
2631 return RemoveConstraint(
ct);
2634 const bool unique_target =
2636 context_->
IsFixed(target_ref);
2637 if (all_included_in_target_domain && unique_target) {
2641 return RemoveConstraint(
ct);
2644 if (unique_target && !context_->
IsFixed(target_ref)) {
2654 bool CpModelPresolver::PresolveTable(ConstraintProto*
ct) {
2657 if (
ct->table().vars().empty()) {
2659 return RemoveConstraint(
ct);
2665 const int num_vars =
ct->table().vars_size();
2666 const int num_tuples =
ct->table().values_size() / num_vars;
2667 std::vector<int64> tuple(num_vars);
2668 std::vector<std::vector<int64>> new_tuples;
2669 new_tuples.reserve(num_tuples);
2670 std::vector<absl::flat_hash_set<int64>> new_domains(num_vars);
2671 std::vector<AffineRelation::Relation> affine_relations;
2673 absl::flat_hash_set<int> visited;
2674 for (
const int ref :
ct->table().vars()) {
2682 bool modified_variables =
false;
2683 for (
int v = 0; v < num_vars; ++v) {
2684 const int ref =
ct->table().vars(v);
2686 affine_relations.push_back(r);
2687 if (r.representative != ref) {
2688 modified_variables =
true;
2692 for (
int i = 0; i < num_tuples; ++i) {
2693 bool delete_row =
false;
2695 for (
int j = 0; j < num_vars; ++j) {
2696 const int ref =
ct->table().vars(j);
2697 int64 v =
ct->table().values(i * num_vars + j);
2698 const AffineRelation::Relation& r = affine_relations[j];
2699 if (r.representative != ref) {
2700 const int64 inverse_value = (v - r.offset) / r.coeff;
2701 if (inverse_value * r.coeff + r.offset != v) {
2714 if (delete_row)
continue;
2715 new_tuples.push_back(tuple);
2716 for (
int j = 0; j < num_vars; ++j) {
2717 const int64 v = tuple[j];
2718 new_domains[j].insert(v);
2724 if (new_tuples.size() < num_tuples || modified_variables) {
2725 ct->mutable_table()->clear_values();
2726 for (
const std::vector<int64>& t : new_tuples) {
2727 for (
const int64 v : t) {
2728 ct->mutable_table()->add_values(v);
2731 if (new_tuples.size() < num_tuples) {
2736 if (modified_variables) {
2737 for (
int j = 0; j < num_vars; ++j) {
2738 const AffineRelation::Relation& r = affine_relations[j];
2739 if (r.representative !=
ct->table().vars(j)) {
2740 ct->mutable_table()->set_vars(j, r.representative);
2744 "table: replace variable by canonical affine one");
2748 if (
ct->table().negated())
return modified_variables;
2751 bool changed =
false;
2752 for (
int j = 0; j < num_vars; ++j) {
2753 const int ref =
ct->table().vars(j);
2757 new_domains[j].end())),
2765 if (num_vars == 1) {
2768 return RemoveConstraint(
ct);
2773 for (
int j = 0; j < num_vars; ++j) prod *= new_domains[j].size();
2774 if (prod == new_tuples.size()) {
2776 return RemoveConstraint(
ct);
2782 if (new_tuples.size() > 0.7 * prod) {
2784 std::vector<std::vector<int64>> var_to_values(num_vars);
2785 for (
int j = 0; j < num_vars; ++j) {
2786 var_to_values[j].assign(new_domains[j].begin(), new_domains[j].end());
2788 std::vector<std::vector<int64>> all_tuples(prod);
2789 for (
int i = 0; i < prod; ++i) {
2790 all_tuples[i].resize(num_vars);
2792 for (
int j = 0; j < num_vars; ++j) {
2793 all_tuples[i][j] = var_to_values[j][
index % var_to_values[j].size()];
2794 index /= var_to_values[j].size();
2800 std::vector<std::vector<int64>> diff(prod - new_tuples.size());
2801 std::set_difference(all_tuples.begin(), all_tuples.end(),
2802 new_tuples.begin(), new_tuples.end(), diff.begin());
2805 ct->mutable_table()->set_negated(!
ct->table().negated());
2806 ct->mutable_table()->clear_values();
2807 for (
const std::vector<int64>& t : diff) {
2808 for (
const int64 v : t)
ct->mutable_table()->add_values(v);
2812 return modified_variables;
2815 bool CpModelPresolver::PresolveAllDiff(ConstraintProto*
ct) {
2819 AllDifferentConstraintProto& all_diff = *
ct->mutable_all_diff();
2821 bool constraint_has_changed =
false;
2823 const int size = all_diff.vars_size();
2826 return RemoveConstraint(
ct);
2830 return RemoveConstraint(
ct);
2833 bool something_was_propagated =
false;
2834 std::vector<int> new_variables;
2835 for (
int i = 0; i < size; ++i) {
2836 if (!context_->
IsFixed(all_diff.vars(i))) {
2837 new_variables.push_back(all_diff.vars(i));
2842 bool propagated =
false;
2843 for (
int j = 0; j < size; ++j) {
2844 if (i == j)
continue;
2847 Domain(
value).Complement())) {
2855 something_was_propagated =
true;
2859 std::sort(new_variables.begin(), new_variables.end());
2860 for (
int i = 1; i < new_variables.size(); ++i) {
2861 if (new_variables[i] == new_variables[i - 1]) {
2863 "Duplicate variable in all_diff");
2867 if (new_variables.size() < all_diff.vars_size()) {
2868 all_diff.mutable_vars()->Clear();
2869 for (
const int var : new_variables) {
2870 all_diff.add_vars(
var);
2873 something_was_propagated =
true;
2874 constraint_has_changed =
true;
2875 if (new_variables.size() <= 1)
continue;
2880 Domain domain = context_->
DomainOf(all_diff.vars(0));
2881 for (
int i = 1; i < all_diff.vars_size(); ++i) {
2884 if (all_diff.vars_size() == domain.Size()) {
2885 absl::flat_hash_map<int64, std::vector<int>> value_to_refs;
2886 for (
const int ref : all_diff.vars()) {
2889 value_to_refs[v].push_back(ref);
2893 bool propagated =
false;
2894 for (
const auto& it : value_to_refs) {
2895 if (it.second.size() == 1 &&
2897 const int ref = it.second.
front();
2906 "all_diff: propagated mandatory values in permutation");
2907 something_was_propagated =
true;
2910 if (!something_was_propagated)
break;
2913 return constraint_has_changed;
2920 std::vector<int> GetLiteralsFromSetPPCConstraint(
const ConstraintProto&
ct) {
2921 std::vector<int> sorted_literals;
2922 if (
ct.constraint_case() == ConstraintProto::kAtMostOne) {
2923 for (
const int literal :
ct.at_most_one().literals()) {
2924 sorted_literals.push_back(
literal);
2926 }
else if (
ct.constraint_case() == ConstraintProto::kBoolOr) {
2927 for (
const int literal :
ct.bool_or().literals()) {
2928 sorted_literals.push_back(
literal);
2930 }
else if (
ct.constraint_case() == ConstraintProto::kExactlyOne) {
2931 for (
const int literal :
ct.exactly_one().literals()) {
2932 sorted_literals.push_back(
literal);
2935 std::sort(sorted_literals.begin(), sorted_literals.end());
2936 return sorted_literals;
2941 void AddImplication(
int lhs,
int rhs, CpModelProto*
proto,
2942 absl::flat_hash_map<int, int>* ref_to_bool_and) {
2943 if (ref_to_bool_and->contains(lhs)) {
2944 const int ct_index = (*ref_to_bool_and)[lhs];
2945 proto->mutable_constraints(ct_index)->mutable_bool_and()->add_literals(rhs);
2946 }
else if (ref_to_bool_and->contains(
NegatedRef(rhs))) {
2947 const int ct_index = (*ref_to_bool_and)[
NegatedRef(rhs)];
2948 proto->mutable_constraints(ct_index)->mutable_bool_and()->add_literals(
2951 (*ref_to_bool_and)[lhs] =
proto->constraints_size();
2952 ConstraintProto*
ct =
proto->add_constraints();
2953 ct->add_enforcement_literal(lhs);
2954 ct->mutable_bool_and()->add_literals(rhs);
2958 template <
typename ClauseContainer>
2959 void ExtractClauses(
bool use_bool_and,
const ClauseContainer& container,
2960 CpModelProto*
proto) {
2967 absl::flat_hash_map<int, int> ref_to_bool_and;
2968 for (
int i = 0; i < container.NumClauses(); ++i) {
2969 const std::vector<Literal>& clause = container.Clause(i);
2970 if (clause.empty())
continue;
2973 if (use_bool_and && clause.size() == 2) {
2974 const int a = clause[0].IsPositive()
2975 ? clause[0].Variable().value()
2977 const int b = clause[1].IsPositive()
2978 ? clause[1].Variable().value()
2985 ConstraintProto*
ct =
proto->add_constraints();
2986 for (
const Literal l : clause) {
2987 if (l.IsPositive()) {
2988 ct->mutable_bool_or()->add_literals(l.Variable().value());
2990 ct->mutable_bool_or()->add_literals(
NegatedRef(l.Variable().value()));
2998 int64 CpModelPresolver::StartMin(
2999 const IntervalConstraintProto&
interval)
const {
3004 int64 CpModelPresolver::EndMax(
const IntervalConstraintProto&
interval)
const {
3009 int64 CpModelPresolver::SizeMin(
const IntervalConstraintProto&
interval)
const {
3014 int64 CpModelPresolver::SizeMax(
const IntervalConstraintProto&
interval)
const {
3019 bool CpModelPresolver::PresolveNoOverlap(ConstraintProto*
ct) {
3021 const NoOverlapConstraintProto&
proto =
ct->no_overlap();
3022 const int initial_num_intervals =
proto.intervals_size();
3026 for (
int i = 0; i <
proto.intervals_size(); ++i) {
3027 const int interval_index =
proto.intervals(i);
3029 .constraint_case() ==
3030 ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET) {
3033 ct->mutable_no_overlap()->set_intervals(new_size++, interval_index);
3035 ct->mutable_no_overlap()->mutable_intervals()->Truncate(new_size);
3039 ct->mutable_no_overlap()->mutable_intervals()->begin(),
3040 ct->mutable_no_overlap()->mutable_intervals()->end(),
3041 [
this](
int i1,
int i2) {
3042 return StartMin(context_->working_model->constraints(i1).interval()) <
3043 StartMin(context_->working_model->constraints(i2).interval());
3052 for (
int i = 0; i <
proto.intervals_size(); ++i) {
3053 const int interval_index =
proto.intervals(i);
3054 const IntervalConstraintProto&
interval =
3055 context_->
working_model->constraints(interval_index).interval();
3056 const int64 end_max_of_previous_intervals = end_max_so_far;
3058 if (StartMin(
interval) >= end_max_of_previous_intervals &&
3059 (i + 1 ==
proto.intervals_size() ||
3061 ->constraints(
proto.intervals(i + 1))
3066 ct->mutable_no_overlap()->set_intervals(new_size++, interval_index);
3068 ct->mutable_no_overlap()->mutable_intervals()->Truncate(new_size);
3070 if (
proto.intervals_size() == 1) {
3072 return RemoveConstraint(
ct);
3074 if (
proto.intervals().empty()) {
3076 return RemoveConstraint(
ct);
3079 return new_size < initial_num_intervals;
3082 bool CpModelPresolver::PresolveCumulative(ConstraintProto*
ct) {
3085 const CumulativeConstraintProto&
proto =
ct->cumulative();
3089 bool changed =
false;
3090 int num_zero_demand_removed = 0;
3091 for (
int i = 0; i <
proto.intervals_size(); ++i) {
3093 .constraint_case() ==
3094 ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET) {
3098 const int demand_ref =
proto.demands(i);
3099 const int64 demand_max = context_->
MaxOf(demand_ref);
3100 if (demand_max == 0) {
3101 num_zero_demand_removed++;
3105 ct->mutable_cumulative()->set_intervals(new_size,
proto.intervals(i));
3106 ct->mutable_cumulative()->set_demands(new_size,
proto.demands(i));
3109 if (new_size <
proto.intervals_size()) {
3111 ct->mutable_cumulative()->mutable_intervals()->Truncate(new_size);
3112 ct->mutable_cumulative()->mutable_demands()->Truncate(new_size);
3115 if (num_zero_demand_removed > 0) {
3116 context_->
UpdateRuleStats(
"cumulative: removed intervals with no demands");
3119 if (new_size == 0) {
3121 return RemoveConstraint(
ct);
3125 if (!context_->
IsFixed(
proto.capacity()))
return changed;
3128 const int num_intervals =
proto.intervals_size();
3129 bool with_start_view =
false;
3130 std::vector<int> start_refs(num_intervals, -1);
3132 int num_duration_one = 0;
3133 int num_greater_half_capacity = 0;
3135 bool has_optional_interval =
false;
3136 for (
int i = 0; i < num_intervals; ++i) {
3138 const ConstraintProto&
ct =
3140 if (!
ct.enforcement_literal().empty()) has_optional_interval =
true;
3141 const IntervalConstraintProto&
interval =
ct.interval();
3142 if (
interval.has_start_view()) with_start_view =
true;
3144 const int demand_ref =
proto.demands(i);
3153 const int64 demand_min = context_->
MinOf(demand_ref);
3154 const int64 demand_max = context_->
MaxOf(demand_ref);
3156 num_greater_half_capacity++;
3160 if (
ct.enforcement_literal().empty()) {
3163 CHECK_EQ(
ct.enforcement_literal().size(), 1);
3169 }
else if (demand_max >
capacity) {
3170 if (
ct.enforcement_literal().empty()) {
3171 context_->
UpdateRuleStats(
"cumulative: demand_max exceeds capacity.");
3180 "cumulative: demand_max of optional interval exceeds capacity.");
3186 if (num_greater_half_capacity == num_intervals) {
3187 if (num_duration_one == num_intervals && !has_optional_interval &&
3190 ConstraintProto* new_ct = context_->
working_model->add_constraints();
3191 auto* arg = new_ct->mutable_all_diff();
3192 for (
const int var : start_refs) arg->add_vars(
var);
3194 return RemoveConstraint(
ct);
3197 ConstraintProto* new_ct = context_->
working_model->add_constraints();
3198 auto* arg = new_ct->mutable_no_overlap();
3203 return RemoveConstraint(
ct);
3210 bool CpModelPresolver::PresolveRoutes(ConstraintProto*
ct) {
3213 RoutesConstraintProto&
proto = *
ct->mutable_routes();
3216 const int num_arcs =
proto.literals_size();
3217 for (
int i = 0; i < num_arcs; ++i) {
3218 const int ref =
proto.literals(i);
3225 proto.set_literals(new_size, ref);
3230 if (new_size < num_arcs) {
3231 proto.mutable_literals()->Truncate(new_size);
3232 proto.mutable_tails()->Truncate(new_size);
3233 proto.mutable_heads()->Truncate(new_size);
3239 bool CpModelPresolver::PresolveCircuit(ConstraintProto*
ct) {
3242 CircuitConstraintProto&
proto = *
ct->mutable_circuit();
3246 ct->mutable_circuit()->mutable_heads());
3250 std::vector<std::vector<int>> incoming_arcs;
3251 std::vector<std::vector<int>> outgoing_arcs;
3253 const int num_arcs =
proto.literals_size();
3254 for (
int i = 0; i < num_arcs; ++i) {
3255 const int ref =
proto.literals(i);
3263 incoming_arcs[
head].push_back(ref);
3264 outgoing_arcs[
tail].push_back(ref);
3272 bool loop_again =
true;
3273 int num_fixed_at_true = 0;
3274 while (loop_again) {
3276 for (
const auto* node_to_refs : {&incoming_arcs, &outgoing_arcs}) {
3277 for (
const std::vector<int>& refs : *node_to_refs) {
3278 if (refs.size() == 1) {
3280 ++num_fixed_at_true;
3289 for (
const int ref : refs) {
3299 if (num_true == 1) {
3300 for (
const int ref : refs) {
3301 if (ref != true_ref) {
3302 if (!context_->
IsFixed(ref)) {
3313 if (num_fixed_at_true > 0) {
3320 int circuit_start = -1;
3321 std::vector<int>
next(num_nodes, -1);
3322 std::vector<int> new_in_degree(num_nodes, 0);
3323 std::vector<int> new_out_degree(num_nodes, 0);
3324 for (
int i = 0; i < num_arcs; ++i) {
3325 const int ref =
proto.literals(i);
3333 circuit_start =
proto.tails(i);
3337 ++new_out_degree[
proto.tails(i)];
3338 ++new_in_degree[
proto.heads(i)];
3341 proto.set_literals(new_size,
proto.literals(i));
3351 for (
int i = 0; i < num_nodes; ++i) {
3353 if (incoming_arcs[i].empty() && outgoing_arcs[i].empty())
continue;
3355 if (new_in_degree[i] == 0 || new_out_degree[i] == 0) {
3361 if (circuit_start != -1) {
3362 std::vector<bool> visited(num_nodes,
false);
3363 int current = circuit_start;
3364 while (current != -1 && !visited[current]) {
3365 visited[current] =
true;
3366 current =
next[current];
3368 if (current == circuit_start) {
3371 for (
int i = 0; i < num_arcs; ++i) {
3372 if (visited[
proto.tails(i)])
continue;
3380 return RemoveConstraint(
ct);
3384 if (num_true == new_size) {
3386 return RemoveConstraint(
ct);
3392 for (
int i = 0; i < num_nodes; ++i) {
3393 for (
const std::vector<int>* arc_literals :
3394 {&incoming_arcs[i], &outgoing_arcs[i]}) {
3395 std::vector<int> literals;
3396 for (
const int ref : *arc_literals) {
3402 literals.push_back(ref);
3404 if (literals.size() == 2 && literals[0] !=
NegatedRef(literals[1])) {
3413 if (new_size < num_arcs) {
3414 proto.mutable_tails()->Truncate(new_size);
3415 proto.mutable_heads()->Truncate(new_size);
3416 proto.mutable_literals()->Truncate(new_size);
3423 bool CpModelPresolver::PresolveAutomaton(ConstraintProto*
ct) {
3426 AutomatonConstraintProto&
proto = *
ct->mutable_automaton();
3427 if (
proto.vars_size() == 0 ||
proto.transition_label_size() == 0) {
3431 bool all_affine =
true;
3432 std::vector<AffineRelation::Relation> affine_relations;
3433 for (
int v = 0; v <
proto.vars_size(); ++v) {
3434 const int var =
ct->automaton().vars(v);
3436 affine_relations.push_back(r);
3437 if (r.representative ==
var) {
3441 if (v > 0 && (r.coeff != affine_relations[v - 1].coeff ||
3442 r.offset != affine_relations[v - 1].offset)) {
3449 for (
int v = 0; v <
proto.vars_size(); ++v) {
3452 const AffineRelation::Relation rep = affine_relations.front();
3454 for (
int t = 0; t <
proto.transition_tail_size(); ++t) {
3455 const int64 label =
proto.transition_label(t);
3456 int64 inverse_label = (label - rep.offset) / rep.coeff;
3457 if (inverse_label * rep.coeff + rep.offset == label) {
3458 if (new_size != t) {
3459 proto.set_transition_tail(new_size,
proto.transition_tail(t));
3460 proto.set_transition_head(new_size,
proto.transition_head(t));
3462 proto.set_transition_label(new_size, inverse_label);
3466 if (new_size <
proto.transition_tail_size()) {
3467 proto.mutable_transition_tail()->Truncate(new_size);
3468 proto.mutable_transition_label()->Truncate(new_size);
3469 proto.mutable_transition_head()->Truncate(new_size);
3477 for (
int v = 1; v <
proto.vars_size(); ++v) {
3482 for (
int t = 0; t <
proto.transition_tail_size(); ++t) {
3483 const int64 label =
proto.transition_label(t);
3484 if (hull.Contains(label)) {
3485 if (new_size != t) {
3486 proto.set_transition_tail(new_size,
proto.transition_tail(t));
3487 proto.set_transition_label(new_size, label);
3488 proto.set_transition_head(new_size,
proto.transition_head(t));
3493 if (new_size <
proto.transition_tail_size()) {
3494 proto.mutable_transition_tail()->Truncate(new_size);
3495 proto.mutable_transition_label()->Truncate(new_size);
3496 proto.mutable_transition_head()->Truncate(new_size);
3501 const int n =
proto.vars_size();
3502 const std::vector<int> vars = {
proto.vars().begin(),
proto.vars().end()};
3505 std::vector<std::set<int64>> reachable_states(n + 1);
3506 reachable_states[0].insert(
proto.starting_state());
3507 reachable_states[n] = {
proto.final_states().begin(),
3508 proto.final_states().end()};
3515 for (
int t = 0; t <
proto.transition_tail_size(); ++t) {
3517 const int64 label =
proto.transition_label(t);
3521 reachable_states[
time + 1].insert(
head);
3525 std::vector<std::set<int64>> reached_values(n);
3529 std::set<int64> new_set;
3530 for (
int t = 0; t <
proto.transition_tail_size(); ++t) {
3532 const int64 label =
proto.transition_label(t);
3538 new_set.insert(
tail);
3539 reached_values[
time].insert(label);
3541 reachable_states[
time].swap(new_set);
3544 bool removed_values =
false;
3549 {reached_values[time].begin(), reached_values[time].end()}),
3554 if (removed_values) {
3560 bool CpModelPresolver::PresolveReservoir(ConstraintProto*
ct) {
3564 bool changed =
false;
3566 ReservoirConstraintProto& mutable_reservoir = *
ct->mutable_reservoir();
3567 if (mutable_reservoir.actives().empty()) {
3569 for (
int i = 0; i < mutable_reservoir.times_size(); ++i) {
3570 mutable_reservoir.add_actives(true_literal);
3575 const auto& demand_is_null = [&](
int i) {
3576 return mutable_reservoir.demands(i) == 0 ||
3582 for (
int i = 0; i < mutable_reservoir.demands_size(); ++i) {
3583 if (demand_is_null(i)) num_zeros++;
3586 if (num_zeros > 0) {
3589 for (
int i = 0; i < mutable_reservoir.demands_size(); ++i) {
3590 if (demand_is_null(i))
continue;
3591 mutable_reservoir.set_demands(new_size, mutable_reservoir.demands(i));
3592 mutable_reservoir.set_times(new_size, mutable_reservoir.times(i));
3593 mutable_reservoir.set_actives(new_size, mutable_reservoir.actives(i));
3597 mutable_reservoir.mutable_demands()->Truncate(new_size);
3598 mutable_reservoir.mutable_times()->Truncate(new_size);
3599 mutable_reservoir.mutable_actives()->Truncate(new_size);
3602 "reservoir: remove zero demands or inactive events.");
3605 const int num_events = mutable_reservoir.demands_size();
3606 int64 gcd = mutable_reservoir.demands().empty()
3608 : std::abs(mutable_reservoir.demands(0));
3609 int num_positives = 0;
3610 int num_negatives = 0;
3611 int64 sum_of_demands = 0;
3612 int64 max_sum_of_positive_demands = 0;
3613 int64 min_sum_of_negative_demands = 0;
3614 for (
int i = 0; i < num_events; ++i) {
3615 const int64 demand = mutable_reservoir.demands(i);
3616 sum_of_demands +=
demand;
3620 max_sum_of_positive_demands +=
demand;
3624 min_sum_of_negative_demands +=
demand;
3628 if (min_sum_of_negative_demands >= mutable_reservoir.min_level() &&
3629 max_sum_of_positive_demands <= mutable_reservoir.max_level()) {
3631 return RemoveConstraint(
ct);
3634 if (min_sum_of_negative_demands > mutable_reservoir.max_level() ||
3635 max_sum_of_positive_demands < mutable_reservoir.min_level()) {
3640 if (min_sum_of_negative_demands > mutable_reservoir.min_level()) {
3641 mutable_reservoir.set_min_level(min_sum_of_negative_demands);
3643 "reservoir: increase min_level to reachable value");
3646 if (max_sum_of_positive_demands < mutable_reservoir.max_level()) {
3647 mutable_reservoir.set_max_level(max_sum_of_positive_demands);
3648 context_->
UpdateRuleStats(
"reservoir: reduce max_level to reachable value");
3651 if (mutable_reservoir.min_level() <= 0 &&
3652 mutable_reservoir.max_level() >= 0 &&
3653 (num_positives == 0 || num_negatives == 0)) {
3657 context_->
working_model->add_constraints()->mutable_linear();
3658 int64 fixed_contrib = 0;
3659 for (
int i = 0; i < mutable_reservoir.demands_size(); ++i) {
3660 const int64 demand = mutable_reservoir.demands(i);
3663 const int active = mutable_reservoir.actives(i);
3665 sum->add_vars(active);
3669 sum->add_coeffs(-
demand);
3673 sum->add_domain(mutable_reservoir.min_level() - fixed_contrib);
3674 sum->add_domain(mutable_reservoir.max_level() - fixed_contrib);
3676 return RemoveConstraint(
ct);
3680 for (
int i = 0; i < mutable_reservoir.demands_size(); ++i) {
3681 mutable_reservoir.set_demands(i, mutable_reservoir.demands(i) / gcd);
3687 const Domain reduced_domain =
3688 Domain({mutable_reservoir.min_level(), mutable_reservoir.max_level()})
3689 .InverseMultiplicationBy(gcd);
3690 mutable_reservoir.set_min_level(reduced_domain.Min());
3691 mutable_reservoir.set_max_level(reduced_domain.Max());
3692 context_->
UpdateRuleStats(
"reservoir: simplify demands and levels by gcd.");
3695 if (num_positives == 1 && num_negatives > 0) {
3697 "TODO reservoir: one producer, multiple consumers.");
3700 absl::flat_hash_set<std::pair<int, int>> time_active_set;
3701 for (
int i = 0; i < mutable_reservoir.demands_size(); ++i) {
3702 const std::pair<int, int> key = std::make_pair(
3703 mutable_reservoir.times(i), mutable_reservoir.actives(i));
3704 if (time_active_set.contains(key)) {
3705 context_->
UpdateRuleStats(
"TODO reservoir: merge synchronized events.");
3708 time_active_set.insert(key);
3718 void CpModelPresolver::ExtractBoolAnd() {
3719 absl::flat_hash_map<int, int> ref_to_bool_and;
3720 const int num_constraints = context_->
working_model->constraints_size();
3721 std::vector<int> to_remove;
3722 for (
int c = 0; c < num_constraints; ++c) {
3726 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr &&
3727 ct.bool_or().literals().size() == 2) {
3731 to_remove.push_back(c);
3735 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kAtMostOne &&
3736 ct.at_most_one().literals().size() == 2) {
3737 AddImplication(
ct.at_most_one().literals(0),
3740 to_remove.push_back(c);
3746 for (
const int c : to_remove) {
3747 ConstraintProto*
ct = context_->
working_model->mutable_constraints(c);
3753 void CpModelPresolver::Probe() {
3757 for (
int i = 0; i < context_->
working_model->variables_size(); ++i) {
3777 auto* local_param =
model.GetOrCreate<SatParameters>();
3778 *local_param = context_->
params();
3779 local_param->set_use_implied_bounds(
false);
3781 model.GetOrCreate<TimeLimit>()->MergeWithGlobalTimeLimit(
3783 model.Register<ModelRandomGenerator>(context_->
random());
3784 auto* encoder =
model.GetOrCreate<IntegerEncoder>();
3785 encoder->DisableImplicationBetweenLiteral();
3786 auto* mapping =
model.GetOrCreate<CpModelMapping>();
3790 auto* sat_solver =
model.GetOrCreate<SatSolver>();
3791 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
3792 if (mapping->ConstraintIsAlreadyLoaded(&
ct))
continue;
3794 if (sat_solver->IsModelUnsat()) {
3798 encoder->AddAllImplicationsBetweenAssociatedLiterals();
3799 if (!sat_solver->Propagate()) {
3807 auto* implication_graph =
model.GetOrCreate<BinaryImplicationGraph>();
3808 auto* prober =
model.GetOrCreate<Prober>();
3809 prober->ProbeBooleanVariables(1.0);
3811 model.GetOrCreate<TimeLimit>()->GetElapsedDeterministicTime());
3812 if (sat_solver->IsModelUnsat() || !implication_graph->DetectEquivalences()) {
3817 CHECK_EQ(sat_solver->CurrentDecisionLevel(), 0);
3818 for (
int i = 0; i < sat_solver->LiteralTrail().
Index(); ++i) {
3819 const Literal l = sat_solver->LiteralTrail()[i];
3820 const int var = mapping->GetProtoVariableFromBooleanVariable(l.Variable());
3827 const int num_variables = context_->
working_model->variables().size();
3828 auto* integer_trail =
model.GetOrCreate<IntegerTrail>();
3829 for (
int var = 0;
var < num_variables; ++
var) {
3832 if (!mapping->IsBoolean(
var)) {
3835 integer_trail->InitialVariableDomain(mapping->Integer(
var)))) {
3842 const Literal l = mapping->Literal(
var);
3843 const Literal r = implication_graph->RepresentativeOf(l);
3846 mapping->GetProtoVariableFromBooleanVariable(r.Variable());
3856 void CpModelPresolver::PresolvePureSatPart() {
3861 const int num_variables = context_->
working_model->variables_size();
3862 SatPostsolver sat_postsolver(num_variables);
3863 SatPresolver sat_presolver(&sat_postsolver);
3864 sat_presolver.SetNumVariables(num_variables);
3865 sat_presolver.SetTimeLimit(context_->
time_limit());
3867 SatParameters params = context_->
params();
3874 if (params.cp_model_postsolve_with_full_solver()) {
3875 params.set_presolve_blocked_clause(
false);
3881 params.set_presolve_use_bva(
false);
3882 sat_presolver.SetParameters(params);
3885 absl::flat_hash_set<int> used_variables;
3886 auto convert = [&used_variables](
int ref) {
3888 if (
RefIsPositive(ref))
return Literal(BooleanVariable(ref),
true);
3889 return Literal(BooleanVariable(
NegatedRef(ref)),
false);
3897 for (
int c = 0; c < context_->
working_model->constraints_size(); ++c) {
3899 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr ||
3900 ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolAnd) {
3919 std::vector<Literal> clause;
3920 int num_removed_constraints = 0;
3921 for (
int i = 0; i < context_->
working_model->constraints_size(); ++i) {
3924 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr) {
3925 ++num_removed_constraints;
3927 for (
const int ref :
ct.bool_or().literals()) {
3928 clause.push_back(convert(ref));
3930 for (
const int ref :
ct.enforcement_literal()) {
3931 clause.push_back(convert(ref).Negated());
3933 sat_presolver.AddClause(clause);
3940 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolAnd) {
3941 ++num_removed_constraints;
3942 std::vector<Literal> clause;
3943 for (
const int ref :
ct.enforcement_literal()) {
3944 clause.push_back(convert(ref).Negated());
3947 for (
const int ref :
ct.bool_and().literals()) {
3948 clause.back() = convert(ref);
3949 sat_presolver.AddClause(clause);
3959 if (num_removed_constraints == 0)
return;
3969 std::vector<bool> can_be_removed(num_variables,
false);
3970 for (
int i = 0; i < num_variables; ++i) {
3972 can_be_removed[i] =
true;
3978 if (used_variables.contains(i) && context_->
IsFixed(i)) {
3980 sat_presolver.AddClause({convert(i)});
3982 sat_presolver.AddClause({convert(
NegatedRef(i))});
3990 const int num_passes = params.presolve_use_bva() ? 4 : 1;
3991 for (
int i = 0; i < num_passes; ++i) {
3992 const int old_num_clause = sat_postsolver.NumClauses();
3993 if (!sat_presolver.Presolve(can_be_removed, context_->
log_info())) {
3994 VLOG(1) <<
"UNSAT during SAT presolve.";
3997 if (old_num_clause == sat_postsolver.NumClauses())
break;
4001 const int new_num_variables = sat_presolver.NumVariables();
4002 if (new_num_variables > context_->
working_model->variables_size()) {
4003 VLOG(1) <<
"New variables added by the SAT presolver.";
4005 i < new_num_variables; ++i) {
4006 IntegerVariableProto* var_proto =
4008 var_proto->add_domain(0);
4009 var_proto->add_domain(1);
4015 ExtractClauses(
true, sat_presolver, context_->
working_model);
4023 ExtractClauses(
false, sat_postsolver,
4031 void CpModelPresolver::ExpandObjective() {
4050 int unique_expanded_constraint = -1;
4051 const bool objective_was_a_single_variable =
4056 const int num_variables = context_->
working_model->variables_size();
4057 const int num_constraints = context_->
working_model->constraints_size();
4058 absl::flat_hash_set<int> relevant_constraints;
4059 std::vector<int> var_to_num_relevant_constraints(num_variables, 0);
4060 for (
int ct_index = 0; ct_index < num_constraints; ++ct_index) {
4061 const ConstraintProto&
ct = context_->
working_model->constraints(ct_index);
4063 if (!
ct.enforcement_literal().empty() ||
4064 ct.constraint_case() != ConstraintProto::ConstraintCase::kLinear ||
4065 ct.linear().domain().size() != 2 ||
4066 ct.linear().domain(0) !=
ct.linear().domain(1)) {
4070 relevant_constraints.insert(ct_index);
4071 const int num_terms =
ct.linear().vars_size();
4072 for (
int i = 0; i < num_terms; ++i) {
4073 var_to_num_relevant_constraints[
PositiveRef(
ct.linear().vars(i))]++;
4077 std::set<int> var_to_process;
4079 const int var = entry.first;
4081 if (var_to_num_relevant_constraints[
var] != 0) {
4082 var_to_process.insert(
var);
4087 int num_expansions = 0;
4088 absl::flat_hash_set<int> processed_vars;
4089 std::vector<int> new_vars_in_objective;
4090 while (!relevant_constraints.empty()) {
4092 int objective_var = -1;
4093 while (!var_to_process.empty()) {
4094 const int var = *var_to_process.begin();
4095 CHECK(!processed_vars.contains(
var));
4096 if (var_to_num_relevant_constraints[
var] == 0) {
4097 processed_vars.insert(
var);
4098 var_to_process.erase(
var);
4103 var_to_process.erase(
var);
4106 objective_var =
var;
4110 if (objective_var == -1)
break;
4112 processed_vars.insert(objective_var);
4113 var_to_process.erase(objective_var);
4115 int expanded_linear_index = -1;
4116 int64 objective_coeff_in_expanded_constraint;
4117 int64 size_of_expanded_constraint = 0;
4118 const auto& non_deterministic_list =
4120 std::vector<int> constraints_with_objective(non_deterministic_list.begin(),
4121 non_deterministic_list.end());
4122 std::sort(constraints_with_objective.begin(),
4123 constraints_with_objective.end());
4124 for (
const int ct_index : constraints_with_objective) {
4125 if (relevant_constraints.count(ct_index) == 0)
continue;
4126 const ConstraintProto&
ct =
4131 relevant_constraints.erase(ct_index);
4132 const int num_terms =
ct.linear().vars_size();
4133 for (
int i = 0; i < num_terms; ++i) {
4134 var_to_num_relevant_constraints[
PositiveRef(
ct.linear().vars(i))]--;
4146 bool is_present =
false;
4147 int64 objective_coeff;
4148 for (
int i = 0; i < num_terms; ++i) {
4149 const int ref =
ct.linear().vars(i);
4150 const int64 coeff =
ct.linear().coeffs(i);
4152 CHECK(!is_present) <<
"Duplicate variables not supported.";
4154 objective_coeff = (ref == objective_var) ? coeff : -coeff;
4167 if (std::abs(objective_coeff) == 1 &&
4168 num_terms > size_of_expanded_constraint) {
4169 expanded_linear_index = ct_index;
4170 size_of_expanded_constraint = num_terms;
4171 objective_coeff_in_expanded_constraint = objective_coeff;
4175 if (expanded_linear_index != -1) {
4176 context_->
UpdateRuleStats(
"objective: expanded objective constraint.");
4180 CHECK_EQ(std::abs(objective_coeff_in_expanded_constraint), 1);
4181 const ConstraintProto&
ct =
4182 context_->
working_model->constraints(expanded_linear_index);
4184 objective_var, objective_coeff_in_expanded_constraint,
ct,
4185 &new_vars_in_objective);
4188 for (
const int var : new_vars_in_objective) {
4189 if (!processed_vars.contains(
var)) var_to_process.insert(
var);
4202 for (
int i = 0; i < size_of_expanded_constraint; ++i) {
4203 const int ref =
ct.linear().vars(i);
4208 -
ct.linear().coeffs(i)))
4209 .RelaxIfTooComplex();
4211 implied_domain = implied_domain.InverseMultiplicationBy(
4212 objective_coeff_in_expanded_constraint);
4216 if (implied_domain.IsIncludedIn(context_->
DomainOf(objective_var))) {
4217 context_->
UpdateRuleStats(
"objective: removed objective constraint.");
4219 context_->
working_model->mutable_constraints(expanded_linear_index)
4223 unique_expanded_constraint = expanded_linear_index;
4233 if (num_expansions == 1 && objective_was_a_single_variable &&
4234 unique_expanded_constraint != -1) {
4236 "objective: removed unique objective constraint.");
4237 ConstraintProto* mutable_ct = context_->
working_model->mutable_constraints(
4238 unique_expanded_constraint);
4239 *(context_->
mapping_model->add_constraints()) = *mutable_ct;
4240 mutable_ct->Clear();
4252 void CpModelPresolver::MergeNoOverlapConstraints() {
4255 const int num_constraints = context_->
working_model->constraints_size();
4256 int old_num_no_overlaps = 0;
4257 int old_num_intervals = 0;
4260 std::vector<int> disjunctive_index;
4261 std::vector<std::vector<Literal>> cliques;
4262 for (
int c = 0; c < num_constraints; ++c) {
4264 if (
ct.constraint_case() != ConstraintProto::ConstraintCase::kNoOverlap) {
4267 std::vector<Literal> clique;
4268 for (
const int i :
ct.no_overlap().intervals()) {
4269 clique.push_back(Literal(BooleanVariable(i),
true));
4271 cliques.push_back(clique);
4272 disjunctive_index.push_back(c);
4274 old_num_no_overlaps++;
4275 old_num_intervals += clique.size();
4277 if (old_num_no_overlaps == 0)
return;
4281 local_model.GetOrCreate<Trail>()->Resize(num_constraints);
4282 auto* graph = local_model.GetOrCreate<BinaryImplicationGraph>();
4283 graph->Resize(num_constraints);
4284 for (
const std::vector<Literal>& clique : cliques) {
4287 CHECK(graph->AddAtMostOne(clique));
4289 CHECK(graph->DetectEquivalences());
4290 graph->TransformIntoMaxCliques(
4291 &cliques, context_->
params().merge_no_overlap_work_limit());
4294 int new_num_no_overlaps = 0;
4295 int new_num_intervals = 0;
4296 for (
int i = 0; i < cliques.size(); ++i) {
4297 const int ct_index = disjunctive_index[i];
4298 ConstraintProto*
ct =
4301 if (cliques[i].empty())
continue;
4302 for (
const Literal l : cliques[i]) {
4303 CHECK(l.IsPositive());
4304 ct->mutable_no_overlap()->add_intervals(l.Variable().value());
4306 new_num_no_overlaps++;
4307 new_num_intervals += cliques[i].size();
4309 if (old_num_intervals != new_num_intervals ||
4310 old_num_no_overlaps != new_num_no_overlaps) {
4311 VLOG(1) << absl::StrCat(
"Merged ", old_num_no_overlaps,
" no-overlaps (",
4312 old_num_intervals,
" intervals) into ",
4313 new_num_no_overlaps,
" no-overlaps (",
4314 new_num_intervals,
" intervals).");
4323 void CpModelPresolver::TransformIntoMaxCliques() {
4326 auto convert = [](
int ref) {
4327 if (
RefIsPositive(ref))
return Literal(BooleanVariable(ref),
true);
4328 return Literal(BooleanVariable(
NegatedRef(ref)),
false);
4330 const int num_constraints = context_->
working_model->constraints_size();
4333 std::vector<std::vector<Literal>> cliques;
4335 for (
int c = 0; c < num_constraints; ++c) {
4336 ConstraintProto*
ct = context_->
working_model->mutable_constraints(c);
4337 if (
ct->constraint_case() == ConstraintProto::ConstraintCase::kAtMostOne) {
4338 std::vector<Literal> clique;
4339 for (
const int ref :
ct->at_most_one().literals()) {
4340 clique.push_back(convert(ref));
4342 cliques.push_back(clique);
4343 if (RemoveConstraint(
ct)) {
4346 }
else if (
ct->constraint_case() ==
4347 ConstraintProto::ConstraintCase::kBoolAnd) {
4348 if (
ct->enforcement_literal().size() != 1)
continue;
4349 const Literal enforcement = convert(
ct->enforcement_literal(0));
4350 for (
const int ref :
ct->bool_and().literals()) {
4351 cliques.push_back({enforcement, convert(ref).Negated()});
4353 if (RemoveConstraint(
ct)) {
4359 const int num_old_cliques = cliques.size();
4363 const int num_variables = context_->
working_model->variables().size();
4364 local_model.GetOrCreate<Trail>()->Resize(num_variables);
4365 auto* graph = local_model.GetOrCreate<BinaryImplicationGraph>();
4366 graph->Resize(num_variables);
4367 for (
const std::vector<Literal>& clique : cliques) {
4368 if (!graph->AddAtMostOne(clique)) {
4372 if (!graph->DetectEquivalences()) {
4375 graph->TransformIntoMaxCliques(
4376 &cliques, context_->
params().merge_at_most_one_work_limit());
4381 for (
int var = 0;
var < num_variables; ++
var) {
4382 const Literal l = Literal(BooleanVariable(
var),
true);
4383 if (graph->RepresentativeOf(l) != l) {
4384 const Literal r = graph->RepresentativeOf(l);
4386 var, r.IsPositive() ? r.Variable().value()
4391 int num_new_cliques = 0;
4392 for (
const std::vector<Literal>& clique : cliques) {
4393 if (clique.empty())
continue;
4396 for (
const Literal
literal : clique) {
4398 ct->mutable_at_most_one()->add_literals(
literal.Variable().value());
4400 ct->mutable_at_most_one()->add_literals(
4406 if (num_new_cliques != num_old_cliques) {
4407 context_->
UpdateRuleStats(
"at_most_one: transformed into max clique.");
4411 LOG(
INFO) <<
"Merged " << num_old_cliques <<
" into " << num_new_cliques
4418 ConstraintProto*
ct = context_->
working_model->mutable_constraints(c);
4421 if (ExploitEquivalenceRelations(c,
ct)) {
4426 if (PresolveEnforcementLiteral(
ct)) {
4431 switch (
ct->constraint_case()) {
4432 case ConstraintProto::ConstraintCase::kBoolOr:
4433 return PresolveBoolOr(
ct);
4434 case ConstraintProto::ConstraintCase::kBoolAnd:
4435 return PresolveBoolAnd(
ct);
4436 case ConstraintProto::ConstraintCase::kAtMostOne:
4437 return PresolveAtMostOne(
ct);
4438 case ConstraintProto::ConstraintCase::kExactlyOne:
4439 return PresolveExactlyOne(
ct);
4440 case ConstraintProto::ConstraintCase::kBoolXor:
4441 return PresolveBoolXor(
ct);
4442 case ConstraintProto::ConstraintCase::kIntMax:
4443 if (
ct->int_max().vars_size() == 2 &&
4445 return PresolveIntAbs(
ct);
4447 return PresolveIntMax(
ct);
4449 case ConstraintProto::ConstraintCase::kIntMin:
4450 return PresolveIntMin(
ct);
4451 case ConstraintProto::ConstraintCase::kLinMax:
4452 return PresolveLinMax(
ct);
4453 case ConstraintProto::ConstraintCase::kLinMin:
4454 return PresolveLinMin(
ct);
4455 case ConstraintProto::ConstraintCase::kIntProd:
4456 return PresolveIntProd(
ct);
4457 case ConstraintProto::ConstraintCase::kIntDiv:
4458 return PresolveIntDiv(
ct);
4459 case ConstraintProto::ConstraintCase::kLinear: {
4460 if (CanonicalizeLinear(
ct)) {
4463 if (PresolveSmallLinear(
ct)) {
4466 if (PropagateDomainsInLinear(c,
ct)) {
4469 if (PresolveSmallLinear(
ct)) {
4473 if (RemoveSingletonInLinear(
ct)) {
4478 if (PresolveSmallLinear(
ct)) {
4482 if (PresolveLinearOnBooleans(
ct)) {
4485 if (
ct->constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
4486 const int old_num_enforcement_literals =
ct->enforcement_literal_size();
4487 ExtractEnforcementLiteralFromLinearConstraint(c,
ct);
4488 if (
ct->constraint_case() ==
4489 ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET) {
4493 if (
ct->enforcement_literal_size() > old_num_enforcement_literals &&
4494 PresolveSmallLinear(
ct)) {
4497 PresolveLinearEqualityModuloTwo(
ct);
4501 case ConstraintProto::ConstraintCase::kInterval:
4502 return PresolveInterval(c,
ct);
4503 case ConstraintProto::ConstraintCase::kElement:
4504 return PresolveElement(
ct);
4505 case ConstraintProto::ConstraintCase::kTable:
4506 return PresolveTable(
ct);
4507 case ConstraintProto::ConstraintCase::kAllDiff:
4508 return PresolveAllDiff(
ct);
4509 case ConstraintProto::ConstraintCase::kNoOverlap:
4510 return PresolveNoOverlap(
ct);
4511 case ConstraintProto::ConstraintCase::kCumulative:
4512 return PresolveCumulative(
ct);
4513 case ConstraintProto::ConstraintCase::kCircuit:
4514 return PresolveCircuit(
ct);
4515 case ConstraintProto::ConstraintCase::kRoutes:
4516 return PresolveRoutes(
ct);
4517 case ConstraintProto::ConstraintCase::kAutomaton:
4518 return PresolveAutomaton(
ct);
4519 case ConstraintProto::ConstraintCase::kReservoir:
4520 return PresolveReservoir(
ct);
4529 bool CpModelPresolver::ProcessSetPPCSubset(
4530 int c1,
int c2,
const std::vector<int>& c2_minus_c1,
4531 const std::vector<int>& original_constraint_index,
4532 std::vector<bool>* removed) {
4535 CHECK(!(*removed)[c1]);
4536 CHECK(!(*removed)[c2]);
4538 ConstraintProto* ct1 = context_->
working_model->mutable_constraints(
4539 original_constraint_index[c1]);
4540 ConstraintProto* ct2 = context_->
working_model->mutable_constraints(
4541 original_constraint_index[c2]);
4543 if ((ct1->constraint_case() == ConstraintProto::kBoolOr ||
4544 ct1->constraint_case() == ConstraintProto::kExactlyOne) &&
4545 (ct2->constraint_case() == ConstraintProto::kAtMostOne ||
4546 ct2->constraint_case() == ConstraintProto::kExactlyOne)) {
4551 for (
const int literal : c2_minus_c1) {
4557 if (ct2->constraint_case() != ConstraintProto::kExactlyOne) {
4558 ConstraintProto copy = *ct2;
4559 (*ct2->mutable_exactly_one()->mutable_literals()) =
4560 copy.at_most_one().literals();
4564 (*removed)[c1] =
true;
4570 if ((ct1->constraint_case() == ConstraintProto::kBoolOr ||
4571 ct1->constraint_case() == ConstraintProto::kExactlyOne) &&
4572 ct2->constraint_case() == ConstraintProto::kBoolOr) {
4575 (*removed)[c2] =
true;
4581 if (ct1->constraint_case() == ConstraintProto::kAtMostOne &&
4582 (ct2->constraint_case() == ConstraintProto::kAtMostOne ||
4583 ct2->constraint_case() == ConstraintProto::kExactlyOne)) {
4585 (*removed)[c1] =
true;
4601 bool CpModelPresolver::ProcessSetPPC() {
4602 const int num_constraints = context_->
working_model->constraints_size();
4606 std::vector<uint64> signatures;
4610 std::vector<std::vector<int>> constraint_literals;
4614 std::vector<std::vector<int>> literals_to_constraints;
4617 std::vector<bool> removed;
4621 std::vector<int> original_constraint_index;
4625 int num_setppc_constraints = 0;
4626 for (
int c = 0; c < num_constraints; ++c) {
4627 ConstraintProto*
ct = context_->
working_model->mutable_constraints(c);
4628 if (
ct->constraint_case() == ConstraintProto::ConstraintCase::kBoolOr ||
4629 ct->constraint_case() == ConstraintProto::ConstraintCase::kAtMostOne ||
4630 ct->constraint_case() == ConstraintProto::ConstraintCase::kExactlyOne) {
4639 if (
ct->constraint_case() == ConstraintProto::ConstraintCase::kBoolOr ||
4640 ct->constraint_case() == ConstraintProto::ConstraintCase::kAtMostOne ||
4641 ct->constraint_case() == ConstraintProto::ConstraintCase::kExactlyOne) {
4642 constraint_literals.push_back(GetLiteralsFromSetPPCConstraint(*
ct));
4645 for (
const int literal : constraint_literals.back()) {
4647 signature |= (
int64{1} << (positive_literal % 64));
4649 if (positive_literal >= literals_to_constraints.size()) {
4650 literals_to_constraints.resize(positive_literal + 1);
4652 literals_to_constraints[positive_literal].push_back(
4653 num_setppc_constraints);
4655 signatures.push_back(signature);
4656 removed.push_back(
false);
4657 original_constraint_index.push_back(c);
4658 num_setppc_constraints++;
4661 VLOG(1) <<
"#setppc constraints: " << num_setppc_constraints;
4664 absl::flat_hash_set<std::pair<int, int>> compared_constraints;
4665 for (
const std::vector<int>& literal_to_constraints :
4666 literals_to_constraints) {
4667 for (
int index1 = 0; index1 < literal_to_constraints.size(); ++index1) {
4670 const int c1 = literal_to_constraints[index1];
4671 if (removed[c1])
continue;
4672 const std::vector<int>& c1_literals = constraint_literals[c1];
4674 for (
int index2 = index1 + 1; index2 < literal_to_constraints.size();
4676 const int c2 = literal_to_constraints[index2];
4677 if (removed[c2])
continue;
4678 if (removed[c1])
break;
4681 if (c1 == c2)
continue;
4685 std::pair<int, int>(c1, c2))) {
4688 compared_constraints.insert({c1, c2});
4692 if (compared_constraints.size() >= 50000)
return true;
4694 const bool smaller = (signatures[c1] & ~signatures[c2]) == 0;
4695 const bool larger = (signatures[c2] & ~signatures[c1]) == 0;
4696 if (!(smaller || larger))
continue;
4699 const std::vector<int>& c2_literals = constraint_literals[c2];
4703 std::vector<int> c1_minus_c2;
4705 std::vector<int> c2_minus_c1;
4708 if (c1_minus_c2.empty()) {
4709 if (!ProcessSetPPCSubset(c1, c2, c2_minus_c1,
4710 original_constraint_index, &removed)) {
4713 }
else if (c2_minus_c1.empty()) {
4714 if (!ProcessSetPPCSubset(c2, c1, c1_minus_c2,
4715 original_constraint_index, &removed)) {
4726 void CpModelPresolver::TryToSimplifyDomain(
int var) {
4734 if (r.representative !=
var)
return;
4749 if (domain.Size() == 2 && (domain.Min() != 0 || domain.Max() != 1)) {
4754 if (domain.NumIntervals() != domain.Size())
return;
4756 const int64 var_min = domain.Min();
4757 int64 gcd = domain[1].start - var_min;
4759 const ClosedInterval& i = domain[
index];
4761 const int64 shifted_value = i.start - var_min;
4765 if (gcd == 1)
break;
4767 if (gcd == 1)
return;
4771 std::vector<int64> scaled_values;
4773 const ClosedInterval& i = domain[
index];
4775 const int64 shifted_value = i.start - var_min;
4776 scaled_values.push_back(shifted_value / gcd);
4788 void CpModelPresolver::EncodeAllAffineRelations() {
4789 int64 num_added = 0;
4794 if (r.representative ==
var)
continue;
4801 if (!PresolveAffineRelationIfAny(
var))
break;
4808 auto* arg =
ct->mutable_linear();
4811 arg->add_vars(r.representative);
4812 arg->add_coeffs(-r.coeff);
4813 arg->add_domain(r.offset);
4814 arg->add_domain(r.offset);
4822 if (context_->
log_info() && num_added > 0) {
4823 LOG(
INFO) << num_added <<
" affine relations still in the model.";
4828 bool CpModelPresolver::PresolveAffineRelationIfAny(
int var) {
4832 if (r.representative ==
var)
return true;
4851 auto* arg =
ct->mutable_linear();
4854 arg->add_vars(r.representative);
4855 arg->add_coeffs(-r.coeff);
4856 arg->add_domain(r.offset);
4857 arg->add_domain(r.offset);
4863 void CpModelPresolver::PresolveToFixPoint() {
4867 const int64 max_num_operations =
4868 context_->
params().cp_model_max_num_presolve_operations() > 0
4869 ? context_->
params().cp_model_max_num_presolve_operations()
4875 absl::flat_hash_set<std::pair<int, int>> var_constraint_pair_already_called;
4880 std::vector<bool> in_queue(context_->
working_model->constraints_size(),
4882 std::deque<int> queue;
4883 for (
int c = 0; c < in_queue.size(); ++c) {
4884 if (context_->
working_model->constraints(c).constraint_case() !=
4885 ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET) {
4895 if (context_->
params().permute_presolve_constraint_order()) {
4896 std::shuffle(queue.begin(), queue.end(), *context_->
random());
4898 std::sort(queue.begin(), queue.end(), [
this](
int a,
int b) {
4899 const int score_a = context_->ConstraintToVars(a).size();
4900 const int score_b = context_->ConstraintToVars(b).size();
4901 return score_a < score_b || (score_a == score_b && a < b);
4911 const int c = queue.front();
4912 in_queue[c] =
false;
4915 const int old_num_constraint =
4919 LOG(
INFO) <<
"Unsat after presolving constraint #" << c
4920 <<
" (warning, dump might be inconsistent): "
4921 << context_->
working_model->constraints(c).ShortDebugString();
4925 const int new_num_constraints =
4927 if (new_num_constraints > old_num_constraint) {
4929 in_queue.resize(new_num_constraints,
true);
4930 for (
int c = old_num_constraint; c < new_num_constraints; ++c) {
4947 const int current_num_variables = context_->
working_model->variables_size();
4948 for (
int v = 0; v < current_num_variables; ++v) {
4950 if (!PresolveAffineRelationIfAny(v))
return;
4955 TryToSimplifyDomain(v);
4964 in_queue.resize(context_->
working_model->constraints_size(),
false);
4969 if (c >= 0 && !in_queue[c]) {
4979 const int num_vars = context_->
working_model->variables_size();
4980 for (
int v = 0; v < num_vars; ++v) {
4982 if (constraints.size() != 1)
continue;
4983 const int c = *constraints.begin();
4984 if (c < 0)
continue;
4990 std::pair<int, int>(v, c))) {
4993 var_constraint_pair_already_called.insert({v, c});
5003 std::sort(queue.begin(), queue.end());
5017 VarDomination var_dom;
5018 DualBoundStrengthening dual_bound_strengthening;
5020 if (!dual_bound_strengthening.Strengthen(context_))
return;
5037 const int num_constraints = context_->
working_model->constraints_size();
5038 for (
int c = 0; c < num_constraints; ++c) {
5039 ConstraintProto*
ct = context_->
working_model->mutable_constraints(c);
5040 switch (
ct->constraint_case()) {
5041 case ConstraintProto::ConstraintCase::kNoOverlap:
5043 if (PresolveNoOverlap(
ct)) {
5047 case ConstraintProto::ConstraintCase::kNoOverlap2D:
5051 case ConstraintProto::ConstraintCase::kCumulative:
5053 if (PresolveCumulative(
ct)) {
5057 case ConstraintProto::ConstraintCase::kBoolOr: {
5060 for (
const auto& pair :
5062 bool modified =
false;
5083 <<
" affine relations were detected.";
5085 <<
" variable equivalence relations were detected.";
5086 std::map<std::string, int> sorted_rules(
context->stats_by_rule_name.begin(),
5087 context->stats_by_rule_name.end());
5088 for (
const auto& entry : sorted_rules) {
5089 if (entry.second == 1) {
5090 LOG(
INFO) <<
"- rule '" << entry.first <<
"' was applied 1 time.";
5092 LOG(
INFO) <<
"- rule '" << entry.first <<
"' was applied " << entry.second
5103 std::vector<int>* postsolve_mapping) {
5109 std::vector<int>* postsolve_mapping)
5110 : postsolve_mapping_(postsolve_mapping), context_(
context) {
5113 context_->
params().keep_all_feasible_solutions_in_presolve() ||
5114 context_->
params().enumerate_all_solutions() ||
5115 context_->
params().fill_tightened_domains_in_response() ||
5116 !context_->
params().cp_model_presolve();
5119 for (
const auto& decision_strategy :
5121 *(context_->
mapping_model->add_search_strategy()) = decision_strategy;
5156 if (!context_->
params().cp_model_presolve()) {
5168 for (
int c = 0; c < context_->
working_model->constraints_size(); ++c) {
5169 ConstraintProto*
ct = context_->
working_model->mutable_constraints(c);
5170 PresolveEnforcementLiteral(
ct);
5171 switch (
ct->constraint_case()) {
5172 case ConstraintProto::ConstraintCase::kBoolOr:
5175 case ConstraintProto::ConstraintCase::kBoolAnd:
5176 PresolveBoolAnd(
ct);
5178 case ConstraintProto::ConstraintCase::kAtMostOne:
5179 PresolveAtMostOne(
ct);
5181 case ConstraintProto::ConstraintCase::kExactlyOne:
5182 PresolveExactlyOne(
ct);
5184 case ConstraintProto::ConstraintCase::kLinear:
5185 CanonicalizeLinear(
ct);
5197 for (
int iter = 0; iter < context_->
params().max_presolve_iterations();
5202 int old_num_non_empty_constraints = 0;
5203 for (
int c = 0; c < context_->
working_model->constraints_size(); ++c) {
5206 if (type == ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET)
continue;
5207 old_num_non_empty_constraints++;
5214 PresolveToFixPoint();
5221 if (context_->
params().cp_model_probing_level() > 0) {
5225 PresolveToFixPoint();
5232 if (context_->
params().cp_model_use_sat_presolve()) {
5235 PresolvePureSatPart();
5248 const int old_size = context_->
working_model->constraints_size();
5249 for (
int c = 0; c < old_size; ++c) {
5250 ConstraintProto*
ct = context_->
working_model->mutable_constraints(c);
5251 if (
ct->constraint_case() != ConstraintProto::ConstraintCase::kLinear) {
5254 ExtractAtMostOneFromLinear(
ct);
5259 if (iter == 0) TransformIntoMaxCliques();
5269 PresolveToFixPoint();
5275 old_num_non_empty_constraints)) {
5282 MergeNoOverlapConstraints();
5292 EncodeAllAffineRelations();
5299 const std::vector<int> duplicates =
5301 if (!duplicates.empty()) {
5302 for (
const int c : duplicates) {
5305 if (type == ConstraintProto::ConstraintCase::kInterval) {
5322 context_->
working_model->add_constraints()->mutable_bool_or();
5334 absl::flat_hash_set<int> used_variables;
5335 for (DecisionStrategyProto& strategy :
5337 DecisionStrategyProto copy = strategy;
5338 strategy.clear_variables();
5339 for (
const int ref : copy.variables()) {
5348 used_variables.insert(
var);
5356 strategy.add_variables(rep);
5357 if (strategy.variable_selection_strategy() !=
5358 DecisionStrategyProto::CHOOSE_FIRST) {
5359 DecisionStrategyProto::AffineTransformation* t =
5360 strategy.add_transformations();
5363 t->set_positive_coeff(std::abs(r.
coeff));
5371 strategy.add_variables(ref);
5377 for (
int i = 0; i < context_->
working_model->variables_size(); ++i) {
5388 postsolve_mapping_->clear();
5389 std::vector<int> mapping(context_->
working_model->variables_size(), -1);
5390 for (
int i = 0; i < context_->
working_model->variables_size(); ++i) {
5395 mapping[i] = postsolve_mapping_->size();
5396 postsolve_mapping_->push_back(i);
5399 if (context_->
params().permute_variable_randomly()) {
5400 std::shuffle(postsolve_mapping_->begin(), postsolve_mapping_->end(),
5402 for (
int i = 0; i < postsolve_mapping_->size(); ++i) {
5403 mapping[(*postsolve_mapping_)[i]] = i;
5428 if (!error.empty()) {
5430 LOG(
INFO) <<
"Error while validating postsolved model: " << error;
5437 if (!error.empty()) {
5439 LOG(
INFO) <<
"Error while validating mapping_model model: " << error;
5453 auto mapping_function = [&mapping](
int* ref) {
5458 for (ConstraintProto& ct_ref : *
proto->mutable_constraints()) {
5464 if (
proto->has_objective()) {
5465 for (
int& mutable_ref : *
proto->mutable_objective()->mutable_vars()) {
5466 mapping_function(&mutable_ref);
5471 for (
int& mutable_ref : *
proto->mutable_assumptions()) {
5472 mapping_function(&mutable_ref);
5477 for (DecisionStrategyProto& strategy : *
proto->mutable_search_strategy()) {
5478 DecisionStrategyProto copy = strategy;
5479 strategy.clear_variables();
5480 for (
const int ref : copy.variables()) {
5486 strategy.clear_transformations();
5487 for (
const auto& transform : copy.transformations()) {
5488 const int ref = transform.var();
5491 auto* new_transform = strategy.add_transformations();
5492 *new_transform = transform;
5499 if (
proto->has_solution_hint()) {
5500 auto* mutable_hint =
proto->mutable_solution_hint();
5502 for (
int i = 0; i < mutable_hint->vars_size(); ++i) {
5503 const int old_ref = mutable_hint->vars(i);
5504 const int64 old_value = mutable_hint->values(i);
5512 const int image = mapping[
var];
5514 mutable_hint->set_vars(new_size, image);
5515 mutable_hint->set_values(new_size,
value);
5520 mutable_hint->mutable_vars()->Truncate(new_size);
5521 mutable_hint->mutable_values()->Truncate(new_size);
5523 proto->clear_solution_hint();
5528 std::vector<IntegerVariableProto> new_variables;
5529 for (
int i = 0; i < mapping.size(); ++i) {
5530 const int image = mapping[i];
5531 if (image < 0)
continue;
5532 if (image >= new_variables.size()) {
5533 new_variables.resize(image + 1, IntegerVariableProto());
5535 new_variables[image].Swap(
proto->mutable_variables(i));
5537 proto->clear_variables();
5538 for (IntegerVariableProto& proto_ref : new_variables) {
5539 proto->add_variables()->Swap(&proto_ref);
5543 for (
const IntegerVariableProto& v :
proto->variables()) {
5549 std::vector<int> result;
5552 ConstraintProto copy;
5553 absl::flat_hash_map<int64, int> equiv_constraints;
5556 const int num_constraints =
model_proto.constraints().size();
5557 for (
int c = 0; c < num_constraints; ++c) {
5558 if (
model_proto.constraints(c).constraint_case() ==
5559 ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET) {
5568 s = copy.SerializeAsString();
5570 const int64 hash = std::hash<std::string>()(s);
5571 const auto insert = equiv_constraints.insert({
hash, c});
5572 if (!insert.second) {
5574 const int other_c_with_same_hash = insert.first->second;
5575 copy =
model_proto.constraints(other_c_with_same_hash);
5577 if (s == copy.SerializeAsString()) {
5578 result.push_back(c);
#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)
bool IsIncludedIn(const Domain &domain) const
Returns true iff D is included in the given domain.
Domain MultiplicationBy(int64 coeff, bool *exact=nullptr) const
Returns {x ∈ Int64, ∃ e ∈ D, x = e * coeff}.
static Domain FromValues(std::vector< int64 > values)
Creates a domain from the union of an unsorted list of integer values.
int64 Size() const
Returns the number of elements in the domain.
Domain InverseMultiplicationBy(const int64 coeff) const
Returns {x ∈ Int64, ∃ e ∈ D, x * coeff = e}.
Domain AdditionWith(const Domain &domain) const
Returns {x ∈ Int64, ∃ a ∈ D, ∃ b ∈ domain, x = a + b}.
ClosedInterval front() const
Domain UnionWith(const Domain &domain) const
Returns the union of D and domain.
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
Domain RelaxIfTooComplex() const
If NumIntervals() is too large, this return a superset of the domain.
Domain DivisionBy(int64 coeff) const
Returns {x ∈ Int64, ∃ e ∈ D, x = e / coeff}.
static int64 GCD64(int64 x, int64 y)
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
void Set(IntegerType index)
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 RemoveEmptyConstraints()
CpModelPresolver(PresolveContext *context, std::vector< int > *postsolve_mapping)
bool PresolveOneConstraint(int c)
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)
bool StoreAbsRelation(int target_ref, int ref)
SparseBitset< int64 > modified_domains
bool ConstraintVariableUsageIsConsistent()
int64 num_presolve_operations
bool ModelIsUnsat() const
bool VariableIsOnlyUsedInEncoding(int ref) 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
int GetOrCreateVarValueEncoding(int ref, int64 value)
bool DomainContains(int ref, int64 value) const
bool StoreLiteralImpliesVarNEqValue(int literal, int var, int64 value)
const Domain & ObjectiveDomain() 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 StoreLiteralImpliesVarEqValue(int literal, int var, int64 value)
std::vector< int > tmp_literals
int64 MaxOf(int ref) const
std::vector< absl::flat_hash_set< int > > var_to_ub_only_constraints
bool ObjectiveDomainIsConstraining() const
CpModelProto * mapping_model
const std::vector< int > & ConstraintToVars(int c) const
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
void InitializeNewDomains()
int GetVariableRepresentative(int ref) const
int NewIntVar(const Domain &domain)
void MarkVariableAsRemoved(int ref)
DomainDeductions deductions
std::vector< Domain > tmp_left_domains
bool DomainIsEmpty(int ref) const
void CanonicalizeDomainOfSizeTwo(int var)
bool LiteralIsTrue(int lit) const
void StoreBooleanEqualityRelation(int ref_a, int ref_b)
int IntervalUsage(int c) const
CpModelProto * working_model
bool SubstituteVariableInObjective(int var_in_equality, int64 coeff_in_equality, const ConstraintProto &equality, std::vector< int > *new_vars_in_objective=nullptr)
int GetOrCreateConstantVar(int64 cst)
bool LiteralIsFalse(int lit) const
const absl::flat_hash_map< int, int64 > & ObjectiveMap() const
void UpdateRuleStats(const std::string &name, int num_times=1)
ABSL_MUST_USE_RESULT bool CanonicalizeObjective()
const SatParameters & params() const
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
const absl::flat_hash_set< int > & VarToConstraints(int var) const
ModelRandomGenerator * random()
ABSL_MUST_USE_RESULT bool SetLiteralToFalse(int lit)
int64 MinOf(int ref) const
absl::flat_hash_set< int > tmp_literal_set
void ReadObjectiveFromProto()
bool CanBeUsedAsLiteral(int ref) const
void RegisterVariablesUsedInAssumptions()
void ExploitFixedDomain(int var)
bool GetAbsRelation(int target_ref, int *ref)
bool StoreAffineRelation(int ref_x, int ref_y, int64 coeff, int64 offset)
CpModelProto const * model_proto
SharedTimeLimit * time_limit
GurobiMPCallbackContext * context
static const int64 kint64max
static const int32 kint32max
static const int32 kint32min
static const int64 kint64min
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void STLSetDifference(const In1 &a, const In2 &b, Out *out, Compare compare)
bool ContainsKey(const Collection &collection, const Key &key)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
bool LoadConstraint(const ConstraintProto &ct, Model *m)
bool RefIsPositive(int ref)
void SetToNegatedLinearExpression(const LinearExpressionProto &input_expr, LinearExpressionProto *output_negated_expr)
const LiteralIndex kNoLiteralIndex(-1)
bool HasEnforcementLiteral(const ConstraintProto &ct)
void ExpandCpModel(PresolveContext *context)
constexpr int kAffineRelationConstraint
bool PresolveCpModel(PresolveContext *context, std::vector< int > *postsolve_mapping)
void ApplyToAllLiteralIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
void SubstituteVariable(int var, int64 var_coeff_in_definition, const ConstraintProto &definition, ConstraintProto *ct)
void DetectDominanceRelations(const PresolveContext &context, VarDomination *var_domination, DualBoundStrengthening *dual_bound_strengthening)
void ApplyToAllIntervalIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
int ReindexArcs(IntContainer *tails, IntContainer *heads)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
std::vector< int > FindDuplicateConstraints(const CpModelProto &model_proto)
void LogInfoFromContext(const PresolveContext *context)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
void ApplyToAllVariableIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
constexpr int kObjectiveConstraint
bool ExploitDominanceRelations(const VarDomination &var_domination, PresolveContext *context)
void ApplyVariableMapping(const std::vector< int > &mapping, const PresolveContext &context)
std::string ValidateCpModel(const CpModelProto &model)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...