27#include "absl/container/flat_hash_set.h"
28#include "absl/log/check.h"
29#include "absl/log/log.h"
30#include "absl/strings/str_cat.h"
31#include "absl/types/span.h"
32#include "google/protobuf/arena.h"
33#include "google/protobuf/repeated_field.h"
34#include "google/protobuf/repeated_ptr_field.h"
35#include "google/protobuf/text_format.h"
57 const int var = lit.Variable().value();
58 return lit.IsPositive() ? var :
NegatedRef(var);
64 : context_(context), lrat_proof_handler_(lrat_proof_handler) {}
66ClauseId ModelCopy::NextInferredClauseId() {
67 return next_inferred_clause_id_++;
72 if (context_->params().ignore_names()) {
73 context_->working_model->clear_variables();
74 context_->working_model->mutable_variables()->Reserve(
77 *context_->working_model->add_variables()->mutable_domain() =
81 *context_->working_model->mutable_variables() = in_model.
variables();
86 for (
const Domain& domain : domains) {
98 std::function<
bool(
int)> active_constraints) {
99 context_->InitializeNewDomains();
100 if (context_->ModelIsUnsat())
return false;
101 const bool ignore_names = context_->params().ignore_names();
105 std::vector<int> constraints_using_intervals;
107 interval_mapping_.assign(in_model.
constraints().size(), -1);
108 boolean_product_encoding_.clear();
115 unit_clause_ids_.clear();
117 starting_constraint_index_ = context_->working_model->constraints_size();
119 if (active_constraints !=
nullptr && !active_constraints(c)) {
124 if (!PrepareEnforcementCopyWithDup(ct))
continue;
126 if (!PrepareEnforcementCopy(ct))
continue;
137 if (!CopyBoolOrWithDupSupport(ct, ClauseId(c + 1))) {
138 return CreateUnsatModel(c, ct);
141 if (!CopyBoolOr(ct))
return CreateUnsatModel(c, ct);
145 if (temp_enforcement_literals_.empty()) {
147 context_->UpdateRuleStats(
"bool_and: non-reified");
148 if (!context_->SetLiteralToTrue(lit)) {
149 return CreateUnsatModel(c, ct);
152 }
else if (first_copy) {
153 if (!CopyBoolAndWithDupSupport(ct))
return CreateUnsatModel(c, ct);
155 if (!CopyBoolAnd(ct))
return CreateUnsatModel(c, ct);
159 if (!CopyLinear(ct, first_copy)) {
160 return CreateUnsatModel(c, ct);
164 if (!CopyIntProd(ct, ignore_names))
return CreateUnsatModel(c, ct);
167 if (!CopyIntDiv(ct, ignore_names))
return CreateUnsatModel(c, ct);
170 if (!CopyIntMod(ct, ignore_names))
return CreateUnsatModel(c, ct);
173 if (!CopyElement(ct))
return CreateUnsatModel(c, ct);
176 if (!CopyTable(ct))
return CreateUnsatModel(c, ct);
179 if (!CopyAutomaton(ct))
return CreateUnsatModel(c, ct);
182 if (!CopyAllDiff(ct))
return CreateUnsatModel(c, ct);
185 if (!CopyLinMax(ct))
return CreateUnsatModel(c, ct);
188 if (!CopyAtMostOne(ct))
return CreateUnsatModel(c, ct);
191 if (!CopyExactlyOne(ct))
return CreateUnsatModel(c, ct);
194 if (!CopyInterval(ct, c, ignore_names))
return CreateUnsatModel(c, ct);
196 if (!AddLinearConstraintForInterval(ct))
197 return CreateUnsatModel(c, ct);
202 constraints_using_intervals.push_back(c);
204 CopyAndMapNoOverlap(ct);
209 constraints_using_intervals.push_back(c);
211 CopyAndMapNoOverlap2D(ct);
216 constraints_using_intervals.push_back(c);
218 if (!CopyAndMapCumulative(ct))
return CreateUnsatModel(c, ct);
225 FinishEnforcementCopy(new_ct);
235 DCHECK(first_copy || constraints_using_intervals.empty());
236 for (
const int c : constraints_using_intervals) {
238 if (!PrepareEnforcementCopyWithDup(ct))
continue;
241 CopyAndMapNoOverlap(ct);
244 CopyAndMapNoOverlap2D(ct);
247 if (!CopyAndMapCumulative(ct))
return CreateUnsatModel(c, ct);
250 LOG(DFATAL) <<
"Shouldn't be here.";
255 ExpandNonAffineExpressions();
261 temp_enforcement_literals_.clear();
268 temp_enforcement_literals_.push_back(lit);
273bool ModelCopy::PrepareEnforcementCopyWithDup(
const ConstraintProto& ct) {
274 temp_enforcement_literals_.clear();
275 temp_enforcement_literals_set_.clear();
276 for (
const int lit : ct.enforcement_literal()) {
277 if (context_->LiteralIsTrue(lit))
continue;
278 if (temp_enforcement_literals_set_.contains(lit)) {
279 context_->UpdateRuleStats(
"enforcement: removed duplicate literal");
284 if (context_->LiteralIsFalse(lit)) {
285 context_->UpdateRuleStats(
"enforcement: always false");
288 if (temp_enforcement_literals_set_.contains(
NegatedRef(lit))) {
289 context_->UpdateRuleStats(
"enforcement: contains x and not(x)");
293 temp_enforcement_literals_.push_back(lit);
294 temp_enforcement_literals_set_.insert(lit);
300 ct->mutable_enforcement_literal()->Add(temp_enforcement_literals_.begin(),
301 temp_enforcement_literals_.end());
304bool ModelCopy::FinishBoolOrCopy(ClauseId clause_id) {
305 if (temp_literals_.empty())
return false;
307 if (temp_literals_.size() == 1) {
308 context_->UpdateRuleStats(
"bool_or: only one literal");
309 if (lrat_proof_handler_ !=
nullptr) {
310 unit_clause_ids_[RefToLiteral(temp_literals_[0])] = clause_id;
312 return context_->SetLiteralToTrue(temp_literals_[0]);
315 context_->working_model->add_constraints()
318 ->Add(temp_literals_.begin(), temp_literals_.end());
323 temp_literals_.clear();
324 for (
const int lit : temp_enforcement_literals_) {
327 for (
const int lit : ct.bool_or().literals()) {
328 if (context_->LiteralIsTrue(lit)) {
331 if (!context_->LiteralIsFalse(lit)) {
332 temp_literals_.push_back(lit);
335 return FinishBoolOrCopy();
339 ClauseId clause_id) {
340 temp_literals_.clear();
341 temp_literals_set_.clear();
342 for (
const int enforcement_lit : temp_enforcement_literals_) {
349 temp_literals_set_.insert(lit);
350 temp_literals_.push_back(lit);
352 for (
const int lit : ct.bool_or().literals()) {
353 if (context_->LiteralIsTrue(lit)) {
354 context_->UpdateRuleStats(
"bool_or: always true");
357 if (context_->LiteralIsFalse(lit))
continue;
358 if (temp_literals_set_.contains(
NegatedRef(lit))) {
359 context_->UpdateRuleStats(
"bool_or: always true");
362 const auto [it, inserted] = temp_literals_set_.insert(lit);
363 if (inserted) temp_literals_.push_back(lit);
365 if (lrat_proof_handler_ !=
nullptr) {
368 temp_clause_.clear();
369 for (
const int lit : ct.enforcement_literal()) {
370 temp_clause_.push_back(RefToLiteral(lit).Negated());
372 for (
const int lit : ct.bool_or().literals()) {
373 temp_clause_.push_back(RefToLiteral(lit));
375 lrat_proof_handler_->AddProblemClause(clause_id, temp_clause_);
377 if (temp_literals_set_.size() != temp_clause_.size()) {
378 temp_clause_ids_.clear();
379 for (
const Literal lit : temp_clause_) {
380 if (!temp_literals_set_.contains(LiteralToRef(lit))) {
381 DCHECK(unit_clause_ids_.contains(lit.Negated())) << lit.Negated();
382 temp_clause_ids_.push_back(unit_clause_ids_[lit.Negated()]);
385 temp_clause_ids_.push_back(clause_id);
386 temp_simplified_clause_.clear();
387 for (
const int lit : temp_literals_set_) {
388 temp_simplified_clause_.push_back(RefToLiteral(lit));
390 ClauseId new_clause_id = NextInferredClauseId();
391 lrat_proof_handler_->AddInferredClause(
392 new_clause_id, temp_simplified_clause_, temp_clause_ids_);
393 lrat_proof_handler_->DeleteClause(clause_id, temp_clause_);
394 clause_id = new_clause_id;
397 return FinishBoolOrCopy(clause_id);
401 bool at_least_one_false =
false;
402 int num_non_fixed_literals = 0;
403 for (
const int lit : ct.bool_and().literals()) {
404 if (context_->LiteralIsFalse(lit)) {
405 at_least_one_false =
true;
408 if (!context_->LiteralIsTrue(lit)) {
409 num_non_fixed_literals++;
413 if (at_least_one_false) {
415 BoolArgumentProto* bool_or =
416 context_->working_model->add_constraints()->mutable_bool_or();
417 for (
const int lit : temp_enforcement_literals_) {
420 return !bool_or->literals().empty();
421 }
else if (num_non_fixed_literals > 0) {
422 ConstraintProto* new_ct = context_->working_model->add_constraints();
423 FinishEnforcementCopy(new_ct);
424 BoolArgumentProto* bool_and = new_ct->mutable_bool_and();
425 bool_and->mutable_literals()->Reserve(num_non_fixed_literals);
426 for (
const int lit : ct.bool_and().literals()) {
427 if (context_->LiteralIsTrue(lit))
continue;
428 bool_and->add_literals(lit);
435 DCHECK(!temp_enforcement_literals_.empty());
437 bool at_least_one_false =
false;
438 temp_literals_.clear();
439 temp_literals_set_.clear();
440 for (
const int lit : ct.bool_and().literals()) {
441 if (context_->LiteralIsFalse(lit)) {
442 context_->UpdateRuleStats(
"bool and: always false");
443 at_least_one_false =
true;
446 if (temp_literals_set_.contains(
NegatedRef(lit))) {
447 context_->UpdateRuleStats(
"bool and: => x and not(x) ");
448 at_least_one_false =
true;
451 if (temp_enforcement_literals_set_.contains(
NegatedRef(lit))) {
452 context_->UpdateRuleStats(
"bool and: not(x) => x");
453 at_least_one_false =
true;
457 if (context_->LiteralIsTrue(lit))
continue;
458 if (temp_enforcement_literals_set_.contains(lit)) {
459 context_->UpdateRuleStats(
"bool and: x => x");
462 const auto [it, inserted] = temp_literals_set_.insert(lit);
463 if (inserted) temp_literals_.push_back(lit);
466 if (at_least_one_false) {
468 BoolArgumentProto* bool_or =
469 context_->working_model->add_constraints()->mutable_bool_or();
470 for (
const int lit : temp_enforcement_literals_) {
473 return !bool_or->literals().empty();
476 if (temp_literals_.empty()) {
477 context_->UpdateRuleStats(
"bool and: empty");
482 ConstraintProto* new_ct = context_->working_model->add_constraints();
483 FinishEnforcementCopy(new_ct);
484 new_ct->mutable_bool_and()->mutable_literals()->Add(temp_literals_.begin(),
485 temp_literals_.end());
489bool ModelCopy::CopyLinearExpression(
491 absl::Span<const int> enforcement_literals) {
492 non_fixed_variables_.clear();
493 non_fixed_coefficients_.clear();
494 int64_t offset = expr.offset();
495 for (
int i = 0;
i < expr.vars_size(); ++
i) {
496 const int ref = expr.vars(
i);
497 const int64_t coeff = expr.coeffs(
i);
498 if (coeff == 0)
continue;
499 if (context_->IsFixed(ref)) {
500 offset += coeff * context_->MinOf(ref);
506 non_fixed_variables_.push_back(ref);
507 non_fixed_coefficients_.push_back(coeff);
509 non_fixed_variables_.push_back(
NegatedRef(ref));
510 non_fixed_coefficients_.push_back(-coeff);
514 dst->set_offset(offset);
515 dst->mutable_vars()->Add(non_fixed_variables_.begin(),
516 non_fixed_variables_.end());
517 dst->mutable_coeffs()->Add(non_fixed_coefficients_.begin(),
518 non_fixed_coefficients_.end());
521 context_->CanonicalizeLinearExpression(enforcement_literals, dst);
525bool ModelCopy::CopyLinear(
const ConstraintProto& ct,
bool canonicalize) {
526 non_fixed_variables_.clear();
527 non_fixed_coefficients_.clear();
529 int64_t min_activity = 0;
530 int64_t max_activity = 0;
531 for (
int i = 0;
i < ct.linear().vars_size(); ++
i) {
532 const int ref = ct.linear().vars(
i);
533 const int64_t coeff = ct.linear().coeffs(
i);
534 if (coeff == 0)
continue;
535 if (context_->IsFixed(ref)) {
536 offset += coeff * context_->MinOf(ref);
541 min_activity += coeff * context_->MinOf(ref);
542 max_activity += coeff * context_->MaxOf(ref);
544 min_activity += coeff * context_->MaxOf(ref);
545 max_activity += coeff * context_->MinOf(ref);
550 non_fixed_variables_.push_back(ref);
551 non_fixed_coefficients_.push_back(coeff);
553 non_fixed_variables_.push_back(
NegatedRef(ref));
554 non_fixed_coefficients_.push_back(-coeff);
558 const Domain implied(min_activity, max_activity);
559 const Domain new_rhs =
563 if (implied.IsIncludedIn(new_rhs)) {
564 context_->UpdateRuleStats(
"linear: always true");
570 if (tight_domain.IsEmpty()) {
571 if (ct.enforcement_literal().empty())
return false;
572 temp_literals_.clear();
573 for (
const int literal : ct.enforcement_literal()) {
574 if (!context_->LiteralIsTrue(literal)) {
575 temp_literals_.push_back(
NegatedRef(literal));
578 context_->working_model->add_constraints()
581 ->Add(temp_literals_.begin(), temp_literals_.end());
582 return !temp_literals_.empty();
585 DCHECK(!non_fixed_variables_.empty());
587 if (non_fixed_variables_.size() == 1 && ct.enforcement_literal().empty()) {
588 context_->UpdateRuleStats(
"linear1: x in domain");
589 return context_->IntersectDomainWith(
590 non_fixed_variables_[0],
591 new_rhs.InverseMultiplicationBy(non_fixed_coefficients_[0]));
594 ConstraintProto* new_ct = context_->working_model->add_constraints();
595 FinishEnforcementCopy(new_ct);
596 LinearConstraintProto* linear = new_ct->mutable_linear();
597 linear->mutable_vars()->Add(non_fixed_variables_.begin(),
598 non_fixed_variables_.end());
599 linear->mutable_coeffs()->Add(non_fixed_coefficients_.begin(),
600 non_fixed_coefficients_.end());
603 context_->CanonicalizeLinearConstraint(new_ct);
606 if (new_ct->linear().vars().empty() &&
608 context_->UpdateRuleStats(
"linear: trivial 0=0");
609 context_->working_model->mutable_constraints()->RemoveLast();
617 ConstraintProto* new_ct = context_->working_model->add_constraints();
618 if (ct.element().vars().empty() && !ct.element().exprs().empty()) {
621 new_ct->mutable_enforcement_literal()->Clear();
622 FinishEnforcementCopy(new_ct);
626 auto fill_expr = [
this](
int var, LinearExpressionProto* expr)
mutable {
627 if (context_->IsFixed(var)) {
628 expr->set_offset(context_->FixedValue(var));
631 expr->mutable_vars()->Reserve(1);
632 expr->mutable_coeffs()->Reserve(1);
638 FinishEnforcementCopy(new_ct);
639 fill_expr(ct.element().index(),
640 new_ct->mutable_element()->mutable_linear_index());
641 fill_expr(ct.element().target(),
642 new_ct->mutable_element()->mutable_linear_target());
643 for (
const int var : ct.element().vars()) {
644 fill_expr(var, new_ct->mutable_element()->add_exprs());
650 ConstraintProto* new_ct = context_->working_model->add_constraints();
651 new_ct->mutable_automaton()->set_starting_state(
652 ct.automaton().starting_state());
653 *new_ct->mutable_automaton()->mutable_final_states() =
654 ct.automaton().final_states();
655 *new_ct->mutable_automaton()->mutable_transition_tail() =
656 ct.automaton().transition_tail();
657 *new_ct->mutable_automaton()->mutable_transition_head() =
658 ct.automaton().transition_head();
659 *new_ct->mutable_automaton()->mutable_transition_label() =
660 ct.automaton().transition_label();
661 for (
const LinearExpressionProto& expr : ct.automaton().exprs()) {
662 CopyLinearExpression(expr, new_ct->mutable_automaton()->add_exprs());
664 FinishEnforcementCopy(new_ct);
666 auto fill_expr = [
this](
int var, LinearExpressionProto* expr)
mutable {
667 if (context_->IsFixed(var)) {
668 expr->set_offset(context_->FixedValue(var));
671 expr->mutable_vars()->Reserve(1);
672 expr->mutable_coeffs()->Reserve(1);
678 for (
const int var : ct.automaton().vars()) {
679 fill_expr(var, new_ct->mutable_automaton()->add_exprs());
686 ConstraintProto* new_ct = context_->working_model->add_constraints();
687 if (ct.table().vars().empty() && !ct.table().exprs().empty()) {
690 new_ct->mutable_enforcement_literal()->Clear();
691 FinishEnforcementCopy(new_ct);
695 auto fill_expr = [
this](
int var, LinearExpressionProto* expr)
mutable {
696 if (context_->IsFixed(var)) {
697 expr->set_offset(context_->FixedValue(var));
700 expr->mutable_vars()->Reserve(1);
701 expr->mutable_coeffs()->Reserve(1);
707 FinishEnforcementCopy(new_ct);
708 for (
const int var : ct.table().vars()) {
709 fill_expr(var, new_ct->mutable_table()->add_exprs());
711 *new_ct->mutable_table()->mutable_values() = ct.table().values();
712 new_ct->mutable_table()->set_negated(ct.table().negated());
713 *new_ct->mutable_enforcement_literal() = ct.enforcement_literal();
719 if (ct.all_diff().exprs().size() <= 1)
return true;
720 ConstraintProto* new_ct = context_->working_model->add_constraints();
721 for (
const LinearExpressionProto& expr : ct.all_diff().exprs()) {
722 CopyLinearExpression(expr, new_ct->mutable_all_diff()->add_exprs());
724 FinishEnforcementCopy(new_ct);
730 ConstraintProto* new_ct =
nullptr;
733 int64_t max_of_fixed_terms = std::numeric_limits<int64_t>::min();
734 for (
const auto& expr : ct.lin_max().exprs()) {
735 const std::optional<int64_t> fixed = context_->FixedValueOrNullopt(expr);
736 if (fixed != std::nullopt) {
737 max_of_fixed_terms = std::max(max_of_fixed_terms, fixed.value());
740 if (new_ct ==
nullptr) {
741 new_ct = context_->working_model->add_constraints();
743 CopyLinearExpression(expr, new_ct->mutable_lin_max()->add_exprs());
749 if (new_ct ==
nullptr && ct.enforcement_literal().empty() &&
750 ct.lin_max().target().vars().size() <= 1) {
751 context_->UpdateRuleStats(
"lin_max: all exprs fixed during copy");
752 return context_->IntersectDomainWith(ct.lin_max().target(),
753 Domain(max_of_fixed_terms));
757 if (max_of_fixed_terms > std::numeric_limits<int64_t>::min()) {
758 if (new_ct ==
nullptr) {
759 new_ct = context_->working_model->add_constraints();
761 new_ct->mutable_lin_max()->add_exprs()->set_offset(max_of_fixed_terms);
765 if (new_ct ==
nullptr)
return false;
766 CopyLinearExpression(ct.lin_max().target(),
767 new_ct->mutable_lin_max()->mutable_target());
768 FinishEnforcementCopy(new_ct);
773 if (!ct.enforcement_literal().empty()) {
774 ConstraintProto new_ct;
775 FinishEnforcementCopy(&new_ct);
777 new_ct.mutable_linear());
778 return CopyLinear(new_ct,
true);
781 temp_literals_.clear();
782 for (
const int lit : ct.at_most_one().literals()) {
783 if (context_->LiteralIsFalse(lit))
continue;
784 temp_literals_.push_back(lit);
785 if (context_->LiteralIsTrue(lit)) num_true++;
788 if (temp_literals_.size() <= 1)
return true;
789 if (num_true > 1)
return false;
792 ConstraintProto* new_ct = context_->working_model->add_constraints();
793 new_ct->mutable_at_most_one()->mutable_literals()->Add(temp_literals_.begin(),
794 temp_literals_.end());
799 if (!ct.enforcement_literal().empty()) {
800 ConstraintProto new_ct;
801 FinishEnforcementCopy(&new_ct);
803 new_ct.mutable_linear());
804 return CopyLinear(new_ct,
true);
807 temp_literals_.clear();
808 for (
const int lit : ct.exactly_one().literals()) {
809 if (context_->LiteralIsFalse(lit))
continue;
810 temp_literals_.push_back(lit);
811 if (context_->LiteralIsTrue(lit)) num_true++;
814 if (temp_literals_.empty() || num_true > 1)
return false;
815 if (temp_literals_.size() == 1 && num_true == 1)
return true;
818 ConstraintProto* new_ct = context_->working_model->add_constraints();
819 new_ct->mutable_exactly_one()->mutable_literals()->Add(temp_literals_.begin(),
820 temp_literals_.end());
826 CHECK_EQ(starting_constraint_index_, 0)
827 <<
"Adding new interval constraints to partially filled model is not "
829 interval_mapping_[
c] = context_->working_model->constraints_size();
830 ConstraintProto* new_ct = context_->working_model->add_constraints();
832 new_ct->set_name(ct.name());
834 if (temp_enforcement_literals_.size() > 1) {
835 temp_enforcement_literals_ = {
836 GetOrCreateVariableForConjunction(&temp_enforcement_literals_)};
838 FinishEnforcementCopy(new_ct);
839 CopyLinearExpression(ct.interval().start(),
840 new_ct->mutable_interval()->mutable_start(),
841 ct.enforcement_literal());
842 CopyLinearExpression(ct.interval().size(),
843 new_ct->mutable_interval()->mutable_size(),
844 ct.enforcement_literal());
845 CopyLinearExpression(ct.interval().end(),
846 new_ct->mutable_interval()->mutable_end(),
847 ct.enforcement_literal());
851bool ModelCopy::CopyIntProd(
const ConstraintProto& ct,
bool ignore_names) {
852 ConstraintProto* new_ct = context_->working_model->add_constraints();
854 new_ct->set_name(ct.name());
856 FinishEnforcementCopy(new_ct);
857 for (
const LinearExpressionProto& expr : ct.int_prod().exprs()) {
858 CopyLinearExpression(expr, new_ct->mutable_int_prod()->add_exprs());
860 CopyLinearExpression(ct.int_prod().target(),
861 new_ct->mutable_int_prod()->mutable_target());
865bool ModelCopy::CopyIntDiv(
const ConstraintProto& ct,
bool ignore_names) {
866 ConstraintProto* new_ct = context_->working_model->add_constraints();
868 new_ct->set_name(ct.name());
870 FinishEnforcementCopy(new_ct);
871 for (
const LinearExpressionProto& expr : ct.int_div().exprs()) {
872 CopyLinearExpression(expr, new_ct->mutable_int_div()->add_exprs());
874 CopyLinearExpression(ct.int_div().target(),
875 new_ct->mutable_int_div()->mutable_target());
879bool ModelCopy::CopyIntMod(
const ConstraintProto& ct,
bool ignore_names) {
880 ConstraintProto* new_ct = context_->working_model->add_constraints();
882 new_ct->set_name(ct.name());
884 FinishEnforcementCopy(new_ct);
885 for (
const LinearExpressionProto& expr : ct.int_mod().exprs()) {
886 CopyLinearExpression(expr, new_ct->mutable_int_mod()->add_exprs());
888 CopyLinearExpression(ct.int_mod().target(),
889 new_ct->mutable_int_mod()->mutable_target());
893bool ModelCopy::AddLinearConstraintForInterval(
const ConstraintProto& ct) {
898 const IntervalConstraintProto& itv = ct.interval();
899 if (itv.size().vars().empty() &&
900 itv.start().offset() + itv.size().offset() == itv.end().offset() &&
901 absl::Span<const int>(itv.start().vars()) ==
902 absl::Span<const int>(itv.end().vars()) &&
903 absl::Span<const int64_t>(itv.start().coeffs()) ==
904 absl::Span<const int64_t>(itv.end().coeffs())) {
907 tmp_constraint_.Clear();
908 *tmp_constraint_.mutable_enforcement_literal() = ct.enforcement_literal();
909 LinearConstraintProto* mutable_linear = tmp_constraint_.mutable_linear();
911 mutable_linear->add_domain(0);
912 mutable_linear->add_domain(0);
916 if (!CopyLinear(tmp_constraint_,
true))
return false;
920 const LinearExpressionProto& size_expr = itv.size();
921 if (context_->MinOf(size_expr) < 0) {
922 tmp_constraint_.Clear();
923 *tmp_constraint_.mutable_enforcement_literal() = ct.enforcement_literal();
924 *tmp_constraint_.mutable_linear()->mutable_vars() = size_expr.vars();
925 *tmp_constraint_.mutable_linear()->mutable_coeffs() = size_expr.coeffs();
926 tmp_constraint_.mutable_linear()->add_domain(-size_expr.offset());
927 tmp_constraint_.mutable_linear()->add_domain(
928 std::numeric_limits<int64_t>::max());
929 if (!CopyLinear(tmp_constraint_,
true))
return false;
935int ModelCopy::GetOrCreateVariableForConjunction(std::vector<int>* literals) {
936 std::sort(literals->begin(), literals->end());
937 auto it = boolean_product_encoding_.find(*literals);
938 if (it != boolean_product_encoding_.end())
return it->second;
939 const int new_var = context_->NewBoolVarWithConjunction(*literals);
940 boolean_product_encoding_[*literals] = new_var;
942 auto* ct1 = context_->working_model->add_constraints();
943 ct1->mutable_bool_or()->mutable_literals()->Reserve(literals->size() + 1);
944 for (
const int literal : *literals) {
945 ct1->mutable_bool_or()->add_literals(
NegatedRef(literal));
947 ct1->mutable_bool_or()->add_literals(new_var);
949 auto* ct2 = context_->working_model->add_constraints();
950 ct2->add_enforcement_literal(new_var);
951 *ct2->mutable_bool_and()->mutable_literals() = {literals->begin(),
958 auto* new_ct = context_->working_model->add_constraints();
959 FinishEnforcementCopy(new_ct);
960 NoOverlapConstraintProto* no_overlap = new_ct->mutable_no_overlap();
961 no_overlap->mutable_intervals()->Reserve(ct.no_overlap().intervals().size());
962 for (
const int index : ct.no_overlap().intervals()) {
963 const int new_index = interval_mapping_[index];
964 if (new_index != -1) {
965 no_overlap->add_intervals(new_index);
972 auto* new_ct = context_->working_model->add_constraints();
973 FinishEnforcementCopy(new_ct);
974 NoOverlap2DConstraintProto* no_overlap_2d = new_ct->mutable_no_overlap_2d();
975 const int num_intervals = ct.no_overlap_2d().x_intervals().size();
976 no_overlap_2d->mutable_x_intervals()->Reserve(num_intervals);
977 no_overlap_2d->mutable_y_intervals()->Reserve(num_intervals);
978 for (
int i = 0;
i < num_intervals; ++
i) {
979 const int new_x = interval_mapping_[ct.no_overlap_2d().x_intervals(
i)];
980 if (new_x == -1)
continue;
981 const int new_y = interval_mapping_[ct.no_overlap_2d().y_intervals(
i)];
982 if (new_y == -1)
continue;
983 no_overlap_2d->add_x_intervals(new_x);
984 no_overlap_2d->add_y_intervals(new_y);
989 if (ct.cumulative().intervals().empty() &&
990 context_->IsFixed(ct.cumulative().capacity())) {
992 const int64_t capacity = context_->FixedValue(ct.cumulative().capacity());
993 if (temp_enforcement_literals_.empty()) {
994 return capacity >= 0;
998 auto* new_ct = context_->working_model->add_constraints();
999 for (
const int literal : temp_enforcement_literals_) {
1000 new_ct->mutable_bool_or()->add_literals(
NegatedRef(literal));
1006 auto* new_ct = context_->working_model->add_constraints();
1007 FinishEnforcementCopy(new_ct);
1008 CumulativeConstraintProto* cumulative = new_ct->mutable_cumulative();
1009 CopyLinearExpression(ct.cumulative().capacity(),
1010 cumulative->mutable_capacity());
1012 const int num_intervals = ct.cumulative().intervals().size();
1013 cumulative->mutable_intervals()->Reserve(num_intervals);
1014 cumulative->mutable_demands()->Reserve(num_intervals);
1015 for (
int i = 0;
i < num_intervals; ++
i) {
1016 const int new_index = interval_mapping_[ct.cumulative().intervals(
i)];
1017 if (new_index != -1) {
1018 cumulative->add_intervals(new_index);
1019 CopyLinearExpression(ct.cumulative().demands(
i),
1020 cumulative->add_demands());
1028 context_->working_model->mutable_constraints()->Clear();
1029 context_->working_model->add_constraints()->mutable_bool_or();
1033 if (context_->ModelIsUnsat())
return false;
1035 std::string proto_string;
1036#if !defined(__PORTABLE_PLATFORM__)
1037 google::protobuf::TextFormat::Printer printer;
1039 printer.PrintToString(ct, &proto_string);
1041 std::string message = absl::StrCat(
1042 "proven during initial copy of constraint #", c,
":\n", proto_string);
1044 if (vars.size() < 10) {
1045 absl::StrAppend(&message,
"With current variable domains:\n");
1046 for (
const int var : vars) {
1047 absl::StrAppend(&message,
"var:", var,
1048 " domain:", context_->DomainOf(var).ToString(),
"\n");
1051 return context_->NotifyThatModelIsUnsat(message);
1054void ModelCopy::ExpandNonAffineExpressions() {
1057 context_->InitializeNewDomains();
1059 non_affine_expression_to_new_var_.clear();
1060 for (
int c = 0;
c < context_->working_model->constraints_size(); ++
c) {
1061 ConstraintProto*
const ct = context_->working_model->mutable_constraints(c);
1062 switch (ct->constraint_case()) {
1064 MaybeExpandNonAffineExpressions(ct->mutable_int_div());
1067 MaybeExpandNonAffineExpressions(ct->mutable_int_mod());
1070 MaybeExpandNonAffineExpressions(ct->mutable_int_prod());
1073 for (LinearExpressionProto& expr :
1074 *ct->mutable_all_diff()->mutable_exprs()) {
1075 MaybeExpandNonAffineExpression(&expr);
1079 if (!ct->element().exprs().empty()) {
1080 MaybeExpandNonAffineExpression(
1081 ct->mutable_element()->mutable_linear_index());
1082 MaybeExpandNonAffineExpression(
1083 ct->mutable_element()->mutable_linear_target());
1084 for (LinearExpressionProto& expr :
1085 *ct->mutable_element()->mutable_exprs()) {
1086 MaybeExpandNonAffineExpression(&expr);
1091 MaybeExpandNonAffineExpression(ct->mutable_interval()->mutable_start());
1092 MaybeExpandNonAffineExpression(ct->mutable_interval()->mutable_end());
1093 MaybeExpandNonAffineExpression(ct->mutable_interval()->mutable_size());
1096 for (LinearExpressionProto& expr :
1097 *ct->mutable_reservoir()->mutable_time_exprs()) {
1098 MaybeExpandNonAffineExpression(&expr);
1103 *ct->mutable_routes()->mutable_dimensions()) {
1104 for (LinearExpressionProto& expr : *node_exprs.mutable_exprs()) {
1105 MaybeExpandNonAffineExpression(&expr);
1110 for (LinearExpressionProto& expr :
1111 *ct->mutable_table()->mutable_exprs()) {
1112 MaybeExpandNonAffineExpression(&expr);
1116 for (LinearExpressionProto& expr :
1117 *ct->mutable_automaton()->mutable_exprs()) {
1118 MaybeExpandNonAffineExpression(&expr);
1131 for (
int i = 0;
i < expr->vars().size(); ++
i) {
1132 if (expr->coeffs(
i) != 0) {
1133 expr->set_vars(new_size, expr->vars(
i));
1134 expr->set_coeffs(new_size, expr->coeffs(
i));
1138 expr->mutable_vars()->Truncate(new_size);
1139 expr->mutable_coeffs()->Truncate(new_size);
1140 if (expr->vars_size() < 2)
return;
1142 int64_t gcd = std::abs(expr->coeffs(0));
1143 for (
int i = 1;
i < expr->coeffs().size(); ++
i) {
1144 gcd = std::gcd(gcd, std::abs(expr->coeffs(
i)));
1147 std::vector<std::pair<int, int64_t>> definition;
1148 for (
int i = 0;
i < expr->vars().size(); ++
i) {
1149 const int var = expr->vars(
i);
1150 const int64_t coeff = expr->coeffs(
i) / gcd;
1152 domain.AdditionWith(context_->DomainOf(var).MultiplicationBy(coeff));
1153 definition.push_back({var, coeff});
1155 std::sort(definition.begin(), definition.end());
1157 auto it = non_affine_expression_to_new_var_.find(definition);
1158 if (it != non_affine_expression_to_new_var_.end()) {
1159 new_var = it->second;
1161 std::vector<std::pair<int, int64_t>> negated_definition;
1162 negated_definition.reserve(definition.size());
1163 for (
const auto [var, coeff] : definition) {
1164 negated_definition.push_back({var, -coeff});
1166 std::sort(negated_definition.begin(), negated_definition.end());
1167 it = non_affine_expression_to_new_var_.find(negated_definition);
1168 if (it != non_affine_expression_to_new_var_.end()) {
1169 new_var = it->second;
1172 new_var = context_->NewIntVar(domain);
1173 non_affine_expression_to_new_var_[definition] = new_var;
1175 context_->working_model->add_constraints()->mutable_linear();
1176 new_linear->add_vars(new_var);
1177 new_linear->add_coeffs(-1);
1178 for (
const auto [var, coeff] : definition) {
1179 new_linear->add_vars(var);
1180 new_linear->add_coeffs(coeff);
1182 new_linear->add_domain(0);
1183 new_linear->add_domain(0);
1184 context_->solution_crush().SetVarToLinearExpression(new_var, definition);
1188 expr->clear_coeffs();
1189 expr->add_vars(new_var);
1190 expr->add_coeffs(gcd);
1193void ModelCopy::MaybeExpandNonAffineExpressions(
1195 MaybeExpandNonAffineExpression(linear_argument->mutable_target());
1196 for (LinearExpressionProto& expr : *linear_argument->mutable_exprs()) {
1197 MaybeExpandNonAffineExpression(&expr);
1204 ModelCopy copier(context, lrat_proof_handler);
1215 const CpModelProto& in_model, absl::Span<const Domain> domains,
1216 std::function<
bool(
int)> active_constraints,
PresolveContext* context,
1217 std::vector<int>* interval_mapping) {
1222 active_constraints)) {
1234 if (!in_model.
name().empty()) {
1252 return expr->vars().empty();
1254 if (!strategy.variables().empty()) {
1255 CHECK(strategy.exprs().empty());
1256 for (
const int ref : strategy.variables()) {
1261 strategy.clear_variables();
1279 for (
int i = 0;
i < num_terms; ++
i) {
1283 if (domain.
IsEmpty())
continue;
1284 const int64_t closest_domain_value = domain.
ClosestValue(value);
1285 if (closest_domain_value != value) {
1288 i, closest_domain_value);
Domain IntersectionWith(const Domain &domain) const
bool Contains(int64_t value) const
Domain AdditionWith(const Domain &domain) const
int64_t ClosestValue(int64_t wanted) const
::int32_t literals(int index) const
ConstraintCase constraint_case() const
::int32_t enforcement_literal(int index) const
const ::operations_research::sat::BoolArgumentProto & bool_and() const
::google::protobuf::RepeatedField<::int32_t > *PROTOBUF_NONNULL mutable_enforcement_literal()
::operations_research::sat::FloatObjectiveProto *PROTOBUF_NONNULL mutable_floating_point_objective()
bool has_symmetry() const
bool has_solution_hint() const
bool has_floating_point_objective() const
const ::operations_research::sat::SymmetryProto & symmetry() const
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
const ::operations_research::sat::FloatObjectiveProto & floating_point_objective() const
const ::operations_research::sat::ConstraintProto & constraints(int index) const
bool has_objective() const
const ::operations_research::sat::DecisionStrategyProto & search_strategy(int index) const
::int32_t assumptions(int index) const
int constraints_size() const
void set_name(Arg_ &&arg, Args_... args)
::operations_research::sat::SymmetryProto *PROTOBUF_NONNULL mutable_symmetry()
::operations_research::sat::PartialVariableAssignment *PROTOBUF_NONNULL mutable_solution_hint()
int variables_size() const
::operations_research::sat::CpObjectiveProto *PROTOBUF_NONNULL mutable_objective()
::google::protobuf::RepeatedField<::int32_t > *PROTOBUF_NONNULL mutable_assumptions()
const ::std::string & name() const
::operations_research::sat::DecisionStrategyProto *PROTOBUF_NONNULL mutable_search_strategy(int index)
const ::operations_research::sat::PartialVariableAssignment & solution_hint() const
const ::operations_research::sat::CpObjectiveProto & objective() const
void add_coeffs(::int64_t value)
void add_vars(::int32_t value)
ModelCopy(PresolveContext *context, LratProofHandler *lrat_proof_handler=nullptr)
void ImportVariablesAndMaybeIgnoreNames(const CpModelProto &in_model)
void CreateVariablesFromDomains(absl::Span< const Domain > domains)
absl::Span< const int64_t > InternalIntervalMapping() const
bool ImportAndSimplifyConstraints(const CpModelProto &in_model, bool first_copy=false, std::function< bool(int)> active_constraints=nullptr)
void set_values(int index, ::int64_t value)
::int64_t values(int index) const
::int32_t vars(int index) const
CpModelProto * working_model
bool LiteralIsFalse(int lit) const
void UpdateRuleStats(std::string_view name, int num_times=1)
bool ModelIsUnsat() const
bool LiteralIsTrue(int lit) const
RoutesConstraintProto_NodeExpressions NodeExpressions
int RemoveIf(RepeatedPtrField< T > *array, const Pred &pr)
bool RefIsPositive(int ref)
std::vector< int > UsedVariables(const ConstraintProto &ct)
void LiteralsToLinear(absl::Span< const int > literals, int64_t lb, int64_t ub, LinearConstraintProto *linear)
bool ImportModelWithBasicPresolveIntoContext(const CpModelProto &in_model, PresolveContext *context, LratProofHandler *lrat_proof_handler)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
void SetupTextFormatPrinter(google::protobuf::TextFormat::Printer *printer)
void CopyEverythingExceptVariablesAndConstraintsFieldsIntoContext(const CpModelProto &in_model, PresolveContext *context)
bool ImportModelAndDomainsWithBasicPresolveIntoContext(const CpModelProto &in_model, absl::Span< const Domain > domains, std::function< bool(int)> active_constraints, PresolveContext *context, std::vector< int > *interval_mapping)
void AddLinearExpressionToLinearConstraint(const LinearExpressionProto &expr, int64_t coefficient, LinearConstraintProto *linear)