28#include "absl/container/flat_hash_set.h"
29#include "absl/container/inlined_vector.h"
30#include "absl/log/check.h"
31#include "absl/log/log.h"
32#include "absl/log/vlog_is_on.h"
33#include "absl/numeric/bits.h"
34#include "absl/types/span.h"
64IntegerVariable CreateVariableWithTightDomain(
65 absl::Span<const AffineExpression> exprs,
Model* model) {
68 auto* integer_trail = model->GetOrCreate<
IntegerTrail>();
70 min = std::min(min, integer_trail->LevelZeroLowerBound(e));
71 max = std::max(max, integer_trail->LevelZeroUpperBound(e));
76IntegerVariable CreateVariableAtOrAboveMinOf(
77 absl::Span<const AffineExpression> exprs,
Model* model) {
78 const IntegerVariable var = CreateVariableWithTightDomain(exprs, model);
79 auto* constraint =
new MinPropagator({exprs.begin(), exprs.end()}, var,
82 model->TakeOwnership(constraint);
87IntegerVariable CreateVariableAtOrBelowMaxOf(
88 absl::Span<const AffineExpression> exprs,
Model* model) {
89 std::vector<AffineExpression> negated_exprs(exprs.begin(), exprs.end());
92 const IntegerVariable var =
93 CreateVariableWithTightDomain(negated_exprs, model);
94 auto* constraint =
new MinPropagator(std::move(negated_exprs), var,
97 model->TakeOwnership(constraint);
104void AddDiffnCumulativeRelationOnX(
105 absl::Span<const Literal> enforcement_literals,
112 bool all_optional =
true;
113 for (
int i = 0;
i < y->NumTasks(); ++
i) {
114 if (!y->IsOptional(
i)) {
115 all_optional =
false;
124 for (
int i = 0;
i < y->NumTasks(); ++
i) {
125 min_start = std::min(min_start, y->LevelZeroStartMin(
i));
126 max_end = std::max(max_end, y->LevelZeroEndMax(
i));
128 if (max_end < min_start) {
135 const IntegerVariable min_start_var =
136 CreateVariableAtOrAboveMinOf(y->Starts(), model);
137 const IntegerVariable max_end_var =
138 CreateVariableAtOrBelowMaxOf(y->Ends(), model);
140 auto* integer_trail = model->GetOrCreate<
IntegerTrail>();
141 if (integer_trail->UpperBound(max_end_var) <
142 integer_trail->LowerBound(min_start_var)) {
149 0,
CapSub(integer_trail->UpperBound(max_end_var).value(),
150 integer_trail->LowerBound(min_start_var).value())));
151 const std::vector<int64_t> coeffs = {-capacity.coeff.value(), -1, 1};
154 enforcement_literals, {capacity.var, min_start_var, max_end_var},
155 coeffs, capacity.constant.value()));
166 if (params.use_timetabling_in_no_overlap_2d()) {
170 model->TakeOwnership(time_tabling);
175 if (params.use_energetic_reasoning_in_no_overlap_2d()) {
180bool AddAtMostOne(absl::Span<const Literal> enforcement_literals,
181 absl::Span<const Literal> literals,
Model* model) {
182 if (enforcement_literals.empty()) {
185 std::vector<Literal> enforcement(enforcement_literals.begin(),
186 enforcement_literals.end());
187 std::vector<LiteralWithCoeff> cst;
188 cst.reserve(literals.size());
189 for (
const Literal l : literals) {
192 return model->GetOrCreate<
SatSolver>()->AddLinearConstraint(
200 const std::vector<Literal>& enforcement_literals,
201 const std::vector<IntervalVariable>& x,
202 const std::vector<IntervalVariable>& y,
Model* model) {
230 const bool add_cumulative_relaxation =
234 if (add_cumulative_relaxation) {
241 bool some_boxes_are_only_optional_on_x =
false;
242 bool some_boxes_are_only_optional_on_y =
false;
243 for (
int i = 0;
i < x.size(); ++
i) {
252 some_boxes_are_only_optional_on_x =
true;
257 some_boxes_are_only_optional_on_y =
true;
260 if (!some_boxes_are_only_optional_on_y) {
261 AddDiffnCumulativeRelationOnX(enforcement_literals, x_helper, y_helper,
264 if (!some_boxes_are_only_optional_on_x) {
265 AddDiffnCumulativeRelationOnX(enforcement_literals, y_helper, x_helper,
301 const int num_boxes = x.size();
305 DCHECK_EQ(sat_solver->CurrentDecisionLevel(), 0);
307 for (
int i = 0;
i < num_boxes; ++
i) {
308 if (repository->
IsAbsent(x[
i]))
continue;
309 if (repository->
IsAbsent(y[
i]))
continue;
311 (!integer_trail->IsFixed(repository->
Size(x[
i])) ||
312 !integer_trail->IsFixed(repository->
Size(y[
i])))) {
315 for (
int j =
i + 1; j < num_boxes; ++j) {
316 if (repository->
IsAbsent(x[j]))
continue;
317 if (repository->
IsAbsent(y[j]))
continue;
319 (!integer_trail->IsFixed(repository->
Size(x[j])) ||
320 !integer_trail->IsFixed(repository->
Size(y[j])))) {
327 repository->
End(x[
i]), repository->
Start(x[j]));
329 repository->
End(x[j]), repository->
Start(x[
i]));
330 if ((integer_trail->LowerBound(repository->
Size(x[
i])) > 0 ||
331 integer_trail->LowerBound(repository->
Size(x[j])) > 0) &&
332 !AddAtMostOne(enforcement_literals, {x_ij, x_ji}, model)) {
333 sat_solver->NotifyThatModelIsUnsat();
340 repository->
End(y[
i]), repository->
Start(y[j]));
342 repository->
End(y[j]), repository->
Start(y[
i]));
343 if ((integer_trail->LowerBound(repository->
Size(y[
i])) > 0 ||
344 integer_trail->LowerBound(repository->
Size(y[j])) > 0) &&
345 !AddAtMostOne(enforcement_literals, {y_ij, y_ji}, model)) {
346 sat_solver->NotifyThatModelIsUnsat();
351 std::vector<Literal> clause = {x_ij, x_ji, y_ij, y_ji};
365 if (sat_solver->ModelIsUnsat())
return;
371#define RETURN_IF_FALSE(f) \
372 if (!(f)) return false;
376 if (!VLOG_IS_ON(1))
return;
377 std::vector<std::pair<std::string, int64_t>> stats;
379 {
"NonOverlappingRectanglesEnergyPropagator/called", num_calls_});
381 {
"NonOverlappingRectanglesEnergyPropagator/conflicts", num_conflicts_});
383 {
"NonOverlappingRectanglesEnergyPropagator/conflicts_two_boxes",
384 num_conflicts_two_boxes_});
385 stats.push_back({
"NonOverlappingRectanglesEnergyPropagator/refined",
386 num_refined_conflicts_});
388 {
"NonOverlappingRectanglesEnergyPropagator/conflicts_with_slack",
389 num_conflicts_with_slack_});
391 shared_stats_->AddStats(stats);
396 if (!helper_.IsEnforced())
return true;
397 if (!helper_.SynchronizeAndSetDirection())
return false;
399 Rectangle bounding_box = {.x_min = std::numeric_limits<IntegerValue>::max(),
400 .x_max = std::numeric_limits<IntegerValue>::min(),
401 .y_min = std::numeric_limits<IntegerValue>::max(),
402 .y_max = std::numeric_limits<IntegerValue>::min()};
403 std::vector<RectangleInRange> active_box_ranges;
404 const int num_boxes = helper_.NumBoxes();
405 active_box_ranges.reserve(num_boxes);
406 for (
int box = 0; box < num_boxes; ++box) {
407 if (!helper_.IsPresent(box))
continue;
411 active_box_ranges.push_back(std::move(rec));
414 if (active_box_ranges.size() < 2) {
419 if (active_box_ranges.size() > 1000) {
425 active_box_ranges.size()))) {
433 std::optional<Conflict> best_conflict =
434 FindConflict(std::move(active_box_ranges));
435 if (!best_conflict.has_value()) {
444 IntegerValue best_explanation_size =
445 best_conflict->opp_result.GetItemsParticipatingOnConflict().size();
446 bool refined =
false;
448 std::vector<RectangleInRange> items_participating_in_conflict;
449 items_participating_in_conflict.reserve(
450 best_conflict->items_for_opp.size());
451 for (
const auto& item :
452 best_conflict->opp_result.GetItemsParticipatingOnConflict()) {
453 items_participating_in_conflict.push_back(
454 best_conflict->items_for_opp[item.index]);
456 std::optional<Conflict> conflict =
457 FindConflict(items_participating_in_conflict);
458 if (!conflict.has_value())
break;
460 if (conflict->opp_result.GetItemsParticipatingOnConflict().size() >=
461 best_explanation_size) {
465 best_explanation_size =
466 conflict->opp_result.GetItemsParticipatingOnConflict().size();
467 best_conflict = std::move(conflict);
471 num_refined_conflicts_ += refined;
472 const std::vector<RectangleInRange> generalized_explanation =
473 GeneralizeExplanation(*best_conflict);
474 if (best_explanation_size == 2) {
475 num_conflicts_two_boxes_++;
477 helper_.ReportConflictFromInfeasibleBoxRanges(generalized_explanation);
481std::optional<NonOverlappingRectanglesEnergyPropagator::Conflict>
482NonOverlappingRectanglesEnergyPropagator::FindConflict(
483 std::vector<RectangleInRange> active_box_ranges) {
484 const auto rectangles_with_too_much_energy =
487 if (rectangles_with_too_much_energy.conflicts.empty() &&
488 rectangles_with_too_much_energy.candidates.empty()) {
492 Conflict best_conflict;
498 constexpr int kSampleSize = 10;
499 absl::InlinedVector<Rectangle, kSampleSize> sampled_rectangles;
500 std::sample(rectangles_with_too_much_energy.conflicts.begin(),
501 rectangles_with_too_much_energy.conflicts.end(),
502 std::back_inserter(sampled_rectangles), 5, *random_);
503 std::sample(rectangles_with_too_much_energy.candidates.begin(),
504 rectangles_with_too_much_energy.candidates.end(),
505 std::back_inserter(sampled_rectangles),
506 kSampleSize - sampled_rectangles.size(), *random_);
507 std::sort(sampled_rectangles.begin(), sampled_rectangles.end(),
508 [](
const Rectangle& a,
const Rectangle&
b) {
509 const bool larger = std::make_pair(a.SizeX(), a.SizeY()) >
510 std::make_pair(b.SizeX(), b.SizeY());
516 DCHECK(a.x_min <= b.x_min && a.x_max >= b.x_max &&
517 a.y_min <= b.y_min && a.y_max >= b.y_max);
520 DCHECK(a.x_min >= b.x_min && a.x_max <= b.x_max &&
521 a.y_min >= b.y_min && a.y_max <= b.y_max);
525 std::vector<IntegerValue> sizes_x, sizes_y;
526 sizes_x.reserve(active_box_ranges.size());
527 sizes_y.reserve(active_box_ranges.size());
528 std::vector<RectangleInRange> filtered_items;
529 filtered_items.reserve(active_box_ranges.size());
530 for (
const Rectangle& r : sampled_rectangles) {
533 filtered_items.clear();
534 for (
int i = 0;
i < active_box_ranges.size(); ++
i) {
535 const RectangleInRange& box = active_box_ranges[
i];
536 const Rectangle intersection = box.GetMinimumIntersection(r);
537 if (intersection.SizeX() > 0 && intersection.SizeY() > 0) {
538 sizes_x.push_back(intersection.SizeX());
539 sizes_y.push_back(intersection.SizeY());
540 filtered_items.push_back(box);
547 const auto opp_result = orthogonal_packing_checker_.TestFeasibility(
548 sizes_x, sizes_y, {r.SizeX(), r.SizeY()},
550 .use_pairwise =
true,
553 .brute_force_threshold = 7,
554 .dff2_max_number_of_parameters_to_check = 100});
556 (best_conflict.opp_result.GetResult() !=
558 best_conflict.opp_result.GetItemsParticipatingOnConflict().size() >
559 opp_result.GetItemsParticipatingOnConflict().size())) {
560 best_conflict.items_for_opp = filtered_items;
561 best_conflict.opp_result = opp_result;
562 best_conflict.rectangle_with_too_much_energy = r;
566 filtered_items.swap(active_box_ranges);
568 if (best_conflict.opp_result.GetResult() ==
570 return best_conflict;
576std::vector<RectangleInRange>
577NonOverlappingRectanglesEnergyPropagator::GeneralizeExplanation(
578 const Conflict& conflict) {
579 const Rectangle& rectangle = conflict.rectangle_with_too_much_energy;
580 OrthogonalPackingResult relaxed_result = conflict.opp_result;
583 int used_slack_count = 0;
584 const auto& items = relaxed_result.GetItemsParticipatingOnConflict();
585 for (
int i = 0;
i < items.size(); ++
i) {
586 if (!relaxed_result.HasSlack()) {
589 const RectangleInRange& range = conflict.items_for_opp[items[
i].index];
590 const RectangleInRange item_in_zero_level_range = {
592 {.x_min = helper_.x_helper().LevelZeroStartMin(range.box_index),
593 .x_max = helper_.x_helper().LevelZeroStartMax(range.box_index) +
595 .y_min = helper_.y_helper().LevelZeroStartMin(range.box_index),
596 .y_max = helper_.y_helper().LevelZeroStartMax(range.box_index) +
598 .x_size = range.x_size,
599 .y_size = range.y_size};
602 const Rectangle max_overlap =
603 item_in_zero_level_range.GetMinimumIntersection(rectangle);
604 used_slack_count += relaxed_result.TryUseSlackToReduceItemSize(
605 i, OrthogonalPackingResult::Coord::kCoordX, max_overlap.SizeX());
606 used_slack_count += relaxed_result.TryUseSlackToReduceItemSize(
607 i, OrthogonalPackingResult::Coord::kCoordY, max_overlap.SizeY());
609 num_conflicts_with_slack_ += (used_slack_count > 0);
610 VLOG_EVERY_N_SEC(2, 3)
611 <<
"Found a conflict on the OPP sub-problem of rectangle: " << rectangle
613 << conflict.opp_result.GetItemsParticipatingOnConflict().size() <<
"/"
614 << conflict.items_for_opp.size() <<
" items.";
616 std::vector<RectangleInRange> ranges_for_explanation;
617 ranges_for_explanation.reserve(conflict.items_for_opp.size());
618 std::vector<OrthogonalPackingResult::Item> sorted_items =
619 relaxed_result.GetItemsParticipatingOnConflict();
622 std::sort(sorted_items.begin(), sorted_items.end(),
623 [](
const OrthogonalPackingResult::Item& a,
624 const OrthogonalPackingResult::Item& b) {
625 return a.size_x * a.size_y > b.size_x * b.size_y;
627 for (
const auto& item : sorted_items) {
628 const RectangleInRange& range = conflict.items_for_opp[item.index];
629 ranges_for_explanation.push_back(
630 RectangleInRange::BiggestWithMinIntersection(rectangle, range,
631 item.size_x, item.size_y));
633 return ranges_for_explanation;
638 const int id = watcher->
Register(
this);
639 helper_.WatchAllBoxes(
id);
649IntegerValue FindCanonicalValue(IntegerValue lb, IntegerValue ub) {
650 if (lb == ub)
return lb;
651 if (lb <= 0 && ub > 0)
return IntegerValue(0);
652 if (lb < 0 && ub <= 0) {
653 return -FindCanonicalValue(-ub, -lb);
657 IntegerValue candidate = ub;
658 for (
int o = 0; o < 62; ++o) {
660 const IntegerValue masked_ub(ub.value() & ~mask);
661 if (masked_ub >= lb) {
662 candidate = masked_ub;
670void SplitDisjointBoxes(
const SchedulingConstraintHelper& x,
671 absl::Span<const int> boxes,
672 std::vector<absl::Span<const int>>* result) {
674 DCHECK(std::is_sorted(boxes.begin(), boxes.end(), [&x](
int a,
int b) {
675 return x.ShiftedStartMin(a) < x.ShiftedStartMin(b);
677 int current_start = 0;
678 std::size_t current_length = 1;
679 IntegerValue current_max_end =
x.EndMax(boxes[0]);
681 for (
int b = 1;
b < boxes.size(); ++
b) {
682 const int box = boxes[
b];
683 if (
x.ShiftedStartMin(box) < current_max_end) {
686 current_max_end = std::max(current_max_end,
x.EndMax(box));
688 if (current_length > 1) {
689 result->emplace_back(&boxes[current_start], current_length);
693 current_max_end =
x.EndMax(box);
698 if (current_length > 1) {
699 result->emplace_back(&boxes[current_start], current_length);
707NonOverlappingRectanglesDisjunctivePropagator::
708 NonOverlappingRectanglesDisjunctivePropagator(
711 x_(helper->NumBoxes(), model),
713 time_limit_(model->GetOrCreate<
TimeLimit>()),
714 overload_checker_(&x_),
715 forward_detectable_precedences_(true, &x_),
716 backward_detectable_precedences_(false, &x_),
717 forward_not_last_(true, &x_),
718 backward_not_last_(false, &x_),
719 forward_edge_finding_(true, &x_),
720 backward_edge_finding_(false, &x_),
721 disjunctive_with_two_items_(&x_) {}
727 int fast_priority,
int slow_priority) {
728 fast_id_ = watcher_->Register(
this);
729 watcher_->SetPropagatorPriority(fast_id_, fast_priority);
730 helper_->WatchAllBoxes(fast_id_);
734 watcher_->NotifyThatPropagatorMayNotReachFixedPointInOnePass(fast_id_);
736 const int slow_id = watcher_->Register(
this);
737 watcher_->SetPropagatorPriority(slow_id, slow_priority);
738 helper_->WatchAllBoxes(slow_id);
741bool NonOverlappingRectanglesDisjunctivePropagator::
742 FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
743 bool fast_propagation, absl::Span<const int> requested_boxes) {
754 indexed_boxes_.resize(helper_->
NumBoxes());
762 absl::flat_hash_set<int> requested_boxes_set;
763 const bool not_all_boxes = requested_boxes.size() != helper_->
NumBoxes();
765 requested_boxes_set = {requested_boxes.begin(), requested_boxes.end()};
771 auto fixed_boxes = already_checked_fixed_boxes_.
view();
772 for (
int i = temp.size(); --
i >= 0;) {
773 const int box = temp[
i].task_index;
774 if (not_all_boxes && !requested_boxes_set.contains(box))
continue;
778 if (!fixed_boxes[box]) {
780 if (x->IsAbsent(box) || y->
IsAbsent(box))
continue;
784 if (x->IsPresent(box) && !y->
IsPresent(box))
continue;
785 if (!x->IsPresent(box) && !y->
IsPresent(box) &&
792 const IntegerValue start_max = -temp[
i].time;
793 const IntegerValue end_min = y->
EndMin(box);
794 if (start_max < end_min) {
795 boxes_data[num_boxes++] = {box, start_max, end_min};
799 if (fixed_boxes[box]) {
802 if (helper_->IsFixed(box)) {
804 fixed_boxes.Set(box);
807 const Rectangle r = {
x->StartMin(box),
x->EndMax(box), start_max,
809 if (num_others == 0) {
810 other_bounding_box = r;
825 if (num_others == 0)
return true;
828 for (
int i = 0;
i < num_boxes; ++
i) {
829 const IndexedInterval& interval = boxes_data[
i];
830 const int box = interval.
index;
831 const Rectangle r = {
x->StartMin(box),
x->EndMax(box), interval.start,
833 if (other_bounding_box.
IsDisjoint(r))
continue;
834 boxes_data[new_size++] = interval;
836 num_boxes = new_size;
840 const auto boxes = absl::MakeSpan(boxes_data, num_boxes);
841 if (boxes.size() < 2)
return true;
852 rectangles_.reserve(boxes.size());
853 for (
const auto [box, y_mandatory_start, y_mandatory_end] : boxes) {
856 rectangles_.push_back(
857 {y_mandatory_start, y_mandatory_end,
858 x->StartMin(box),
x->EndMin(box)});
862 const size_t n = rectangles_.size();
863 time_limit_->AdvanceDeterministicTime(
864 static_cast<double>(n) *
static_cast<double>(absl::bit_width(n)) *
867 if (opt_pair == std::nullopt) {
874 order_.assign(
x->NumTasks(), 0);
877 for (
const auto [t, _lit, _time] :
x->TaskByIncreasingShiftedStartMin()) {
884 boxes_to_propagate_.clear();
885 reduced_overlapping_boxes_.clear();
886 int work_done = boxes.size();
887 for (
int i = 0;
i < events_overlapping_boxes_.size(); ++
i) {
888 work_done += events_overlapping_boxes_[
i].size();
889 SplitDisjointBoxes(*x, events_overlapping_boxes_[
i], &disjoint_boxes_);
890 for (
const absl::Span<const int> sub_boxes : disjoint_boxes_) {
894 const auto& insertion = reduced_overlapping_boxes_.insert(sub_boxes);
895 if (insertion.second) boxes_to_propagate_.push_back(sub_boxes);
900 time_limit_->AdvanceDeterministicTime(
static_cast<double>(work_done) * 1e-8);
906 for (
const absl::Span<const int> boxes : boxes_to_propagate_) {
909 if (!fast_propagation && boxes.size() <= 2)
continue;
911 x_.SetExtraExplanationForItemCallback(
nullptr);
912 if (!x_.ResetFromSubset(*x, boxes))
return false;
915 IntegerValue lb(std::numeric_limits<int64_t>::min());
916 IntegerValue ub(std::numeric_limits<int64_t>::max());
917 for (
const int b : boxes) {
919 ub = std::min(ub, y->
EndMin(
b) - 1);
931 const IntegerValue line_to_use_for_reason = FindCanonicalValue(lb, ub);
934 x_.SetExtraExplanationForItemCallback(
935 [&boxes, y, line_to_use_for_reason](
936 absl::Span<const int> items, std::vector<Literal>* literal_reason,
937 std::vector<IntegerLiteral>* integer_reason) {
939 if (items.size() > 5) {
941 for (
const int t : items) {
948 for (
int i = 0;
i < items.size(); ++
i) {
949 for (
int j =
i + 1; j < items.size(); ++j) {
950 const int t = items[
i];
951 const int u = items[j];
960 if (fast_propagation) {
961 if (x_.NumTasks() == 2) {
972 DCHECK_GT(x_.NumTasks(), 2);
988 if (!helper_->IsEnforced())
return true;
989 if (!helper_->SynchronizeAndSetDirection(
true,
true,
false))
return false;
993 const bool backtrack_since_last_call = !rev_is_in_dive_;
994 watcher_->SetUntilNextBacktrack(&rev_is_in_dive_);
995 if (backtrack_since_last_call ||
996 last_helper_inprocessing_count_ != helper_->InProcessingCount()) {
997 last_helper_inprocessing_count_ = helper_->InProcessingCount();
998 const int num_tasks = helper_->NumBoxes();
999 already_checked_fixed_boxes_.ClearAndResize(num_tasks);
1005 const bool fast_propagation = watcher_->GetCurrentId() == fast_id_;
1006 for (
const auto subset : helper_->connected_components().AsVectorOfSpan()) {
1007 if (!FindBoxesThatMustOverlapAHorizontalLineAndPropagate(fast_propagation,
1013 if (!helper_->SynchronizeAndSetDirection(
true,
true,
true))
return false;
1015 for (
const auto subset : helper_->connected_components().AsVectorOfSpan()) {
1016 if (!FindBoxesThatMustOverlapAHorizontalLineAndPropagate(fast_propagation,
1026 const int id = watcher->
Register(
this);
1027 helper_->WatchAllBoxes(
id);
1033 if (!VLOG_IS_ON(1))
return;
1034 std::vector<std::pair<std::string, int64_t>> stats;
1035 stats.push_back({
"RectanglePairwisePropagator/called", num_calls_});
1036 stats.push_back({
"RectanglePairwisePropagator/pairwise_conflicts",
1037 num_pairwise_conflicts_});
1038 stats.push_back({
"RectanglePairwisePropagator/pairwise_propagations",
1039 num_pairwise_propagations_});
1041 shared_stats_->AddStats(stats);
1045 if (!helper_->IsEnforced())
return true;
1046 if (!helper_->SynchronizeAndSetDirection())
return false;
1049 std::vector<PairwiseRestriction> restrictions;
1051 for (
int component_index = 0;
1052 component_index < helper_->connected_components().size();
1053 ++component_index) {
1054 horizontal_zero_area_boxes_.clear();
1055 vertical_zero_area_boxes_.clear();
1056 point_zero_area_boxes_.clear();
1057 fixed_non_zero_area_boxes_.clear();
1058 non_fixed_non_zero_area_boxes_.clear();
1059 for (
int b : helper_->connected_components()[component_index]) {
1060 if (!helper_->IsPresent(
b))
continue;
1061 const auto [x_size_max, y_size_max] = helper_->GetBoxSizesMax(
b);
1063 if (x_size_max == 0) {
1064 if (y_size_max == 0) {
1065 point_zero_area_boxes_.push_back(std::move(box));
1067 vertical_zero_area_boxes_.push_back(std::move(box));
1069 }
else if (y_size_max == 0) {
1070 horizontal_zero_area_boxes_.push_back(std::move(box));
1075 fixed_non_zero_area_boxes_.push_back(std::move(box));
1077 non_fixed_non_zero_area_boxes_.push_back(std::move(box));
1086 non_fixed_non_zero_area_boxes_, fixed_non_zero_area_boxes_,
1090 for (
auto& non_zero_area_boxes :
1091 {fixed_non_zero_area_boxes_, non_fixed_non_zero_area_boxes_}) {
1093 non_zero_area_boxes, horizontal_zero_area_boxes_, &restrictions));
1095 non_zero_area_boxes, vertical_zero_area_boxes_, &restrictions));
1097 non_zero_area_boxes, point_zero_area_boxes_, &restrictions));
1102 vertical_zero_area_boxes_, horizontal_zero_area_boxes_, &restrictions));
1110bool RectanglePairwisePropagator::FindRestrictionsAndPropagateConflict(
1111 absl::Span<const ItemWithVariableSize> items1,
1112 absl::Span<const ItemWithVariableSize> items2,
1113 std::vector<PairwiseRestriction>* restrictions) {
1114 const int max_pairs =
1116 if (items1.size() * items2.size() > max_pairs) {
1120 for (
const PairwiseRestriction& restriction : *restrictions) {
1121 if (restriction.type ==
1129bool RectanglePairwisePropagator::PropagateTwoBoxes(
1131 if (restriction.type ==
1133 num_pairwise_conflicts_++;
1135 num_pairwise_propagations_++;
1137 return helper_->PropagateRelativePosition(
1138 restriction.first_index, restriction.second_index, restriction.type);
1141#undef RETURN_IF_FALSE
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)
void SetPropagatorPriority(int id, int priority)
int Register(PropagatorInterface *propagator)
IntegerVariable AddIntegerVariable(IntegerValue lower_bound, IntegerValue upper_bound)
SchedulingConstraintHelper * GetOrCreateHelper(std::vector< Literal > enforcement_literals, const std::vector< IntervalVariable > &variables, bool register_as_disjunctive_helper=false)
AffineExpression Start(IntervalVariable i) const
AffineExpression Size(IntervalVariable i) const
AffineExpression End(IntervalVariable i) const
Literal GetOrCreatePrecedenceLiteral(AffineExpression x, AffineExpression y)
bool IsAbsent(IntervalVariable i) const
Literal PresenceLiteral(IntervalVariable i) const
bool IsOptional(IntervalVariable i) const
NoOverlap2DConstraintHelper * GetOrCreate2DHelper(std::vector< Literal > enforcement_literals, const std::vector< IntervalVariable > &x_variables, const std::vector< IntervalVariable > &y_variables)
void RegisterWith(GenericLiteralWatcher *watcher)
T Add(std::function< T(Model *)> f)
SchedulingConstraintHelper & y_helper() const
SchedulingConstraintHelper & x_helper() const
void Register(int fast_priority, int slow_priority)
~NonOverlappingRectanglesDisjunctivePropagator() override
~NonOverlappingRectanglesEnergyPropagator() override
int RegisterWith(GenericLiteralWatcher *watcher)
int RegisterWith(GenericLiteralWatcher *watcher)
~RectanglePairwisePropagator() override
int RegisterWith(GenericLiteralWatcher *watcher)
bool use_try_edge_reasoning_in_no_overlap_2d() const
bool use_timetabling_in_no_overlap_2d() const
bool use_energetic_reasoning_in_no_overlap_2d() const
::int32_t max_pairs_pairwise_reasoning_in_no_overlap_2d() const
bool use_linear3_for_no_overlap_2d_precedences() const
bool use_area_energetic_reasoning_in_no_overlap_2d() const
::int32_t no_overlap_2d_boolean_relations_limit() const
void AddStartMaxReason(int t, IntegerValue upper_bound)
absl::Span< const TaskTime > TaskByIncreasingNegatedStartMax()
void AppendAndResetReason(std::vector< IntegerLiteral > *integer_reason, std::vector< Literal > *literal_reason)
IntegerValue StartMax(int t) const
bool IsPresent(int t) const
Literal PresenceLiteral(int index) const
void AddEndMinReason(int t, IntegerValue lower_bound)
void AddReasonForBeingBeforeAssumingNoOverlap(int before, int after)
bool IsAbsent(int t) const
IntegerValue EndMin(int t) const
bool IsOptional(int t) const
std::tuple< int64_t, int64_t, const double > Coefficient
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::function< void(Model *)> EnforcedClause(absl::Span< const Literal > enforcement_literals, absl::Span< const Literal > clause)
void CreateAndRegisterTryEdgePropagator(NoOverlap2DConstraintHelper *helper, Model *model, GenericLiteralWatcher *watcher, int priority)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
std::optional< std::pair< int, int > > FindOneIntersectionIfPresent(absl::Span< const Rectangle > rectangles)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
void AddNonOverlappingRectangles(const std::vector< Literal > &enforcement_literals, const std::vector< IntervalVariable > &x, const std::vector< IntervalVariable > &y, Model *model)
void AppendPairwiseRestrictions(absl::Span< const ItemWithVariableSize > items, std::vector< PairwiseRestriction > *result)
void ConstructOverlappingSets(absl::Span< IndexedInterval > intervals, CompactVectorVector< int > *result, absl::Span< const int > order)
void CreateAndRegisterMandatoryOverlapPropagator(NoOverlap2DConstraintHelper *helper, Model *model, GenericLiteralWatcher *watcher, int priority)
std::function< void(Model *)> ConditionalWeightedSumGreaterOrEqual(absl::Span< const Literal > enforcement_literals, absl::Span< const IntegerVariable > vars, absl::Span< const int64_t > coefficients, int64_t upper_bound)
FindRectanglesResult FindRectanglesWithEnergyConflictMC(const std::vector< RectangleInRange > &intervals, absl::BitGenRef random, double temperature, double candidate_energy_usage_factor)
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
IntegerValue CapSubI(IntegerValue a, IntegerValue b)
bool AtMinOrMaxInt64I(IntegerValue t)
void AddCumulativeOverloadChecker(AffineExpression capacity, SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands, Model *model)
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
int64_t CapSub(int64_t x, int64_t y)
#define RETURN_IF_FALSE(f)
void GrowToInclude(const Rectangle &other)
IntegerValue SizeY() const
IntegerValue SizeX() const
bool IsDisjoint(const Rectangle &other) const