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);
111 bool all_optional =
true;
112 for (
int i = 0;
i < y->NumTasks(); ++
i) {
113 if (!y->IsOptional(
i)) {
114 all_optional =
false;
123 for (
int i = 0;
i < y->NumTasks(); ++
i) {
124 min_start = std::min(min_start, y->LevelZeroStartMin(
i));
125 max_end = std::max(max_end, y->LevelZeroEndMax(
i));
127 if (max_end < min_start) {
134 const IntegerVariable min_start_var =
135 CreateVariableAtOrAboveMinOf(y->Starts(), model);
136 const IntegerVariable max_end_var =
137 CreateVariableAtOrBelowMaxOf(y->Ends(), model);
139 auto* integer_trail = model->GetOrCreate<
IntegerTrail>();
140 if (integer_trail->UpperBound(max_end_var) <
141 integer_trail->LowerBound(min_start_var)) {
148 0,
CapSub(integer_trail->UpperBound(max_end_var).value(),
149 integer_trail->LowerBound(min_start_var).value())));
150 const std::vector<int64_t> coeffs = {-capacity.coeff.value(), -1, 1};
153 coeffs, capacity.constant.value()));
164 if (params.use_timetabling_in_no_overlap_2d()) {
168 model->TakeOwnership(time_tabling);
173 if (params.use_energetic_reasoning_in_no_overlap_2d()) {
181 const std::vector<IntervalVariable>& y,
210 const bool add_cumulative_relaxation =
214 if (add_cumulative_relaxation) {
219 bool some_boxes_are_only_optional_on_x =
false;
220 bool some_boxes_are_only_optional_on_y =
false;
221 for (
int i = 0;
i < x.size(); ++
i) {
230 some_boxes_are_only_optional_on_x =
true;
235 some_boxes_are_only_optional_on_y =
true;
238 if (!some_boxes_are_only_optional_on_y) {
239 AddDiffnCumulativeRelationOnX(x_helper, y_helper, model);
241 if (!some_boxes_are_only_optional_on_x) {
242 AddDiffnCumulativeRelationOnX(y_helper, x_helper, model);
272 const int num_boxes = x.size();
277 DCHECK_EQ(sat_solver->CurrentDecisionLevel(), 0);
279 for (
int i = 0;
i < num_boxes; ++
i) {
280 if (repository->
IsAbsent(x[
i]))
continue;
281 if (repository->
IsAbsent(y[
i]))
continue;
282 for (
int j =
i + 1; j < num_boxes; ++j) {
283 if (repository->
IsAbsent(x[j]))
continue;
284 if (repository->
IsAbsent(y[j]))
continue;
288 repository->
End(x[
i]), repository->
Start(x[j]));
290 repository->
End(x[j]), repository->
Start(x[
i]));
291 if ((integer_trail->LowerBound(repository->
Size(x[
i])) > 0 ||
292 integer_trail->LowerBound(repository->
Size(x[j])) > 0) &&
293 !implications->AddAtMostOne({x_ij, x_ji})) {
294 sat_solver->NotifyThatModelIsUnsat();
300 repository->
End(y[
i]), repository->
Start(y[j]));
302 repository->
End(y[j]), repository->
Start(y[
i]));
303 if ((integer_trail->LowerBound(repository->
Size(y[
i])) > 0 ||
304 integer_trail->LowerBound(repository->
Size(y[j])) > 0) &&
305 !implications->AddAtMostOne({y_ij, y_ji})) {
306 sat_solver->NotifyThatModelIsUnsat();
311 std::vector<Literal> clause = {x_ij, x_ji, y_ij, y_ji};
325 if (!sat_solver->AddProblemClause(clause)) {
333#define RETURN_IF_FALSE(f) \
334 if (!(f)) return false;
338 if (!VLOG_IS_ON(1))
return;
339 std::vector<std::pair<std::string, int64_t>> stats;
341 {
"NonOverlappingRectanglesEnergyPropagator/called", num_calls_});
343 {
"NonOverlappingRectanglesEnergyPropagator/conflicts", num_conflicts_});
345 {
"NonOverlappingRectanglesEnergyPropagator/conflicts_two_boxes",
346 num_conflicts_two_boxes_});
347 stats.push_back({
"NonOverlappingRectanglesEnergyPropagator/refined",
348 num_refined_conflicts_});
350 {
"NonOverlappingRectanglesEnergyPropagator/conflicts_with_slack",
351 num_conflicts_with_slack_});
353 shared_stats_->AddStats(stats);
358 const int num_boxes = helper_.NumBoxes();
359 if (!helper_.SynchronizeAndSetDirection())
return false;
361 Rectangle bounding_box = {.x_min = std::numeric_limits<IntegerValue>::max(),
362 .x_max = std::numeric_limits<IntegerValue>::min(),
363 .y_min = std::numeric_limits<IntegerValue>::max(),
364 .y_max = std::numeric_limits<IntegerValue>::min()};
365 std::vector<RectangleInRange> active_box_ranges;
366 active_box_ranges.reserve(num_boxes);
367 for (
int box = 0; box < num_boxes; ++box) {
368 if (!helper_.IsPresent(box))
continue;
372 active_box_ranges.push_back(std::move(rec));
375 if (active_box_ranges.size() < 2) {
380 if (active_box_ranges.size() > 1000) {
386 active_box_ranges.size()))) {
394 std::optional<Conflict> best_conflict =
395 FindConflict(std::move(active_box_ranges));
396 if (!best_conflict.has_value()) {
405 IntegerValue best_explanation_size =
406 best_conflict->opp_result.GetItemsParticipatingOnConflict().size();
407 bool refined =
false;
409 std::vector<RectangleInRange> items_participating_in_conflict;
410 items_participating_in_conflict.reserve(
411 best_conflict->items_for_opp.size());
412 for (
const auto& item :
413 best_conflict->opp_result.GetItemsParticipatingOnConflict()) {
414 items_participating_in_conflict.push_back(
415 best_conflict->items_for_opp[item.index]);
417 std::optional<Conflict> conflict =
418 FindConflict(items_participating_in_conflict);
419 if (!conflict.has_value())
break;
421 if (conflict->opp_result.GetItemsParticipatingOnConflict().size() >=
422 best_explanation_size) {
426 best_explanation_size =
427 conflict->opp_result.GetItemsParticipatingOnConflict().size();
428 best_conflict = std::move(conflict);
432 num_refined_conflicts_ += refined;
433 const std::vector<RectangleInRange> generalized_explanation =
434 GeneralizeExplanation(*best_conflict);
435 if (best_explanation_size == 2) {
436 num_conflicts_two_boxes_++;
438 helper_.ReportConflictFromInfeasibleBoxRanges(generalized_explanation);
442std::optional<NonOverlappingRectanglesEnergyPropagator::Conflict>
443NonOverlappingRectanglesEnergyPropagator::FindConflict(
444 std::vector<RectangleInRange> active_box_ranges) {
445 const auto rectangles_with_too_much_energy =
448 if (rectangles_with_too_much_energy.conflicts.empty() &&
449 rectangles_with_too_much_energy.candidates.empty()) {
453 Conflict best_conflict;
459 constexpr int kSampleSize = 10;
460 absl::InlinedVector<Rectangle, kSampleSize> sampled_rectangles;
461 std::sample(rectangles_with_too_much_energy.conflicts.begin(),
462 rectangles_with_too_much_energy.conflicts.end(),
463 std::back_inserter(sampled_rectangles), 5, *random_);
464 std::sample(rectangles_with_too_much_energy.candidates.begin(),
465 rectangles_with_too_much_energy.candidates.end(),
466 std::back_inserter(sampled_rectangles),
467 kSampleSize - sampled_rectangles.size(), *random_);
468 std::sort(sampled_rectangles.begin(), sampled_rectangles.end(),
469 [](
const Rectangle& a,
const Rectangle&
b) {
470 const bool larger = std::make_pair(a.SizeX(), a.SizeY()) >
471 std::make_pair(b.SizeX(), b.SizeY());
477 DCHECK(a.x_min <= b.x_min && a.x_max >= b.x_max &&
478 a.y_min <= b.y_min && a.y_max >= b.y_max);
481 DCHECK(a.x_min >= b.x_min && a.x_max <= b.x_max &&
482 a.y_min >= b.y_min && a.y_max <= b.y_max);
486 std::vector<IntegerValue> sizes_x, sizes_y;
487 sizes_x.reserve(active_box_ranges.size());
488 sizes_y.reserve(active_box_ranges.size());
489 std::vector<RectangleInRange> filtered_items;
490 filtered_items.reserve(active_box_ranges.size());
491 for (
const Rectangle& r : sampled_rectangles) {
494 filtered_items.clear();
495 for (
int i = 0;
i < active_box_ranges.size(); ++
i) {
496 const RectangleInRange& box = active_box_ranges[
i];
497 const Rectangle intersection = box.GetMinimumIntersection(r);
498 if (intersection.SizeX() > 0 && intersection.SizeY() > 0) {
499 sizes_x.push_back(intersection.SizeX());
500 sizes_y.push_back(intersection.SizeY());
501 filtered_items.push_back(box);
508 const auto opp_result = orthogonal_packing_checker_.TestFeasibility(
509 sizes_x, sizes_y, {r.SizeX(), r.SizeY()},
511 .use_pairwise =
true,
514 .brute_force_threshold = 7,
515 .dff2_max_number_of_parameters_to_check = 100});
517 (best_conflict.opp_result.GetResult() !=
519 best_conflict.opp_result.GetItemsParticipatingOnConflict().size() >
520 opp_result.GetItemsParticipatingOnConflict().size())) {
521 best_conflict.items_for_opp = filtered_items;
522 best_conflict.opp_result = opp_result;
523 best_conflict.rectangle_with_too_much_energy = r;
527 filtered_items.swap(active_box_ranges);
529 if (best_conflict.opp_result.GetResult() ==
531 return best_conflict;
537std::vector<RectangleInRange>
538NonOverlappingRectanglesEnergyPropagator::GeneralizeExplanation(
539 const Conflict& conflict) {
540 const Rectangle& rectangle = conflict.rectangle_with_too_much_energy;
541 OrthogonalPackingResult relaxed_result = conflict.opp_result;
544 int used_slack_count = 0;
545 const auto& items = relaxed_result.GetItemsParticipatingOnConflict();
546 for (
int i = 0;
i < items.size(); ++
i) {
547 if (!relaxed_result.HasSlack()) {
550 const RectangleInRange& range = conflict.items_for_opp[items[
i].index];
551 const RectangleInRange item_in_zero_level_range = {
553 {.x_min = helper_.x_helper().LevelZeroStartMin(range.box_index),
554 .x_max = helper_.x_helper().LevelZeroStartMax(range.box_index) +
556 .y_min = helper_.y_helper().LevelZeroStartMin(range.box_index),
557 .y_max = helper_.y_helper().LevelZeroStartMax(range.box_index) +
559 .x_size = range.x_size,
560 .y_size = range.y_size};
563 const Rectangle max_overlap =
564 item_in_zero_level_range.GetMinimumIntersection(rectangle);
565 used_slack_count += relaxed_result.TryUseSlackToReduceItemSize(
566 i, OrthogonalPackingResult::Coord::kCoordX, max_overlap.SizeX());
567 used_slack_count += relaxed_result.TryUseSlackToReduceItemSize(
568 i, OrthogonalPackingResult::Coord::kCoordY, max_overlap.SizeY());
570 num_conflicts_with_slack_ += (used_slack_count > 0);
571 VLOG_EVERY_N_SEC(2, 3)
572 <<
"Found a conflict on the OPP sub-problem of rectangle: " << rectangle
574 << conflict.opp_result.GetItemsParticipatingOnConflict().size() <<
"/"
575 << conflict.items_for_opp.size() <<
" items.";
577 std::vector<RectangleInRange> ranges_for_explanation;
578 ranges_for_explanation.reserve(conflict.items_for_opp.size());
579 std::vector<OrthogonalPackingResult::Item> sorted_items =
580 relaxed_result.GetItemsParticipatingOnConflict();
583 std::sort(sorted_items.begin(), sorted_items.end(),
584 [](
const OrthogonalPackingResult::Item& a,
585 const OrthogonalPackingResult::Item& b) {
586 return a.size_x * a.size_y > b.size_x * b.size_y;
588 for (
const auto& item : sorted_items) {
589 const RectangleInRange& range = conflict.items_for_opp[item.index];
590 ranges_for_explanation.push_back(
591 RectangleInRange::BiggestWithMinIntersection(rectangle, range,
592 item.size_x, item.size_y));
594 return ranges_for_explanation;
599 const int id = watcher->
Register(
this);
600 helper_.WatchAllBoxes(
id);
610IntegerValue FindCanonicalValue(IntegerValue lb, IntegerValue ub) {
611 if (lb == ub)
return lb;
612 if (lb <= 0 && ub > 0)
return IntegerValue(0);
613 if (lb < 0 && ub <= 0) {
614 return -FindCanonicalValue(-ub, -lb);
618 IntegerValue candidate = ub;
619 for (
int o = 0; o < 62; ++o) {
621 const IntegerValue masked_ub(ub.value() & ~mask);
622 if (masked_ub >= lb) {
623 candidate = masked_ub;
631void SplitDisjointBoxes(
const SchedulingConstraintHelper& x,
632 absl::Span<const int> boxes,
633 std::vector<absl::Span<const int>>* result) {
635 DCHECK(std::is_sorted(boxes.begin(), boxes.end(), [&x](
int a,
int b) {
636 return x.ShiftedStartMin(a) < x.ShiftedStartMin(b);
638 int current_start = 0;
639 std::size_t current_length = 1;
640 IntegerValue current_max_end =
x.EndMax(boxes[0]);
642 for (
int b = 1;
b < boxes.size(); ++
b) {
643 const int box = boxes[
b];
644 if (
x.ShiftedStartMin(box) < current_max_end) {
647 current_max_end = std::max(current_max_end,
x.EndMax(box));
649 if (current_length > 1) {
650 result->emplace_back(&boxes[current_start], current_length);
654 current_max_end =
x.EndMax(box);
659 if (current_length > 1) {
660 result->emplace_back(&boxes[current_start], current_length);
668NonOverlappingRectanglesDisjunctivePropagator::
669 NonOverlappingRectanglesDisjunctivePropagator(
672 x_(helper->NumBoxes(), model),
674 time_limit_(model->GetOrCreate<
TimeLimit>()),
675 overload_checker_(&x_),
676 forward_detectable_precedences_(true, &x_),
677 backward_detectable_precedences_(false, &x_),
678 forward_not_last_(true, &x_),
679 backward_not_last_(false, &x_),
680 forward_edge_finding_(true, &x_),
681 backward_edge_finding_(false, &x_),
682 disjunctive_with_two_items_(&x_) {}
688 int fast_priority,
int slow_priority) {
689 fast_id_ = watcher_->Register(
this);
690 watcher_->SetPropagatorPriority(fast_id_, fast_priority);
691 helper_->WatchAllBoxes(fast_id_);
695 watcher_->NotifyThatPropagatorMayNotReachFixedPointInOnePass(fast_id_);
697 const int slow_id = watcher_->Register(
this);
698 watcher_->SetPropagatorPriority(slow_id, slow_priority);
699 helper_->WatchAllBoxes(slow_id);
702bool NonOverlappingRectanglesDisjunctivePropagator::
703 FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
704 bool fast_propagation, absl::Span<const int> requested_boxes) {
715 indexed_boxes_.resize(helper_->
NumBoxes());
723 absl::flat_hash_set<int> requested_boxes_set;
724 const bool not_all_boxes = requested_boxes.size() != helper_->
NumBoxes();
726 requested_boxes_set = {requested_boxes.begin(), requested_boxes.end()};
732 auto fixed_boxes = already_checked_fixed_boxes_.
view();
733 for (
int i = temp.size(); --
i >= 0;) {
734 const int box = temp[
i].task_index;
735 if (not_all_boxes && !requested_boxes_set.contains(box))
continue;
739 if (!fixed_boxes[box]) {
741 if (x->IsAbsent(box) || y->
IsAbsent(box))
continue;
745 if (x->IsPresent(box) && !y->
IsPresent(box))
continue;
746 if (!x->IsPresent(box) && !y->
IsPresent(box) &&
753 const IntegerValue start_max = -temp[
i].time;
754 const IntegerValue end_min = y->
EndMin(box);
755 if (start_max < end_min) {
756 boxes_data[num_boxes++] = {box, start_max, end_min};
760 if (fixed_boxes[box]) {
763 if (helper_->IsFixed(box)) {
765 fixed_boxes.Set(box);
768 const Rectangle r = {
x->StartMin(box),
x->EndMax(box), start_max,
770 if (num_others == 0) {
771 other_bounding_box = r;
786 if (num_others == 0)
return true;
789 for (
int i = 0;
i < num_boxes; ++
i) {
790 const IndexedInterval& interval = boxes_data[
i];
791 const int box = interval.
index;
792 const Rectangle r = {
x->StartMin(box),
x->EndMax(box), interval.start,
794 if (other_bounding_box.
IsDisjoint(r))
continue;
795 boxes_data[new_size++] = interval;
797 num_boxes = new_size;
801 const auto boxes = absl::MakeSpan(boxes_data, num_boxes);
802 if (boxes.size() < 2)
return true;
813 rectangles_.reserve(boxes.size());
814 for (
const auto [box, y_mandatory_start, y_mandatory_end] : boxes) {
817 rectangles_.push_back(
818 {y_mandatory_start, y_mandatory_end,
819 x->StartMin(box),
x->EndMin(box)});
823 const size_t n = rectangles_.size();
824 time_limit_->AdvanceDeterministicTime(
825 static_cast<double>(n) *
static_cast<double>(absl::bit_width(n)) *
828 if (opt_pair == std::nullopt) {
835 order_.assign(
x->NumTasks(), 0);
838 for (
const auto [t, _lit, _time] :
x->TaskByIncreasingShiftedStartMin()) {
845 boxes_to_propagate_.clear();
846 reduced_overlapping_boxes_.clear();
847 int work_done = boxes.size();
848 for (
int i = 0;
i < events_overlapping_boxes_.size(); ++
i) {
849 work_done += events_overlapping_boxes_[
i].size();
850 SplitDisjointBoxes(*x, events_overlapping_boxes_[
i], &disjoint_boxes_);
851 for (
const absl::Span<const int> sub_boxes : disjoint_boxes_) {
855 const auto& insertion = reduced_overlapping_boxes_.insert(sub_boxes);
856 if (insertion.second) boxes_to_propagate_.push_back(sub_boxes);
861 time_limit_->AdvanceDeterministicTime(
static_cast<double>(work_done) * 1e-8);
867 for (
const absl::Span<const int> boxes : boxes_to_propagate_) {
870 if (!fast_propagation && boxes.size() <= 2)
continue;
872 x_.ClearOtherHelper();
873 if (!x_.ResetFromSubset(*x, boxes))
return false;
876 IntegerValue lb(std::numeric_limits<int64_t>::min());
877 IntegerValue ub(std::numeric_limits<int64_t>::max());
878 for (
const int b : boxes) {
880 ub = std::min(ub, y->
EndMin(
b) - 1);
892 const IntegerValue line_to_use_for_reason = FindCanonicalValue(lb, ub);
895 x_.SetOtherHelper(y, boxes, line_to_use_for_reason);
897 if (fast_propagation) {
898 if (x_.NumTasks() == 2) {
909 DCHECK_GT(x_.NumTasks(), 2);
925 if (!helper_->SynchronizeAndSetDirection(
true,
true,
false))
return false;
929 const bool backtrack_since_last_call = !rev_is_in_dive_;
930 watcher_->SetUntilNextBacktrack(&rev_is_in_dive_);
931 if (backtrack_since_last_call ||
932 last_helper_inprocessing_count_ != helper_->InProcessingCount()) {
933 last_helper_inprocessing_count_ = helper_->InProcessingCount();
934 const int num_tasks = helper_->NumBoxes();
935 already_checked_fixed_boxes_.ClearAndResize(num_tasks);
941 const bool fast_propagation = watcher_->GetCurrentId() == fast_id_;
942 for (
const auto subset : helper_->connected_components().AsVectorOfSpan()) {
943 if (!FindBoxesThatMustOverlapAHorizontalLineAndPropagate(fast_propagation,
949 if (!helper_->SynchronizeAndSetDirection(
true,
true,
true))
return false;
951 for (
const auto subset : helper_->connected_components().AsVectorOfSpan()) {
952 if (!FindBoxesThatMustOverlapAHorizontalLineAndPropagate(fast_propagation,
962 const int id = watcher->
Register(
this);
963 helper_->WatchAllBoxes(
id);
969 if (!VLOG_IS_ON(1))
return;
970 std::vector<std::pair<std::string, int64_t>> stats;
971 stats.push_back({
"RectanglePairwisePropagator/called", num_calls_});
972 stats.push_back({
"RectanglePairwisePropagator/pairwise_conflicts",
973 num_pairwise_conflicts_});
974 stats.push_back({
"RectanglePairwisePropagator/pairwise_propagations",
975 num_pairwise_propagations_});
977 shared_stats_->AddStats(stats);
981 if (!helper_->SynchronizeAndSetDirection())
return false;
984 std::vector<PairwiseRestriction> restrictions;
986 for (
int component_index = 0;
987 component_index < helper_->connected_components().size();
989 horizontal_zero_area_boxes_.clear();
990 vertical_zero_area_boxes_.clear();
991 point_zero_area_boxes_.clear();
992 fixed_non_zero_area_boxes_.clear();
993 non_fixed_non_zero_area_boxes_.clear();
994 for (
int b : helper_->connected_components()[component_index]) {
995 if (!helper_->IsPresent(
b))
continue;
996 const auto [x_size_max, y_size_max] = helper_->GetBoxSizesMax(
b);
998 if (x_size_max == 0) {
999 if (y_size_max == 0) {
1000 point_zero_area_boxes_.push_back(std::move(box));
1002 vertical_zero_area_boxes_.push_back(std::move(box));
1004 }
else if (y_size_max == 0) {
1005 horizontal_zero_area_boxes_.push_back(std::move(box));
1010 fixed_non_zero_area_boxes_.push_back(std::move(box));
1012 non_fixed_non_zero_area_boxes_.push_back(std::move(box));
1021 non_fixed_non_zero_area_boxes_, fixed_non_zero_area_boxes_,
1025 for (
auto& non_zero_area_boxes :
1026 {fixed_non_zero_area_boxes_, non_fixed_non_zero_area_boxes_}) {
1028 non_zero_area_boxes, horizontal_zero_area_boxes_, &restrictions));
1030 non_zero_area_boxes, vertical_zero_area_boxes_, &restrictions));
1032 non_zero_area_boxes, point_zero_area_boxes_, &restrictions));
1037 vertical_zero_area_boxes_, horizontal_zero_area_boxes_, &restrictions));
1045bool RectanglePairwisePropagator::FindRestrictionsAndPropagateConflict(
1046 absl::Span<const ItemWithVariableSize> items1,
1047 absl::Span<const ItemWithVariableSize> items2,
1048 std::vector<PairwiseRestriction>* restrictions) {
1049 const int max_pairs =
1051 if (items1.size() * items2.size() > max_pairs) {
1055 for (
const PairwiseRestriction& restriction : *restrictions) {
1056 if (restriction.type ==
1064bool RectanglePairwisePropagator::PropagateTwoBoxes(
1066 if (restriction.type ==
1068 num_pairwise_conflicts_++;
1070 num_pairwise_propagations_++;
1072 return helper_->PropagateRelativePosition(
1073 restriction.first_index, restriction.second_index, restriction.type);
1076#undef RETURN_IF_FALSE
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)
void SetPropagatorPriority(int id, int priority)
int Register(PropagatorInterface *propagator)
Registers a propagator and returns its unique ids.
IntegerVariable AddIntegerVariable(IntegerValue lower_bound, IntegerValue upper_bound)
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
Returns whether or not a interval is optional and the associated literal.
SchedulingConstraintHelper * GetOrCreateHelper(const std::vector< IntervalVariable > &variables, bool register_as_disjunctive_helper=false)
NoOverlap2DConstraintHelper * GetOrCreate2DHelper(const std::vector< IntervalVariable > &x_variables, const std::vector< IntervalVariable > &y_variables)
void RegisterWith(GenericLiteralWatcher *watcher)
SchedulingConstraintHelper & y_helper() const
SchedulingConstraintHelper & x_helper() const
void Register(int fast_priority, int slow_priority)
~NonOverlappingRectanglesDisjunctivePropagator() override
Propagates using a box energy reasoning.
~NonOverlappingRectanglesEnergyPropagator() override
int RegisterWith(GenericLiteralWatcher *watcher)
int RegisterWith(GenericLiteralWatcher *watcher)
Propagator that compares the boxes pairwise.
~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
absl::Span< const TaskTime > TaskByIncreasingNegatedStartMax()
IntegerValue StartMax(int t) const
bool IsPresent(int t) const
Literal PresenceLiteral(int index) const
bool IsAbsent(int t) const
IntegerValue EndMin(int t) const
bool IsOptional(int t) const
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::function< void(Model *)> WeightedSumGreaterOrEqual(absl::Span< const IntegerVariable > vars, const VectorInt &coefficients, int64_t lower_bound)
Weighted sum >= constant.
void CreateAndRegisterTryEdgePropagator(NoOverlap2DConstraintHelper *helper, Model *model, GenericLiteralWatcher *watcher, int priority)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
Returns the vector of the negated variables.
std::optional< std::pair< int, int > > FindOneIntersectionIfPresent(absl::Span< const Rectangle > rectangles)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
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)
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)
void AddNonOverlappingRectangles(const std::vector< IntervalVariable > &x, const std::vector< IntervalVariable > &y, Model *model)
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)
Overflows and saturated arithmetic.
In SWIG mode, we don't want anything besides these top-level includes.
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