20 #include "absl/container/flat_hash_set.h"
38 if (encoder ==
nullptr)
return false;
39 if (!encoder->VariableIsFullyEncoded(
var))
return false;
50 std::vector<Literal> at_most_one;
52 for (
const auto value_literal : encoding) {
53 const Literal lit = value_literal.literal;
54 const IntegerValue
delta = value_literal.value - var_min;
56 at_most_one.push_back(lit);
57 if (!at_least_one.
AddLiteralTerm(lit, IntegerValue(1)))
return false;
58 if (
delta != IntegerValue(0)) {
72 std::pair<IntegerValue, IntegerValue> GetMinAndMaxNotEncoded(
74 const absl::flat_hash_set<IntegerValue>& encoded_values,
76 const auto* domains =
model.Get<IntegerDomains>();
77 if (domains ==
nullptr ||
var >= domains->size()) {
84 for (
const ClosedInterval
interval : (*domains)[
var]) {
95 const auto& domain = (*domains)[
var];
96 for (
int i = domain.NumIntervals() - 1; i >= 0; --i) {
97 const ClosedInterval
interval = domain[i];
116 if (encoder ==
nullptr || integer_trail ==
nullptr)
return;
118 const std::vector<IntegerEncoder::ValueLiteralPair>& encoding =
119 encoder->PartialDomainEncoding(
var);
120 if (encoding.empty())
return;
122 std::vector<Literal> at_most_one_ct;
123 absl::flat_hash_set<IntegerValue> encoded_values;
124 for (
const auto value_literal : encoding) {
133 at_most_one_ct.push_back(
literal);
134 encoded_values.insert(value_literal.value);
136 if (encoded_values.empty())
return;
142 const auto pair = GetMinAndMaxNotEncoded(
var, encoded_values,
model);
152 for (
const auto value_literal : encoding) {
153 const Literal lit = value_literal.literal;
156 encoding_ct.
AddLiteralTerm(lit, IntegerValue(-value_literal.value)));
164 const IntegerValue d_min = pair.first;
167 for (
const auto value_literal : encoding) {
169 d_min - value_literal.value));
173 const IntegerValue d_max = pair.second;
176 for (
const auto value_literal : encoding) {
178 d_max - value_literal.value));
192 if (integer_trail ==
nullptr || encoder ==
nullptr)
return;
194 const std::map<IntegerValue, Literal>& greater_than_encoding =
195 encoder->PartialGreaterThanEncoding(
var);
196 if (greater_than_encoding.empty())
return;
201 IntegerValue prev_used_bound = integer_trail->
LowerBound(
var);
206 for (
const auto entry : greater_than_encoding) {
207 if (entry.first <= prev_used_bound)
continue;
209 const LiteralIndex literal_index = entry.second.Index();
210 const IntegerValue diff = prev_used_bound - entry.first;
219 prev_used_bound = entry.first;
220 prev_literal_index = literal_index;
228 IntegerValue prev_used_bound = integer_trail->LowerBound(
NegationOf(
var));
232 for (
const auto entry :
234 if (entry.first <= prev_used_bound)
continue;
235 const IntegerValue diff = prev_used_bound - entry.first;
239 prev_used_bound = entry.first;
247 void AppendEnforcedUpperBound(
const Literal enforcing_lit,
248 const IntegerVariable target,
249 const IntegerVariable bounding_var, Model*
model,
250 LinearRelaxation* relaxation) {
251 IntegerTrail* integer_trail =
model->GetOrCreate<IntegerTrail>();
252 const IntegerValue max_target_value = integer_trail->UpperBound(target);
253 const IntegerValue min_var_value = integer_trail->LowerBound(bounding_var);
254 const IntegerValue max_term_value = max_target_value - min_var_value;
256 lc.AddTerm(target, IntegerValue(1));
257 lc.AddTerm(bounding_var, IntegerValue(-1));
258 CHECK(lc.AddLiteralTerm(enforcing_lit, max_term_value));
259 relaxation->linear_constraints.push_back(lc.Build());
264 void AppendEnforcedLinearExpression(
265 const std::vector<Literal>& enforcing_literals,
266 const LinearExpression& expr,
const IntegerValue rhs_domain_min,
267 const IntegerValue rhs_domain_max,
const Model&
model,
268 LinearRelaxation* relaxation) {
269 CHECK_EQ(expr.offset, IntegerValue(0));
271 const IntegerTrail* integer_trail =
model.Get<IntegerTrail>();
272 const IntegerValue min_expr_value =
275 if (rhs_domain_min > min_expr_value) {
280 for (
const Literal&
literal : enforcing_literals) {
282 rhs_domain_min - min_expr_value));
284 for (
int i = 0; i < canonical_expr.vars.size(); i++) {
285 lc.AddTerm(canonical_expr.vars[i], canonical_expr.coeffs[i]);
287 relaxation->linear_constraints.push_back(lc.Build());
289 const IntegerValue max_expr_value =
291 if (rhs_domain_max < max_expr_value) {
296 for (
const Literal&
literal : enforcing_literals) {
298 rhs_domain_max - max_expr_value));
300 for (
int i = 0; i < canonical_expr.vars.size(); i++) {
301 lc.AddTerm(canonical_expr.vars[i], canonical_expr.coeffs[i]);
303 relaxation->linear_constraints.push_back(lc.Build());
307 bool AllLiteralsHaveViews(
const IntegerEncoder& encoder,
308 const std::vector<Literal>& literals) {
309 for (
const Literal lit : literals) {
310 if (!encoder.LiteralOrNegationHasView(lit))
return false;
327 int linearization_level,
333 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr) {
334 if (linearization_level < 2)
return;
336 for (
const int enforcement_ref :
ct.enforcement_literal()) {
340 for (
const int ref :
ct.bool_or().literals()) {
344 }
else if (
ct.constraint_case() ==
345 ConstraintProto::ConstraintCase::kBoolAnd) {
349 if (linearization_level < 2)
return;
351 if (
ct.enforcement_literal().size() == 1) {
353 for (
const int ref :
ct.bool_and().literals()) {
355 {enforcement, mapping->
Literal(ref).Negated()});
362 int num_literals =
ct.bool_and().literals_size();
365 for (
const int ref :
ct.bool_and().literals()) {
368 for (
const int enforcement_ref :
ct.enforcement_literal()) {
370 IntegerValue(num_literals)));
373 }
else if (
ct.constraint_case() ==
374 ConstraintProto::ConstraintCase::kAtMostOne) {
377 mapping->Literals(
ct.at_most_one().literals()));
378 }
else if (
ct.constraint_case() ==
379 ConstraintProto::ConstraintCase::kExactlyOne) {
381 const std::vector<Literal> literals =
382 mapping->Literals(
ct.exactly_one().literals());
383 if (linearization_level >= 2 || AllLiteralsHaveViews(encoder, literals)) {
385 for (
const Literal lit : literals) {
389 }
else if (linearization_level == 1) {
394 }
else if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kIntMax) {
396 const IntegerVariable target = mapping->Integer(
ct.int_max().target());
397 const std::vector<IntegerVariable> vars =
398 mapping->Integers(
ct.int_max().vars());
401 }
else if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kIntMin) {
403 const IntegerVariable negative_target =
405 const std::vector<IntegerVariable> negative_vars =
409 }
else if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
412 }
else if (
ct.constraint_case() ==
413 ConstraintProto::ConstraintCase::kCircuit) {
415 const int num_arcs =
ct.circuit().literals_size();
416 CHECK_EQ(num_arcs,
ct.circuit().tails_size());
417 CHECK_EQ(num_arcs,
ct.circuit().heads_size());
421 std::map<int, std::vector<Literal>> incoming_arc_constraints;
422 std::map<int, std::vector<Literal>> outgoing_arc_constraints;
423 for (
int i = 0; i < num_arcs; i++) {
425 const int tail =
ct.circuit().tails(i);
426 const int head =
ct.circuit().heads(i);
430 outgoing_arc_constraints[
tail].push_back(arc);
431 incoming_arc_constraints[
head].push_back(arc);
433 for (
const auto* node_map :
434 {&outgoing_arc_constraints, &incoming_arc_constraints}) {
435 for (
const auto& entry : *node_map) {
436 const std::vector<Literal>& exactly_one = entry.second;
437 if (exactly_one.size() > 1) {
440 for (
const Literal l : exactly_one) {
450 }
else if (
ct.constraint_case() ==
451 ConstraintProto::ConstraintCase::kElement) {
452 const IntegerVariable
index = mapping->Integer(
ct.element().index());
453 const IntegerVariable target = mapping->Integer(
ct.element().target());
454 const std::vector<IntegerVariable> vars =
455 mapping->Integers(
ct.element().vars());
460 constraint.
AddTerm(target, IntegerValue(-1));
463 const IntegerVariable
var = vars[literal_value.value.value()];
473 }
else if (
ct.constraint_case() ==
474 ConstraintProto::ConstraintCase::kInterval) {
477 if (linearization_level < 2)
return;
478 if (
ct.interval().has_start_view())
return;
479 const IntegerVariable start = mapping->Integer(
ct.interval().start());
480 const IntegerVariable size = mapping->Integer(
ct.interval().size());
481 const IntegerVariable end = mapping->Integer(
ct.interval().end());
483 const bool size_is_fixed = integer_trail->
IsFixed(size);
484 const IntegerValue rhs =
485 size_is_fixed ? -integer_trail->
LowerBound(size) : IntegerValue(0);
487 lc.
AddTerm(start, IntegerValue(1));
488 if (!size_is_fixed) {
489 lc.
AddTerm(size, IntegerValue(1));
491 lc.
AddTerm(end, IntegerValue(-1));
497 AppendEnforcedLinearExpression(
498 mapping->Literals(
ct.enforcement_literal()), expr, tmp_lc.
ub,
499 tmp_lc.
ub, *
model, relaxation);
503 }
else if (
ct.constraint_case() ==
504 ConstraintProto::ConstraintCase::kNoOverlap) {
507 }
else if (
ct.constraint_case() ==
508 ConstraintProto::ConstraintCase::kCumulative) {
516 const std::vector<IntegerVariable>& demands,
517 IntegerValue capacity_upper_bound,
Model*
model,
522 model->TakeOwnership(helper);
523 const int num_intervals = helper->NumTasks();
530 int num_variable_sizes = 0;
531 int num_optionals = 0;
534 min_of_starts =
std::min(min_of_starts, helper->StartMin(
index));
537 if (helper->IsOptional(
index)) {
541 if (!helper->SizeIsFixed(
index) ||
542 (!demands.empty() && !integer_trail->
IsFixed(demands[
index]))) {
543 num_variable_sizes++;
547 VLOG(2) <<
"Span [" << min_of_starts <<
".." << max_of_ends <<
"] with "
548 << num_optionals <<
" optional intervals, and " << num_variable_sizes
549 <<
" variable size intervals out of " << num_intervals
552 if (num_variable_sizes + num_optionals == 0)
return;
554 const IntegerVariable span_start =
557 IntegerValue(0), max_of_ends - min_of_starts);
558 const IntegerVariable span_end =
561 IntervalVariable span_var;
562 if (num_optionals < num_intervals) {
566 span_var =
model->Add(
573 lc.
AddTerm(span_size, -capacity_upper_bound);
574 for (
int i = 0; i < num_intervals; ++i) {
575 const IntegerValue demand_lower_bound =
576 demands.empty() ? IntegerValue(1)
578 const bool demand_is_fixed =
579 demands.empty() || integer_trail->
IsFixed(demands[i]);
580 if (!helper->IsOptional(i)) {
581 if (helper->SizeIsFixed(i) && !demands.empty()) {
582 lc.
AddTerm(demands[i], helper->SizeMin(i));
583 }
else if (demand_is_fixed) {
584 lc.
AddTerm(helper->Sizes()[i], demand_lower_bound);
593 lc.
AddTerm(helper->Sizes()[i], demand_lower_bound);
594 lc.
AddTerm(demands[i], helper->SizeMin(i));
595 lc.
AddConstant(-helper->SizeMin(i) * demand_lower_bound);
599 helper->SizeMin(i) * demand_lower_bound)) {
608 const ConstraintProto&
ct,
612 if (linearization_level < 2)
return;
616 const std::vector<IntegerVariable> demands =
618 std::vector<IntervalVariable> intervals =
619 mapping->Intervals(
ct.cumulative().intervals());
620 const IntegerValue capacity_upper_bound =
622 mapping->Integer(
ct.cumulative().capacity()));
627 const ConstraintProto&
ct,
631 if (linearization_level < 2)
return;
635 std::vector<IntervalVariable> intervals =
638 IntegerValue(1),
model, relaxation);
642 const std::vector<IntegerVariable>& vars,
647 for (
const IntegerVariable
var : vars) {
650 if (target ==
var)
continue;
653 lc.
AddTerm(target, IntegerValue(-1));
658 if (linearization_level < 2)
return;
662 if (vars.size() == 2) {
665 encoder->GetOrCreateLiteralAssociatedToEquality(y, IntegerValue(1));
666 AppendEnforcedUpperBound(y_lit, target, vars[0],
model, relaxation);
671 {y_lit}, {target, vars[0]}, {IntegerValue(1), IntegerValue(-1)},
672 IntegerValue(0),
model);
674 model->TakeOwnership(upper_bound1);
675 AppendEnforcedUpperBound(y_lit.
Negated(), target, vars[1],
model,
678 {y_lit.
Negated()}, {target, vars[1]},
679 {IntegerValue(1), IntegerValue(-1)}, IntegerValue(0),
model);
681 model->TakeOwnership(upper_bound2);
690 std::vector<Literal> exactly_one_literals;
691 exactly_one_literals.reserve(vars.size());
692 for (
const IntegerVariable
var : vars) {
693 if (target ==
var)
continue;
699 encoder->GetOrCreateLiteralAssociatedToEquality(y, IntegerValue(1));
701 AppendEnforcedUpperBound(y_lit, target,
var,
model, relaxation);
703 {y_lit}, {target,
var}, {IntegerValue(1), IntegerValue(-1)},
704 IntegerValue(0),
model);
706 model->TakeOwnership(upper_bound_constraint);
707 exactly_one_literals.push_back(y_lit);
716 IntegerVariable target,
const std::vector<LinearExpression>& exprs,
722 for (
int i = 0; i < expr.vars.size(); ++i) {
723 lc.
AddTerm(expr.vars[i], expr.coeffs[i]);
725 lc.
AddTerm(target, IntegerValue(-1));
733 const int num_exprs = exprs.size();
738 std::vector<IntegerVariable> z_vars;
739 std::vector<Literal> z_lits;
740 z_vars.reserve(num_exprs);
741 z_lits.reserve(num_exprs);
744 std::vector<Literal> exactly_one_literals;
745 for (
int i = 0; i < num_exprs; ++i) {
750 z_lits.push_back(z_lit);
753 local_expr.
vars.push_back(target);
754 local_expr.
coeffs = exprs[i].coeffs;
755 local_expr.
coeffs.push_back(IntegerValue(1));
769 std::vector<IntegerVariable> x_vars;
770 for (
int i = 0; i < num_exprs; ++i) {
771 x_vars.insert(x_vars.end(), exprs[i].vars.begin(), exprs[i].vars.end());
775 DCHECK(std::all_of(x_vars.begin(), x_vars.end(), [](IntegerVariable
var) {
776 return VariableIsPositive(var);
779 std::vector<std::vector<IntegerValue>> sum_of_max_corner_diff(
780 num_exprs, std::vector<IntegerValue>(num_exprs, IntegerValue(0)));
783 for (
int i = 0; i < num_exprs; ++i) {
784 for (
int j = 0; j < num_exprs; ++j) {
785 if (i == j)
continue;
786 for (
const IntegerVariable x_var : x_vars) {
789 const IntegerValue diff =
791 sum_of_max_corner_diff[i][j] +=
std::max(diff * lb, diff * ub);
795 for (
int i = 0; i < num_exprs; ++i) {
797 lc.
AddTerm(target, IntegerValue(1));
798 for (
int j = 0; j < exprs[i].vars.size(); ++j) {
799 lc.
AddTerm(exprs[i].vars[j], -exprs[i].coeffs[j]);
801 for (
int j = 0; j < num_exprs; ++j) {
803 -exprs[j].offset - sum_of_max_corner_diff[i][j]));
813 const int linearization_level,
825 const IntegerValue rhs_domain_min =
826 IntegerValue(constraint_proto.linear().domain(0));
827 const IntegerValue rhs_domain_max =
828 IntegerValue(constraint_proto.linear().domain(
829 constraint_proto.linear().domain_size() - 1));
836 for (
int i = 0; i < constraint_proto.linear().vars_size(); i++) {
837 const int ref = constraint_proto.linear().vars(i);
838 const int64_t coeff = constraint_proto.linear().coeffs(i);
839 lc.
AddTerm(mapping->Integer(ref), IntegerValue(coeff));
846 if (linearization_level < 2)
return;
850 if (!mapping->IsHalfEncodingConstraint(&constraint_proto) &&
851 constraint_proto.linear().vars_size() <= 1) {
855 std::vector<Literal> enforcing_literals;
856 enforcing_literals.reserve(constraint_proto.enforcement_literal_size());
857 for (
const int enforcement_ref : constraint_proto.enforcement_literal()) {
858 enforcing_literals.push_back(mapping->Literal(enforcement_ref));
861 expr.
vars.reserve(constraint_proto.linear().vars_size());
862 expr.
coeffs.reserve(constraint_proto.linear().vars_size());
863 for (
int i = 0; i < constraint_proto.linear().vars_size(); i++) {
864 int ref = constraint_proto.linear().vars(i);
865 IntegerValue coeff(constraint_proto.linear().coeffs(i));
870 const IntegerVariable int_var = mapping->Integer(ref);
871 expr.
vars.push_back(int_var);
872 expr.
coeffs.push_back(coeff);
874 AppendEnforcedLinearExpression(enforcing_literals, expr, rhs_domain_min,
875 rhs_domain_max,
model, relaxation);
#define CHECK_EQ(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK_GT(val1, val2)
#define DCHECK(condition)
#define VLOG(verboselevel)
std::vector< IntegerVariable > Integers(const List &list) const
std::vector< IntervalVariable > Intervals(const ProtoIndices &indices) const
Literal GetOrCreateLiteralAssociatedToEquality(IntegerVariable var, IntegerValue value)
std::vector< ValueLiteralPair > FullDomainEncoding(IntegerVariable var) const
void RegisterWith(GenericLiteralWatcher *watcher)
bool IsFixed(IntegerVariable i) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerVariable AddIntegerVariable(IntegerValue lower_bound, IntegerValue upper_bound)
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
IntegerValue LowerBound(IntegerVariable i) const
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff)
void AddConstant(IntegerValue value)
void AddTerm(IntegerVariable var, IntegerValue coeff)
Literal(int signed_value)
Class that owns everything related to a particular optimization model.
int CurrentDecisionLevel() const
CpModelProto const * model_proto
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
bool ContainsKey(const Collection &collection, const Key &key)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::vector< IntegerVariable > AppendLinMaxRelaxation(IntegerVariable target, const std::vector< LinearExpression > &exprs, Model *model, LinearRelaxation *relaxation)
IntegerValue LinExprLowerBound(const LinearExpression &expr, const IntegerTrail &integer_trail)
std::function< IntervalVariable(Model *)> NewInterval(int64_t min_start, int64_t max_end, int64_t size)
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
void AppendLinearConstraintRelaxation(const ConstraintProto &constraint_proto, const int linearization_level, const Model &model, LinearRelaxation *relaxation)
bool RefIsPositive(int ref)
const LiteralIndex kNoLiteralIndex(-1)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
std::function< BooleanVariable(Model *)> NewBooleanVariable()
bool HasEnforcementLiteral(const ConstraintProto &ct)
std::function< std::vector< IntegerEncoder::ValueLiteralPair >Model *)> FullyEncodeVariable(IntegerVariable var)
bool AppendFullEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
const IntegerVariable kNoIntegerVariable(-1)
LinearExpression CanonicalizeExpr(const LinearExpression &expr)
void AppendMaxRelaxation(IntegerVariable target, const std::vector< IntegerVariable > &vars, int linearization_level, Model *model, LinearRelaxation *relaxation)
std::function< IntegerVariable(Model *)> NewIntegerVariableFromLiteral(Literal lit)
IntegerValue GetCoefficient(const IntegerVariable var, const LinearExpression &expr)
void TryToLinearizeConstraint(const CpModelProto &model_proto, const ConstraintProto &ct, Model *model, int linearization_level, LinearRelaxation *relaxation)
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
void AppendPartialEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
std::function< void(Model *)> SpanOfIntervals(IntervalVariable span, const std::vector< IntervalVariable > &intervals)
void AddCumulativeCut(const std::vector< IntervalVariable > &intervals, const std::vector< IntegerVariable > &demands, IntegerValue capacity_upper_bound, Model *model, LinearRelaxation *relaxation)
void AppendNoOverlapRelaxation(const CpModelProto &model_proto, const ConstraintProto &ct, int linearization_level, Model *model, LinearRelaxation *relaxation)
std::function< bool(const Model &)> IsFixed(IntegerVariable v)
std::function< IntervalVariable(Model *)> NewOptionalInterval(int64_t min_start, int64_t max_end, int64_t size, Literal is_present)
IntegerValue LinExprUpperBound(const LinearExpression &expr, const IntegerTrail &integer_trail)
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
void AppendCumulativeRelaxation(const CpModelProto &model_proto, const ConstraintProto &ct, int linearization_level, Model *model, LinearRelaxation *relaxation)
void AppendPartialGreaterThanEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
Collection of objects used to extend the Constraint Solver library.
std::vector< IntegerValue > coeffs
std::vector< IntegerVariable > vars
std::vector< IntegerValue > coeffs
std::vector< IntegerVariable > vars
std::vector< std::vector< Literal > > at_most_ones
std::vector< LinearConstraint > linear_constraints