18 #include "absl/container/flat_hash_set.h"
35 if (encoder ==
nullptr)
return false;
36 if (!encoder->VariableIsFullyEncoded(
var))
return false;
47 std::vector<Literal> at_most_one;
49 for (
const auto value_literal : encoding) {
50 const Literal lit = value_literal.literal;
51 const IntegerValue
delta = value_literal.value - var_min;
52 DCHECK_GE(
delta, IntegerValue(0));
53 at_most_one.push_back(lit);
54 if (!at_least_one.
AddLiteralTerm(lit, IntegerValue(1)))
return false;
55 if (
delta != IntegerValue(0)) {
69 std::pair<IntegerValue, IntegerValue> GetMinAndMaxNotEncoded(
71 const absl::flat_hash_set<IntegerValue>& encoded_values,
73 const auto* domains =
model.Get<IntegerDomains>();
74 if (domains ==
nullptr ||
var >= domains->size()) {
81 for (
const ClosedInterval
interval : (*domains)[
var]) {
92 const auto& domain = (*domains)[
var];
93 for (
int i = domain.NumIntervals() - 1; i >= 0; --i) {
94 const ClosedInterval
interval = domain[i];
113 if (encoder ==
nullptr || integer_trail ==
nullptr)
return;
115 const std::vector<IntegerEncoder::ValueLiteralPair>& encoding =
116 encoder->PartialDomainEncoding(
var);
117 if (encoding.empty())
return;
119 std::vector<Literal> at_most_one_ct;
120 absl::flat_hash_set<IntegerValue> encoded_values;
121 for (
const auto value_literal : encoding) {
130 at_most_one_ct.push_back(
literal);
131 encoded_values.insert(value_literal.value);
133 if (encoded_values.empty())
return;
139 const auto pair = GetMinAndMaxNotEncoded(
var, encoded_values,
model);
149 for (
const auto value_literal : encoding) {
150 const Literal lit = value_literal.literal;
153 encoding_ct.
AddLiteralTerm(lit, IntegerValue(-value_literal.value)));
161 const IntegerValue d_min = pair.first;
164 for (
const auto value_literal : encoding) {
166 d_min - value_literal.value));
170 const IntegerValue d_max = pair.second;
173 for (
const auto value_literal : encoding) {
175 d_max - value_literal.value));
189 if (integer_trail ==
nullptr || encoder ==
nullptr)
return;
191 const std::map<IntegerValue, Literal>& greater_than_encoding =
192 encoder->PartialGreaterThanEncoding(
var);
193 if (greater_than_encoding.empty())
return;
198 IntegerValue prev_used_bound = integer_trail->
LowerBound(
var);
203 for (
const auto entry : greater_than_encoding) {
204 if (entry.first <= prev_used_bound)
continue;
206 const LiteralIndex literal_index = entry.second.Index();
207 const IntegerValue diff = prev_used_bound - entry.first;
216 prev_used_bound = entry.first;
217 prev_literal_index = literal_index;
225 IntegerValue prev_used_bound = integer_trail->LowerBound(
NegationOf(
var));
229 for (
const auto entry :
231 if (entry.first <= prev_used_bound)
continue;
232 const IntegerValue diff = prev_used_bound - entry.first;
236 prev_used_bound = entry.first;
244 void AppendEnforcedUpperBound(
const Literal enforcing_lit,
245 const IntegerVariable target,
246 const IntegerVariable bounding_var, Model*
model,
247 LinearRelaxation* relaxation) {
248 IntegerTrail* integer_trail =
model->GetOrCreate<IntegerTrail>();
249 const IntegerValue max_target_value = integer_trail->UpperBound(target);
250 const IntegerValue min_var_value = integer_trail->LowerBound(bounding_var);
251 const IntegerValue max_term_value = max_target_value - min_var_value;
253 lc.AddTerm(target, IntegerValue(1));
254 lc.AddTerm(bounding_var, IntegerValue(-1));
255 CHECK(lc.AddLiteralTerm(enforcing_lit, max_term_value));
256 relaxation->linear_constraints.push_back(lc.Build());
261 void AppendEnforcedLinearExpression(
262 const std::vector<Literal>& enforcing_literals,
263 const LinearExpression& expr,
const IntegerValue rhs_domain_min,
264 const IntegerValue rhs_domain_max,
const Model&
model,
265 LinearRelaxation* relaxation) {
266 CHECK_EQ(expr.offset, IntegerValue(0));
268 const IntegerTrail* integer_trail =
model.Get<IntegerTrail>();
269 const IntegerValue min_expr_value =
272 if (rhs_domain_min > min_expr_value) {
277 for (
const Literal&
literal : enforcing_literals) {
279 rhs_domain_min - min_expr_value));
281 for (
int i = 0; i < canonical_expr.vars.size(); i++) {
282 lc.AddTerm(canonical_expr.vars[i], canonical_expr.coeffs[i]);
284 relaxation->linear_constraints.push_back(lc.Build());
286 const IntegerValue max_expr_value =
288 if (rhs_domain_max < max_expr_value) {
293 for (
const Literal&
literal : enforcing_literals) {
295 rhs_domain_max - max_expr_value));
297 for (
int i = 0; i < canonical_expr.vars.size(); i++) {
298 lc.AddTerm(canonical_expr.vars[i], canonical_expr.coeffs[i]);
300 relaxation->linear_constraints.push_back(lc.Build());
316 int linearization_level,
319 DCHECK_GT(linearization_level, 0);
321 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr) {
322 if (linearization_level < 2)
return;
324 for (
const int enforcement_ref :
ct.enforcement_literal()) {
328 for (
const int ref :
ct.bool_or().literals()) {
332 }
else if (
ct.constraint_case() ==
333 ConstraintProto::ConstraintCase::kBoolAnd) {
337 if (linearization_level < 2)
return;
339 if (
ct.enforcement_literal().size() == 1) {
341 for (
const int ref :
ct.bool_and().literals()) {
343 {enforcement, mapping->
Literal(ref).Negated()});
350 int num_literals =
ct.bool_and().literals_size();
353 for (
const int ref :
ct.bool_and().literals()) {
356 for (
const int enforcement_ref :
ct.enforcement_literal()) {
358 IntegerValue(num_literals)));
361 }
else if (
ct.constraint_case() ==
362 ConstraintProto::ConstraintCase::kAtMostOne) {
364 std::vector<Literal> at_most_one;
365 for (
const int ref :
ct.at_most_one().literals()) {
366 at_most_one.push_back(mapping->Literal(ref));
369 }
else if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kIntMax) {
371 const IntegerVariable target = mapping->Integer(
ct.int_max().target());
372 const std::vector<IntegerVariable> vars =
373 mapping->Integers(
ct.int_max().vars());
376 }
else if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kIntMin) {
378 const IntegerVariable negative_target =
380 const std::vector<IntegerVariable> negative_vars =
384 }
else if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
387 }
else if (
ct.constraint_case() ==
388 ConstraintProto::ConstraintCase::kCircuit) {
390 const int num_arcs =
ct.circuit().literals_size();
391 CHECK_EQ(num_arcs,
ct.circuit().tails_size());
392 CHECK_EQ(num_arcs,
ct.circuit().heads_size());
396 std::map<int, std::vector<Literal>> incoming_arc_constraints;
397 std::map<int, std::vector<Literal>> outgoing_arc_constraints;
398 for (
int i = 0; i < num_arcs; i++) {
400 const int tail =
ct.circuit().tails(i);
401 const int head =
ct.circuit().heads(i);
405 outgoing_arc_constraints[
tail].push_back(arc);
406 incoming_arc_constraints[
head].push_back(arc);
408 for (
const auto* node_map :
409 {&outgoing_arc_constraints, &incoming_arc_constraints}) {
410 for (
const auto& entry : *node_map) {
411 const std::vector<Literal>& exactly_one = entry.second;
412 if (exactly_one.size() > 1) {
415 for (
const Literal l : exactly_one) {
425 }
else if (
ct.constraint_case() ==
426 ConstraintProto::ConstraintCase::kElement) {
427 const IntegerVariable
index = mapping->Integer(
ct.element().index());
428 const IntegerVariable target = mapping->Integer(
ct.element().target());
429 const std::vector<IntegerVariable> vars =
430 mapping->Integers(
ct.element().vars());
435 constraint.
AddTerm(target, IntegerValue(-1));
438 const IntegerVariable
var = vars[literal_value.value.value()];
448 }
else if (
ct.constraint_case() ==
449 ConstraintProto::ConstraintCase::kInterval) {
450 if (linearization_level < 2)
return;
451 const IntegerVariable start = mapping->Integer(
ct.interval().start());
452 const IntegerVariable size = mapping->Integer(
ct.interval().size());
453 const IntegerVariable end = mapping->Integer(
ct.interval().end());
455 const bool size_is_fixed =
457 const IntegerValue rhs =
458 size_is_fixed ? -integer_trail->
LowerBound(size) : IntegerValue(0);
460 lc.
AddTerm(start, IntegerValue(1));
461 if (!size_is_fixed) {
462 lc.
AddTerm(size, IntegerValue(1));
464 lc.
AddTerm(end, IntegerValue(-1));
469 AppendEnforcedLinearExpression(
470 mapping->Literals(
ct.enforcement_literal()), expr, rhs, rhs, *
model,
475 }
else if (
ct.constraint_case() ==
476 ConstraintProto::ConstraintCase::kNoOverlap) {
484 const ConstraintProto&
ct,
487 CHECK(
ct.has_no_overlap());
488 if (linearization_level < 3)
return;
490 if (
ct.no_overlap().intervals_size() < 2)
return;
492 const int64 num_intervals =
ct.no_overlap().intervals_size();
495 for (
int index1 = 0; index1 < num_intervals; ++index1) {
496 const int interval_index1 =
ct.no_overlap().intervals(index1);
499 const IntervalConstraintProto interval1 =
500 model_proto.constraints(interval_index1).interval();
501 const IntegerVariable start1 = mapping->Integer(interval1.start());
502 const IntegerVariable end1 = mapping->Integer(interval1.end());
503 for (
int index2 = index1 + 1; index2 < num_intervals; ++index2) {
504 const int interval_index2 =
ct.no_overlap().intervals(index2);
508 const IntervalConstraintProto interval2 =
509 model_proto.constraints(interval_index2).interval();
510 const IntegerVariable start2 = mapping->Integer(interval2.start());
511 const IntegerVariable end2 = mapping->Integer(interval2.end());
520 const bool interval_1_can_precede_2 =
522 const bool interval_2_can_precede_1 =
525 if (interval_1_can_precede_2 && interval_2_can_precede_1) {
526 const IntegerVariable interval1_precedes_interval2 =
528 const Literal interval1_precedes_interval2_lit =
529 encoder->GetOrCreateLiteralAssociatedToEquality(
530 interval1_precedes_interval2, IntegerValue(1));
533 AppendEnforcedUpperBound(interval1_precedes_interval2_lit, end1, start2,
535 AppendEnforcedUpperBound(interval1_precedes_interval2_lit.
Negated(),
536 end2, start1,
model, relaxation);
537 }
else if (interval_1_can_precede_2) {
540 lc.
AddTerm(end1, IntegerValue(1));
541 lc.
AddTerm(start2, IntegerValue(-1));
543 }
else if (interval_2_can_precede_1) {
546 lc.
AddTerm(end2, IntegerValue(1));
547 lc.
AddTerm(start1, IntegerValue(-1));
555 const std::vector<IntegerVariable>& vars,
560 for (
const IntegerVariable
var : vars) {
563 if (target ==
var)
continue;
566 lc.
AddTerm(target, IntegerValue(-1));
571 if (linearization_level < 2)
return;
575 if (vars.size() == 2) {
578 encoder->GetOrCreateLiteralAssociatedToEquality(y, IntegerValue(1));
579 AppendEnforcedUpperBound(y_lit, target, vars[0],
model, relaxation);
584 {y_lit}, {target, vars[0]}, {IntegerValue(1), IntegerValue(-1)},
585 IntegerValue(0),
model);
587 model->TakeOwnership(upper_bound1);
588 AppendEnforcedUpperBound(y_lit.
Negated(), target, vars[1],
model,
591 {y_lit.
Negated()}, {target, vars[1]},
592 {IntegerValue(1), IntegerValue(-1)}, IntegerValue(0),
model);
594 model->TakeOwnership(upper_bound2);
603 std::vector<Literal> exactly_one_literals;
604 exactly_one_literals.reserve(vars.size());
605 for (
const IntegerVariable
var : vars) {
606 if (target ==
var)
continue;
612 encoder->GetOrCreateLiteralAssociatedToEquality(y, IntegerValue(1));
614 AppendEnforcedUpperBound(y_lit, target,
var,
model, relaxation);
616 {y_lit}, {target,
var}, {IntegerValue(1), IntegerValue(-1)},
617 IntegerValue(0),
model);
619 model->TakeOwnership(upper_bound_constraint);
620 exactly_one_literals.push_back(y_lit);
629 IntegerVariable target,
const std::vector<LinearExpression>& exprs,
635 for (
int i = 0; i < expr.vars.size(); ++i) {
636 lc.
AddTerm(expr.vars[i], expr.coeffs[i]);
638 lc.
AddTerm(target, IntegerValue(-1));
646 const int num_exprs = exprs.size();
651 std::vector<IntegerVariable> z_vars;
652 std::vector<Literal> z_lits;
653 z_vars.reserve(num_exprs);
654 z_lits.reserve(num_exprs);
657 std::vector<Literal> exactly_one_literals;
658 for (
int i = 0; i < num_exprs; ++i) {
663 z_lits.push_back(z_lit);
666 local_expr.
vars.push_back(target);
667 local_expr.
coeffs = exprs[i].coeffs;
668 local_expr.
coeffs.push_back(IntegerValue(1));
672 model->TakeOwnership(upper_bound);
682 std::vector<IntegerVariable> x_vars;
683 for (
int i = 0; i < num_exprs; ++i) {
684 x_vars.insert(x_vars.end(), exprs[i].vars.begin(), exprs[i].vars.end());
688 DCHECK(std::all_of(x_vars.begin(), x_vars.end(), [](IntegerVariable
var) {
689 return VariableIsPositive(var);
692 std::vector<std::vector<IntegerValue>> sum_of_max_corner_diff(
693 num_exprs, std::vector<IntegerValue>(num_exprs, IntegerValue(0)));
696 for (
int i = 0; i < num_exprs; ++i) {
697 for (
int j = 0; j < num_exprs; ++j) {
698 if (i == j)
continue;
699 for (
const IntegerVariable x_var : x_vars) {
702 const IntegerValue diff =
704 sum_of_max_corner_diff[i][j] +=
std::max(diff * lb, diff * ub);
708 for (
int i = 0; i < num_exprs; ++i) {
710 lc.
AddTerm(target, IntegerValue(1));
711 for (
int j = 0; j < exprs[i].vars.size(); ++j) {
712 lc.
AddTerm(exprs[i].vars[j], -exprs[i].coeffs[j]);
714 for (
int j = 0; j < num_exprs; ++j) {
716 -exprs[j].offset - sum_of_max_corner_diff[i][j]));
726 const int linearization_level,
738 const IntegerValue rhs_domain_min =
739 IntegerValue(constraint_proto.linear().domain(0));
740 const IntegerValue rhs_domain_max =
741 IntegerValue(constraint_proto.linear().domain(
742 constraint_proto.linear().domain_size() - 1));
747 for (
int i = 0; i < constraint_proto.linear().vars_size(); i++) {
748 const int ref = constraint_proto.linear().vars(i);
749 const int64 coeff = constraint_proto.linear().coeffs(i);
750 lc.
AddTerm(mapping->Integer(ref), IntegerValue(coeff));
757 if (linearization_level < 2)
return;
761 if (!mapping->IsHalfEncodingConstraint(&constraint_proto) &&
762 constraint_proto.linear().vars_size() <= 1) {
766 std::vector<Literal> enforcing_literals;
767 enforcing_literals.reserve(constraint_proto.enforcement_literal_size());
768 for (
const int enforcement_ref : constraint_proto.enforcement_literal()) {
769 enforcing_literals.push_back(mapping->Literal(enforcement_ref));
772 expr.
vars.reserve(constraint_proto.linear().vars_size());
773 expr.
coeffs.reserve(constraint_proto.linear().vars_size());
774 for (
int i = 0; i < constraint_proto.linear().vars_size(); i++) {
775 int ref = constraint_proto.linear().vars(i);
776 IntegerValue coeff(constraint_proto.linear().coeffs(i));
781 const IntegerVariable int_var = mapping->Integer(ref);
782 expr.
vars.push_back(int_var);
783 expr.
coeffs.push_back(coeff);
785 AppendEnforcedLinearExpression(enforcing_literals, expr, rhs_domain_min,
786 rhs_domain_max,
model, relaxation);