24#include "absl/container/flat_hash_set.h"
25#include "absl/log/check.h"
26#include "absl/strings/str_cat.h"
27#include "absl/types/span.h"
28#include "google/protobuf/arena.h"
29#include "google/protobuf/repeated_field.h"
30#include "google/protobuf/repeated_ptr_field.h"
31#include "google/protobuf/text_format.h"
34#include "ortools/sat/cp_model.pb.h"
37#include "ortools/sat/sat_parameters.pb.h"
46 const CpModelProto& in_model) {
47 if (context_->params().ignore_names()) {
48 context_->working_model->clear_variables();
49 context_->working_model->mutable_variables()->Reserve(
50 in_model.variables_size());
51 for (
const IntegerVariableProto& var_proto : in_model.variables()) {
52 *context_->working_model->add_variables()->mutable_domain() =
56 *context_->working_model->mutable_variables() = in_model.variables();
61 for (
const Domain& domain : domains) {
72 const CpModelProto& in_model,
bool first_copy,
73 std::function<
bool(
int)> active_constraints) {
74 context_->InitializeNewDomains();
75 if (context_->ModelIsUnsat())
return false;
76 const bool ignore_names = context_->params().ignore_names();
80 std::vector<int> constraints_using_intervals;
82 interval_mapping_.assign(in_model.constraints().size(), -1);
84 starting_constraint_index_ = context_->working_model->constraints_size();
85 for (
int c = 0; c < in_model.constraints_size(); ++c) {
86 if (active_constraints !=
nullptr && !active_constraints(c))
continue;
87 const ConstraintProto& ct = in_model.constraints(c);
89 if (!PrepareEnforcementCopyWithDup(ct))
continue;
91 if (!PrepareEnforcementCopy(ct))
continue;
97 switch (ct.constraint_case()) {
98 case ConstraintProto::CONSTRAINT_NOT_SET:
100 case ConstraintProto::kBoolOr:
102 if (!CopyBoolOrWithDupSupport(ct))
return CreateUnsatModel(c, ct);
104 if (!CopyBoolOr(ct))
return CreateUnsatModel(c, ct);
107 case ConstraintProto::kBoolAnd:
108 if (temp_enforcement_literals_.empty()) {
109 for (
const int lit : ct.bool_and().literals()) {
110 context_->UpdateRuleStats(
"bool_and: non-reified.");
111 if (!context_->SetLiteralToTrue(lit)) {
112 return CreateUnsatModel(c, ct);
115 }
else if (first_copy) {
116 if (!CopyBoolAndWithDupSupport(ct))
return CreateUnsatModel(c, ct);
118 if (!CopyBoolAnd(ct))
return CreateUnsatModel(c, ct);
121 case ConstraintProto::kLinear:
122 if (!CopyLinear(ct, first_copy)) {
123 return CreateUnsatModel(c, ct);
126 case ConstraintProto::kIntProd:
127 if (!CopyIntProd(ct, ignore_names))
return CreateUnsatModel(c, ct);
129 case ConstraintProto::kIntDiv:
130 if (!CopyIntDiv(ct, ignore_names))
return CreateUnsatModel(c, ct);
132 case ConstraintProto::kIntMod:
133 if (!CopyIntMod(ct, ignore_names))
return CreateUnsatModel(c, ct);
135 case ConstraintProto::kElement:
136 if (!CopyElement(ct))
return CreateUnsatModel(c, ct);
138 case ConstraintProto::kTable:
139 if (!CopyTable(ct))
return CreateUnsatModel(c, ct);
141 case ConstraintProto::kAutomaton:
142 if (!CopyAutomaton(ct))
return CreateUnsatModel(c, ct);
144 case ConstraintProto::kAllDiff:
145 if (!CopyAllDiff(ct))
return CreateUnsatModel(c, ct);
147 case ConstraintProto::kLinMax:
148 if (!CopyLinMax(ct))
return CreateUnsatModel(c, ct);
150 case ConstraintProto::kAtMostOne:
151 if (!CopyAtMostOne(ct))
return CreateUnsatModel(c, ct);
153 case ConstraintProto::kExactlyOne:
154 if (!CopyExactlyOne(ct))
return CreateUnsatModel(c, ct);
156 case ConstraintProto::kInterval:
157 if (!CopyInterval(ct, c, ignore_names))
return CreateUnsatModel(c, ct);
159 if (!AddLinearConstraintForInterval(ct))
160 return CreateUnsatModel(c, ct);
163 case ConstraintProto::kNoOverlap:
165 constraints_using_intervals.push_back(c);
167 CopyAndMapNoOverlap(ct);
170 case ConstraintProto::kNoOverlap2D:
172 constraints_using_intervals.push_back(c);
174 CopyAndMapNoOverlap2D(ct);
177 case ConstraintProto::kCumulative:
179 constraints_using_intervals.push_back(c);
181 if (!CopyAndMapCumulative(ct))
return CreateUnsatModel(c, ct);
185 ConstraintProto* new_ct = context_->working_model->add_constraints();
187 new_ct->mutable_enforcement_literal()->Clear();
188 FinishEnforcementCopy(new_ct);
191 new_ct->clear_name();
198 DCHECK(first_copy || constraints_using_intervals.empty());
199 for (
const int c : constraints_using_intervals) {
200 const ConstraintProto& ct = in_model.constraints(c);
201 switch (ct.constraint_case()) {
202 case ConstraintProto::kNoOverlap:
203 CopyAndMapNoOverlap(ct);
205 case ConstraintProto::kNoOverlap2D:
206 CopyAndMapNoOverlap2D(ct);
208 case ConstraintProto::kCumulative:
209 if (!CopyAndMapCumulative(ct))
return CreateUnsatModel(c, ct);
212 LOG(DFATAL) <<
"Shouldn't be here.";
219bool ModelCopy::PrepareEnforcementCopy(
const ConstraintProto& ct) {
220 temp_enforcement_literals_.clear();
221 for (
const int lit : ct.enforcement_literal()) {
227 temp_enforcement_literals_.push_back(lit);
232bool ModelCopy::PrepareEnforcementCopyWithDup(
const ConstraintProto& ct) {
233 temp_enforcement_literals_.clear();
234 temp_enforcement_literals_set_.clear();
235 for (
const int lit : ct.enforcement_literal()) {
236 if (context_->LiteralIsTrue(lit))
continue;
237 if (temp_enforcement_literals_set_.contains(lit)) {
238 context_->UpdateRuleStats(
"enforcement: removed duplicate literal");
243 if (context_->LiteralIsFalse(lit)) {
244 context_->UpdateRuleStats(
"enforcement: always false");
247 if (temp_enforcement_literals_set_.contains(
NegatedRef(lit))) {
248 context_->UpdateRuleStats(
"enforcement: contains x and not(x)");
252 temp_enforcement_literals_.push_back(lit);
253 temp_enforcement_literals_set_.insert(lit);
258void ModelCopy::FinishEnforcementCopy(ConstraintProto* ct) {
259 ct->mutable_enforcement_literal()->Add(temp_enforcement_literals_.begin(),
260 temp_enforcement_literals_.end());
263bool ModelCopy::FinishBoolOrCopy() {
264 if (temp_literals_.empty())
return false;
266 if (temp_literals_.size() == 1) {
267 context_->UpdateRuleStats(
"bool_or: only one literal");
268 return context_->SetLiteralToTrue(temp_literals_[0]);
271 context_->working_model->add_constraints()
274 ->Add(temp_literals_.begin(), temp_literals_.end());
278bool ModelCopy::CopyBoolOr(
const ConstraintProto& ct) {
279 temp_literals_.clear();
280 for (
const int lit : temp_enforcement_literals_) {
283 for (
const int lit : ct.bool_or().literals()) {
284 if (context_->LiteralIsTrue(lit)) {
287 if (!context_->LiteralIsFalse(lit)) {
288 temp_literals_.push_back(lit);
291 return FinishBoolOrCopy();
294bool ModelCopy::CopyBoolOrWithDupSupport(
const ConstraintProto& ct) {
295 temp_literals_.clear();
296 temp_literals_set_.clear();
297 for (
const int enforcement_lit : temp_enforcement_literals_) {
304 temp_literals_set_.insert(lit);
305 temp_literals_.push_back(lit);
307 for (
const int lit : ct.bool_or().literals()) {
308 if (context_->LiteralIsTrue(lit)) {
309 context_->UpdateRuleStats(
"bool_or: always true");
312 if (context_->LiteralIsFalse(lit))
continue;
313 if (temp_literals_set_.contains(
NegatedRef(lit))) {
314 context_->UpdateRuleStats(
"bool_or: always true");
317 const auto [it, inserted] = temp_literals_set_.insert(lit);
318 if (inserted) temp_literals_.push_back(lit);
320 return FinishBoolOrCopy();
323bool ModelCopy::CopyBoolAnd(
const ConstraintProto& ct) {
324 bool at_least_one_false =
false;
325 int num_non_fixed_literals = 0;
326 for (
const int lit : ct.bool_and().literals()) {
327 if (context_->LiteralIsFalse(lit)) {
328 at_least_one_false =
true;
331 if (!context_->LiteralIsTrue(lit)) {
332 num_non_fixed_literals++;
336 if (at_least_one_false) {
338 BoolArgumentProto* bool_or =
339 context_->working_model->add_constraints()->mutable_bool_or();
340 for (
const int lit : temp_enforcement_literals_) {
343 return !bool_or->literals().empty();
344 }
else if (num_non_fixed_literals > 0) {
345 ConstraintProto* new_ct = context_->working_model->add_constraints();
346 FinishEnforcementCopy(new_ct);
347 BoolArgumentProto* bool_and = new_ct->mutable_bool_and();
348 bool_and->mutable_literals()->Reserve(num_non_fixed_literals);
349 for (
const int lit : ct.bool_and().literals()) {
350 if (context_->LiteralIsTrue(lit))
continue;
351 bool_and->add_literals(lit);
357bool ModelCopy::CopyBoolAndWithDupSupport(
const ConstraintProto& ct) {
358 DCHECK(!temp_enforcement_literals_.empty());
360 bool at_least_one_false =
false;
361 temp_literals_.clear();
362 temp_literals_set_.clear();
363 for (
const int lit : ct.bool_and().literals()) {
364 if (context_->LiteralIsFalse(lit)) {
365 context_->UpdateRuleStats(
"bool and: always false");
366 at_least_one_false =
true;
369 if (temp_literals_set_.contains(
NegatedRef(lit))) {
370 context_->UpdateRuleStats(
"bool and: => x and not(x) ");
371 at_least_one_false =
true;
374 if (temp_enforcement_literals_set_.contains(
NegatedRef(lit))) {
375 context_->UpdateRuleStats(
"bool and: not(x) => x");
376 at_least_one_false =
true;
380 if (context_->LiteralIsTrue(lit))
continue;
381 if (temp_enforcement_literals_set_.contains(lit)) {
382 context_->UpdateRuleStats(
"bool and: x => x");
385 const auto [it, inserted] = temp_literals_set_.insert(lit);
386 if (inserted) temp_literals_.push_back(lit);
389 if (at_least_one_false) {
391 BoolArgumentProto* bool_or =
392 context_->working_model->add_constraints()->mutable_bool_or();
393 for (
const int lit : temp_enforcement_literals_) {
396 return !bool_or->literals().empty();
399 if (temp_literals_.empty()) {
400 context_->UpdateRuleStats(
"bool and: empty");
405 ConstraintProto* new_ct = context_->working_model->add_constraints();
406 FinishEnforcementCopy(new_ct);
407 new_ct->mutable_bool_and()->mutable_literals()->Add(temp_literals_.begin(),
408 temp_literals_.end());
412bool ModelCopy::CopyLinearExpression(
413 const LinearExpressionProto& expr, LinearExpressionProto* dst,
414 absl::Span<const int> enforcement_literals) {
415 non_fixed_variables_.clear();
416 non_fixed_coefficients_.clear();
417 int64_t offset = expr.offset();
418 for (
int i = 0;
i < expr.vars_size(); ++
i) {
419 const int ref = expr.vars(
i);
420 const int64_t coeff = expr.coeffs(
i);
421 if (coeff == 0)
continue;
422 if (context_->IsFixed(ref)) {
423 offset += coeff * context_->MinOf(ref);
429 non_fixed_variables_.push_back(ref);
430 non_fixed_coefficients_.push_back(coeff);
432 non_fixed_variables_.push_back(
NegatedRef(ref));
433 non_fixed_coefficients_.push_back(-coeff);
437 dst->set_offset(offset);
438 dst->mutable_vars()->Add(non_fixed_variables_.begin(),
439 non_fixed_variables_.end());
440 dst->mutable_coeffs()->Add(non_fixed_coefficients_.begin(),
441 non_fixed_coefficients_.end());
444 context_->CanonicalizeLinearExpression(enforcement_literals, dst);
448bool ModelCopy::CopyLinear(
const ConstraintProto& ct,
bool canonicalize) {
449 non_fixed_variables_.clear();
450 non_fixed_coefficients_.clear();
452 int64_t min_activity = 0;
453 int64_t max_activity = 0;
454 for (
int i = 0;
i < ct.linear().vars_size(); ++
i) {
455 const int ref = ct.linear().vars(
i);
456 const int64_t coeff = ct.linear().coeffs(
i);
457 if (coeff == 0)
continue;
458 if (context_->IsFixed(ref)) {
459 offset += coeff * context_->MinOf(ref);
464 min_activity += coeff * context_->MinOf(ref);
465 max_activity += coeff * context_->MaxOf(ref);
467 min_activity += coeff * context_->MaxOf(ref);
468 max_activity += coeff * context_->MinOf(ref);
473 non_fixed_variables_.push_back(ref);
474 non_fixed_coefficients_.push_back(coeff);
476 non_fixed_variables_.push_back(
NegatedRef(ref));
477 non_fixed_coefficients_.push_back(-coeff);
481 const Domain implied(min_activity, max_activity);
482 const Domain new_rhs =
486 if (implied.IsIncludedIn(new_rhs)) {
487 context_->UpdateRuleStats(
"linear: always true");
493 if (tight_domain.IsEmpty()) {
494 if (ct.enforcement_literal().empty())
return false;
495 temp_literals_.clear();
496 for (
const int literal : ct.enforcement_literal()) {
497 if (!context_->LiteralIsTrue(literal)) {
498 temp_literals_.push_back(
NegatedRef(literal));
501 context_->working_model->add_constraints()
504 ->Add(temp_literals_.begin(), temp_literals_.end());
505 return !temp_literals_.empty();
508 DCHECK(!non_fixed_variables_.empty());
510 if (non_fixed_variables_.size() == 1 && ct.enforcement_literal().empty()) {
511 context_->UpdateRuleStats(
"linear1: x in domain");
512 return context_->IntersectDomainWith(
513 non_fixed_variables_[0],
514 new_rhs.InverseMultiplicationBy(non_fixed_coefficients_[0]));
517 ConstraintProto* new_ct = context_->working_model->add_constraints();
518 FinishEnforcementCopy(new_ct);
519 LinearConstraintProto* linear = new_ct->mutable_linear();
520 linear->mutable_vars()->Add(non_fixed_variables_.begin(),
521 non_fixed_variables_.end());
522 linear->mutable_coeffs()->Add(non_fixed_coefficients_.begin(),
523 non_fixed_coefficients_.end());
526 context_->CanonicalizeLinearConstraint(new_ct);
531bool ModelCopy::CopyElement(
const ConstraintProto& ct) {
532 ConstraintProto* new_ct = context_->working_model->add_constraints();
533 if (ct.element().vars().empty() && !ct.element().exprs().empty()) {
539 auto fill_expr = [
this](
int var, LinearExpressionProto* expr)
mutable {
540 if (context_->IsFixed(var)) {
541 expr->set_offset(context_->FixedValue(var));
544 expr->mutable_vars()->Reserve(1);
545 expr->mutable_coeffs()->Reserve(1);
551 fill_expr(ct.element().index(),
552 new_ct->mutable_element()->mutable_linear_index());
553 fill_expr(ct.element().target(),
554 new_ct->mutable_element()->mutable_linear_target());
555 for (
const int var : ct.element().vars()) {
556 fill_expr(var, new_ct->mutable_element()->add_exprs());
561bool ModelCopy::CopyAutomaton(
const ConstraintProto& ct) {
562 ConstraintProto* new_ct = context_->working_model->add_constraints();
564 if (new_ct->automaton().vars().empty())
return true;
566 auto fill_expr = [
this](
int var, LinearExpressionProto* expr)
mutable {
567 if (context_->IsFixed(var)) {
568 expr->set_offset(context_->FixedValue(var));
571 expr->mutable_vars()->Reserve(1);
572 expr->mutable_coeffs()->Reserve(1);
578 for (
const int var : ct.automaton().vars()) {
579 fill_expr(var, new_ct->mutable_automaton()->add_exprs());
581 new_ct->mutable_automaton()->clear_vars();
586bool ModelCopy::CopyTable(
const ConstraintProto& ct) {
587 ConstraintProto* new_ct = context_->working_model->add_constraints();
588 if (ct.table().vars().empty() && !ct.table().exprs().empty()) {
594 auto fill_expr = [
this](
int var, LinearExpressionProto* expr)
mutable {
595 if (context_->IsFixed(var)) {
596 expr->set_offset(context_->FixedValue(var));
599 expr->mutable_vars()->Reserve(1);
600 expr->mutable_coeffs()->Reserve(1);
606 for (
const int var : ct.table().vars()) {
607 fill_expr(var, new_ct->mutable_table()->add_exprs());
609 *new_ct->mutable_table()->mutable_values() = ct.table().values();
610 new_ct->mutable_table()->set_negated(ct.table().negated());
611 *new_ct->mutable_enforcement_literal() = ct.enforcement_literal();
616bool ModelCopy::CopyAllDiff(
const ConstraintProto& ct) {
617 if (ct.all_diff().exprs().size() <= 1)
return true;
618 ConstraintProto* new_ct = context_->working_model->add_constraints();
619 for (
const LinearExpressionProto& expr : ct.all_diff().exprs()) {
620 CopyLinearExpression(expr, new_ct->mutable_all_diff()->add_exprs());
625bool ModelCopy::CopyLinMax(
const ConstraintProto& ct) {
627 ConstraintProto* new_ct =
nullptr;
630 int64_t max_of_fixed_terms = std::numeric_limits<int64_t>::min();
631 for (
const auto& expr : ct.lin_max().exprs()) {
632 const std::optional<int64_t> fixed = context_->FixedValueOrNullopt(expr);
633 if (fixed != std::nullopt) {
634 max_of_fixed_terms = std::max(max_of_fixed_terms, fixed.value());
637 if (new_ct ==
nullptr) {
638 new_ct = context_->working_model->add_constraints();
640 CopyLinearExpression(expr, new_ct->mutable_lin_max()->add_exprs());
646 if (new_ct ==
nullptr && ct.enforcement_literal().empty() &&
647 ct.lin_max().target().vars().size() <= 1) {
648 context_->UpdateRuleStats(
"lin_max: all exprs fixed during copy");
649 return context_->IntersectDomainWith(ct.lin_max().target(),
650 Domain(max_of_fixed_terms));
654 if (max_of_fixed_terms > std::numeric_limits<int64_t>::min()) {
655 if (new_ct ==
nullptr) {
656 new_ct = context_->working_model->add_constraints();
658 new_ct->mutable_lin_max()->add_exprs()->set_offset(max_of_fixed_terms);
662 if (new_ct ==
nullptr)
return false;
663 CopyLinearExpression(ct.lin_max().target(),
664 new_ct->mutable_lin_max()->mutable_target());
668bool ModelCopy::CopyAtMostOne(
const ConstraintProto& ct) {
670 temp_literals_.clear();
671 for (
const int lit : ct.at_most_one().literals()) {
672 if (context_->LiteralIsFalse(lit))
continue;
673 temp_literals_.push_back(lit);
674 if (context_->LiteralIsTrue(lit)) num_true++;
677 if (temp_literals_.size() <= 1)
return true;
678 if (num_true > 1)
return false;
681 ConstraintProto* new_ct = context_->working_model->add_constraints();
682 FinishEnforcementCopy(new_ct);
683 new_ct->mutable_at_most_one()->mutable_literals()->Add(temp_literals_.begin(),
684 temp_literals_.end());
688bool ModelCopy::CopyExactlyOne(
const ConstraintProto& ct) {
690 temp_literals_.clear();
691 for (
const int lit : ct.exactly_one().literals()) {
692 if (context_->LiteralIsFalse(lit))
continue;
693 temp_literals_.push_back(lit);
694 if (context_->LiteralIsTrue(lit)) num_true++;
697 if (temp_literals_.empty() || num_true > 1)
return false;
698 if (temp_literals_.size() == 1 && num_true == 1)
return true;
701 ConstraintProto* new_ct = context_->working_model->add_constraints();
702 FinishEnforcementCopy(new_ct);
703 new_ct->mutable_exactly_one()->mutable_literals()->Add(temp_literals_.begin(),
704 temp_literals_.end());
708bool ModelCopy::CopyInterval(
const ConstraintProto& ct,
int c,
710 CHECK_EQ(starting_constraint_index_, 0)
711 <<
"Adding new interval constraints to partially filled model is not "
713 interval_mapping_[
c] = context_->working_model->constraints_size();
714 ConstraintProto* new_ct = context_->working_model->add_constraints();
716 new_ct->set_name(ct.name());
718 *new_ct->mutable_enforcement_literal() = ct.enforcement_literal();
719 CopyLinearExpression(ct.interval().start(),
720 new_ct->mutable_interval()->mutable_start(),
721 ct.enforcement_literal());
722 CopyLinearExpression(ct.interval().size(),
723 new_ct->mutable_interval()->mutable_size(),
724 ct.enforcement_literal());
725 CopyLinearExpression(ct.interval().end(),
726 new_ct->mutable_interval()->mutable_end(),
727 ct.enforcement_literal());
731bool ModelCopy::CopyIntProd(
const ConstraintProto& ct,
bool ignore_names) {
732 ConstraintProto* new_ct = context_->working_model->add_constraints();
734 new_ct->set_name(ct.name());
736 for (
const LinearExpressionProto& expr : ct.int_prod().exprs()) {
737 CopyLinearExpression(expr, new_ct->mutable_int_prod()->add_exprs());
739 CopyLinearExpression(ct.int_prod().target(),
740 new_ct->mutable_int_prod()->mutable_target());
744bool ModelCopy::CopyIntDiv(
const ConstraintProto& ct,
bool ignore_names) {
745 ConstraintProto* new_ct = context_->working_model->add_constraints();
747 new_ct->set_name(ct.name());
749 for (
const LinearExpressionProto& expr : ct.int_div().exprs()) {
750 CopyLinearExpression(expr, new_ct->mutable_int_div()->add_exprs());
752 CopyLinearExpression(ct.int_div().target(),
753 new_ct->mutable_int_div()->mutable_target());
757bool ModelCopy::CopyIntMod(
const ConstraintProto& ct,
bool ignore_names) {
758 ConstraintProto* new_ct = context_->working_model->add_constraints();
760 new_ct->set_name(ct.name());
762 for (
const LinearExpressionProto& expr : ct.int_mod().exprs()) {
763 CopyLinearExpression(expr, new_ct->mutable_int_mod()->add_exprs());
765 CopyLinearExpression(ct.int_mod().target(),
766 new_ct->mutable_int_mod()->mutable_target());
770bool ModelCopy::AddLinearConstraintForInterval(
const ConstraintProto& ct) {
775 const IntervalConstraintProto& itv = ct.interval();
776 if (itv.size().vars().empty() &&
777 itv.start().offset() + itv.size().offset() == itv.end().offset() &&
778 absl::Span<const int>(itv.start().vars()) ==
779 absl::Span<const int>(itv.end().vars()) &&
780 absl::Span<const int64_t>(itv.start().coeffs()) ==
781 absl::Span<const int64_t>(itv.end().coeffs())) {
784 tmp_constraint_.Clear();
785 *tmp_constraint_.mutable_enforcement_literal() = ct.enforcement_literal();
786 LinearConstraintProto* mutable_linear = tmp_constraint_.mutable_linear();
788 mutable_linear->add_domain(0);
789 mutable_linear->add_domain(0);
793 if (!CopyLinear(tmp_constraint_,
true))
return false;
797 const LinearExpressionProto& size_expr = itv.size();
798 if (context_->MinOf(size_expr) < 0) {
799 tmp_constraint_.Clear();
800 *tmp_constraint_.mutable_enforcement_literal() = ct.enforcement_literal();
801 *tmp_constraint_.mutable_linear()->mutable_vars() = size_expr.vars();
802 *tmp_constraint_.mutable_linear()->mutable_coeffs() = size_expr.coeffs();
803 tmp_constraint_.mutable_linear()->add_domain(-size_expr.offset());
804 tmp_constraint_.mutable_linear()->add_domain(
805 std::numeric_limits<int64_t>::max());
806 if (!CopyLinear(tmp_constraint_,
true))
return false;
812void ModelCopy::CopyAndMapNoOverlap(
const ConstraintProto& ct) {
815 context_->working_model->add_constraints()->mutable_no_overlap();
816 new_ct->mutable_intervals()->Reserve(ct.no_overlap().intervals().size());
817 for (
const int index : ct.no_overlap().intervals()) {
818 const int new_index = interval_mapping_[index];
819 if (new_index != -1) {
820 new_ct->add_intervals(new_index);
825void ModelCopy::CopyAndMapNoOverlap2D(
const ConstraintProto& ct) {
828 context_->working_model->add_constraints()->mutable_no_overlap_2d();
830 const int num_intervals = ct.no_overlap_2d().x_intervals().size();
831 new_ct->mutable_x_intervals()->Reserve(num_intervals);
832 new_ct->mutable_y_intervals()->Reserve(num_intervals);
833 for (
int i = 0;
i < num_intervals; ++
i) {
834 const int new_x = interval_mapping_[ct.no_overlap_2d().x_intervals(
i)];
835 if (new_x == -1)
continue;
836 const int new_y = interval_mapping_[ct.no_overlap_2d().y_intervals(
i)];
837 if (new_y == -1)
continue;
838 new_ct->add_x_intervals(new_x);
839 new_ct->add_y_intervals(new_y);
843bool ModelCopy::CopyAndMapCumulative(
const ConstraintProto& ct) {
844 if (ct.cumulative().intervals().empty() &&
845 context_->IsFixed(ct.cumulative().capacity())) {
847 return context_->FixedValue(ct.cumulative().capacity()) >= 0;
851 context_->working_model->add_constraints()->mutable_cumulative();
852 CopyLinearExpression(ct.cumulative().capacity(), new_ct->mutable_capacity());
854 const int num_intervals = ct.cumulative().intervals().size();
855 new_ct->mutable_intervals()->Reserve(num_intervals);
856 new_ct->mutable_demands()->Reserve(num_intervals);
857 for (
int i = 0;
i < num_intervals; ++
i) {
858 const int new_index = interval_mapping_[ct.cumulative().intervals(
i)];
859 if (new_index != -1) {
860 new_ct->add_intervals(new_index);
861 *new_ct->add_demands() = ct.cumulative().demands(
i);
868bool ModelCopy::CreateUnsatModel(
int c,
const ConstraintProto& ct) {
869 context_->working_model->mutable_constraints()->Clear();
870 context_->working_model->add_constraints()->mutable_bool_or();
874 if (context_->ModelIsUnsat())
return false;
876 std::string proto_string;
877#if !defined(__PORTABLE_PLATFORM__)
878 google::protobuf::TextFormat::Printer printer;
880 printer.PrintToString(ct, &proto_string);
882 std::string message = absl::StrCat(
883 "proven during initial copy of constraint #", c,
":\n", proto_string);
885 if (vars.size() < 10) {
886 absl::StrAppend(&message,
"With current variable domains:\n");
887 for (
const int var : vars) {
888 absl::StrAppend(&message,
"var:", var,
889 " domain:", context_->DomainOf(var).ToString(),
"\n");
892 return context_->NotifyThatModelIsUnsat(message);
908 const CpModelProto& in_model, absl::Span<const Domain> domains,
909 std::function<
bool(
int)> active_constraints,
PresolveContext* context) {
910 CHECK_EQ(domains.size(), in_model.variables_size());
914 active_constraints)) {
924 if (!in_model.name().empty()) {
927 if (in_model.has_objective()) {
928 *context->
working_model->mutable_objective() = in_model.objective();
930 if (in_model.has_floating_point_objective()) {
931 *context->
working_model->mutable_floating_point_objective() =
932 in_model.floating_point_objective();
934 if (!in_model.search_strategy().empty()) {
937 in_model.search_strategy();
938 for (DecisionStrategyProto& strategy :
941 [](
const LinearExpressionProto* expr) {
942 return expr->vars().empty();
944 if (!strategy.variables().empty()) {
945 CHECK(strategy.exprs().empty());
946 for (
const int ref : strategy.variables()) {
947 LinearExpressionProto* expr = strategy.add_exprs();
951 strategy.clear_variables();
955 if (!in_model.assumptions().empty()) {
956 *context->
working_model->mutable_assumptions() = in_model.assumptions();
958 if (in_model.has_symmetry()) {
959 *context->
working_model->mutable_symmetry() = in_model.symmetry();
961 if (in_model.has_solution_hint()) {
962 *context->
working_model->mutable_solution_hint() = in_model.solution_hint();
968 const int num_terms = in_model.solution_hint().vars().size();
969 for (
int i = 0;
i < num_terms; ++
i) {
970 const int var = in_model.solution_hint().vars(
i);
971 const int64_t value = in_model.solution_hint().values(
i);
973 if (domain.
IsEmpty())
continue;
974 const int64_t closest_domain_value = domain.
ClosestValue(value);
975 if (closest_domain_value != value) {
978 i, closest_domain_value);
Domain IntersectionWith(const Domain &domain) const
Domain AdditionWith(const Domain &domain) const
int64_t ClosestValue(int64_t wanted) const
void ImportVariablesAndMaybeIgnoreNames(const CpModelProto &in_model)
void CreateVariablesFromDomains(absl::Span< const Domain > domains)
ModelCopy(PresolveContext *context)
bool ImportAndSimplifyConstraints(const CpModelProto &in_model, bool first_copy=false, std::function< bool(int)> active_constraints=nullptr)
CpModelProto * working_model
bool LiteralIsFalse(int lit) const
void UpdateRuleStats(const std::string &name, int num_times=1)
bool ModelIsUnsat() const
bool LiteralIsTrue(int lit) const
int RemoveIf(RepeatedPtrField< T > *array, const Pred &pr)
bool RefIsPositive(int ref)
bool ImportModelAndDomainsWithBasicPresolveIntoContext(const CpModelProto &in_model, absl::Span< const Domain > domains, std::function< bool(int)> active_constraints, PresolveContext *context)
bool ImportModelWithBasicPresolveIntoContext(const CpModelProto &in_model, PresolveContext *context)
std::vector< int > UsedVariables(const ConstraintProto &ct)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
Serializes a Domain into the domain field of a proto.
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Reads a Domain from the domain field of a proto.
void SetupTextFormatPrinter(google::protobuf::TextFormat::Printer *printer)
void CopyEverythingExceptVariablesAndConstraintsFieldsIntoContext(const CpModelProto &in_model, PresolveContext *context)
Copies the non constraint, non variables part of the model.
void AddLinearExpressionToLinearConstraint(const LinearExpressionProto &expr, int64_t coefficient, LinearConstraintProto *linear)
int NegatedRef(int ref)
Small utility functions to deal with negative variable/literal references.
In SWIG mode, we don't want anything besides these top-level includes.