23 #include "absl/base/attributes.h"
24 #include "absl/container/btree_map.h"
25 #include "absl/container/flat_hash_set.h"
31 #include "ortools/sat/cp_model.pb.h"
45 #include "ortools/sat/sat_parameters.pb.h"
59 if (encoder ==
nullptr)
return false;
60 if (!encoder->VariableIsFullyEncoded(
var))
return false;
71 std::vector<Literal> at_most_one;
73 for (
const auto value_literal : encoding) {
74 const Literal lit = value_literal.literal;
75 const IntegerValue
delta = value_literal.value - var_min;
76 DCHECK_GE(
delta, IntegerValue(0));
77 at_most_one.push_back(lit);
78 if (!at_least_one.
AddLiteralTerm(lit, IntegerValue(1)))
return false;
79 if (
delta != IntegerValue(0)) {
92 std::pair<IntegerValue, IntegerValue> GetMinAndMaxNotEncoded(
94 const absl::flat_hash_set<IntegerValue>& encoded_values,
99 const auto* domains =
model.Get<IntegerDomains>();
100 if (domains ==
nullptr ||
index >= domains->size()) {
107 for (
const int64_t v : (*domains)[
index].Values()) {
108 if (!encoded_values.contains(IntegerValue(v))) {
109 min = IntegerValue(v);
115 const Domain negated_domain = (*domains)[
index].Negation();
116 for (
const int64_t v : negated_domain.Values()) {
117 if (!encoded_values.contains(IntegerValue(-v))) {
118 max = IntegerValue(-v);
126 bool LinMaxContainsOnlyOneVarInExpressions(
const ConstraintProto&
ct) {
127 CHECK_EQ(
ct.constraint_case(), ConstraintProto::ConstraintCase::kLinMax);
128 int current_var = -1;
129 for (
const LinearExpressionProto& expr :
ct.lin_max().exprs()) {
130 if (expr.vars().empty())
continue;
131 if (expr.vars().size() > 1)
return false;
133 if (current_var == -1) {
135 }
else if (
var != current_var) {
147 void CollectAffineExpressionWithSingleVariable(
148 const ConstraintProto&
ct, CpModelMapping* mapping, IntegerVariable*
var,
149 std::vector<std::pair<IntegerValue, IntegerValue>>* affines) {
150 DCHECK(LinMaxContainsOnlyOneVarInExpressions(
ct));
151 CHECK_EQ(
ct.constraint_case(), ConstraintProto::ConstraintCase::kLinMax);
154 for (
const LinearExpressionProto& expr :
ct.lin_max().exprs()) {
155 if (expr.vars().empty()) {
156 affines->push_back({IntegerValue(0), IntegerValue(expr.offset())});
158 CHECK_EQ(expr.vars().size(), 1);
159 const IntegerVariable affine_var = mapping->Integer(expr.vars(0));
164 CHECK_EQ(affine_var, *
var);
166 {IntegerValue(expr.coeffs(0)), IntegerValue(expr.offset())});
170 {IntegerValue(-expr.coeffs(0)), IntegerValue(expr.offset())});
181 int* num_tight,
int* num_loose) {
184 if (encoder ==
nullptr || integer_trail ==
nullptr)
return;
186 std::vector<Literal> at_most_one_ct;
187 absl::flat_hash_set<IntegerValue> encoded_values;
188 std::vector<ValueLiteralPair> encoding;
190 const std::vector<ValueLiteralPair>& initial_encoding =
191 encoder->PartialDomainEncoding(
var);
192 if (initial_encoding.empty())
return;
193 for (
const auto value_literal : initial_encoding) {
202 encoding.push_back(value_literal);
203 at_most_one_ct.push_back(
literal);
204 encoded_values.insert(value_literal.value);
207 if (encoded_values.empty())
return;
214 const auto [min_not_encoded, max_not_encoded] =
215 GetMinAndMaxNotEncoded(
var, encoded_values,
model);
220 const IntegerValue rhs = encoding[0].value;
225 for (
const auto value_literal : encoding) {
226 const Literal lit = value_literal.literal;
229 const IntegerValue
delta = value_literal.value - rhs;
230 if (
delta != IntegerValue(0)) {
231 CHECK_GE(
delta, IntegerValue(0));
245 if (min_not_encoded == max_not_encoded) {
246 const IntegerValue rhs = min_not_encoded;
249 for (
const auto value_literal : encoding) {
251 rhs - value_literal.value));
260 const IntegerValue d_min = min_not_encoded;
263 for (
const auto value_literal : encoding) {
265 d_min - value_literal.value));
269 const IntegerValue d_max = max_not_encoded;
272 for (
const auto value_literal : encoding) {
274 d_max - value_literal.value));
289 if (integer_trail ==
nullptr || encoder ==
nullptr)
return;
291 const auto& greater_than_encoding = encoder->PartialGreaterThanEncoding(
var);
292 if (greater_than_encoding.empty())
return;
297 IntegerValue prev_used_bound = integer_trail->
LowerBound(
var);
302 for (
const auto entry : greater_than_encoding) {
303 if (entry.value <= prev_used_bound)
continue;
305 const LiteralIndex literal_index = entry.literal.Index();
306 const IntegerValue diff = prev_used_bound - entry.value;
315 prev_used_bound = entry.value;
316 prev_literal_index = literal_index;
324 IntegerValue prev_used_bound = integer_trail->LowerBound(
NegationOf(
var));
328 for (
const auto entry :
330 if (entry.value <= prev_used_bound)
continue;
331 const IntegerValue diff = prev_used_bound - entry.value;
335 prev_used_bound = entry.value;
343 bool AllLiteralsHaveViews(
const IntegerEncoder& encoder,
344 const std::vector<Literal>& literals) {
345 for (
const Literal lit : literals) {
346 if (!encoder.LiteralOrNegationHasView(lit))
return false;
357 for (
const int enforcement_ref :
ct.enforcement_literal()) {
358 CHECK(lc.AddLiteralTerm(mapping->Literal(
NegatedRef(enforcement_ref)),
361 for (
const int ref :
ct.bool_or().literals()) {
362 CHECK(lc.AddLiteralTerm(mapping->Literal(ref), IntegerValue(1)));
379 if (
ct.enforcement_literal().size() == 1) {
381 for (
const int ref :
ct.bool_and().literals()) {
383 {enforcement, mapping->
Literal(ref).Negated()});
404 if (activity_helper !=
nullptr) {
405 std::vector<int> negated_lits;
406 for (
const int ref :
ct.bool_and().literals()) {
409 for (absl::Span<const int> part :
412 for (
const int negated_ref : part) {
415 for (
const int enforcement_ref :
ct.enforcement_literal()) {
417 mapping->Literal(
NegatedRef(enforcement_ref)), IntegerValue(-1)));
423 for (
const int ref :
ct.bool_and().literals()) {
426 for (
const int enforcement_ref :
ct.enforcement_literal()) {
428 mapping->Literal(
NegatedRef(enforcement_ref)), IntegerValue(-1)));
442 mapping->Literals(
ct.at_most_one().literals()));
451 const std::vector<Literal> literals =
452 mapping->Literals(
ct.exactly_one().literals());
453 if (AllLiteralsHaveViews(*encoder, literals)) {
455 for (
const Literal lit : literals) {
470 if (num_literals == 1) {
475 encoder->GetOrCreateLiteralAssociatedToEquality(
var, IntegerValue(1));
479 if (num_literals == 2) {
482 encoder->GetOrCreateLiteralAssociatedToEquality(
var, IntegerValue(1));
488 encoder->AssociateToIntegerEqualValue(lit.
Negated(), var2, IntegerValue(1));
493 std::vector<Literal> literals;
495 for (
int i = 0; i < num_literals; ++i) {
498 encoder->GetOrCreateLiteralAssociatedToEquality(
var, IntegerValue(1));
499 literals.push_back(lit);
511 const int num_arcs =
ct.circuit().literals_size();
512 CHECK_EQ(num_arcs,
ct.circuit().tails_size());
513 CHECK_EQ(num_arcs,
ct.circuit().heads_size());
517 absl::btree_map<int, std::vector<Literal>> incoming_arc_constraints;
518 absl::btree_map<int, std::vector<Literal>> outgoing_arc_constraints;
519 for (
int i = 0; i < num_arcs; i++) {
520 const Literal arc = mapping->Literal(
ct.circuit().literals(i));
521 const int tail =
ct.circuit().tails(i);
522 const int head =
ct.circuit().heads(i);
526 outgoing_arc_constraints[
tail].push_back(
arc);
527 incoming_arc_constraints[
head].push_back(
arc);
529 for (
const auto* node_map :
530 {&outgoing_arc_constraints, &incoming_arc_constraints}) {
531 for (
const auto& entry : *node_map) {
532 const std::vector<Literal>& exactly_one = entry.second;
533 if (exactly_one.size() > 1) {
536 for (
const Literal l : exactly_one) {
552 const int num_arcs =
ct.routes().literals_size();
553 CHECK_EQ(num_arcs,
ct.routes().tails_size());
554 CHECK_EQ(num_arcs,
ct.routes().heads_size());
560 absl::btree_map<int, std::vector<Literal>> incoming_arc_constraints;
561 absl::btree_map<int, std::vector<Literal>> outgoing_arc_constraints;
562 for (
int i = 0; i < num_arcs; i++) {
563 const Literal arc = mapping->Literal(
ct.routes().literals(i));
564 const int tail =
ct.routes().tails(i);
565 const int head =
ct.routes().heads(i);
569 outgoing_arc_constraints[
tail].push_back(
arc);
570 incoming_arc_constraints[
head].push_back(
arc);
572 for (
const auto* node_map :
573 {&outgoing_arc_constraints, &incoming_arc_constraints}) {
574 for (
const auto& entry : *node_map) {
575 if (entry.first == 0)
continue;
576 const std::vector<Literal>& exactly_one = entry.second;
577 if (exactly_one.size() > 1) {
580 for (
const Literal l : exactly_one) {
592 for (
const Literal& incoming_arc : incoming_arc_constraints[0]) {
593 CHECK(zero_node_balance_lc.
AddLiteralTerm(incoming_arc, IntegerValue(1)));
595 for (
const Literal& outgoing_arc : outgoing_arc_constraints[0]) {
596 CHECK(zero_node_balance_lc.
AddLiteralTerm(outgoing_arc, IntegerValue(-1)));
603 std::vector<int> tails(
ct.circuit().tails().begin(),
604 ct.circuit().tails().end());
605 std::vector<int> heads(
ct.circuit().heads().begin(),
606 ct.circuit().heads().end());
608 std::vector<Literal> literals = mapping->
Literals(
ct.circuit().literals());
612 num_nodes, tails, heads, literals, m));
617 std::vector<int> tails(
ct.routes().tails().begin(),
618 ct.routes().tails().end());
619 std::vector<int> heads(
ct.routes().heads().begin(),
620 ct.routes().heads().end());
622 std::vector<Literal> literals = mapping->
Literals(
ct.routes().literals());
625 for (
int i = 0; i <
ct.routes().tails_size(); ++i) {
626 num_nodes =
std::max(num_nodes, 1 +
ct.routes().tails(i));
627 num_nodes =
std::max(num_nodes, 1 +
ct.routes().heads(i));
629 if (
ct.routes().demands().empty() ||
ct.routes().capacity() == 0) {
634 const std::vector<int64_t> demands(
ct.routes().demands().begin(),
635 ct.routes().demands().end());
637 num_nodes, tails, heads, literals, demands,
ct.routes().capacity(), m));
657 const std::vector<AffineExpression>& demands,
669 for (
int i = 0; i < intervals.size(); ++i) {
670 if (repository->IsAbsent(intervals[i]))
continue;
672 horizon, integer_trail->
UpperBound(repository->End(intervals[i])));
676 for (
int i = 0; i < intervals.size(); ++i) {
677 if (repository->IsAbsent(intervals[i]))
continue;
679 if (integer_trail->
IsFixed(demands[i]) &&
680 integer_trail->
FixedValue(demands[i]) == capacity_value &&
683 integer_trail->
LowerBound(repository->Size(intervals[i])) > 0 &&
684 repository->IsPresent(intervals[i])) {
696 std::vector<IntervalVariable> intervals =
698 const IntegerValue one(1);
699 std::vector<AffineExpression> demands(intervals.size(), one);
700 const int makespan_index =
702 std::optional<AffineExpression> makespan;
705 if (makespan_index != -1) {
706 makespan = repository->
Start(intervals[makespan_index]);
708 intervals.erase(intervals.begin() + makespan_index);
716 model->TakeOwnership(demands_helper);
720 if (
model->GetOrCreate<SatParameters>()->linearization_level() > 1) {
730 std::vector<IntervalVariable> intervals =
732 std::vector<AffineExpression> demands =
733 mapping->Affines(
ct.cumulative().demands());
735 const int makespan_index =
737 std::optional<AffineExpression> makespan;
739 if (makespan_index != -1) {
741 makespan = repository->
Start(intervals[makespan_index]);
742 demands.erase(demands.begin() + makespan_index);
743 intervals.erase(intervals.begin() + makespan_index);
751 model->TakeOwnership(demands_helper);
756 if (
model->GetOrCreate<SatParameters>()->linearization_level() > 1) {
768 const std::optional<AffineExpression>& makespan,
770 const int num_intervals = helper->
NumTasks();
773 std::vector<Literal> presence_literals;
774 std::vector<AffineExpression> starts;
775 std::vector<AffineExpression> ends;
776 std::vector<Literal> clause;
777 std::vector<int> active_interval_indices;
778 bool at_least_one_interval_is_present =
false;
781 int num_variable_energies = 0;
782 int num_optionals = 0;
791 presence_literals.push_back(task_lit);
792 clause.push_back(task_lit);
794 at_least_one_interval_is_present =
true;
795 presence_literals.push_back(
798 active_interval_indices.push_back(
index);
804 num_variable_energies++;
811 VLOG(2) <<
"Span [" << min_of_starts <<
".." << max_of_ends <<
"] with "
812 << num_optionals <<
" optional intervals, and "
813 << num_variable_energies <<
" variable energy tasks out of "
814 << num_intervals <<
" intervals";
818 if (num_variable_energies + num_optionals == 0)
return;
821 for (
const int i : active_interval_indices) {
823 const IntegerValue energy_min = demands_helper->
EnergyMin(i);
824 DCHECK_GT(energy_min, 0);
830 const std::vector<LiteralValueValue>& product =
832 if (!product.empty()) {
840 demands_helper->
Demands()[i], integer_trail);
846 const Literal cumulative_is_not_empty =
847 at_least_one_interval_is_present
850 if (!at_least_one_interval_is_present) {
851 for (
const Literal task_lit : clause) {
852 sat_solver->AddBinaryClause(task_lit.Negated(), cumulative_is_not_empty);
854 clause.push_back(cumulative_is_not_empty.Negated());
855 sat_solver->AddProblemClause(clause,
false);
860 const IntegerVariable span_start =
863 starts, presence_literals));
869 if (!makespan.has_value()) {
871 ends, presence_literals));
882 CHECK(
ct.has_no_overlap_2d());
886 std::vector<IntervalVariable> x_intervals =
887 mapping->
Intervals(
ct.no_overlap_2d().x_intervals());
888 std::vector<IntervalVariable> y_intervals =
889 mapping->Intervals(
ct.no_overlap_2d().y_intervals());
898 std::vector<AffineExpression> x_sizes;
899 std::vector<AffineExpression> y_sizes;
900 for (
int i = 0; i <
ct.no_overlap_2d().x_intervals_size(); ++i) {
901 x_sizes.push_back(intervals_repository->Size(x_intervals[i]));
902 y_sizes.push_back(intervals_repository->Size(y_intervals[i]));
903 x_min =
std::min(x_min, integer_trail->LevelZeroLowerBound(
904 intervals_repository->Start(x_intervals[i])));
905 x_max =
std::max(x_max, integer_trail->LevelZeroUpperBound(
906 intervals_repository->End(x_intervals[i])));
907 y_min =
std::min(y_min, integer_trail->LevelZeroLowerBound(
908 intervals_repository->Start(y_intervals[i])));
909 y_max =
std::max(y_max, integer_trail->LevelZeroUpperBound(
910 intervals_repository->End(y_intervals[i])));
913 const IntegerValue max_area =
915 CapSub(y_max.value(), y_min.value())));
919 for (
int i = 0; i <
ct.no_overlap_2d().x_intervals_size(); ++i) {
920 if (intervals_repository->IsPresent(x_intervals[i]) &&
921 intervals_repository->IsPresent(y_intervals[i])) {
922 const std::vector<LiteralValueValue>
energy =
929 }
else if (intervals_repository->IsPresent(x_intervals[i]) ||
930 intervals_repository->IsPresent(y_intervals[i]) ||
931 (intervals_repository->PresenceLiteral(x_intervals[i]) ==
932 intervals_repository->PresenceLiteral(y_intervals[i]))) {
934 const Literal presence_literal =
935 intervals_repository->IsPresent(x_intervals[i])
936 ? intervals_repository->PresenceLiteral(y_intervals[i])
937 : intervals_repository->PresenceLiteral(x_intervals[i]);
938 const IntegerValue area_min =
940 integer_trail->LevelZeroLowerBound(y_sizes[i]);
957 NegationOf(mapping->GetExprFromProto(
ct.lin_max().target()));
958 for (
int i = 0; i <
ct.lin_max().exprs_size(); ++i) {
960 mapping->GetExprFromProto(
ct.lin_max().exprs(i));
975 std::vector<std::pair<IntegerValue, IntegerValue>> affines;
977 CollectAffineExpressionWithSingleVariable(
ct, mapping, &
var, &affines);
995 std::vector<std::pair<IntegerValue, IntegerValue>> affines;
997 CollectAffineExpressionWithSingleVariable(
ct, mapping, &
var, &affines);
1004 if (
ct.lin_max().target().vars().empty())
return;
1009 target_expr,
var, affines,
"AffineMax",
model));
1017 IntegerVariable target,
const std::vector<Literal>& alternative_literals,
1018 const std::vector<LinearExpression>& exprs,
Model*
model,
1020 const int num_exprs = exprs.size();
1024 for (
int i = 0; i < num_exprs; ++i) {
1027 local_expr.
vars.push_back(target);
1028 local_expr.
coeffs = exprs[i].coeffs;
1029 local_expr.
coeffs.push_back(IntegerValue(1));
1039 std::vector<std::vector<IntegerValue>> sum_of_max_corner_diff(
1040 num_exprs, std::vector<IntegerValue>(num_exprs, IntegerValue(0)));
1044 absl::flat_hash_map<std::pair<int, IntegerVariable>, IntegerValue> cache;
1045 for (
int i = 0; i < num_exprs; ++i) {
1046 for (
int j = 0; j < exprs[i].vars.size(); ++j) {
1047 cache[std::make_pair(i, exprs[i].vars[j])] = exprs[i].coeffs[j];
1050 const auto get_coeff = [&cache](IntegerVariable
var,
int index) {
1051 const auto it = cache.find(std::make_pair(
index,
var));
1052 if (it == cache.end())
return IntegerValue(0);
1057 std::vector<IntegerVariable> active_vars;
1058 for (
int i = 0; i + 1 < num_exprs; ++i) {
1059 for (
int j = i + 1; j < num_exprs; ++j) {
1060 active_vars = exprs[i].vars;
1061 active_vars.insert(active_vars.end(), exprs[j].vars.begin(),
1062 exprs[j].vars.end());
1064 for (
const IntegerVariable x_var : active_vars) {
1065 const IntegerValue diff = get_coeff(x_var, j) - get_coeff(x_var, i);
1066 if (diff == 0)
continue;
1070 sum_of_max_corner_diff[i][j] +=
std::max(diff * lb, diff * ub);
1071 sum_of_max_corner_diff[j][i] +=
std::max(-diff * lb, -diff * ub);
1076 for (
int i = 0; i < num_exprs; ++i) {
1078 lc.
AddTerm(target, IntegerValue(1));
1079 for (
int j = 0; j < exprs[i].vars.size(); ++j) {
1080 lc.
AddTerm(exprs[i].vars[j], -exprs[i].coeffs[j]);
1082 for (
int j = 0; j < num_exprs; ++j) {
1084 -exprs[j].offset - sum_of_max_corner_diff[i][j]));
1091 bool linearize_enforced_constraints,
1104 const IntegerValue rhs_domain_min = IntegerValue(
ct.linear().domain(0));
1105 const IntegerValue rhs_domain_max =
1106 IntegerValue(
ct.linear().domain(
ct.linear().domain_size() - 1));
1113 for (
int i = 0; i <
ct.linear().vars_size(); i++) {
1114 const int ref =
ct.linear().vars(i);
1115 const int64_t coeff =
ct.linear().coeffs(i);
1116 lc.
AddTerm(mapping->Integer(ref), IntegerValue(coeff));
1123 if (!linearize_enforced_constraints)
return;
1127 if (!mapping->IsHalfEncodingConstraint(&
ct) &&
ct.linear().vars_size() <= 1) {
1131 std::vector<Literal> enforcing_literals;
1132 enforcing_literals.reserve(
ct.enforcement_literal_size());
1133 for (
const int enforcement_ref :
ct.enforcement_literal()) {
1134 enforcing_literals.push_back(mapping->Literal(enforcement_ref));
1138 std::vector<std::pair<int, int64_t>> bool_terms;
1139 IntegerValue min_activity(0);
1140 IntegerValue max_activity(0);
1142 for (
int i = 0; i <
ct.linear().vars_size(); i++) {
1143 const int ref =
ct.linear().vars(i);
1144 const IntegerValue coeff(
ct.linear().coeffs(i));
1145 const IntegerVariable int_var = mapping->Integer(ref);
1150 const IntegerValue lb = integer_trail->LowerBound(int_var);
1151 const IntegerValue ub = integer_trail->UpperBound(int_var);
1152 if (lb == 0 && ub == 1 && activity_helper !=
nullptr) {
1153 bool_terms.push_back({ref, coeff.value()});
1156 min_activity += coeff * lb;
1157 max_activity += coeff * ub;
1159 min_activity += coeff * ub;
1160 max_activity += coeff * lb;
1164 if (activity_helper !=
nullptr) {
1171 if (rhs_domain_min > min_activity) {
1180 for (
int i = 0; i <
ct.linear().vars_size(); i++) {
1181 const int ref =
ct.linear().vars(i);
1182 const IntegerValue coeff(
ct.linear().coeffs(i));
1183 const IntegerVariable int_var = mapping->Integer(ref);
1188 if (rhs_domain_max < max_activity) {
1197 for (
int i = 0; i <
ct.linear().vars_size(); i++) {
1198 const int ref =
ct.linear().vars(i);
1199 const IntegerValue coeff(
ct.linear().coeffs(i));
1200 const IntegerVariable int_var = mapping->Integer(ref);
1219 const ConstraintProto&
ct,
1224 DCHECK_GT(linearization_level, 0);
1226 switch (
ct.constraint_case()) {
1227 case ConstraintProto::ConstraintCase::kBoolOr: {
1228 if (linearization_level > 1) {
1233 case ConstraintProto::ConstraintCase::kBoolAnd: {
1234 if (linearization_level > 1) {
1239 case ConstraintProto::ConstraintCase::kAtMostOne: {
1243 case ConstraintProto::ConstraintCase::kExactlyOne: {
1247 case ConstraintProto::ConstraintCase::kIntProd: {
1248 const LinearArgumentProto& int_prod =
ct.int_prod();
1249 if (int_prod.exprs_size() == 2 &&
1251 int_prod.exprs(1))) {
1260 case ConstraintProto::ConstraintCase::kLinMax: {
1262 const bool is_affine_max = LinMaxContainsOnlyOneVarInExpressions(
ct);
1263 if (is_affine_max) {
1268 if (linearization_level > 1) {
1269 if (is_affine_max) {
1271 }
else if (
ct.lin_max().exprs().size() < 100) {
1277 case ConstraintProto::ConstraintCase::kAllDiff: {
1282 case ConstraintProto::ConstraintCase::kLinear: {
1284 ct, linearization_level > 1,
model,
1285 relaxation, activity_helper);
1288 case ConstraintProto::ConstraintCase::kCircuit: {
1290 if (linearization_level > 1) {
1295 case ConstraintProto::ConstraintCase::kRoutes: {
1297 if (linearization_level > 1) {
1302 case ConstraintProto::ConstraintCase::kNoOverlap: {
1306 case ConstraintProto::ConstraintCase::kCumulative: {
1310 case ConstraintProto::ConstraintCase::kNoOverlap2D: {
1317 if (linearization_level > 1) {
1333 if (
ct.int_prod().exprs_size() != 2)
return;
1342 IntegerValue x_lb = integer_trail->
LowerBound(x);
1343 IntegerValue x_ub = integer_trail->
UpperBound(x);
1344 IntegerValue y_lb = integer_trail->
LowerBound(y);
1345 IntegerValue y_ub = integer_trail->
UpperBound(y);
1348 if (x_lb < 0 && x_ub > 0)
return;
1349 if (y_lb < 0 && y_ub > 0)
return;
1363 z, x, y, linearization_level, m));
1375 IntegerValue x_lb = integer_trail->LowerBound(x);
1376 IntegerValue x_ub = integer_trail->UpperBound(x);
1378 if (x_lb == x_ub)
return;
1381 if (x_lb < 0 && x_ub > 0)
return;
1386 const IntegerValue tmp = x_ub;
1392 if (x_ub > (int64_t{1} << 31))
return;
1401 if (x_lb + 1 < x_ub) {
1417 const IntegerValue x_lb = integer_trail->LowerBound(x);
1418 const IntegerValue x_ub = integer_trail->UpperBound(x);
1421 if (x_lb < 0 && x_ub > 0)
return;
1433 int linearization_level,
Model* m,
1438 const int num_exprs =
ct.all_diff().exprs_size();
1440 const std::vector<AffineExpression> exprs =
1441 mapping->Affines(
ct.all_diff().exprs());
1447 if (integer_trail->IsFixed(expr)) {
1448 union_of_domains = union_of_domains.
UnionWith(
1449 Domain(integer_trail->FixedValue(expr).value()));
1451 union_of_domains = union_of_domains.
UnionWith(
1452 integer_trail->InitialVariableDomain(expr.var)
1453 .MultiplicationBy(expr.coeff.value())
1454 .AdditionWith(
Domain(expr.constant.value())));
1458 if (union_of_domains.
Size() == num_exprs) {
1460 int64_t sum_of_values = 0;
1461 for (
const int64_t v : union_of_domains.
Values()) {
1469 }
else if (num_exprs <=
1470 m->
GetOrCreate<SatParameters>()->max_all_diff_cut_size() &&
1471 linearization_level > 1) {
1501 const std::optional<AffineExpression>& makespan,
1504 helper, demands_helper,
capacity, m));
1509 helper, demands_helper,
capacity, m));
1513 bool has_variable_part =
false;
1515 for (
int i = 0; i < helper->
NumTasks(); ++i) {
1517 has_variable_part =
true;
1522 has_variable_part =
true;
1528 helper, demands_helper,
capacity, makespan, m));
1533 const std::optional<AffineExpression>& makespan,
1541 bool has_variable_or_optional_part =
false;
1542 for (
int i = 0; i < helper->
NumTasks(); ++i) {
1545 has_variable_or_optional_part =
true;
1549 if (has_variable_or_optional_part) {
1560 std::vector<IntervalVariable> x_intervals =
1561 mapping->
Intervals(
ct.no_overlap_2d().x_intervals());
1562 std::vector<IntervalVariable> y_intervals =
1563 mapping->Intervals(
ct.no_overlap_2d().y_intervals());
1570 bool has_variable_part =
false;
1571 for (
int i = 0; i < x_intervals.size(); ++i) {
1573 if (intervals_repository->
IsAbsent(x_intervals[i]) ||
1574 intervals_repository->
IsAbsent(y_intervals[i])) {
1579 if (!intervals_repository->
IsPresent(x_intervals[i]) ||
1580 !intervals_repository->
IsPresent(y_intervals[i])) {
1581 has_variable_part =
true;
1586 if (intervals_repository->
MinSize(x_intervals[i]) !=
1587 intervals_repository->
MaxSize(x_intervals[i]) ||
1588 intervals_repository->
MinSize(y_intervals[i]) !=
1589 intervals_repository->
MaxSize(y_intervals[i])) {
1590 has_variable_part =
true;
1594 if (has_variable_part) {
1602 if (!m->
GetOrCreate<SatParameters>()->add_lin_max_cuts())
return;
1607 if (
ct.lin_max().target().vars_size() != 1)
return;
1608 if (
ct.lin_max().target().coeffs(0) != 1)
return;
1609 if (
ct.lin_max().target().offset() != 0)
return;
1611 const IntegerVariable target =
1612 mapping->
Integer(
ct.lin_max().target().vars(0));
1613 std::vector<LinearExpression> exprs;
1614 exprs.reserve(
ct.lin_max().exprs_size());
1615 for (
int i = 0; i <
ct.lin_max().exprs_size(); ++i) {
1622 const std::vector<Literal> alternative_literals =
1632 std::vector<IntegerVariable> z_vars;
1634 for (
const Literal lit : alternative_literals) {
1635 z_vars.push_back(encoder->GetLiteralView(lit));
1653 int num_exactly_one_elements = 0;
1655 for (
const IntegerVariable
var :
1656 implied_bounds->GetElementEncodedVariables()) {
1657 for (
const auto& [
index, literal_value_list] :
1658 implied_bounds->GetElementEncodings(
var)) {
1664 absl::flat_hash_set<IntegerValue> values;
1665 for (
const auto& literal_value : literal_value_list) {
1666 min_value =
std::min(min_value, literal_value.value);
1667 values.insert(literal_value.value);
1669 if (values.size() == literal_value_list.size())
continue;
1673 linear_encoding.
AddTerm(
var, IntegerValue(-1));
1674 for (
const auto& [
value,
literal] : literal_value_list) {
1675 const IntegerValue delta_min =
value - min_value;
1676 if (delta_min != 0) {
1683 ++num_exactly_one_elements;
1688 if (num_exactly_one_elements != 0) {
1691 "[ElementLinearRelaxation]"
1692 " #from_exactly_one:",
1693 num_exactly_one_elements);
1700 const SatParameters& params = *m->
GetOrCreate<SatParameters>();
1704 if (params.linearization_level() > 1) {
1711 &relaxation, &activity_bound_helper);
1715 int num_loose_equality_encoding_relaxations = 0;
1716 int num_tight_equality_encoding_relaxations = 0;
1717 int num_inequality_encoding_relaxations = 0;
1719 for (
int i = 0; i <
model_proto.variables_size(); ++i) {
1720 if (mapping->IsBoolean(i))
continue;
1722 const IntegerVariable
var = mapping->Integer(i);
1727 var, *m, &relaxation, &num_tight_equality_encoding_relaxations,
1728 &num_loose_equality_encoding_relaxations);
1743 ++num_inequality_encoding_relaxations;
1749 if (params.linearization_level() >= 2) {
1761 if (num_tight_equality_encoding_relaxations != 0 ||
1762 num_loose_equality_encoding_relaxations != 0 ||
1763 num_inequality_encoding_relaxations != 0) {
1765 "[EncodingLinearRelaxation]"
1766 " #tight_equality:",
1767 num_tight_equality_encoding_relaxations,
1768 " #loose_equality:", num_loose_equality_encoding_relaxations,
1769 " #inequality:", num_inequality_encoding_relaxations);
1774 "[LinearRelaxationBeforeCliqueExpansion]"
1785 for (
const std::vector<Literal>& at_most_one : relaxation.
at_most_ones) {
1786 if (at_most_one.empty())
continue;
1792 const bool unused ABSL_ATTRIBUTE_UNUSED =
1815 if (params.linearization_level() > 1 && params.add_clique_cuts()) {
1817 for (
int i = 0; i <
model_proto.variables_size(); ++i) {
1818 if (!mapping->IsBoolean(i))
continue;
1822 const bool unused ABSL_ATTRIBUTE_UNUSED =
1828 if (!expr.
vars.empty()) {
1837 "[FinalLinearRelaxation]"
We call domain any subset of Int64 = [kint64min, kint64max].
int64_t Size() const
Returns the number of elements in the domain.
Domain UnionWith(const Domain &domain) const
Returns the union of D and domain.
DomainIteratorBeginEnd Values() const &
std::vector< absl::Span< const int > > PartitionLiteralsIntoAmo(absl::Span< const int > literals)
int64_t ComputeMinActivity(absl::Span< const std::pair< int, int64_t >> terms, std::vector< std::array< int64_t, 2 >> *conditional=nullptr)
int64_t ComputeMaxActivity(absl::Span< const std::pair< int, int64_t >> terms, std::vector< std::array< int64_t, 2 >> *conditional=nullptr)
void AddAllAtMostOnes(const CpModelProto &proto)
std::vector< sat::Literal > Literals(const ProtoIndices &indices) const
std::vector< IntervalVariable > Intervals(const ProtoIndices &indices) const
IntegerVariable Integer(int ref) const
std::vector< ValueLiteralPair > FullDomainEncoding(IntegerVariable var) const
bool IsFixed(IntegerVariable i) const
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerVariable AddIntegerVariable(IntegerValue lower_bound, IntegerValue upper_bound)
IntegerValue FixedValue(IntegerVariable i) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
IntegerValue LowerBound(IntegerVariable i) const
IntegerValue MaxSize(IntervalVariable i) const
AffineExpression Start(IntervalVariable i) const
IntegerValue MinSize(IntervalVariable i) const
bool IsPresent(IntervalVariable i) const
bool IsAbsent(IntervalVariable i) const
SchedulingConstraintHelper * GetOrCreateHelper(const std::vector< IntervalVariable > &variables)
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff=IntegerValue(1))
ABSL_MUST_USE_RESULT bool AddDecomposedProduct(const std::vector< LiteralValueValue > &product)
void AddLinearExpression(const LinearExpression &expr)
LinearConstraint BuildConstraint(IntegerValue lb, IntegerValue ub)
LinearExpression BuildExpression()
void AddTerm(IntegerVariable var, IntegerValue coeff)
void AddQuadraticLowerBound(AffineExpression left, AffineExpression right, IntegerTrail *integer_trail, bool *is_quadratic=nullptr)
Literal(int signed_value)
Class that owns everything related to a particular optimization model.
T Get(std::function< T(const Model &)> f) const
Similar to Add() but this is const.
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
int CurrentDecisionLevel() const
bool IsPresent(int t) const
bool SizeIsFixed(int t) const
bool IsAbsent(int t) const
IntegerValue EndMax(int t) const
const std::vector< AffineExpression > & Starts() const
bool IsOptional(int t) const
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
IntegerValue StartMin(int t) const
Literal PresenceLiteral(int index) const
const std::vector< AffineExpression > & Sizes() const
const std::vector< AffineExpression > & Ends() const
bool DemandIsFixed(int t) const
const std::vector< std::vector< LiteralValueValue > > & DecomposedEnergies() const
IntegerValue EnergyMin(int t) const
void CacheAllEnergyValues()
const std::vector< AffineExpression > & Demands() const
CpModelProto const * model_proto
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
CutGenerator CreateCumulativeEnergyCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, const std::optional< AffineExpression > &makespan, Model *model)
void AppendCumulativeRelaxationAndCutGenerator(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
CutGenerator CreateNoOverlap2dEnergyCutGenerator(const std::vector< IntervalVariable > &x_intervals, const std::vector< IntervalVariable > &y_intervals, Model *model)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
void AppendLinMaxRelaxationPart1(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AppendBoolOrRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
CutGenerator CreateNoOverlapCompletionTimeCutGenerator(SchedulingConstraintHelper *helper, Model *model)
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
bool AppendFullEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
CutGenerator CreateStronglyConnectedGraphCutGenerator(int num_nodes, std::vector< int > tails, std::vector< int > heads, std::vector< Literal > literals, Model *model)
LinearConstraint ComputeHyperplanBelowSquare(AffineExpression x, AffineExpression square, IntegerValue x_value, Model *model)
CutGenerator CreateAllDifferentCutGenerator(const std::vector< AffineExpression > &exprs, Model *model)
const LiteralIndex kNoLiteralIndex(-1)
void AddMaxAffineCutGenerator(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AppendAtMostOneRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
std::function< BooleanVariable(Model *)> NewBooleanVariable()
bool HasEnforcementLiteral(const ConstraintProto &ct)
CutGenerator CreateCVRPCutGenerator(int num_nodes, std::vector< int > tails, std::vector< int > heads, std::vector< Literal > literals, std::vector< int64_t > demands, int64_t capacity, Model *model)
LinearExpression PositiveVarExpr(const LinearExpression &expr)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
void AddAllDiffRelaxationAndCutGenerator(const ConstraintProto &ct, int linearization_level, Model *m, LinearRelaxation *relaxation)
const IntegerVariable kNoIntegerVariable(-1)
CutGenerator CreateCumulativePrecedenceCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, Model *model)
void AppendLinearConstraintRelaxation(const ConstraintProto &ct, bool linearize_enforced_constraints, Model *model, LinearRelaxation *relaxation, ActivityBoundHelper *activity_helper)
CutGenerator CreateCumulativeCompletionTimeCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, Model *model)
void AddNoOverlap2dCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
void AddNoOverlapCutGenerator(SchedulingConstraintHelper *helper, const std::optional< AffineExpression > &makespan, Model *m, LinearRelaxation *relaxation)
int DetectMakespan(const std::vector< IntervalVariable > &intervals, const std::vector< AffineExpression > &demands, const AffineExpression &capacity, Model *model)
void AddIntProdCutGenerator(const ConstraintProto &ct, int linearization_level, Model *m, LinearRelaxation *relaxation)
void AddCircuitCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
bool LinearExpressionProtosAreEqual(const LinearExpressionProto &a, const LinearExpressionProto &b, int64_t b_scaling)
std::function< IntegerVariable(Model *)> NewIntegerVariableFromLiteral(Literal lit)
CutGenerator CreateMaxAffineCutGenerator(LinearExpression target, IntegerVariable var, std::vector< std::pair< IntegerValue, IntegerValue >> affines, const std::string cut_name, Model *model)
CutGenerator CreateNoOverlap2dCompletionTimeCutGenerator(const std::vector< IntervalVariable > &x_intervals, const std::vector< IntervalVariable > &y_intervals, Model *model)
IntegerVariable PositiveVariable(IntegerVariable i)
CutGenerator CreateLinMaxCutGenerator(const IntegerVariable target, const std::vector< LinearExpression > &exprs, const std::vector< IntegerVariable > &z_vars, Model *model)
void TryToLinearizeConstraint(const CpModelProto &model_proto, const ConstraintProto &ct, int linearization_level, Model *model, LinearRelaxation *relaxation, ActivityBoundHelper *activity_helper)
CutGenerator CreatePositiveMultiplicationCutGenerator(AffineExpression z, AffineExpression x, AffineExpression y, int linearization_level, Model *model)
bool BuildMaxAffineUpConstraint(const LinearExpression &target, IntegerVariable var, const std::vector< std::pair< IntegerValue, IntegerValue >> &affines, Model *model, LinearConstraintBuilder *builder)
void AppendMaxAffineRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AppendExactlyOneRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
CutGenerator CreateCumulativeTimeTableCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, Model *model)
int ReindexArcs(IntContainer *tails, IntContainer *heads, absl::flat_hash_map< int, int > *mapping_output=nullptr)
CutGenerator CreateSquareCutGenerator(AffineExpression y, AffineExpression x, int linearization_level, Model *model)
std::function< void(Model *)> EqualMaxOfSelectedVariables(Literal enforcement_literal, AffineExpression target, const std::vector< AffineExpression > &exprs, const std::vector< Literal > &selectors)
std::vector< Literal > CreateAlternativeLiteralsWithView(int num_literals, Model *model, LinearRelaxation *relaxation)
LinearConstraint ComputeHyperplanAboveSquare(AffineExpression x, AffineExpression square, IntegerValue x_lb, IntegerValue x_ub, Model *model)
void AppendElementEncodingRelaxation(Model *m, LinearRelaxation *relaxation)
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
void AppendCircuitRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AddCumulativeCutGenerator(const AffineExpression &capacity, SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const std::optional< AffineExpression > &makespan, Model *m, LinearRelaxation *relaxation)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
void AppendSquareRelaxation(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
int64_t SafeDoubleToInt64(double value)
void AddSquareCutGenerator(const ConstraintProto &ct, int linearization_level, Model *m, LinearRelaxation *relaxation)
bool IntervalIsVariable(const IntervalVariable interval, IntervalsRepository *intervals_repository)
void AppendBoolAndRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation, ActivityBoundHelper *activity_helper)
std::vector< LiteralValueValue > TryToDecomposeProduct(const AffineExpression &left, const AffineExpression &right, Model *model)
void AddLinMaxCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
void AppendRoutesRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AppendNoOverlap2dRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
std::function< bool(const Model &)> IsFixed(IntegerVariable v)
PositiveOnlyIndex GetPositiveOnlyIndex(IntegerVariable var)
CutGenerator CreateNoOverlapEnergyCutGenerator(SchedulingConstraintHelper *helper, const std::optional< AffineExpression > &makespan, Model *model)
void AppendRelaxationForEqualityEncoding(IntegerVariable var, const Model &model, LinearRelaxation *relaxation, int *num_tight, int *num_loose)
CutGenerator CreateCliqueCutGenerator(const std::vector< IntegerVariable > &base_variables, Model *model)
void AddCumulativeRelaxation(const AffineExpression &capacity, SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const std::optional< AffineExpression > &makespan, Model *model, LinearRelaxation *relaxation)
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
bool VariableIsPositive(IntegerVariable i)
void AddRoutesCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
CutGenerator CreateNoOverlapPrecedenceCutGenerator(SchedulingConstraintHelper *helper, Model *model)
LinearRelaxation ComputeLinearRelaxation(const CpModelProto &model_proto, Model *m)
void AppendNoOverlapRelaxationAndCutGenerator(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
std::function< void(Model *)> EqualMinOfSelectedVariables(Literal enforcement_literal, AffineExpression target, const std::vector< AffineExpression > &exprs, const std::vector< Literal > &selectors)
void AppendLinMaxRelaxationPart2(IntegerVariable target, const std::vector< Literal > &alternative_literals, const std::vector< LinearExpression > &exprs, Model *model, LinearRelaxation *relaxation)
void AppendPartialGreaterThanEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
Collection of objects used to extend the Constraint Solver library.
int64_t CapSub(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
std::optional< int64_t > end
AffineExpression Negated() const
std::vector< IntegerValue > coeffs
std::vector< IntegerVariable > vars
std::vector< std::vector< Literal > > at_most_ones
std::vector< LinearConstraint > linear_constraints
std::vector< CutGenerator > cut_generators
#define SOLVER_LOG(logger,...)
#define VLOG(verboselevel)