24#include "absl/base/attributes.h"
25#include "absl/container/btree_map.h"
26#include "absl/container/flat_hash_map.h"
27#include "absl/container/flat_hash_set.h"
28#include "absl/log/check.h"
29#include "absl/types/span.h"
30#include "google/protobuf/message.h"
70std::pair<IntegerValue, IntegerValue> GetMinAndMaxNotEncoded(
72 const absl::flat_hash_set<IntegerValue>& encoded_values,
78 if (domains ==
nullptr || index >= domains->size()) {
85 for (
const int64_t v : (*domains)[index].
Values()) {
86 if (!encoded_values.contains(IntegerValue(v))) {
87 min = IntegerValue(v);
93 const Domain negated_domain = (*domains)[index].Negation();
94 for (
const int64_t v : negated_domain.Values()) {
95 if (!encoded_values.contains(IntegerValue(-v))) {
96 max = IntegerValue(-v);
109void CollectAffineExpressionWithSingleVariable(
111 std::vector<std::pair<IntegerValue, IntegerValue>>* affines) {
117 if (expr.vars().empty()) {
118 affines->push_back({IntegerValue(0), IntegerValue(expr.offset())});
120 CHECK_EQ(expr.vars().size(), 1);
121 const IntegerVariable affine_var = mapping->Integer(expr.vars(0));
126 CHECK_EQ(affine_var, *var);
128 {IntegerValue(expr.coeffs(0)), IntegerValue(expr.offset())});
132 {IntegerValue(-expr.coeffs(0)), IntegerValue(expr.offset())});
143 int* num_tight,
int* num_loose) {
146 if (encoder ==
nullptr || integer_trail ==
nullptr)
return;
148 std::vector<Literal> at_most_one_ct;
149 absl::flat_hash_set<IntegerValue> encoded_values;
150 std::vector<ValueLiteralPair> encoding;
152 const std::vector<ValueLiteralPair>& initial_encoding =
153 encoder->PartialDomainEncoding(var);
154 if (initial_encoding.empty())
return;
155 for (
const auto value_literal : initial_encoding) {
156 const Literal literal = value_literal.literal;
164 encoding.push_back(value_literal);
165 at_most_one_ct.push_back(literal);
166 encoded_values.insert(value_literal.value);
169 if (encoded_values.empty())
return;
176 const auto [min_not_encoded, max_not_encoded] =
177 GetMinAndMaxNotEncoded(var, encoded_values, model);
182 const IntegerValue rhs = encoding[0].value;
186 encoding_ct.
AddTerm(var, IntegerValue(1));
187 for (
const auto value_literal : encoding) {
188 const Literal lit = value_literal.literal;
191 const IntegerValue delta = value_literal.value - rhs;
192 if (delta != IntegerValue(0)) {
193 CHECK_GE(delta, IntegerValue(0));
214 if (min_not_encoded == max_not_encoded) {
215 const IntegerValue rhs = min_not_encoded;
217 encoding_ct.
AddTerm(var, IntegerValue(1));
218 for (
const auto value_literal : encoding) {
220 rhs - value_literal.value));
234 const IntegerValue d_min = min_not_encoded;
236 lower_bound_ct.
AddTerm(var, IntegerValue(1));
237 for (
const auto value_literal : encoding) {
239 d_min - value_literal.value));
249 const IntegerValue d_max = max_not_encoded;
251 upper_bound_ct.
AddTerm(var, IntegerValue(1));
252 for (
const auto value_literal : encoding) {
254 d_max - value_literal.value));
271 if (integer_trail ==
nullptr || encoder ==
nullptr)
return;
273 const auto& greater_than_encoding = encoder->PartialGreaterThanEncoding(var);
274 if (greater_than_encoding.empty())
return;
279 IntegerValue prev_used_bound = integer_trail->LowerBound(var);
281 builder.
AddTerm(var, IntegerValue(1));
283 for (
const auto entry : greater_than_encoding) {
284 if (entry.value <= prev_used_bound)
continue;
286 const LiteralIndex literal_index = entry.literal.Index();
287 const IntegerValue diff = prev_used_bound - entry.value;
296 prev_used_bound = entry.value;
297 prev_literal_index = literal_index;
307 IntegerValue prev_used_bound = integer_trail->LowerBound(
NegationOf(var));
309 builder.
AddTerm(var, IntegerValue(-1));
310 for (
const auto entry :
311 encoder->PartialGreaterThanEncoding(
NegationOf(var))) {
312 if (entry.value <= prev_used_bound)
continue;
313 const IntegerValue diff = prev_used_bound - entry.value;
317 prev_used_bound = entry.value;
327bool AllLiteralsHaveViews(
const IntegerEncoder& encoder,
328 absl::Span<const Literal> literals) {
329 for (
const Literal lit : literals) {
330 if (!encoder.LiteralOrNegationHasView(lit))
return false;
342 CHECK(lc.AddLiteralTerm(mapping->Literal(
NegatedRef(enforcement_ref)),
346 CHECK(lc.AddLiteralTerm(mapping->Literal(ref), IntegerValue(1)));
367 {enforcement, mapping->
Literal(ref).Negated()});
388 if (activity_helper !=
nullptr) {
389 std::vector<int> negated_lits;
393 for (absl::Span<const int> part :
396 for (
const int negated_ref : part) {
401 mapping->Literal(
NegatedRef(enforcement_ref)), IntegerValue(-1)));
412 mapping->Literal(
NegatedRef(enforcement_ref)), IntegerValue(-1)));
435 const std::vector<Literal> literals =
437 if (AllLiteralsHaveViews(*encoder, literals)) {
439 for (
const Literal lit : literals) {
454 if (num_literals == 1) {
459 encoder->GetOrCreateLiteralAssociatedToEquality(var, IntegerValue(1));
463 if (num_literals == 2) {
466 encoder->GetOrCreateLiteralAssociatedToEquality(var, IntegerValue(1));
472 encoder->AssociateToIntegerEqualValue(lit.
Negated(), var2, IntegerValue(1));
477 std::vector<Literal> literals;
479 for (
int i = 0;
i < num_literals; ++
i) {
482 encoder->GetOrCreateLiteralAssociatedToEquality(var, IntegerValue(1));
483 literals.push_back(lit);
501 absl::btree_map<int, std::vector<Literal>> incoming_arc_constraints;
502 absl::btree_map<int, std::vector<Literal>> outgoing_arc_constraints;
503 for (
int i = 0;
i < num_arcs;
i++) {
507 outgoing_arc_constraints[tail].push_back(arc);
508 incoming_arc_constraints[head].push_back(arc);
510 for (
const auto* node_map :
511 {&outgoing_arc_constraints, &incoming_arc_constraints}) {
512 for (
const auto& entry : *node_map) {
513 const std::vector<Literal>& exactly_one = entry.second;
514 if (exactly_one.size() > 1) {
517 for (
const Literal l : exactly_one) {
541 absl::btree_map<int, std::vector<Literal>> incoming_arc_constraints;
542 absl::btree_map<int, std::vector<Literal>> outgoing_arc_constraints;
543 for (
int i = 0;
i < num_arcs;
i++) {
547 outgoing_arc_constraints[tail].push_back(arc);
548 incoming_arc_constraints[head].push_back(arc);
550 for (
const auto* node_map :
551 {&outgoing_arc_constraints, &incoming_arc_constraints}) {
552 for (
const auto& entry : *node_map) {
553 if (entry.first == 0)
continue;
554 const std::vector<Literal>& exactly_one = entry.second;
555 if (exactly_one.size() > 1) {
558 for (
const Literal l : exactly_one) {
570 for (
const Literal& incoming_arc : incoming_arc_constraints[0]) {
571 CHECK(zero_node_balance_lc.
AddLiteralTerm(incoming_arc, IntegerValue(1)));
573 for (
const Literal& outgoing_arc : outgoing_arc_constraints[0]) {
574 CHECK(zero_node_balance_lc.
AddLiteralTerm(outgoing_arc, IntegerValue(-1)));
590 num_nodes, tails, heads, literals, m));
595 std::vector<int> tails(ct.
routes().
tails().begin(),
597 std::vector<int> heads(ct.
routes().
heads().begin(),
603 for (
int i = 0;
i < ct.
routes().tails_size(); ++
i) {
604 num_nodes = std::max(num_nodes, 1 + ct.
routes().
tails(
i));
605 num_nodes = std::max(num_nodes, 1 + ct.
routes().
heads(
i));
608 if (num_dimensions == 0) {
613 std::vector<AffineExpression> flat_node_dim_expressions(
615 for (
int d = 0; d < num_dimensions; ++d) {
617 for (
int n = 0; n < node_exprs.size(); ++n) {
620 flat_node_dim_expressions[n * num_dimensions + d];
621 if (expr.
vars().empty()) {
631 num_nodes, tails, heads, literals, flat_node_dim_expressions, m));
651 absl::Span<const AffineExpression> demands,
658 if (!integer_trail->
IsFixed(capacity)) {
664 for (
int i = 0;
i < intervals.size(); ++
i) {
665 if (repository->IsAbsent(intervals[
i]))
continue;
667 horizon, integer_trail->
UpperBound(repository->End(intervals[
i])));
670 const IntegerValue capacity_value = integer_trail->
FixedValue(capacity);
671 for (
int i = 0;
i < intervals.size(); ++
i) {
672 if (!repository->IsPresent(intervals[
i]))
continue;
674 if (integer_trail->
IsFixed(demands[
i]) &&
675 integer_trail->
FixedValue(demands[
i]) == capacity_value &&
678 integer_trail->
LowerBound(repository->Size(intervals[
i])) > 0) {
687std::optional<AffineExpression> DetectMakespanFromPrecedences(
688 const SchedulingConstraintHelper& helper,
Model* model) {
689 if (helper.NumTasks() == 0)
return {};
691 const absl::Span<const AffineExpression> ends = helper.Ends();
692 std::vector<IntegerVariable> end_vars;
693 for (
const AffineExpression&
end : ends) {
696 if (
end.coeff != 1)
return {};
697 end_vars.push_back(
end.var);
700 std::vector<FullIntegerPrecedence> output;
703 for (
const auto& p : output) {
705 if (p.indices.size() != ends.size())
continue;
710 for (
int i = 0;
i < p.indices.size(); ++
i) {
720 std::min(min_delta, p.offsets[
i] - ends[p.indices[
i]].constant);
722 VLOG(2) <<
"Makespan detected >= ends + " << min_delta;
738 std::vector<IntervalVariable> intervals =
740 const IntegerValue one(1);
741 std::vector<AffineExpression> demands(intervals.size(), one);
744 std::optional<AffineExpression> makespan;
745 const std::optional<int> makespan_index =
747 if (makespan_index.has_value()) {
748 makespan = repository->
Start(intervals[makespan_index.value()]);
750 intervals.erase(intervals.begin() + makespan_index.value());
759 if (!makespan.has_value()) {
760 makespan = DetectMakespanFromPrecedences(*helper, model);
776 std::vector<IntervalVariable> intervals =
778 std::vector<AffineExpression> demands =
781 const std::optional<int> makespan_index =
783 std::optional<AffineExpression> makespan;
785 if (makespan_index.has_value()) {
787 makespan = repository->
Start(intervals[makespan_index.value()]);
788 demands.erase(demands.begin() + makespan_index.value());
789 intervals.erase(intervals.begin() + makespan_index.value());
799 if (!makespan.has_value()) {
800 makespan = DetectMakespanFromPrecedences(*helper, model);
818 const std::optional<AffineExpression>& makespan,
820 const int num_intervals = helper->
NumTasks();
825 int num_variable_energies = 0;
826 int num_optionals = 0;
827 int64_t sizes_gcd = 0;
828 int64_t demands_gcd = 0;
829 int64_t num_active_intervals = 0;
831 for (
int index = 0; index < num_intervals; ++index) {
832 if (helper->
IsAbsent(index))
continue;
838 if (demands_helper->
EnergyMin(index) > 0) {
846 num_variable_energies++;
848 if (sizes_gcd != 1) {
850 sizes_gcd = std::gcd(helper->
SizeMin(index).value(), sizes_gcd);
855 if (demands_gcd != 1) {
858 std::gcd(demands_helper->
DemandMin(index).value(), demands_gcd);
863 num_active_intervals++;
864 min_of_starts = std::min(min_of_starts, helper->
StartMin(index));
865 max_of_ends = std::max(max_of_ends, helper->
EndMax(index));
868 VLOG(2) <<
"Span [" << min_of_starts <<
".." << max_of_ends <<
"] with "
869 << num_optionals <<
" optional intervals, and "
870 << num_variable_energies <<
" variable energy tasks out of "
871 << num_active_intervals <<
"/" << num_intervals <<
" intervals"
872 << (makespan.has_value() ?
", and 1 makespan" :
"")
873 <<
" sizes_gcd: " << sizes_gcd <<
" demands_gcd: " << demands_gcd;
876 if (num_active_intervals == 0)
return;
880 if (num_variable_energies + num_optionals == 0 && sizes_gcd == 1 &&
886 if (sizes_gcd != 1 && !makespan.has_value()) {
887 VLOG(2) <<
"Cumulative relaxation: sizes_gcd = " << sizes_gcd
888 <<
", demands_gcd = " << demands_gcd
889 <<
", no makespan, capacity is " << capacity;
892 if (!integer_trail->
IsFixed(capacity)) demands_gcd = 1;
894 for (
int index = 0; index < num_intervals; ++index) {
895 if (helper->
IsAbsent(index))
continue;
896 if (helper->
SizeMax(index) == 0 ||
902 const IntegerValue energy_min = demands_helper->
EnergyMin(index);
903 if (energy_min == 0)
continue;
904 DCHECK_EQ(energy_min % (sizes_gcd * demands_gcd), 0);
906 energy_min / sizes_gcd / demands_gcd)) {
911 std::vector<LiteralValueValue> product =
913 if (!product.empty()) {
917 DCHECK_EQ(entry.left_value % sizes_gcd, 0);
918 entry.left_value /= sizes_gcd;
919 DCHECK_EQ(entry.right_value % demands_gcd, 0);
920 entry.right_value /= demands_gcd;
925 const IntegerValue local_size =
927 DCHECK_EQ(local_size % sizes_gcd, 0);
928 if (demands_gcd == 1) {
930 local_size / sizes_gcd);
932 const IntegerValue local_demand =
934 DCHECK_EQ(local_demand % demands_gcd, 0);
935 lc.
AddConstant(local_size * local_demand / sizes_gcd / demands_gcd);
942 if (integer_trail->
IsFixed(capacity)) {
943 const IntegerValue span = max_of_ends - min_of_starts;
944 const IntegerValue fixed_capacity = integer_trail->
FixedValue(capacity);
949 DCHECK_EQ(demands_gcd, 1);
950 lc.
AddTerm(capacity, -(max_of_ends - min_of_starts) / sizes_gcd);
959 for (
int index = 0; index < num_intervals; ++index) {
960 if (helper->
IsAbsent(index))
continue;
962 const IntegerValue energy_min = demands_helper->
EnergyMin(index);
963 if (energy_min == 0)
continue;
968 const std::vector<LiteralValueValue>& product =
970 if (!product.empty()) {
978 demands_helper->
Demands()[index],
989 IntegerValue max_for_overflow_check = std::max(-min_of_starts, max_of_ends);
990 if (makespan.has_value()) {
991 max_for_overflow_check = std::max(
992 max_for_overflow_check, integer_trail->
UpperBound(makespan.value()));
1000 makespan.has_value() ? makespan.value() : max_of_ends;
1006 absl::Span<const int> component,
Model* model,
1009 std::optional<Rectangle> bounding_box;
1011 for (
const int b : component) {
1014 if (curr_bounding_box.
x_min > curr_bounding_box.
x_max ||
1015 curr_bounding_box.
y_min > curr_bounding_box.
y_max) {
1019 if (bounding_box.has_value()) {
1020 bounding_box->GrowToInclude(
1027 if (!bounding_box.has_value()) {
1032 const IntegerValue max_area = bounding_box->CapArea();
1036 for (
const int b : component) {
1042 const std::vector<LiteralValueValue> energy =
1043 product_decomposer->
TryToDecompose(x_size_affine, y_size_affine);
1044 if (!energy.empty()) {
1054 const Literal presence_literal =
1058 const auto& [x_size, y_size] =
1060 const IntegerValue area_min = x_size * y_size;
1069 manager->
Add(std::move(ct));
1080 std::vector<IntervalVariable> x_intervals =
1082 std::vector<IntervalVariable> y_intervals =
1087 intervals_repository->GetOrCreate2DHelper({},
1088 x_intervals, y_intervals);
1092 result.only_run_at_level_zero =
true;
1095 &no_overlap_helper->x_helper(), model, &result.vars,
1098 &no_overlap_helper->y_helper(), model, &result.vars,
1100 result.generate_cuts = [no_overlap_helper, model, product_decomposer,
1101 last_level_zero_bound_change_idx = int64_t{-1}](
1103 if (last_level_zero_bound_change_idx ==
1104 no_overlap_helper->LastLevelZeroChangeIdx()) {
1107 last_level_zero_bound_change_idx =
1108 no_overlap_helper->LastLevelZeroChangeIdx();
1109 for (
const auto& component :
1110 no_overlap_helper->connected_components().AsVectorOfSpan()) {
1112 component, model, no_overlap_helper, manager, product_decomposer);
1127 for (
int i = 0;
i < ct.
lin_max().exprs_size(); ++
i) {
1134 model, relaxation, activity_helper);
1145 IntegerVariable var;
1146 std::vector<std::pair<IntegerValue, IntegerValue>> affines;
1148 CollectAffineExpressionWithSingleVariable(ct, mapping, &var, &affines);
1160 model, relaxation, activity_helper);
1166 IntegerVariable var;
1167 std::vector<std::pair<IntegerValue, IntegerValue>> affines;
1169 CollectAffineExpressionWithSingleVariable(ct, mapping, &var, &affines);
1181 target_expr, var, affines,
"AffineMax", model));
1189 absl::Span<const Literal> alternative_literals,
1190 absl::Span<const LinearExpression> exprs,
1192 const int num_exprs = exprs.size();
1196 for (
int i = 0;
i < num_exprs; ++
i) {
1199 local_expr.
vars.push_back(target);
1200 local_expr.
coeffs = exprs[
i].coeffs;
1201 local_expr.
coeffs.push_back(IntegerValue(1));
1204 local_expr.
coeffs, exprs[
i].offset, model);
1211 std::vector<std::vector<IntegerValue>> sum_of_max_corner_diff(
1212 num_exprs, std::vector<IntegerValue>(num_exprs, IntegerValue(0)));
1216 absl::flat_hash_map<std::pair<int, IntegerVariable>, IntegerValue> cache;
1217 for (
int i = 0;
i < num_exprs; ++
i) {
1218 for (
int j = 0; j < exprs[
i].vars.size(); ++j) {
1219 cache[std::make_pair(
i, exprs[
i].vars[j])] = exprs[
i].coeffs[j];
1222 const auto get_coeff = [&cache](IntegerVariable var,
int index) {
1223 const auto it = cache.find(std::make_pair(index, var));
1224 if (it == cache.end())
return IntegerValue(0);
1229 std::vector<IntegerVariable> active_vars;
1230 for (
int i = 0;
i + 1 < num_exprs; ++
i) {
1231 for (
int j =
i + 1; j < num_exprs; ++j) {
1232 active_vars = exprs[
i].vars;
1233 active_vars.insert(active_vars.end(), exprs[j].vars.begin(),
1234 exprs[j].vars.end());
1236 for (
const IntegerVariable x_var : active_vars) {
1237 const IntegerValue diff = get_coeff(x_var, j) - get_coeff(x_var,
i);
1238 if (diff == 0)
continue;
1242 sum_of_max_corner_diff[
i][j] += std::max(diff * lb, diff * ub);
1243 sum_of_max_corner_diff[j][
i] += std::max(-diff * lb, -diff * ub);
1248 for (
int i = 0;
i < num_exprs; ++
i) {
1250 lc.
AddTerm(target, IntegerValue(1));
1251 for (
int j = 0; j < exprs[
i].vars.size(); ++j) {
1252 lc.
AddTerm(exprs[
i].vars[j], -exprs[
i].coeffs[j]);
1254 for (
int j = 0; j < num_exprs; ++j) {
1256 -exprs[j].offset - sum_of_max_corner_diff[
i][j]));
1263bool LinearizeEnforcedConstraints(
Model* model) {
1264 return model->GetOrCreate<SatParameters>()->linearization_level() > 1;
1278 const IntegerValue rhs_domain_min = IntegerValue(ct.
linear().
domain(0));
1279 const IntegerValue rhs_domain_max =
1281 if (rhs_domain_min == std::numeric_limits<int64_t>::min() &&
1282 rhs_domain_max == std::numeric_limits<int64_t>::max())
1287 for (
int i = 0;
i < ct.
linear().vars_size();
i++) {
1289 const IntegerVariable int_var = mapping->Integer(ref);
1293 lc.
AddTerm(int_var, IntegerValue(coeff));
1301 if (!LinearizeEnforcedConstraints(model))
return;
1309 if (!mapping->IsHalfEncodingConstraint(&ct) && ct.
linear().
vars_size() <= 1 &&
1315 relaxation, activity_helper);
1323 if (enforcement.empty()) {
1327 if (!LinearizeEnforcedConstraints(model))
return;
1330 std::vector<Literal> enforcing_literals;
1331 enforcing_literals.reserve(enforcement.size());
1332 for (
const int enforcement_ref : enforcement) {
1333 enforcing_literals.push_back(mapping->Literal(enforcement_ref));
1337 std::vector<std::pair<int, int64_t>> bool_terms;
1338 IntegerValue min_activity(0);
1339 IntegerValue max_activity(0);
1341 for (
int i = 0;
i < linear_constraint.num_terms;
i++) {
1342 const IntegerValue coeff = linear_constraint.coeffs[
i];
1343 const IntegerVariable int_var = linear_constraint.vars[
i];
1344 const int ref = mapping->GetProtoVariableFromIntegerVariable(int_var);
1346 const IntegerValue lb = integer_trail->LowerBound(int_var);
1347 const IntegerValue ub = integer_trail->UpperBound(int_var);
1348 if (lb == 0 && ub == 1 && activity_helper !=
nullptr && ref != -1) {
1349 bool_terms.push_back({ref, coeff.value()});
1352 min_activity += coeff * lb;
1353 max_activity += coeff * ub;
1355 min_activity += coeff * ub;
1356 max_activity += coeff * lb;
1360 if (activity_helper !=
nullptr) {
1367 if (linear_constraint.lb > min_activity) {
1372 const IntegerValue term =
CapSubI(linear_constraint.lb, min_activity);
1374 for (
const Literal& literal : enforcing_literals) {
1377 for (
int i = 0;
i < linear_constraint.num_terms;
i++) {
1378 lc.
AddTerm(linear_constraint.vars[
i], linear_constraint.coeffs[
i]);
1385 if (linear_constraint.ub < max_activity) {
1390 const IntegerValue term =
CapSubI(linear_constraint.ub, max_activity);
1392 for (
const Literal& literal : enforcing_literals) {
1395 for (
int i = 0;
i < linear_constraint.num_terms;
i++) {
1396 lc.
AddTerm(linear_constraint.vars[
i], linear_constraint.coeffs[
i]);
1418 int linearization_level,
Model* model,
1422 DCHECK_GT(linearization_level, 0);
1427 if (linearization_level > 1) {
1433 if (linearization_level > 1) {
1452 int_prod.
exprs(1))) {
1464 const bool is_affine_max =
1466 if (is_affine_max) {
1473 if (is_affine_max) {
1497 if (linearization_level > 1) {
1507 if (linearization_level > 1) {
1528 if (linearization_level > 1) {
1544 LOG(INFO) <<
"Possible overflow in linearization of: "
1545 << google::protobuf::ShortFormat(ct);
1565 IntegerValue x_lb = integer_trail->
LowerBound(x);
1566 IntegerValue x_ub = integer_trail->
UpperBound(x);
1567 IntegerValue y_lb = integer_trail->
LowerBound(y);
1568 IntegerValue y_ub = integer_trail->
UpperBound(y);
1571 if (x_lb < 0 && x_ub > 0)
return;
1572 if (y_lb < 0 && y_ub > 0)
return;
1586 z, x, y, linearization_level, m));
1598 IntegerValue x_lb = integer_trail->LowerBound(x);
1599 IntegerValue x_ub = integer_trail->UpperBound(x);
1601 if (x_lb == x_ub)
return;
1604 if (x_lb < 0 && x_ub > 0)
return;
1609 const IntegerValue tmp = x_ub;
1615 if (x_ub > (int64_t{1} << 31))
return;
1624 if (x_lb + 1 < x_ub) {
1640 const IntegerValue x_lb = integer_trail->LowerBound(x);
1641 const IntegerValue x_ub = integer_trail->UpperBound(x);
1644 if (x_lb < 0 && x_ub > 0)
return;
1656 int linearization_level,
Model* m,
1663 const std::vector<AffineExpression> exprs =
1670 if (integer_trail->IsFixed(expr)) {
1671 union_of_domains = union_of_domains.
UnionWith(
1672 Domain(integer_trail->FixedValue(expr).value()));
1674 union_of_domains = union_of_domains.
UnionWith(
1675 integer_trail->InitialVariableDomain(expr.var)
1676 .MultiplicationBy(expr.coeff.value())
1677 .AdditionWith(
Domain(expr.constant.value())));
1681 if (union_of_domains.
Size() == num_exprs) {
1683 int64_t sum_of_values = 0;
1684 for (
const int64_t v : union_of_domains.
Values()) {
1692 }
else if (num_exprs <=
1694 linearization_level > 1) {
1703 if (intervals_repository->
IsAbsent(interval)) {
1708 if (!intervals_repository->
IsPresent(interval)) {
1713 if (intervals_repository->
MinSize(interval) !=
1714 intervals_repository->
MaxSize(interval)) {
1724 const std::optional<AffineExpression>& makespan,
1727 helper, demands_helper, capacity, m));
1732 helper, demands_helper, capacity, m));
1736 bool has_variable_part =
false;
1740 has_variable_part =
true;
1745 has_variable_part =
true;
1749 if (has_variable_part || !integer_trail->
IsFixed(capacity)) {
1751 helper, demands_helper, capacity, makespan, m));
1756 const std::optional<AffineExpression>& makespan,
1764 bool has_variable_or_optional_part =
false;
1768 has_variable_or_optional_part =
true;
1772 if (has_variable_or_optional_part) {
1784 std::vector<IntervalVariable> x_intervals =
1786 std::vector<IntervalVariable> y_intervals =
1793 x_intervals, y_intervals);
1799 bool has_variable_part =
false;
1800 for (
int i = 0;
i < x_intervals.size(); ++
i) {
1802 if (intervals_repository->
IsAbsent(x_intervals[
i]) ||
1803 intervals_repository->
IsAbsent(y_intervals[
i])) {
1808 if (!intervals_repository->
IsPresent(x_intervals[
i]) ||
1809 !intervals_repository->
IsPresent(y_intervals[
i])) {
1810 has_variable_part =
true;
1815 if (intervals_repository->
MinSize(x_intervals[
i]) !=
1816 intervals_repository->
MaxSize(x_intervals[
i]) ||
1817 intervals_repository->
MinSize(y_intervals[
i]) !=
1818 intervals_repository->
MaxSize(y_intervals[
i])) {
1819 has_variable_part =
true;
1824 if (!has_variable_part)
return;
1841 const IntegerVariable target =
1843 std::vector<LinearExpression> exprs;
1845 for (
int i = 0;
i < ct.
lin_max().exprs_size(); ++
i) {
1852 const std::vector<Literal> alternative_literals =
1862 std::vector<IntegerVariable> z_vars;
1864 for (
const Literal lit : alternative_literals) {
1865 z_vars.push_back(encoder->GetLiteralView(lit));
1884 int num_exactly_one_elements = 0;
1885 for (
const IntegerVariable var :
1886 element_encodings->GetElementEncodedVariables()) {
1887 for (
const auto& [index, literal_value_list] :
1888 element_encodings->Get(var)) {
1890 for (
const auto& literal_value : literal_value_list) {
1891 min_value = std::min(min_value, literal_value.value);
1895 builder.
AddTerm(var, IntegerValue(-1));
1896 for (
const auto& [value, literal] : literal_value_list) {
1897 const IntegerValue delta_min = value - min_value;
1898 if (delta_min != 0) {
1905 ++num_exactly_one_elements;
1913 if (num_exactly_one_elements != 0) {
1916 "[ElementLinearRelaxation]"
1917 " #from_exactly_one:",
1918 num_exactly_one_elements);
1930 activity_bound_helper.AddAllAtMostOnes(model_proto);
1934 for (
const auto& ct : model_proto.
constraints()) {
1936 &relaxation, &activity_bound_helper);
1940 int num_loose_equality_encoding_relaxations = 0;
1941 int num_tight_equality_encoding_relaxations = 0;
1942 int num_inequality_encoding_relaxations = 0;
1945 if (mapping->IsBoolean(
i))
continue;
1947 const IntegerVariable var = mapping->Integer(
i);
1952 var, *m, &relaxation, &num_tight_equality_encoding_relaxations,
1953 &num_loose_equality_encoding_relaxations);
1968 ++num_inequality_encoding_relaxations;
1986 if (num_tight_equality_encoding_relaxations != 0 ||
1987 num_loose_equality_encoding_relaxations != 0 ||
1988 num_inequality_encoding_relaxations != 0) {
1990 "[EncodingLinearRelaxation]"
1991 " #tight_equality:",
1992 num_tight_equality_encoding_relaxations,
1993 " #loose_equality:", num_loose_equality_encoding_relaxations,
1994 " #inequality:", num_inequality_encoding_relaxations);
1999 "[LinearRelaxationBeforeCliqueExpansion]"
2010 for (
const std::vector<Literal>& at_most_one : relaxation.
at_most_ones) {
2011 if (at_most_one.empty())
continue;
2014 for (
const Literal literal : at_most_one) {
2017 const bool unused ABSL_ATTRIBUTE_UNUSED =
2034 if (lc.
lb > 0 || lc.
ub < 0) {
2069 "[FinalLinearRelaxation]"
DomainIteratorBeginEnd Values() const &
Domain UnionWith(const Domain &domain) const
static IntegralType FloorOfRatio(IntegralType numerator, IntegralType denominator)
int64_t ComputeMaxActivity(absl::Span< const std::pair< int, int64_t > > terms, std::vector< std::array< int64_t, 2 > > *conditional=nullptr)
int64_t ComputeMinActivity(absl::Span< const std::pair< int, int64_t > > terms, std::vector< std::array< int64_t, 2 > > *conditional=nullptr)
std::vector< absl::Span< const int > > PartitionLiteralsIntoAmo(absl::Span< const int > literals)
const ::operations_research::sat::LinearExpressionProto & exprs(int index) const
::int32_t literals(int index) const
::int32_t heads(int index) const
::int32_t literals(int index) const
int literals_size() const
::int32_t tails(int index) const
ConstraintCase constraint_case() const
::int32_t enforcement_literal(int index) const
const ::operations_research::sat::BoolArgumentProto & exactly_one() const
const ::operations_research::sat::NoOverlapConstraintProto & no_overlap() const
const ::operations_research::sat::AllDifferentConstraintProto & all_diff() const
const ::operations_research::sat::LinearConstraintProto & linear() const
const ::operations_research::sat::BoolArgumentProto & at_most_one() const
const ::operations_research::sat::RoutesConstraintProto & routes() const
const ::operations_research::sat::LinearArgumentProto & lin_max() const
const ::operations_research::sat::BoolArgumentProto & bool_and() const
const ::operations_research::sat::BoolArgumentProto & bool_or() const
const ::operations_research::sat::LinearArgumentProto & int_prod() const
const ::operations_research::sat::CircuitConstraintProto & circuit() const
const ::operations_research::sat::CumulativeConstraintProto & cumulative() const
bool has_no_overlap_2d() const
const ::operations_research::sat::NoOverlap2DConstraintProto & no_overlap_2d() const
std::vector< IntervalVariable > Intervals(const ProtoIndices &indices) const
std::vector< sat::Literal > Literals(const ProtoIndices &indices) const
IntegerVariable Integer(int ref) const
const ::operations_research::sat::ConstraintProto & constraints(int index) const
int variables_size() const
const ::operations_research::sat::LinearExpressionProto & demands(int index) const
::int32_t intervals(int index) const
const ::operations_research::sat::LinearExpressionProto & capacity() const
IntegerValue LowerBound(IntegerVariable i) const
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue FixedValue(IntegerVariable i) const
bool IsFixed(IntegerVariable i) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit)
SchedulingConstraintHelper * GetOrCreateHelper(std::vector< Literal > enforcement_literals, const std::vector< IntervalVariable > &variables, bool register_as_disjunctive_helper=false)
SchedulingDemandHelper * GetOrCreateDemandHelper(SchedulingConstraintHelper *helper, absl::Span< const AffineExpression > demands)
AffineExpression Start(IntervalVariable i) const
bool IsAbsent(IntervalVariable i) const
NoOverlap2DConstraintHelper * GetOrCreate2DHelper(std::vector< Literal > enforcement_literals, const std::vector< IntervalVariable > &x_variables, const std::vector< IntervalVariable > &y_variables)
IntegerValue MaxSize(IntervalVariable i) const
IntegerValue MinSize(IntervalVariable i) const
bool IsPresent(IntervalVariable i) const
const ::operations_research::sat::LinearExpressionProto & exprs(int index) const
const ::operations_research::sat::LinearExpressionProto & target() const
void AddQuadraticLowerBound(AffineExpression left, AffineExpression right, IntegerTrail *integer_trail, bool *is_quadratic=nullptr)
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff=IntegerValue(1))
void AddTerm(IntegerVariable var, IntegerValue coeff)
void AddLinearExpression(const LinearExpression &expr)
void AddConstant(IntegerValue value)
LinearConstraint BuildConstraint(IntegerValue lb, IntegerValue ub)
ABSL_MUST_USE_RESULT bool AddDecomposedProduct(absl::Span< const LiteralValueValue > product)
ConstraintIndex Add(LinearConstraint ct, bool *added=nullptr, bool *folded=nullptr)
void RegisterWith(GenericLiteralWatcher *watcher)
::int32_t vars(int index) const
::int64_t coeffs(int index) const
::int64_t domain(int index) const
::int64_t coeffs(int index) const
::int32_t vars(int index) const
Literal(int signed_value)
T Add(std::function< T(Model *)> f)
T Get(std::function< T(const Model &)> f) const
Similar to Add() but this is const.
SchedulingConstraintHelper & y_helper() const
Rectangle GetLevelZeroBoundingRectangle(int index) const
bool IsPresent(int index) const
std::pair< IntegerValue, IntegerValue > GetLevelZeroBoxSizesMin(int index) const
SchedulingConstraintHelper & x_helper() const
::int32_t y_intervals(int index) const
::int32_t x_intervals(int index) const
::int32_t intervals(int index) const
std::vector< LiteralValueValue > TryToDecompose(const AffineExpression &left, const AffineExpression &right)
const ::operations_research::sat::LinearExpressionProto & exprs(int index) const
::int32_t heads(int index) const
const ::operations_research::sat::RoutesConstraintProto_NodeExpressions & dimensions(int index) const
int literals_size() const
int dimensions_size() const
::int32_t literals(int index) const
::int32_t tails(int index) const
::int32_t linearization_level() const
double merge_at_most_one_work_limit() const
bool add_lin_max_cuts() const
::int32_t max_all_diff_cut_size() const
void NotifyThatModelIsUnsat()
ABSL_MUST_USE_RESULT bool FinishPropagation(std::optional< ConflictCallback > callback=std::nullopt)
int CurrentDecisionLevel() const
bool SizeIsFixed(int t) const
absl::Span< const AffineExpression > Sizes() const
bool IsPresent(int t) const
Literal PresenceLiteral(int index) const
IntegerValue SizeMax(int t) const
IntegerValue StartMin(int t) const
IntegerValue SizeMin(int t) const
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
IntegerValue EndMax(int t) const
bool IsAbsent(int t) const
bool IsOptional(int t) const
bool DemandIsFixed(int t) const
bool CacheAllEnergyValues()
const std::vector< std::vector< LiteralValueValue > > & DecomposedEnergies() const
IntegerValue DemandMax(int t) const
const std::vector< AffineExpression > & Demands() const
IntegerValue DemandMin(int t) const
IntegerValue EnergyMin(int t) const
void ComputeFullPrecedences(absl::Span< const IntegerVariable > vars, std::vector< FullIntegerPrecedence > *output)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
std::vector< typename Map::mapped_type > Values(const Map &map, const Keys &keys)
void AppendNoOverlapRelaxationAndCutGenerator(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AppendCumulativeRelaxationAndCutGenerator(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
CutGenerator CreateCumulativeCompletionTimeCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, Model *model)
void AddSquareCutGenerator(const ConstraintProto &ct, int linearization_level, Model *m, LinearRelaxation *relaxation)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
void AppendExactlyOneRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
bool BuildMaxAffineUpConstraint(const LinearExpression &target, IntegerVariable var, absl::Span< const std::pair< IntegerValue, IntegerValue > > affines, Model *model, LinearConstraintBuilder *builder)
std::optional< int > DetectMakespan(absl::Span< const IntervalVariable > intervals, absl::Span< const AffineExpression > demands, const AffineExpression &capacity, Model *model)
void AddCumulativeCutGenerator(const AffineExpression &capacity, SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const std::optional< AffineExpression > &makespan, Model *m, LinearRelaxation *relaxation)
void AppendLinMaxRelaxationPart2(IntegerVariable target, absl::Span< const Literal > alternative_literals, absl::Span< const LinearExpression > exprs, Model *model, LinearRelaxation *relaxation)
bool LinearExpressionProtosAreEqual(const LinearExpressionProto &a, const LinearExpressionProto &b, int64_t b_scaling)
void AddCumulativeRelaxation(const AffineExpression &capacity, SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const std::optional< AffineExpression > &makespan, Model *model, LinearRelaxation *relaxation)
void AppendRoutesRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
CutGenerator CreatePositiveMultiplicationCutGenerator(AffineExpression z, AffineExpression x, AffineExpression y, int linearization_level, Model *model)
CutGenerator CreateStronglyConnectedGraphCutGenerator(int num_nodes, absl::Span< const int > tails, absl::Span< const int > heads, absl::Span< const Literal > literals, Model *model)
bool ProdOverflow(IntegerValue t, IntegerValue value)
CutGenerator CreateNoOverlap2dEnergyCutGenerator(NoOverlap2DConstraintHelper *helper, Model *model)
const LiteralIndex kNoLiteralIndex(-1)
void AddMaxAffineCutGenerator(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AddCircuitCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
LinearConstraint ComputeHyperplanBelowSquare(AffineExpression x, AffineExpression square, IntegerValue x_value, Model *model)
CutGenerator CreateCumulativePrecedenceCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, Model *model)
LinearRelaxation ComputeLinearRelaxation(const CpModelProto &model_proto, Model *m)
bool HasEnforcementLiteral(const ConstraintProto &ct)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
std::function< bool(const Model &)> IsFixed(IntegerVariable v)
void AddIntProdCutGenerator(const ConstraintProto &ct, int linearization_level, Model *m, LinearRelaxation *relaxation)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
void AddRoutesCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
CutGenerator CreateCumulativeEnergyCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, const std::optional< AffineExpression > &makespan, Model *model)
void AppendMaxAffineRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation, ActivityBoundHelper *activity_helper)
const IntegerVariable kNoIntegerVariable(-1)
bool PossibleOverflow(const IntegerTrail &integer_trail, const LinearConstraint &constraint)
CutGenerator CreateLinMaxCutGenerator(const IntegerVariable target, absl::Span< const LinearExpression > exprs, absl::Span< const IntegerVariable > z_vars, Model *model)
void TryToLinearizeConstraint(const CpModelProto &, const ConstraintProto &ct, int linearization_level, Model *model, LinearRelaxation *relaxation, ActivityBoundHelper *activity_helper)
void AddIntegerVariableFromIntervals(const SchedulingConstraintHelper *helper, Model *model, std::vector< IntegerVariable > *vars, int mask)
CutGenerator CreateAllDifferentCutGenerator(absl::Span< const AffineExpression > exprs, Model *model)
CutGenerator CreateSquareCutGenerator(AffineExpression y, AffineExpression x, int linearization_level, Model *model)
void AppendPartialGreaterThanEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
IntegerVariable PositiveVariable(IntegerVariable i)
CutGenerator CreateCumulativeTimeTableCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, Model *model)
int64_t SafeDoubleToInt64(double value)
CutGenerator CreateNoOverlapCompletionTimeCutGenerator(SchedulingConstraintHelper *helper, Model *model)
CutGenerator CreateMaxAffineCutGenerator(LinearExpression target, IntegerVariable var, std::vector< std::pair< IntegerValue, IntegerValue > > affines, const std::string cut_name, Model *model)
int ReindexArcs(IntContainer *tails, IntContainer *heads, absl::flat_hash_map< int, int > *mapping_output=nullptr)
void AppendCircuitRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
CutGenerator CreateNoOverlap2dCompletionTimeCutGenerator(NoOverlap2DConstraintHelper *helper, Model *model)
void AppendElementEncodingRelaxation(Model *m, LinearRelaxation *relaxation)
LinearConstraint ComputeHyperplanAboveSquare(AffineExpression x, AffineExpression square, IntegerValue x_lb, IntegerValue x_ub, Model *model)
void AppendLinearConstraintRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation, ActivityBoundHelper *activity_helper)
void AppendNoOverlap2dRelaxationForComponent(absl::Span< const int > component, Model *model, NoOverlap2DConstraintHelper *no_overlap_helper, LinearConstraintManager *manager, ProductDecomposer *product_decomposer)
bool ExpressionsContainsOnlyOneVar(const ExpressionList &exprs)
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
CutGenerator CreateNoOverlapEnergyCutGenerator(SchedulingConstraintHelper *helper, const std::optional< AffineExpression > &makespan, Model *model)
CutGenerator CreateCVRPCutGenerator(int num_nodes, absl::Span< const int > tails, absl::Span< const int > heads, absl::Span< const Literal > literals, absl::Span< const AffineExpression > flat_node_dim_expressions, Model *model)
bool IntervalIsVariable(const IntervalVariable interval, IntervalsRepository *intervals_repository)
void AppendSquareRelaxation(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
CutGenerator CreateNoOverlapPrecedenceCutGenerator(SchedulingConstraintHelper *helper, Model *model)
void AddLinMaxCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
std::function< void(Model *)> ExactlyOneConstraint(absl::Span< const Literal > literals)
LinearExpression PositiveVarExpr(const LinearExpression &expr)
void AppendBoolAndRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation, ActivityBoundHelper *activity_helper)
void AddAllDiffRelaxationAndCutGenerator(const ConstraintProto &ct, int linearization_level, Model *m, LinearRelaxation *relaxation)
void AppendNoOverlap2dRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
PositiveOnlyIndex GetPositiveOnlyIndex(IntegerVariable var)
void AppendAtMostOneRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AddNoOverlapCutGenerator(SchedulingConstraintHelper *helper, const std::optional< AffineExpression > &makespan, Model *m, LinearRelaxation *relaxation)
void AddNoOverlap2dCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
void AppendLinMaxRelaxationPart1(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation, ActivityBoundHelper *activity_helper)
bool VariableIsPositive(IntegerVariable i)
std::vector< Literal > CreateAlternativeLiteralsWithView(int num_literals, Model *model, LinearRelaxation *relaxation)
IntegerValue CapSubI(IntegerValue a, IntegerValue b)
bool AtMinOrMaxInt64I(IntegerValue t)
LinearConstraintPropagator< false > IntegerSumLE
void AppendBoolOrRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AppendRelaxationForEqualityEncoding(IntegerVariable var, const Model &model, LinearRelaxation *relaxation, int *num_tight, int *num_loose)
ClosedInterval::Iterator end(ClosedInterval interval)
IntegerLiteral GreaterOrEqual(IntegerValue bound) const
AffineExpression Negated() const
IntegerLiteral LowerOrEqual(IntegerValue bound) const
std::unique_ptr< IntegerValue[]> coeffs
std::unique_ptr< IntegerVariable[]> vars
std::vector< IntegerVariable > vars
std::vector< IntegerValue > coeffs
std::vector< CutGenerator > cut_generators
std::vector< std::vector< Literal > > at_most_ones
std::vector< LinearConstraint > linear_constraints
#define SOLVER_LOG(logger,...)