28#include "absl/container/flat_hash_set.h"
29#include "absl/container/inlined_vector.h"
30#include "absl/log/check.h"
31#include "absl/types/span.h"
43#include "ortools/sat/sat_parameters.pb.h"
53IntegerVariable CreateVariableWithTightDomain(
54 absl::Span<const AffineExpression> exprs, Model*
model) {
57 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
58 for (
const AffineExpression& e : exprs) {
59 min = std::min(
min, integer_trail->LevelZeroLowerBound(e));
60 max = std::max(
max, integer_trail->LevelZeroUpperBound(e));
62 return integer_trail->AddIntegerVariable(
min,
max);
67IntegerVariable CreateVariableEqualToMinOf(
68 absl::Span<const AffineExpression> exprs, Model*
model) {
69 std::vector<LinearExpression> converted;
70 for (
const AffineExpression& affine : exprs) {
72 e.offset = affine.constant;
74 e.vars.push_back(affine.var);
75 e.coeffs.push_back(affine.coeff);
77 converted.push_back(e);
80 LinearExpression target;
81 const IntegerVariable
var = CreateVariableWithTightDomain(exprs,
model);
82 target.vars.push_back(
var);
83 target.coeffs.push_back(IntegerValue(1));
88IntegerVariable CreateVariableEqualToMaxOf(
89 absl::Span<const AffineExpression> exprs, Model*
model) {
90 std::vector<LinearExpression> converted;
91 for (
const AffineExpression& affine : exprs) {
93 e.offset = affine.constant;
95 e.vars.push_back(affine.var);
96 e.coeffs.push_back(affine.coeff);
101 LinearExpression target;
102 const IntegerVariable
var = CreateVariableWithTightDomain(exprs,
model);
104 target.coeffs.push_back(IntegerValue(1));
111void AddDiffnCumulativeRelationOnX(SchedulingConstraintHelper*
x,
112 SchedulingConstraintHelper*
y,
115 const IntegerVariable min_start_var =
116 CreateVariableEqualToMinOf(
y->Starts(),
model);
117 const IntegerVariable max_end_var =
118 CreateVariableEqualToMaxOf(
y->Ends(),
model);
121 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
123 0,
CapSub(integer_trail->UpperBound(max_end_var).value(),
124 integer_trail->LowerBound(min_start_var).value()))));
125 const std::vector<int64_t> coeffs = {-capacity.coeff.value(), -1, 1};
128 coeffs, capacity.constant.value()));
130 SchedulingDemandHelper* demands =
131 model->GetOrCreate<IntervalsRepository>()->GetOrCreateDemandHelper(
137 const SatParameters& params = *
model->GetOrCreate<SatParameters>();
138 if (params.use_timetabling_in_no_overlap_2d()) {
139 TimeTablingPerTask* time_tabling =
140 new TimeTablingPerTask(capacity,
x, demands,
model);
141 time_tabling->RegisterWith(
model->GetOrCreate<GenericLiteralWatcher>());
142 model->TakeOwnership(time_tabling);
147 if (params.use_energetic_reasoning_in_no_overlap_2d()) {
154void ClearAndAddMandatoryOverlapReason(
int box1,
int box2,
155 SchedulingConstraintHelper* helper) {
156 helper->ClearReason();
157 helper->AddPresenceReason(box1);
158 helper->AddPresenceReason(box2);
159 helper->AddReasonForBeingBefore(box1, box2);
160 helper->AddReasonForBeingBefore(box2, box1);
163bool ClearAndAddTwoBoxesConflictReason(
int box1,
int box2,
164 SchedulingConstraintHelper*
x,
165 SchedulingConstraintHelper*
y) {
166 ClearAndAddMandatoryOverlapReason(box1, box2,
x);
167 ClearAndAddMandatoryOverlapReason(box1, box2,
y);
168 x->ImportOtherReasons(*
y);
169 return x->ReportConflict();
175 const std::vector<IntervalVariable>&
y,
185 model->TakeOwnership(constraint);
192 model->TakeOwnership(pairwise_propagator);
194 const SatParameters& params = *
model->GetOrCreate<SatParameters>();
195 const bool add_cumulative_relaxation =
196 params.use_timetabling_in_no_overlap_2d() ||
197 params.use_energetic_reasoning_in_no_overlap_2d();
199 if (add_cumulative_relaxation) {
201 bool some_boxes_are_only_optional_on_x =
false;
202 bool some_boxes_are_only_optional_on_y =
false;
203 for (
int i = 0;
i <
x.size(); ++
i) {
204 if (x_helper->IsOptional(
i) && y_helper->
IsOptional(
i) &&
209 if (x_helper->IsOptional(
i) && !y_helper->
IsOptional(
i)) {
212 some_boxes_are_only_optional_on_x =
true;
214 if (y_helper->
IsOptional(
i) && !x_helper->IsOptional(
i)) {
217 some_boxes_are_only_optional_on_y =
true;
220 if (!some_boxes_are_only_optional_on_y) {
221 AddDiffnCumulativeRelationOnX(x_helper, y_helper,
model);
223 if (!some_boxes_are_only_optional_on_x) {
224 AddDiffnCumulativeRelationOnX(y_helper, x_helper,
model);
228 if (params.use_area_energetic_reasoning_in_no_overlap_2d()) {
234 model->TakeOwnership(energy_constraint);
238#define RETURN_IF_FALSE(f) \
239 if (!(f)) return false;
243 if (!VLOG_IS_ON(1))
return;
244 std::vector<std::pair<std::string, int64_t>> stats;
246 {
"NonOverlappingRectanglesEnergyPropagator/called", num_calls_});
248 {
"NonOverlappingRectanglesEnergyPropagator/conflicts", num_conflicts_});
250 {
"NonOverlappingRectanglesEnergyPropagator/conflicts_two_boxes",
251 num_conflicts_two_boxes_});
252 stats.push_back({
"NonOverlappingRectanglesEnergyPropagator/refined",
253 num_refined_conflicts_});
255 {
"NonOverlappingRectanglesEnergyPropagator/conflicts_with_slack",
256 num_conflicts_with_slack_});
263 const int num_boxes = x_.
NumTasks();
267 Rectangle bounding_box = {.x_min = std::numeric_limits<IntegerValue>::max(),
268 .x_max = std::numeric_limits<IntegerValue>::min(),
269 .y_min = std::numeric_limits<IntegerValue>::max(),
270 .y_max = std::numeric_limits<IntegerValue>::min()};
271 std::vector<RectangleInRange> active_box_ranges;
272 active_box_ranges.reserve(num_boxes);
273 for (
int box = 0; box < num_boxes; ++box) {
277 bounding_box.x_min = std::min(bounding_box.x_min, x_.
StartMin(box));
278 bounding_box.x_max = std::max(bounding_box.x_max, x_.
EndMax(box));
279 bounding_box.y_min = std::min(bounding_box.y_min, y_.
StartMin(box));
280 bounding_box.y_max = std::max(bounding_box.y_max, y_.
EndMax(box));
284 .bounding_area = {.x_min = x_.
StartMin(box),
292 if (active_box_ranges.size() < 2) {
297 if (active_box_ranges.size() > 1000) {
301 if (std::max(bounding_box.SizeX(), bounding_box.SizeY()) *
302 active_box_ranges.size() >
303 std::numeric_limits<int32_t>::max()) {
311 std::optional<Conflict> best_conflict =
312 FindConflict(std::move(active_box_ranges));
313 if (!best_conflict.has_value()) {
322 IntegerValue best_explanation_size =
323 best_conflict->opp_result.GetItemsParticipatingOnConflict().size();
324 bool refined =
false;
326 std::vector<RectangleInRange> items_participating_in_conflict;
327 items_participating_in_conflict.reserve(
328 best_conflict->items_for_opp.size());
329 for (
const auto& item :
330 best_conflict->opp_result.GetItemsParticipatingOnConflict()) {
331 items_participating_in_conflict.push_back(
332 best_conflict->items_for_opp[item.index]);
334 std::optional<Conflict> conflict =
335 FindConflict(items_participating_in_conflict);
336 if (!conflict.has_value())
break;
338 if (conflict->opp_result.GetItemsParticipatingOnConflict().size() >=
339 best_explanation_size) {
343 best_explanation_size =
344 conflict->opp_result.GetItemsParticipatingOnConflict().size();
345 best_conflict = std::move(conflict);
349 num_refined_conflicts_ += refined;
350 const std::vector<RectangleInRange> generalized_explanation =
351 GeneralizeExplanation(*best_conflict);
352 if (best_explanation_size == 2) {
353 num_conflicts_two_boxes_++;
355 BuildAndReportEnergyTooLarge(generalized_explanation);
359std::optional<NonOverlappingRectanglesEnergyPropagator::Conflict>
360NonOverlappingRectanglesEnergyPropagator::FindConflict(
361 std::vector<RectangleInRange> active_box_ranges) {
362 const auto rectangles_with_too_much_energy =
365 if (rectangles_with_too_much_energy.conflicts.empty() &&
366 rectangles_with_too_much_energy.candidates.empty()) {
370 Conflict best_conflict;
376 constexpr int kSampleSize = 10;
377 absl::InlinedVector<Rectangle, kSampleSize> sampled_rectangles;
378 std::sample(rectangles_with_too_much_energy.conflicts.begin(),
379 rectangles_with_too_much_energy.conflicts.end(),
380 std::back_inserter(sampled_rectangles), 5, *random_);
381 std::sample(rectangles_with_too_much_energy.candidates.begin(),
382 rectangles_with_too_much_energy.candidates.end(),
383 std::back_inserter(sampled_rectangles),
384 kSampleSize - sampled_rectangles.size(), *random_);
385 std::sort(sampled_rectangles.begin(), sampled_rectangles.end(),
386 [](
const Rectangle&
a,
const Rectangle&
b) {
387 const bool larger = std::make_pair(a.SizeX(), a.SizeY()) >
388 std::make_pair(b.SizeX(), b.SizeY());
394 DCHECK(a.x_min <= b.x_min && a.x_max >= b.x_max &&
395 a.y_min <= b.y_min && a.y_max >= b.y_max);
398 DCHECK(a.x_min >= b.x_min && a.x_max <= b.x_max &&
399 a.y_min >= b.y_min && a.y_max <= b.y_max);
403 std::vector<IntegerValue> sizes_x, sizes_y;
404 sizes_x.reserve(active_box_ranges.size());
405 sizes_y.reserve(active_box_ranges.size());
406 std::vector<RectangleInRange> filtered_items;
407 filtered_items.reserve(active_box_ranges.size());
408 for (
const Rectangle& r : sampled_rectangles) {
411 filtered_items.clear();
412 for (
int i = 0;
i < active_box_ranges.size(); ++
i) {
413 const RectangleInRange& box = active_box_ranges[
i];
414 const Rectangle intersection = box.GetMinimumIntersection(r);
415 if (intersection.SizeX() > 0 && intersection.SizeY() > 0) {
416 sizes_x.push_back(intersection.SizeX());
417 sizes_y.push_back(intersection.SizeY());
418 filtered_items.push_back(box);
425 const auto opp_result = orthogonal_packing_checker_.TestFeasibility(
426 sizes_x, sizes_y, {r.SizeX(), r.SizeY()},
427 OrthogonalPackingOptions{
428 .use_pairwise =
true,
431 .brute_force_threshold = 7,
432 .dff2_max_number_of_parameters_to_check = 100});
434 (best_conflict.opp_result.GetResult() !=
436 best_conflict.opp_result.GetItemsParticipatingOnConflict().size() >
437 opp_result.GetItemsParticipatingOnConflict().size())) {
438 best_conflict.items_for_opp = filtered_items;
439 best_conflict.opp_result = opp_result;
440 best_conflict.rectangle_with_too_much_energy = r;
444 filtered_items.swap(active_box_ranges);
446 if (best_conflict.opp_result.GetResult() ==
448 return best_conflict;
454std::vector<RectangleInRange>
455NonOverlappingRectanglesEnergyPropagator::GeneralizeExplanation(
456 const Conflict& conflict) {
457 const Rectangle& rectangle = conflict.rectangle_with_too_much_energy;
458 OrthogonalPackingResult relaxed_result = conflict.opp_result;
461 int used_slack_count = 0;
462 const auto& items = relaxed_result.GetItemsParticipatingOnConflict();
463 for (
int i = 0;
i < items.size(); ++
i) {
464 if (!relaxed_result.HasSlack()) {
467 const RectangleInRange&
range = conflict.items_for_opp[items[
i].index];
468 const RectangleInRange item_in_zero_level_range = {
469 .bounding_area = {.x_min = x_.LevelZeroStartMin(
range.box_index),
470 .x_max = x_.LevelZeroStartMax(
range.box_index) +
472 .y_min = y_.LevelZeroStartMin(
range.box_index),
473 .y_max = y_.LevelZeroStartMax(
range.box_index) +
475 .x_size =
range.x_size,
476 .y_size =
range.y_size};
479 const Rectangle max_overlap =
480 item_in_zero_level_range.GetMinimumIntersection(rectangle);
481 used_slack_count += relaxed_result.TryUseSlackToReduceItemSize(
482 i, OrthogonalPackingResult::Coord::kCoordX, max_overlap.SizeX());
483 used_slack_count += relaxed_result.TryUseSlackToReduceItemSize(
484 i, OrthogonalPackingResult::Coord::kCoordY, max_overlap.SizeY());
486 num_conflicts_with_slack_ += (used_slack_count > 0);
487 VLOG_EVERY_N_SEC(2, 3)
488 <<
"Found a conflict on the OPP sub-problem of rectangle: " << rectangle
490 << conflict.opp_result.GetItemsParticipatingOnConflict().size() <<
"/"
491 << conflict.items_for_opp.size() <<
" items.";
493 std::vector<RectangleInRange> ranges_for_explanation;
494 ranges_for_explanation.reserve(conflict.items_for_opp.size());
495 std::vector<OrthogonalPackingResult::Item> sorted_items =
496 relaxed_result.GetItemsParticipatingOnConflict();
499 std::sort(sorted_items.begin(), sorted_items.end(),
500 [](
const OrthogonalPackingResult::Item&
a,
501 const OrthogonalPackingResult::Item&
b) {
502 return a.size_x * a.size_y > b.size_x * b.size_y;
504 for (
const auto& item : sorted_items) {
505 const RectangleInRange&
range = conflict.items_for_opp[item.index];
506 ranges_for_explanation.push_back(
507 RectangleInRange::BiggestWithMinIntersection(rectangle,
range,
508 item.size_x, item.size_y));
510 return ranges_for_explanation;
513int NonOverlappingRectanglesEnergyPropagator::RegisterWith(
515 const int id = watcher->
Register(
this);
516 x_.WatchAllTasks(
id);
517 y_.WatchAllTasks(
id);
521bool NonOverlappingRectanglesEnergyPropagator::BuildAndReportEnergyTooLarge(
522 const std::vector<RectangleInRange>& ranges) {
523 if (ranges.size() == 2) {
524 num_conflicts_two_boxes_++;
525 return ClearAndAddTwoBoxesConflictReason(ranges[0].box_index,
526 ranges[1].box_index, &x_, &y_);
530 for (
const auto&
range : ranges) {
531 const int b =
range.box_index;
533 x_.AddStartMinReason(
b,
range.bounding_area.x_min);
534 y_.AddStartMinReason(
b,
range.bounding_area.y_min);
536 x_.AddStartMaxReason(
b,
range.bounding_area.x_max -
range.x_size);
537 y_.AddStartMaxReason(
b,
range.bounding_area.y_max -
range.y_size);
539 x_.AddSizeMinReason(
b);
540 y_.AddSizeMinReason(
b);
542 x_.AddPresenceReason(
b);
543 y_.AddPresenceReason(
b);
545 x_.ImportOtherReasons(y_);
546 return x_.ReportConflict();
555IntegerValue FindCanonicalValue(IntegerValue lb, IntegerValue ub) {
556 if (lb == ub)
return lb;
557 if (lb <= 0 && ub > 0)
return IntegerValue(0);
558 if (lb < 0 && ub <= 0) {
559 return -FindCanonicalValue(-ub, -lb);
563 IntegerValue candidate = ub;
564 for (
int o = 0; o < 62; ++o) {
566 const IntegerValue masked_ub(ub.value() & ~mask);
567 if (masked_ub >= lb) {
568 candidate = masked_ub;
576void SplitDisjointBoxes(
const SchedulingConstraintHelper&
x,
577 absl::Span<int> boxes,
578 std::vector<absl::Span<int>>* result) {
580 std::sort(boxes.begin(), boxes.end(), [&
x](
int a,
int b) {
581 return x.ShiftedStartMin(a) < x.ShiftedStartMin(b);
583 int current_start = 0;
584 std::size_t current_length = 1;
585 IntegerValue current_max_end =
x.EndMax(boxes[0]);
587 for (
int b = 1;
b < boxes.size(); ++
b) {
588 const int box = boxes[
b];
589 if (
x.ShiftedStartMin(box) < current_max_end) {
592 current_max_end = std::max(current_max_end,
x.EndMax(box));
594 if (current_length > 1) {
595 result->emplace_back(&boxes[current_start], current_length);
599 current_max_end =
x.EndMax(box);
604 if (current_length > 1) {
605 result->emplace_back(&boxes[current_start], current_length);
616bool LeftBoxBeforeRightBoxOnFirstDimension(
int left,
int right,
617 SchedulingConstraintHelper*
x,
618 SchedulingConstraintHelper*
y) {
620 const IntegerValue left_end_min =
x->EndMin(left);
621 if (left_end_min >
x->StartMin(right)) {
623 x->AddPresenceReason(left);
624 x->AddPresenceReason(right);
625 x->AddReasonForBeingBefore(left, right);
626 x->AddEndMinReason(left, left_end_min);
629 ClearAndAddMandatoryOverlapReason(left, right,
y);
631 x->ImportOtherReasons(*
y);
637 const IntegerValue right_start_max =
x->StartMax(right);
638 if (right_start_max < x->EndMax(left)) {
640 x->AddPresenceReason(left);
641 x->AddPresenceReason(right);
642 x->AddReasonForBeingBefore(left, right);
643 x->AddStartMaxReason(right, right_start_max);
646 ClearAndAddMandatoryOverlapReason(left, right,
y);
648 x->ImportOtherReasons(*
y);
660NonOverlappingRectanglesDisjunctivePropagator::
668 overload_checker_(&x_),
669 forward_detectable_precedences_(true, &x_),
670 backward_detectable_precedences_(false, &x_),
671 forward_not_last_(true, &x_),
672 backward_not_last_(false, &x_),
673 forward_edge_finding_(true, &x_),
674 backward_edge_finding_(false, &x_) {}
680 int fast_priority,
int slow_priority) {
681 fast_id_ = watcher_->
Register(
this);
690 const int slow_id = watcher_->
Register(
this);
696bool NonOverlappingRectanglesDisjunctivePropagator::
697 FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
702 if (!
y->SynchronizeAndSetTimeDirection(
true))
return false;
706 indexed_boxes_.clear();
707 const auto temp =
y->TaskByIncreasingNegatedStartMax();
708 for (
int i = temp.size(); --
i >= 0;) {
709 const int box = temp[
i].task_index;
711 if (
x.IsAbsent(box) ||
y->IsAbsent(box))
continue;
715 if (
x.IsPresent(box) && !
y->IsPresent(box))
continue;
716 if (!
x.IsPresent(box) && !
y->IsPresent(box) &&
717 x.PresenceLiteral(box) !=
y->PresenceLiteral(box)) {
722 const IntegerValue
end_min =
y->EndMin(box);
729 if (indexed_boxes_.size() < 2)
return true;
731 &events_overlapping_boxes_);
734 boxes_to_propagate_.clear();
735 reduced_overlapping_boxes_.clear();
736 for (
int i = 0;
i < events_overlapping_boxes_.size(); ++
i) {
737 SplitDisjointBoxes(
x, absl::MakeSpan(events_overlapping_boxes_[
i]),
739 for (absl::Span<int> sub_boxes : disjoint_boxes_) {
743 const auto& insertion = reduced_overlapping_boxes_.insert(sub_boxes);
744 if (insertion.second) boxes_to_propagate_.push_back(sub_boxes);
751 for (
const absl::Span<const int> boxes : boxes_to_propagate_) {
754 if (!fast_propagation && boxes.size() <= 2)
continue;
760 IntegerValue lb(std::numeric_limits<int64_t>::min());
761 IntegerValue ub(std::numeric_limits<int64_t>::max());
762 for (
const int b : boxes) {
763 lb = std::max(lb,
y->StartMax(
b));
764 ub = std::min(ub,
y->EndMin(
b) - 1);
776 const IntegerValue line_to_use_for_reason = FindCanonicalValue(lb, ub);
781 if (fast_propagation) {
811 const bool fast_propagation = watcher_->
GetCurrentId() == fast_id_;
813 fast_propagation, global_x_, &global_y_));
817 fast_propagation, global_y_, &global_x_));
824bool NonOverlappingRectanglesDisjunctivePropagator::
825 PropagateOnXWhenOnlyTwoBoxes() {
833 ClearAndAddMandatoryOverlapReason(0, 1, &x_);
838 return LeftBoxBeforeRightBoxOnFirstDimension(0, 1, &x_,
nullptr);
841 return LeftBoxBeforeRightBoxOnFirstDimension(1, 0, &x_,
nullptr);
850 const int id = watcher->
Register(
this);
858 if (!VLOG_IS_ON(1))
return;
859 std::vector<std::pair<std::string, int64_t>> stats;
860 stats.push_back({
"RectanglePairwisePropagator/called", num_calls_});
861 stats.push_back({
"RectanglePairwisePropagator/pairwise_conflicts",
862 num_pairwise_conflicts_});
863 stats.push_back({
"RectanglePairwisePropagator/pairwise_propagations",
864 num_pairwise_propagations_});
875 horizontal_zero_area_boxes_.clear();
876 vertical_zero_area_boxes_.clear();
877 point_zero_area_boxes_.clear();
878 non_zero_area_boxes_.clear();
881 const IntegerValue x_size_max = global_x_.
SizeMax(
b);
882 const IntegerValue y_size_max = global_y_.
SizeMax(
b);
884 if (x_size_max == 0) {
885 if (y_size_max == 0) {
886 box = &point_zero_area_boxes_.emplace_back();
888 box = &vertical_zero_area_boxes_.emplace_back();
890 }
else if (y_size_max == 0) {
891 box = &horizontal_zero_area_boxes_.emplace_back();
893 box = &non_zero_area_boxes_.emplace_back();
896 .x = {.start_min = global_x_.
StartMin(
b),
898 .end_min = global_x_.
EndMin(
b),
899 .end_max = global_x_.
EndMax(
b)},
900 .y = {.start_min = global_y_.
StartMin(
b),
902 .end_min = global_y_.
EndMin(
b),
903 .end_max = global_y_.
EndMax(
b)}};
906 std::vector<PairwiseRestriction> restrictions;
907 RETURN_IF_FALSE(FindRestrictionsAndPropagateConflict(non_zero_area_boxes_,
912 non_zero_area_boxes_, horizontal_zero_area_boxes_, &restrictions));
914 non_zero_area_boxes_, vertical_zero_area_boxes_, &restrictions));
916 non_zero_area_boxes_, point_zero_area_boxes_, &restrictions));
920 vertical_zero_area_boxes_, horizontal_zero_area_boxes_, &restrictions));
928bool RectanglePairwisePropagator::FindRestrictionsAndPropagateConflict(
929 const std::vector<ItemForPairwiseRestriction>& items,
930 std::vector<PairwiseRestriction>* restrictions) {
931 const int max_pairs =
932 params_->max_pairs_pairwise_reasoning_in_no_overlap_2d();
933 if (items.size() * (items.size() - 1) / 2 > max_pairs) {
937 for (
const PairwiseRestriction& restriction : *restrictions) {
938 if (restriction.type ==
946bool RectanglePairwisePropagator::FindRestrictionsAndPropagateConflict(
947 const std::vector<ItemForPairwiseRestriction>& items1,
948 const std::vector<ItemForPairwiseRestriction>& items2,
949 std::vector<PairwiseRestriction>* restrictions) {
950 const int max_pairs =
951 params_->max_pairs_pairwise_reasoning_in_no_overlap_2d();
952 if (items1.size() * items2.size() > max_pairs) {
956 for (
const PairwiseRestriction& restriction : *restrictions) {
957 if (restriction.type ==
965bool RectanglePairwisePropagator::PropagateTwoBoxes(
966 const PairwiseRestriction& restriction) {
967 const int box1 = restriction.first_index;
968 const int box2 = restriction.second_index;
969 switch (restriction.type) {
971 num_pairwise_conflicts_++;
972 return ClearAndAddTwoBoxesConflictReason(box1, box2, &global_x_,
975 num_pairwise_propagations_++;
976 return LeftBoxBeforeRightBoxOnFirstDimension(box1, box2, &global_x_,
979 num_pairwise_propagations_++;
980 return LeftBoxBeforeRightBoxOnFirstDimension(box2, box1, &global_x_,
983 num_pairwise_propagations_++;
984 return LeftBoxBeforeRightBoxOnFirstDimension(box1, box2, &global_y_,
987 num_pairwise_propagations_++;
988 return LeftBoxBeforeRightBoxOnFirstDimension(box2, box1, &global_y_,
993#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.
SchedulingConstraintHelper * GetOrCreateHelper(const std::vector< IntervalVariable > &variables, bool register_as_disjunctive_helper=false)
void Register(int fast_priority, int slow_priority)
~NonOverlappingRectanglesDisjunctivePropagator() override
Propagates using a box energy reasoning.
~NonOverlappingRectanglesEnergyPropagator() override
int RegisterWith(GenericLiteralWatcher *watcher)
Propagator that compares the boxes pairwise.
~RectanglePairwisePropagator() override
int RegisterWith(GenericLiteralWatcher *watcher)
void WatchAllTasks(int id, bool watch_max_side=true)
IntegerValue StartMax(int t) const
int NumTasks() const
Returns the number of task.
ABSL_MUST_USE_RESULT bool ReportConflict()
bool IsPresent(int t) const
void SetTimeDirection(bool is_forward)
Literal PresenceLiteral(int index) const
ABSL_MUST_USE_RESULT bool ResetFromSubset(const SchedulingConstraintHelper &other, absl::Span< const int > tasks)
IntegerValue SizeMax(int t) const
IntegerValue StartMin(int t) const
IntegerValue SizeMin(int t) const
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
void SetOtherHelper(SchedulingConstraintHelper *other_helper, absl::Span< const int > map_to_other_helper, IntegerValue event)
IntegerValue EndMax(int t) const
IntegerValue EndMin(int t) const
bool IsOptional(int t) const
void AddStats(absl::Span< const std::pair< std::string, int64_t > > stats)
Adds a bunch of stats, adding count for the same key together.
std::function< void(Model *)> WeightedSumGreaterOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
Weighted sum >= constant.
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
void AppendPairwiseRestrictions(absl::Span< const ItemForPairwiseRestriction > items, std::vector< PairwiseRestriction > *result)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Returns the vector of the negated variables.
void ConstructOverlappingSets(bool already_sorted, std::vector< IndexedInterval > *intervals, std::vector< std::vector< int > > *result)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
FindRectanglesResult FindRectanglesWithEnergyConflictMC(const std::vector< RectangleInRange > &intervals, absl::BitGenRef random, double temperature, double candidate_energy_usage_factor)
std::function< void(Model *)> IsEqualToMinOf(IntegerVariable min_var, const std::vector< IntegerVariable > &vars)
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)
void AddCumulativeOverloadChecker(AffineExpression capacity, SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands, Model *model)
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)
const std::optional< Range > & range