32#include "absl/container/flat_hash_set.h"
33#include "absl/container/inlined_vector.h"
34#include "absl/log/check.h"
35#include "absl/random/bit_gen_ref.h"
36#include "absl/types/span.h"
48bool Rectangle::IsDisjoint(
const Rectangle& other)
const {
49 return x_min >= other.x_max || other.x_min >= x_max || y_min >= other.y_max ||
53absl::InlinedVector<Rectangle, 4> Rectangle::SetDifference(
54 const Rectangle& other)
const {
55 const Rectangle intersect = Intersect(other);
56 if (intersect.SizeX() == 0) {
67 absl::InlinedVector<Rectangle, 4> result;
68 if (x_min < intersect.x_min) {
70 result.push_back({.x_min = x_min,
71 .x_max = intersect.x_min,
75 if (x_max > intersect.x_max) {
77 result.push_back({.x_min = intersect.x_max,
82 if (y_min < intersect.y_min) {
84 result.push_back({.x_min = intersect.x_min,
85 .x_max = intersect.x_max,
87 .y_max = intersect.y_min});
89 if (y_max > intersect.y_max) {
91 result.push_back({.x_min = intersect.x_min,
92 .x_max = intersect.x_max,
93 .y_min = intersect.y_max,
101 absl::Span<const Rectangle> rectangles, absl::Span<int> active_rectangles) {
102 if (active_rectangles.empty())
return {};
104 std::vector<absl::Span<int>> result;
105 const int size = active_rectangles.size();
110 for (
int j =
end; j <
size; ++j) {
111 if (!rectangles[active_rectangles[
i]].IsDisjoint(
112 rectangles[active_rectangles[j]])) {
113 std::swap(active_rectangles[
end++], active_rectangles[j]);
118 result.push_back(active_rectangles.subspan(
start,
end -
start));
126 SchedulingConstraintHelper*
x,
130 IntegerValue total_energy(0);
131 for (
const int b : boxes) {
132 const IntegerValue x_min =
x->ShiftedStartMin(
b);
133 const IntegerValue x_max =
x->ShiftedEndMax(
b);
134 if (x_min < bounding_box.x_min || x_max > bounding_box.x_max)
continue;
135 const IntegerValue y_min =
y->ShiftedStartMin(
b);
136 const IntegerValue y_max =
y->ShiftedEndMax(
b);
137 if (y_min < bounding_box.y_min || y_max > bounding_box.y_max)
continue;
139 x->AddEnergyMinInIntervalReason(
b, bounding_box.x_min, bounding_box.x_max);
140 y->AddEnergyMinInIntervalReason(
b, bounding_box.y_min, bounding_box.y_max);
142 x->AddPresenceReason(
b);
143 y->AddPresenceReason(
b);
145 total_energy +=
x->SizeMin(
b) *
y->SizeMin(
b);
149 if (total_energy > bounding_box.Area())
break;
152 CHECK_GT(total_energy, bounding_box.Area());
153 x->ImportOtherReasons(*
y);
154 return x->ReportConflict();
158 const std::vector<IntegerValue>& energies,
159 absl::Span<const int> boxes,
160 Rectangle* conflict) {
162 std::vector<IntegerValue> x_starts;
163 std::vector<TaskTime> boxes_by_increasing_x_max;
164 for (
const int b : boxes) {
165 x_starts.push_back(rectangles[
b].x_min);
166 boxes_by_increasing_x_max.push_back({
b, rectangles[
b].x_max});
169 std::sort(boxes_by_increasing_x_max.begin(), boxes_by_increasing_x_max.end());
171 std::vector<IntegerValue> y_starts;
172 std::vector<IntegerValue> energy_sum;
173 std::vector<TaskTime> boxes_by_increasing_y_max;
175 std::vector<std::vector<int>> stripes(x_starts.size());
176 for (
int i = 0;
i < boxes_by_increasing_x_max.size(); ++
i) {
177 const int b = boxes_by_increasing_x_max[
i].task_index;
178 const IntegerValue x_min = rectangles[
b].x_min;
179 const IntegerValue x_max = rectangles[
b].x_max;
180 for (
int j = 0; j < x_starts.size(); ++j) {
181 if (x_starts[j] > x_min)
break;
182 stripes[j].push_back(
b);
187 boxes_by_increasing_y_max.clear();
188 for (
const int b : stripes[j]) {
189 y_starts.push_back(rectangles[
b].y_min);
190 boxes_by_increasing_y_max.push_back({
b, rectangles[
b].y_max});
193 std::sort(boxes_by_increasing_y_max.begin(),
194 boxes_by_increasing_y_max.end());
196 const IntegerValue x_size = x_max - x_starts[j];
197 energy_sum.assign(y_starts.size(), IntegerValue(0));
198 for (
int i = 0;
i < boxes_by_increasing_y_max.size(); ++
i) {
199 const int b = boxes_by_increasing_y_max[
i].task_index;
200 const IntegerValue y_min = rectangles[
b].y_min;
201 const IntegerValue y_max = rectangles[
b].y_max;
202 for (
int j = 0; j < y_starts.size(); ++j) {
203 if (y_starts[j] > y_min)
break;
204 energy_sum[j] += energies[
b];
205 if (energy_sum[j] > x_size * (y_max - y_starts[j])) {
206 if (conflict !=
nullptr) {
207 *conflict = rectangles[
b];
208 for (
int k = 0; k <
i; ++k) {
209 const int task_index = boxes_by_increasing_y_max[k].task_index;
210 if (rectangles[task_index].y_min >= y_starts[j]) {
211 conflict->GrowToInclude(rectangles[task_index]);
225 absl::Span<const Rectangle> rectangles,
226 absl::Span<const IntegerValue> rectangle_energies,
227 IntegerValue* x_threshold, IntegerValue* y_threshold,
228 Rectangle* conflict) {
234 std::vector<IntegerValue> starts;
235 std::vector<TaskTime> task_by_increasing_x_max;
236 for (
const int t : local_boxes) {
237 const IntegerValue x_min =
238 transpose ? rectangles[t].y_min : rectangles[t].x_min;
239 const IntegerValue x_max =
240 transpose ? rectangles[t].y_max : rectangles[t].x_max;
241 starts.push_back(x_min);
242 task_by_increasing_x_max.push_back({t, x_max});
248 std::sort(task_by_increasing_x_max.begin(), task_by_increasing_x_max.end());
252 IntegerValue max_conflict_height(0);
255 absl::flat_hash_set<std::pair<IntegerValue, IntegerValue>> stripes;
258 std::vector<IntegerValue> energies(starts.size(), IntegerValue(0));
261 std::vector<IntegerValue> energy_at_max_y(starts.size(), IntegerValue(0));
262 std::vector<IntegerValue> energy_at_min_y(starts.size(), IntegerValue(0));
269 const IntegerValue threshold = transpose ? *y_threshold : *x_threshold;
270 for (
int i = 0;
i < task_by_increasing_x_max.size(); ++
i) {
271 const int t = task_by_increasing_x_max[
i].task_index;
273 const IntegerValue
energy = rectangle_energies[t];
274 IntegerValue x_min = rectangles[t].x_min;
275 IntegerValue x_max = rectangles[t].x_max;
276 IntegerValue y_min = rectangles[t].y_min;
277 IntegerValue y_max = rectangles[t].y_max;
279 std::swap(x_min, y_min);
280 std::swap(x_max, y_max);
284 while (first_j + 1 < starts.size() && x_max - starts[first_j] > threshold) {
287 for (
int j = first_j; starts[j] <= x_min; ++j) {
288 const IntegerValue old_energy_at_max = energy_at_max_y[j];
289 const IntegerValue old_energy_at_min = energy_at_min_y[j];
293 const bool is_disjoint = y_min >= y_maxs[j] || y_max <= y_mins[j];
295 if (y_min <= y_mins[j]) {
296 if (y_min < y_mins[j]) {
298 energy_at_min_y[j] =
energy;
300 energy_at_min_y[j] +=
energy;
304 if (y_max >= y_maxs[j]) {
305 if (y_max > y_maxs[j]) {
307 energy_at_max_y[j] =
energy;
309 energy_at_max_y[j] +=
energy;
316 if (is_disjoint)
continue;
318 const IntegerValue
width = x_max - starts[j];
320 if (y_max - y_min > conflict_height)
continue;
321 if (conflict_height >= y_maxs[j] - y_mins[j]) {
323 if (conflict !=
nullptr) {
324 *conflict = rectangles[t];
325 for (
int k = 0; k <
i; ++k) {
326 const int task_index = task_by_increasing_x_max[k].task_index;
327 const IntegerValue task_x_min = transpose
328 ? rectangles[task_index].y_min
329 : rectangles[task_index].x_min;
330 if (task_x_min < starts[j])
continue;
331 conflict->GrowToInclude(rectangles[task_index]);
339 IntegerValue can_remove = std::min(old_energy_at_min, old_energy_at_max);
340 if (old_energy_at_min < old_energy_at_max) {
341 if (y_maxs[j] - y_min >=
345 can_remove = old_energy_at_max;
347 }
else if (old_energy_at_max < old_energy_at_min) {
348 if (y_max - y_mins[j] >=
350 can_remove = old_energy_at_min;
357 if (y_max - y_min > conflict_height)
continue;
359 if (VLOG_IS_ON(2)) stripes.insert({starts[j], x_max});
360 max_conflict_height = std::max(max_conflict_height, conflict_height);
364 VLOG(2) <<
" num_starts: " << starts.size() - 1 <<
"/" << local_boxes.size()
365 <<
" conflict_height: " << max_conflict_height
366 <<
" num_stripes:" << stripes.size() <<
" (<= " << threshold <<
")";
369 *x_threshold = std::min(*x_threshold, max_conflict_height);
371 *y_threshold = std::min(*y_threshold, max_conflict_height);
377 absl::Span<const Rectangle> cached_rectangles, absl::Span<int> boxes,
378 IntegerValue threshold_x, IntegerValue threshold_y,
379 absl::BitGenRef random) {
381 for (
const int b : boxes) {
382 const Rectangle& dim = cached_rectangles[
b];
383 if (dim.x_max - dim.x_min > threshold_x)
continue;
384 if (dim.y_max - dim.y_min > threshold_y)
continue;
385 boxes[new_size++] =
b;
387 if (new_size == 0)
return {};
388 std::shuffle(&boxes[0], &boxes[0] + new_size, random);
389 return {&boxes[0], new_size};
393 absl::Span<const Rectangle> cached_rectangles,
394 absl::Span<const IntegerValue> energies, absl::Span<int> boxes) {
396 std::sort(boxes.begin(), boxes.end(), [cached_rectangles](
int a,
int b) {
397 return cached_rectangles[a].Area() < cached_rectangles[b].Area();
400 IntegerValue total_energy(0);
401 for (
const int box : boxes) total_energy += energies[box];
405 int new_size = boxes.size();
406 while (new_size > 0 &&
407 cached_rectangles[boxes[new_size - 1]].Area() >= total_energy) {
409 total_energy -= energies[boxes[new_size]];
411 return boxes.subspan(0, new_size);
420 std::vector<IndexedInterval>* intervals,
421 std::vector<std::vector<int>>* result) {
423 if (already_sorted) {
424 DCHECK(std::is_sorted(intervals->begin(), intervals->end(),
427 std::sort(intervals->begin(), intervals->end(),
432 const int size = intervals->size();
438 for (
int end_index = 0; end_index <
size;) {
439 const IntegerValue
time = (*intervals)[end_index].start;
444 if (min_end_in_set <=
time) {
445 result->push_back({});
447 for (
int i = start_index;
i < end_index; ++
i) {
448 result->back().push_back((*intervals)[
i].
index);
450 std::swap((*intervals)[start_index++], (*intervals)[
i]);
452 min_end_in_set = std::min(min_end_in_set, (*intervals)[
i].
end);
457 if (result->back().size() == 1) result->pop_back();
462 min_end_in_set = std::min(min_end_in_set, (*intervals)[end_index].
end);
464 }
while (end_index <
size && (*intervals)[end_index].
start ==
time);
469 std::vector<IndexedInterval>* intervals,
470 std::vector<std::vector<int>>* components) {
472 if (intervals->empty())
return;
473 if (intervals->size() == 1) {
474 components->push_back({intervals->front().index});
485 std::sort(intervals->begin(), intervals->end(),
488 IntegerValue end_max_so_far = (*intervals)[0].end;
489 components->push_back({(*intervals)[0].index});
490 for (
int i = 1;
i < intervals->size(); ++
i) {
492 if (
interval.start >= end_max_so_far) {
493 components->push_back({
interval.index});
495 components->back().push_back(
interval.index);
497 end_max_so_far = std::max(end_max_so_far,
interval.end);
502 std::vector<IndexedInterval>* intervals) {
503 std::vector<int> articulation_points;
504 if (intervals->size() < 3)
return articulation_points;
511 std::sort(intervals->begin(), intervals->end(),
512 IndexedInterval::ComparatorByStart());
514 IntegerValue end_max_so_far = (*intervals)[0].end;
515 int index_of_max = 0;
517 for (
int i = 1;
i < intervals->size(); ++
i) {
518 const IndexedInterval&
interval = (*intervals)[
i];
519 if (
interval.start >= end_max_so_far) {
530 if (articulation_points.empty() ||
531 articulation_points.back() != index_of_max) {
532 articulation_points.push_back(index_of_max);
536 if (
interval.end > end_max_so_far) {
537 prev_end_max = end_max_so_far;
540 }
else if (
interval.end > prev_end_max) {
545 for (
int&
index : articulation_points)
index = (*intervals)[
index].index;
546 return articulation_points;
550bool IsZeroOrPowerOfTwo(
int value) {
return (
value & (
value - 1)) == 0; }
552void AppendPairwiseRestriction(
const ItemForPairwiseRestriction& item1,
553 const ItemForPairwiseRestriction& item2,
554 std::vector<PairwiseRestriction>* result) {
557 (item1.x.end_min <= item2.x.start_max) +
559 2 * (item2.x.end_min <= item1.x.start_max) +
561 4 * (item1.y.end_min <= item2.y.start_max) +
563 8 * (item2.y.end_min <= item1.y.start_max);
565 if (!IsZeroOrPowerOfTwo(state)) {
573 {.first_index = item1.index,
574 .second_index = item2.index,
579 if (item1.x.end_min > item2.x.start_min ||
580 item2.x.start_max < item1.x.end_max) {
581 result->push_back({.first_index = item1.index,
582 .second_index = item2.index,
583 .type = PairwiseRestriction::
584 PairwiseRestrictionType::FIRST_LEFT_OF_SECOND});
588 if (item2.x.end_min > item1.x.start_min ||
589 item1.x.start_max < item2.x.end_max) {
590 result->push_back({.first_index = item1.index,
591 .second_index = item2.index,
592 .type = PairwiseRestriction::
593 PairwiseRestrictionType::FIRST_RIGHT_OF_SECOND});
598 if (item1.y.end_min > item2.y.start_min ||
599 item2.y.start_max < item1.y.end_max) {
600 result->push_back({.first_index = item1.index,
601 .second_index = item2.index,
602 .type = PairwiseRestriction::
603 PairwiseRestrictionType::FIRST_BELOW_SECOND});
607 if (item2.y.end_min > item1.y.start_min ||
608 item1.y.start_max < item2.y.end_max) {
609 result->push_back({.first_index = item1.index,
610 .second_index = item2.index,
611 .type = PairwiseRestriction::
612 PairwiseRestrictionType::FIRST_ABOVE_SECOND});
622 absl::Span<const ItemForPairwiseRestriction> items,
623 std::vector<PairwiseRestriction>* result) {
624 for (
int i1 = 0; i1 + 1 < items.size(); ++i1) {
625 for (
int i2 = i1 + 1; i2 < items.size(); ++i2) {
626 AppendPairwiseRestriction(items[i1], items[i2], result);
632 absl::Span<const ItemForPairwiseRestriction> items,
633 absl::Span<const ItemForPairwiseRestriction> other_items,
634 std::vector<PairwiseRestriction>* result) {
635 for (
int i1 = 0; i1 < items.size(); ++i1) {
636 for (
int i2 = 0; i2 < other_items.size(); ++i2) {
637 AppendPairwiseRestriction(items[i1], other_items[i2], result);
644 num_rectangles_added_ = 0;
648 IntegerValue y_min, IntegerValue y_max) {
649 DCHECK_LE(x_min, x_max);
650 if (x_min == x_max)
return;
653 StartRectangleEvent(num_rectangles_added_, x_min, y_min, y_max));
654 events_.push_back(EndRectangleEvent(num_rectangles_added_, x_max));
655 ++num_rectangles_added_;
660 IntegerValue y_height) {
661 DCHECK_LE(x_min, x_max);
662 if (x_min == x_max)
return;
664 events_.push_back(ChangeMandatoryProfileEvent(x_min, y_height));
665 events_.push_back(ChangeMandatoryProfileEvent(x_max, -y_height));
669 std::vector<CapacityProfile::Rectangle>* result) {
670 std::sort(events_.begin(), events_.end());
673 IntegerValue mandatory_capacity(0);
679 for (
int i = 0;
i < events_.size();) {
680 const IntegerValue current_time = events_[
i].time;
681 for (;
i < events_.size(); ++
i) {
682 const Event&
event = events_[
i];
683 if (event.time != current_time)
break;
685 switch (events_[
i].type) {
686 case START_RECTANGLE: {
687 min_pq.Add({
event.index, -
event.y_min});
688 max_pq.Add({
event.index,
event.y_max});
691 case END_RECTANGLE: {
692 min_pq.Remove(event.index);
693 max_pq.Remove(event.index);
696 case CHANGE_MANDATORY_PROFILE: {
697 mandatory_capacity +=
event.y_min;
703 DCHECK(!max_pq.IsEmpty() || mandatory_capacity == 0);
704 const IntegerValue new_height =
707 : max_pq.Top().
value + min_pq.Top().
value - mandatory_capacity;
708 if (new_height != result->back().height) {
709 result->push_back({current_time, new_height});
715 std::sort(events_.begin(), events_.end());
719 IntegerValue area(0);
721 IntegerValue previous_height(0);
723 for (
int i = 0;
i < events_.size();) {
724 const IntegerValue current_time = events_[
i].time;
725 for (;
i < events_.size(); ++
i) {
726 const Event&
event = events_[
i];
727 if (event.time != current_time)
break;
729 switch (event.type) {
730 case START_RECTANGLE: {
731 min_pq.Add({
event.index, -
event.y_min});
732 max_pq.Add({
event.index,
event.y_max});
735 case END_RECTANGLE: {
736 min_pq.Remove(event.index);
737 max_pq.Remove(event.index);
740 case CHANGE_MANDATORY_PROFILE: {
745 const IntegerValue new_height =
746 max_pq.IsEmpty() ? IntegerValue(0)
748 if (previous_height != 0) {
749 area += previous_height * (current_time - previous_time);
751 previous_time = current_time;
752 previous_height = new_height;
758 IntegerValue range_max, IntegerValue
size,
759 IntegerValue interval_min,
760 IntegerValue interval_max) {
763 const IntegerValue overlap_on_left =
764 std::min(range_min +
size, interval_max) -
765 std::max(range_min, interval_min);
769 const IntegerValue overlap_on_right =
770 std::min(range_max, interval_max) -
771 std::max(range_max -
size, interval_min);
773 return std::max(IntegerValue(0), std::min(overlap_on_left, overlap_on_right));
777 const std::vector<RectangleInRange>& intervals)
778 : intervals_(intervals) {
780 if (intervals_.empty()) {
783 interval_points_sorted_by_x_.reserve(intervals_.size() * 4 + 2);
784 interval_points_sorted_by_y_.reserve(intervals_.size() * 4 + 2);
786 Rectangle bounding_box = {.x_min = std::numeric_limits<IntegerValue>::max(),
787 .x_max = std::numeric_limits<IntegerValue>::min(),
788 .y_min = std::numeric_limits<IntegerValue>::max(),
789 .y_max = std::numeric_limits<IntegerValue>::min()};
791 for (
int i = 0;
i < intervals_.size(); ++
i) {
796 std::min(bounding_box.x_min,
interval.bounding_area.x_min);
798 std::max(bounding_box.x_max,
interval.bounding_area.x_max);
800 std::min(bounding_box.y_min,
interval.bounding_area.y_min);
802 std::max(bounding_box.y_max,
interval.bounding_area.y_max);
804 interval_points_sorted_by_x_.push_back({
interval.bounding_area.x_min,
i});
805 interval_points_sorted_by_x_.push_back(
807 interval_points_sorted_by_x_.push_back(
809 interval_points_sorted_by_x_.push_back({
interval.bounding_area.x_max,
i});
811 interval_points_sorted_by_y_.push_back({
interval.bounding_area.y_min,
i});
812 interval_points_sorted_by_y_.push_back(
814 interval_points_sorted_by_y_.push_back(
816 interval_points_sorted_by_y_.push_back({
interval.bounding_area.y_max,
i});
819 full_energy_ = minimum_energy_;
822 interval_points_sorted_by_x_.push_back({bounding_box.x_min - 1, -1});
823 interval_points_sorted_by_x_.push_back({bounding_box.x_max + 1, -1});
824 interval_points_sorted_by_y_.push_back({bounding_box.y_min - 1, -1});
825 interval_points_sorted_by_y_.push_back({bounding_box.y_max + 1, -1});
827 auto comparator = [](
const IntervalPoint&
a,
const IntervalPoint&
b) {
828 return std::tie(
a.value,
a.index) < std::tie(
b.value,
b.index);
833 grouped_intervals_sorted_by_x_.reserve(interval_points_sorted_by_x_.size());
834 grouped_intervals_sorted_by_y_.reserve(interval_points_sorted_by_y_.size());
836 while (
i < interval_points_sorted_by_x_.size()) {
838 while (
i < interval_points_sorted_by_x_.size() &&
839 interval_points_sorted_by_x_[
i].value ==
840 interval_points_sorted_by_x_[idx_begin].value) {
843 grouped_intervals_sorted_by_x_.push_back(
844 {interval_points_sorted_by_x_[idx_begin].value,
845 absl::Span<IntervalPoint>(interval_points_sorted_by_x_)
846 .subspan(idx_begin,
i - idx_begin)});
850 while (
i < interval_points_sorted_by_y_.size()) {
852 while (
i < interval_points_sorted_by_y_.size() &&
853 interval_points_sorted_by_y_[
i].value ==
854 interval_points_sorted_by_y_[idx_begin].value) {
857 grouped_intervals_sorted_by_y_.push_back(
858 {interval_points_sorted_by_y_[idx_begin].value,
859 absl::Span<IntervalPoint>(interval_points_sorted_by_y_)
860 .subspan(idx_begin,
i - idx_begin)});
868 indexes_[
Edge::RIGHT] = grouped_intervals_sorted_by_x_.size() - 1;
870 indexes_[
Edge::TOP] = grouped_intervals_sorted_by_y_.size() - 1;
873 next_indexes_[
Edge::RIGHT] = grouped_intervals_sorted_by_x_.size() - 2;
875 next_indexes_[
Edge::TOP] = grouped_intervals_sorted_by_y_.size() - 2;
877 minimum_energy_ = full_energy_;
878 ranges_touching_both_boundaries_[0].clear();
879 ranges_touching_both_boundaries_[1].clear();
881 for (
int i = 0;
i < 4; ++
i) {
882 corner_count_[
i] = 0;
883 intersect_length_[
i] = 0;
884 cached_delta_energy_[
i] = 0;
896 .x_min = grouped_intervals_sorted_by_x_[indexes_[
Edge::LEFT]].coordinate,
897 .x_max = grouped_intervals_sorted_by_x_[indexes_[
Edge::RIGHT]].coordinate,
899 grouped_intervals_sorted_by_y_[indexes_[
Edge::BOTTOM]].coordinate,
900 .y_max = grouped_intervals_sorted_by_y_[indexes_[
Edge::TOP]].coordinate};
910bool CanConsumeEnergy(
const Rectangle& rectangle,
911 const RectangleInRange& item) {
912 return (rectangle.x_max > item.bounding_area.x_max - item.x_size) &&
913 (rectangle.y_max > item.bounding_area.y_max - item.y_size) &&
914 (rectangle.x_min < item.bounding_area.x_min + item.x_size) &&
915 (rectangle.y_min < item.bounding_area.y_min + item.y_size);
918std::array<bool, 4> GetPossibleEdgeIntersection(
const Rectangle& rectangle,
919 const RectangleInRange&
range) {
920 std::array<bool, 4> result;
922 result[Edge::LEFT] = rectangle.x_min >=
range.bounding_area.x_min;
923 result[Edge::BOTTOM] = rectangle.y_min >=
range.bounding_area.y_min;
924 result[Edge::RIGHT] = rectangle.x_max <=
range.bounding_area.x_max;
925 result[Edge::TOP] = rectangle.y_max <=
range.bounding_area.y_max;
936 IntegerValue intersect_length[4] = {0, 0, 0, 0};
937 IntegerValue corner_count[4] = {0, 0, 0, 0};
944 for (
int interval_idx = 0; interval_idx < intervals_.size(); interval_idx++) {
945 const RectangleInRange&
range = intervals_[interval_idx];
947 const Rectangle min_intersect =
948 range.GetMinimumIntersection(current_rectangle);
949 CHECK_LE(min_intersect.SizeX(),
range.x_size);
950 CHECK_LE(min_intersect.SizeY(),
range.y_size);
951 energy += min_intersect.Area();
953 std::array<bool, 4> touching_boundary = {
false,
false,
false,
false};
954 CHECK_EQ(CanConsumeEnergy(current_rectangle,
range) &&
955 current_rectangle.Area() != 0,
956 range.GetMinimumIntersectionArea(current_rectangle) != 0);
957 if (CanConsumeEnergy(current_rectangle,
range)) {
958 touching_boundary = GetPossibleEdgeIntersection(current_rectangle,
range);
973 current_rectangle.y_min, current_rectangle.y_max);
979 current_rectangle.y_min, current_rectangle.y_max);
985 current_rectangle.x_min, current_rectangle.x_max);
991 current_rectangle.x_min, current_rectangle.x_max);
1014 CHECK_EQ(
energy, minimum_energy_);
1015 for (
int i = 0;
i < 4;
i++) {
1016 CHECK_EQ(intersect_length[
i], intersect_length_[
i]);
1017 CHECK_EQ(corner_count[
i], corner_count_[
i]);
1030 struct OrthogonalInfo {
1041struct EdgeInfoHolder {
1046 static constexpr EdgeInfo
kLeft = {
1047 .opposite_edge = Edge::RIGHT,
1048 .shrink_direction = Direction::LEFT_AND_RIGHT,
1049 .orthogonal_shrink_direction = Direction::TOP_AND_BOTTOM,
1050 .orthogonal_edges = {
1051 {.edge = Edge::BOTTOM, .adjacent_corner = Corner::BOTTOM_LEFT},
1052 {.edge = Edge::TOP, .adjacent_corner = Corner::TOP_LEFT}}};
1054 static constexpr EdgeInfo
kRight = {
1055 .opposite_edge = Edge::LEFT,
1056 .shrink_direction = Direction::LEFT_AND_RIGHT,
1057 .orthogonal_shrink_direction = Direction::TOP_AND_BOTTOM,
1058 .orthogonal_edges = {
1059 {.edge = Edge::BOTTOM, .adjacent_corner = Corner::BOTTOM_RIGHT},
1060 {.edge = Edge::TOP, .adjacent_corner = Corner::TOP_RIGHT}}};
1061 static constexpr EdgeInfo
kBottom = {
1062 .opposite_edge = Edge::TOP,
1063 .shrink_direction = Direction::TOP_AND_BOTTOM,
1064 .orthogonal_shrink_direction = Direction::LEFT_AND_RIGHT,
1065 .orthogonal_edges = {
1066 {.edge = Edge::LEFT, .adjacent_corner = Corner::BOTTOM_LEFT},
1067 {.edge = Edge::RIGHT, .adjacent_corner = Corner::BOTTOM_RIGHT}}};
1068 static constexpr EdgeInfo
kTop = {
1069 .opposite_edge = Edge::BOTTOM,
1070 .shrink_direction = Direction::TOP_AND_BOTTOM,
1071 .orthogonal_shrink_direction = Direction::LEFT_AND_RIGHT,
1072 .orthogonal_edges = {
1073 {.edge = Edge::LEFT, .adjacent_corner = Corner::TOP_LEFT},
1074 {.edge = Edge::RIGHT, .adjacent_corner = Corner::TOP_RIGHT}}};
1081 return EdgeInfoHolder::kLeft;
1083 return EdgeInfoHolder::kRight;
1085 return EdgeInfoHolder::kBottom;
1087 return EdgeInfoHolder::kTop;
1092 const RectangleInRange&
range,
1093 const Rectangle& rectangle) {
1094 switch (direction) {
1098 rectangle.x_min, rectangle.x_max);
1102 rectangle.y_min, rectangle.y_max);
1108template <ProbingRectangle::Edge edge>
1109void ProbingRectangle::ShrinkImpl() {
1110 constexpr EdgeInfo e = GetEdgeInfo(
edge);
1112 bool update_next_index[4] = {
false,
false,
false,
false};
1113 update_next_index[
edge] =
true;
1115 IntegerValue step_1d_size;
1117 const std::vector<PointsForCoordinate>& sorted_intervals =
1119 ? grouped_intervals_sorted_by_x_
1120 : grouped_intervals_sorted_by_y_;
1123 indexes_[
edge] = next_indexes_[
edge];
1128 step_1d_size = current_rectangle.x_min - prev_rectangle.x_min;
1129 next_indexes_[
edge] =
1130 std::min(indexes_[
edge] + 1, indexes_[e.opposite_edge]);
1131 next_indexes_[e.opposite_edge] =
1132 std::max(indexes_[
edge], next_indexes_[e.opposite_edge]);
1135 step_1d_size = current_rectangle.y_min - prev_rectangle.y_min;
1136 next_indexes_[
edge] =
1137 std::min(indexes_[
edge] + 1, indexes_[e.opposite_edge]);
1138 next_indexes_[e.opposite_edge] =
1139 std::max(indexes_[
edge], next_indexes_[e.opposite_edge]);
1142 step_1d_size = prev_rectangle.x_max - current_rectangle.x_max;
1143 next_indexes_[
edge] =
1144 std::max(indexes_[
edge] - 1, indexes_[e.opposite_edge]);
1145 next_indexes_[e.opposite_edge] =
1146 std::min(indexes_[
edge], next_indexes_[e.opposite_edge]);
1149 step_1d_size = prev_rectangle.y_max - current_rectangle.y_max;
1150 next_indexes_[
edge] =
1151 std::max(indexes_[
edge] - 1, indexes_[e.opposite_edge]);
1152 next_indexes_[e.opposite_edge] =
1153 std::min(indexes_[
edge], next_indexes_[e.opposite_edge]);
1157 absl::Span<ProbingRectangle::IntervalPoint> items_touching_coordinate =
1158 sorted_intervals[indexes_[
edge]].items_touching_coordinate;
1160 IntegerValue delta_corner_count[4] = {0, 0, 0, 0};
1161 for (
const auto& item : items_touching_coordinate) {
1162 const RectangleInRange&
range = intervals_[item.index];
1163 if (!CanConsumeEnergy(prev_rectangle,
range)) {
1168 const std::array<bool, 4> touching_boundary_before =
1169 GetPossibleEdgeIntersection(prev_rectangle,
range);
1170 const std::array<bool, 4> touching_boundary_after =
1171 CanConsumeEnergy(current_rectangle,
range)
1172 ? GetPossibleEdgeIntersection(current_rectangle,
range)
1173 :
std::array<bool, 4>({
false,
false,
false,
false});
1175 bool remove_corner[4] = {
false,
false,
false,
false};
1176 auto erase_item = [
this, &prev_rectangle, &
range, &touching_boundary_before,
1177 &remove_corner](
Edge edge_to_erase) {
1178 const EdgeInfo& erase_info = GetEdgeInfo(edge_to_erase);
1179 intersect_length_[edge_to_erase] -= GetSmallest1DIntersection(
1180 erase_info.orthogonal_shrink_direction,
range, prev_rectangle);
1182 if (touching_boundary_before[erase_info.orthogonal_edges[0].edge] &&
1183 touching_boundary_before[erase_info.orthogonal_edges[1].edge]) {
1187 for (
const auto [orthogonal_edge, corner] : erase_info.orthogonal_edges) {
1188 if (touching_boundary_before[orthogonal_edge]) {
1189 remove_corner[corner] =
true;
1194 if (touching_boundary_after[
edge] && !touching_boundary_before[
edge]) {
1195 if (touching_boundary_before[e.opposite_edge]) {
1196 ranges_touching_both_boundaries_[e.shrink_direction].insert(item.index);
1197 erase_item(e.opposite_edge);
1200 intersect_length_[
edge] += GetSmallest1DIntersection(
1201 e.orthogonal_shrink_direction,
range, prev_rectangle);
1203 if (!touching_boundary_before[e.orthogonal_edges[0].edge] ||
1204 !touching_boundary_before[e.orthogonal_edges[1].edge]) {
1205 for (
const auto [orthogonal_edge, corner] : e.orthogonal_edges) {
1206 if (touching_boundary_before[orthogonal_edge]) {
1207 delta_corner_count[corner]++;
1214 for (
int i = 0;
i < 4;
i++) {
1216 const EdgeInfo& info = GetEdgeInfo(edge_to_update);
1217 const bool remove_edge = touching_boundary_before[edge_to_update] &&
1218 !touching_boundary_after[edge_to_update];
1223 update_next_index[edge_to_update] =
true;
1225 if (touching_boundary_before[info.opposite_edge]) {
1226 ranges_touching_both_boundaries_[info.shrink_direction].erase(
1229 erase_item(edge_to_update);
1233 for (
int i = 0;
i < 4;
i++) {
1234 corner_count_[
i] -= remove_corner[
i];
1239 for (
const int idx : ranges_touching_both_boundaries_[e.
shrink_direction]) {
1240 const RectangleInRange&
range = intervals_[idx];
1241 const std::array<bool, 2> touching_corner =
1243 ? std::array<bool, 2>(
1244 {current_rectangle.y_min >=
range.bounding_area.y_min,
1245 current_rectangle.y_max <=
range.bounding_area.y_max})
1246 :
std::array<bool, 2>(
1247 {current_rectangle.x_min >=
range.bounding_area.x_min,
1248 current_rectangle.x_max <=
range.bounding_area.x_max});
1249 if (touching_corner[0] == touching_corner[1]) {
1256 const IntegerValue incr =
1257 GetSmallest1DIntersection(e.shrink_direction,
range, prev_rectangle) -
1258 GetSmallest1DIntersection(e.shrink_direction,
range, current_rectangle);
1259 for (
int i = 0;
i < 2;
i++) {
1260 if (touching_corner[
i]) {
1261 intersect_length_[e.orthogonal_edges[
i].edge] -= incr;
1266 for (
const auto [orthogonal_edge, corner] : e.orthogonal_edges) {
1267 intersect_length_[orthogonal_edge] -= corner_count_[corner] * step_1d_size;
1270 for (
int i = 0;
i < 4;
i++) {
1271 corner_count_[
i] += delta_corner_count[
i];
1274 auto points_consume_energy =
1276 ¤t_rectangle](absl::Span<ProbingRectangle::IntervalPoint> points) {
1277 for (
const auto& item : points) {
1278 const RectangleInRange&
range = intervals_[item.index];
1279 if (CanConsumeEnergy(current_rectangle,
range)) {
1288 if (points_consume_energy(
1289 grouped_intervals_sorted_by_x_[next_indexes_[
Edge::LEFT]]
1290 .items_touching_coordinate)) {
1298 if (points_consume_energy(
1299 grouped_intervals_sorted_by_y_[next_indexes_[
Edge::BOTTOM]]
1300 .items_touching_coordinate)) {
1308 if (points_consume_energy(
1309 grouped_intervals_sorted_by_x_[next_indexes_[
Edge::RIGHT]]
1310 .items_touching_coordinate)) {
1318 if (points_consume_energy(
1319 grouped_intervals_sorted_by_y_[next_indexes_[
Edge::TOP]]
1320 .items_touching_coordinate)) {
1326 probe_area_ = current_rectangle.Area();
1327 CacheShrinkDeltaEnergy(0);
1328 CacheShrinkDeltaEnergy(1);
1334 ShrinkImpl<Edge::LEFT>();
1337 ShrinkImpl<Edge::BOTTOM>();
1340 ShrinkImpl<Edge::RIGHT>();
1343 ShrinkImpl<Edge::TOP>();
1350 const std::vector<PointsForCoordinate>& sorted_intervals =
1352 ? grouped_intervals_sorted_by_x_
1353 : grouped_intervals_sorted_by_y_;
1354 const IntegerValue coordinate =
1355 sorted_intervals[next_indexes_[
edge]].coordinate;
1358 return (coordinate - current_rectangle.x_min) * current_rectangle.SizeY();
1360 return (coordinate - current_rectangle.y_min) * current_rectangle.SizeX();
1362 return (current_rectangle.x_max - coordinate) * current_rectangle.SizeY();
1364 return (current_rectangle.y_max - coordinate) * current_rectangle.SizeX();
1368void ProbingRectangle::CacheShrinkDeltaEnergy(
int dimension) {
1370 Rectangle next_rectangle_up = current_rectangle;
1371 Rectangle next_rectangle_down = current_rectangle;
1372 IntegerValue step_1d_size_up, step_1d_size_down;
1373 IntegerValue units_crossed_up, units_crossed_down;
1374 IntegerValue* delta_energy_up_ptr;
1375 IntegerValue* delta_energy_down_ptr;
1377 if (dimension == 0) {
1385 next_rectangle_up.x_min =
1386 grouped_intervals_sorted_by_x_[next_indexes_[
Edge::LEFT]].coordinate;
1387 next_rectangle_down.x_max =
1388 grouped_intervals_sorted_by_x_[next_indexes_[
Edge::RIGHT]].coordinate;
1390 step_1d_size_up = next_rectangle_up.x_min - current_rectangle.x_min;
1391 step_1d_size_down = current_rectangle.x_max - next_rectangle_down.x_max;
1392 units_crossed_up = intersect_length_[
Edge::LEFT];
1393 units_crossed_down = intersect_length_[
Edge::RIGHT];
1394 delta_energy_up_ptr = &cached_delta_energy_[
Edge::LEFT];
1395 delta_energy_down_ptr = &cached_delta_energy_[
Edge::RIGHT];
1403 next_rectangle_up.y_min =
1404 grouped_intervals_sorted_by_y_[next_indexes_[
Edge::BOTTOM]].coordinate;
1405 next_rectangle_down.y_max =
1406 grouped_intervals_sorted_by_y_[next_indexes_[
Edge::TOP]].coordinate;
1408 step_1d_size_up = next_rectangle_up.y_min - current_rectangle.y_min;
1409 step_1d_size_down = current_rectangle.y_max - next_rectangle_down.y_max;
1411 units_crossed_down = intersect_length_[
Edge::TOP];
1412 delta_energy_up_ptr = &cached_delta_energy_[
Edge::BOTTOM];
1413 delta_energy_down_ptr = &cached_delta_energy_[
Edge::TOP];
1415 IntegerValue delta_energy_up = 0;
1416 IntegerValue delta_energy_down = 0;
1419 for (
const int idx : ranges_touching_both_boundaries_[dimension]) {
1420 const RectangleInRange&
range = intervals_[idx];
1423 current_rectangle.x_min, current_rectangle.x_max);
1426 current_rectangle.y_min, current_rectangle.y_max);
1427 const IntegerValue curr = curr_x * curr_y;
1428 delta_energy_up += curr;
1429 delta_energy_down += curr;
1431 if (dimension == 0) {
1434 next_rectangle_up.x_min, next_rectangle_up.x_max);
1437 next_rectangle_down.x_min, next_rectangle_down.x_max);
1439 delta_energy_up -= curr_y * up_x;
1440 delta_energy_down -= curr_y * down_x;
1444 next_rectangle_up.y_min, next_rectangle_up.y_max);
1447 next_rectangle_down.y_min, next_rectangle_down.y_max);
1449 delta_energy_up -= curr_x * up_y;
1450 delta_energy_down -= curr_x * down_y;
1453 delta_energy_up += units_crossed_up * step_1d_size_up;
1454 delta_energy_down += units_crossed_down * step_1d_size_down;
1455 *delta_energy_up_ptr = delta_energy_up;
1456 *delta_energy_down_ptr = delta_energy_down;
1471std::vector<double> GetExpTable() {
1472 std::vector<double> table(101);
1473 for (
int i = 0;
i <= 100; ++
i) {
1474 table[
i] = std::exp(-(
i - 50) / 5.0);
1482 const std::vector<RectangleInRange>& intervals, absl::BitGenRef random,
1483 double temperature,
double candidate_energy_usage_factor) {
1487 static const std::vector<double>* cached_probabilities =
1488 new std::vector<double>(GetExpTable());
1490 const double inv_temp = 1.0 / temperature;
1491 absl::InlinedVector<ProbingRectangle::Edge, 4> candidates;
1492 absl::InlinedVector<IntegerValue, 4> energy_deltas;
1493 absl::InlinedVector<double, 4> weights;
1497 if (min_energy > rect_area) {
1499 }
else if (min_energy.value() >
1500 candidate_energy_usage_factor * rect_area.value()) {
1503 if (min_energy == 0) {
1507 energy_deltas.clear();
1509 for (
int border_idx = 0; border_idx < 4; ++border_idx) {
1515 candidates.push_back(border);
1518 energy_deltas.push_back(delta_energy - delta_area);
1520 const IntegerValue min_energy_delta =
1521 *std::min_element(energy_deltas.begin(), energy_deltas.end());
1523 for (
const IntegerValue delta_slack : energy_deltas) {
1524 const int64_t table_lookup =
1525 std::max((int64_t)0,
1526 std::min((int64_t)((delta_slack - min_energy_delta).
value() *
1530 weights.push_back((*cached_probabilities)[table_lookup]);
1541std::string
RenderDot(std::optional<Rectangle> bb,
1542 absl::Span<const Rectangle>
solution) {
1543 const std::vector<std::string> colors = {
"red",
"green",
"blue",
1544 "cyan",
"yellow",
"purple"};
1545 std::stringstream ss;
1546 ss <<
"digraph {\n";
1547 ss <<
" graph [ bgcolor=lightgray ]\n";
1548 ss <<
" node [style=filled]\n";
1549 if (bb.has_value()) {
1550 ss <<
" bb [fillcolor=\"grey\" pos=\"" << 2 * bb->x_min + bb->SizeX()
1551 <<
"," << 2 * bb->y_min + bb->SizeY()
1552 <<
"!\" shape=box width=" << 2 * bb->SizeX()
1553 <<
" height=" << 2 * bb->SizeY() <<
"]\n";
1556 ss <<
" " <<
i <<
" [fillcolor=\"" << colors[
i % colors.size()]
1559 <<
"!\" shape=box width=" << 2 *
solution[
i].SizeX()
1560 <<
" height=" << 2 *
solution[
i].SizeY() <<
"]\n";
1567 const Rectangle& bounding_box, std::vector<Rectangle> ocupied_rectangles) {
1568 std::vector<Rectangle> empty_spaces = {bounding_box};
1569 std::vector<Rectangle> new_empty_spaces;
1571 std::sort(ocupied_rectangles.begin(), ocupied_rectangles.end(),
1572 [](
const Rectangle&
a,
const Rectangle&
b) {
1573 return std::tuple(a.x_min, -a.x_max, a.y_min) <
1574 std::tuple(b.x_min, -b.x_max, b.y_min);
1576 for (
const Rectangle& ocupied_rectangle : ocupied_rectangles) {
1577 new_empty_spaces.clear();
1578 for (
const auto& empty_space : empty_spaces) {
1579 for (Rectangle& r : empty_space.SetDifference(ocupied_rectangle)) {
1580 new_empty_spaces.push_back(std::move(r));
1583 empty_spaces.swap(new_empty_spaces);
1584 if (empty_spaces.empty()) {
1588 return empty_spaces;
void AddMandatoryConsumption(IntegerValue x_min, IntegerValue x_max, IntegerValue y_height)
void AddRectangle(IntegerValue x_min, IntegerValue x_max, IntegerValue y_min, IntegerValue y_max)
Adds a rectangle to the current shape.
IntegerValue GetBoundingArea()
void BuildResidualCapacityProfile(std::vector< Rectangle > *result)
IntegerValue GetShrinkDeltaEnergy(Edge edge) const
How much of GetMinimumEnergy() will change if Shrink() is called.
void Reset()
Reset to the bounding box of the whole set.
IntegerValue GetCurrentRectangleArea() const
IntegerValue GetMinimumEnergy() const
IntegerValue GetShrinkDeltaArea(Edge edge) const
How much of GetCurrentRectangleArea() will change if Shrink() is called.
void ValidateInvariants() const
ProbingRectangle(const std::vector< RectangleInRange > &intervals)
It will initialize with the bounding box of the whole set.
Rectangle GetCurrentRectangle() const
bool CanShrink(Edge edge) const
static constexpr EdgeInfo kBottom
static constexpr EdgeInfo kTop
static constexpr EdgeInfo kLeft
Direction orthogonal_shrink_direction
OrthogonalInfo orthogonal_edges[2]
Lower coordinate one first (ie., BOTTOM before TOP, LEFT before RIGHT).
Direction shrink_direction
static constexpr EdgeInfo kRight
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
void AppendPairwiseRestrictions(absl::Span< const ItemForPairwiseRestriction > items, std::vector< PairwiseRestriction > *result)
bool BoxesAreInEnergyConflict(const std::vector< Rectangle > &rectangles, const std::vector< IntegerValue > &energies, absl::Span< const int > boxes, Rectangle *conflict)
absl::Span< int > FilterBoxesAndRandomize(absl::Span< const Rectangle > cached_rectangles, absl::Span< int > boxes, IntegerValue threshold_x, IntegerValue threshold_y, absl::BitGenRef random)
bool AnalyzeIntervals(bool transpose, absl::Span< const int > local_boxes, absl::Span< const Rectangle > rectangles, absl::Span< const IntegerValue > rectangle_energies, IntegerValue *x_threshold, IntegerValue *y_threshold, Rectangle *conflict)
void ConstructOverlappingSets(bool already_sorted, std::vector< IndexedInterval > *intervals, std::vector< std::vector< int > > *result)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
std::vector< Rectangle > FindEmptySpaces(const Rectangle &bounding_box, std::vector< Rectangle > ocupied_rectangles)
absl::Span< int > FilterBoxesThatAreTooLarge(absl::Span< const Rectangle > cached_rectangles, absl::Span< const IntegerValue > energies, absl::Span< int > boxes)
std::vector< int > GetIntervalArticulationPoints(std::vector< IndexedInterval > *intervals)
int WeightedPick(absl::Span< const double > input, absl::BitGenRef random)
std::ostream & operator<<(std::ostream &os, const BoolVar &var)
std::string RenderDot(std::optional< Rectangle > bb, absl::Span< const Rectangle > solution)
bool ReportEnergyConflict(Rectangle bounding_box, absl::Span< const int > boxes, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y)
std::vector< absl::Span< int > > GetOverlappingRectangleComponents(absl::Span< const Rectangle > rectangles, absl::Span< int > active_rectangles)
FindRectanglesResult FindRectanglesWithEnergyConflictMC(const std::vector< RectangleInRange > &intervals, absl::BitGenRef random, double temperature, double candidate_energy_usage_factor)
IntegerValue Smallest1DIntersection(IntegerValue range_min, IntegerValue range_max, IntegerValue size, IntegerValue interval_min, IntegerValue interval_max)
void GetOverlappingIntervalComponents(std::vector< IndexedInterval > *intervals, std::vector< std::vector< int > > *components)
In SWIG mode, we don't want anything besides these top-level includes.
std::optional< int64_t > end
const std::optional< Range > & range
std::vector< Rectangle > conflicts
std::vector< Rectangle > candidates