34#include "absl/algorithm/container.h"
35#include "absl/container/btree_set.h"
36#include "absl/container/flat_hash_map.h"
37#include "absl/container/flat_hash_set.h"
38#include "absl/container/inlined_vector.h"
39#include "absl/log/check.h"
40#include "absl/random/bit_gen_ref.h"
41#include "absl/types/optional.h"
42#include "absl/types/span.h"
65 if (intersect.
SizeX() == 0) {
76 absl::InlinedVector<Rectangle, 4> result;
79 result.push_back({.x_min =
x_min,
80 .x_max = intersect.
x_min,
86 result.push_back({.x_min = intersect.
x_max,
93 result.push_back({.x_min = intersect.
x_min,
94 .x_max = intersect.
x_max,
96 .y_max = intersect.
y_min});
100 result.push_back({.x_min = intersect.
x_min,
101 .x_max = intersect.
x_max,
102 .y_min = intersect.
y_max,
110 absl::Span<const Rectangle> rectangles) {
111 if (rectangles.empty())
return {};
113 std::vector<std::pair<int, int>> intersections =
115 const int num_intersections = intersections.size();
116 intersections.reserve(num_intersections * 2 + 1);
117 for (
int i = 0;
i < num_intersections; ++
i) {
118 intersections.push_back({intersections[
i].second, intersections[
i].first});
127 for (
int i = 0;
i < components.size(); ++
i) {
128 absl::Span<const int> component = components[
i];
130 for (
const int r : component) {
131 result.AppendToLastVector(r);
142 IntegerValue total_energy(0);
143 for (
const int b : boxes) {
144 const IntegerValue x_min = x->ShiftedStartMin(
b);
145 const IntegerValue x_max = x->ShiftedEndMax(
b);
146 if (x_min < bounding_box.x_min || x_max > bounding_box.x_max)
continue;
149 if (y_min < bounding_box.y_min || y_max > bounding_box.y_max)
continue;
151 x->AddEnergyMinInIntervalReason(
b, bounding_box.x_min, bounding_box.x_max);
154 x->AddPresenceReason(
b);
157 total_energy += x->SizeMin(
b) * y->
SizeMin(
b);
161 if (total_energy > bounding_box.Area())
break;
164 CHECK_GT(total_energy, bounding_box.Area());
165 x->ImportOtherReasons(*y);
166 return x->ReportConflict();
170 absl::Span<const IntegerValue> energies,
171 absl::Span<const int> boxes,
174 std::vector<IntegerValue> x_starts;
175 std::vector<TaskTime> boxes_by_increasing_x_max;
176 for (
const int b : boxes) {
177 x_starts.push_back(rectangles[
b].x_min);
178 boxes_by_increasing_x_max.push_back({
b, rectangles[
b].x_max});
181 std::sort(boxes_by_increasing_x_max.begin(), boxes_by_increasing_x_max.end());
183 std::vector<IntegerValue> y_starts;
184 std::vector<IntegerValue> energy_sum;
185 std::vector<TaskTime> boxes_by_increasing_y_max;
187 std::vector<std::vector<int>> stripes(x_starts.size());
188 for (
int i = 0;
i < boxes_by_increasing_x_max.size(); ++
i) {
189 const int b = boxes_by_increasing_x_max[
i].task_index;
190 const IntegerValue x_min = rectangles[
b].x_min;
191 const IntegerValue x_max = rectangles[
b].x_max;
192 for (
int j = 0; j < x_starts.size(); ++j) {
193 if (x_starts[j] > x_min)
break;
194 stripes[j].push_back(
b);
199 boxes_by_increasing_y_max.clear();
200 for (
const int b : stripes[j]) {
201 y_starts.push_back(rectangles[
b].y_min);
202 boxes_by_increasing_y_max.push_back({
b, rectangles[
b].y_max});
205 std::sort(boxes_by_increasing_y_max.begin(),
206 boxes_by_increasing_y_max.end());
208 const IntegerValue x_size = x_max - x_starts[j];
209 energy_sum.assign(y_starts.size(), IntegerValue(0));
210 for (
int i = 0;
i < boxes_by_increasing_y_max.size(); ++
i) {
211 const int b = boxes_by_increasing_y_max[
i].task_index;
212 const IntegerValue y_min = rectangles[
b].y_min;
213 const IntegerValue y_max = rectangles[
b].y_max;
214 for (
int j = 0; j < y_starts.size(); ++j) {
215 if (y_starts[j] > y_min)
break;
216 energy_sum[j] += energies[
b];
217 if (energy_sum[j] > x_size * (y_max - y_starts[j])) {
218 if (conflict !=
nullptr) {
219 *conflict = rectangles[
b];
220 for (
int k = 0; k <
i; ++k) {
221 const int task_index = boxes_by_increasing_y_max[k].task_index;
222 if (rectangles[task_index].y_min >= y_starts[j]) {
237 absl::Span<const Rectangle> rectangles,
238 absl::Span<const IntegerValue> rectangle_energies,
239 IntegerValue* x_threshold, IntegerValue* y_threshold,
246 std::vector<IntegerValue> starts;
247 std::vector<TaskTime> task_by_increasing_x_max;
248 for (
const int t : local_boxes) {
249 const IntegerValue x_min =
250 transpose ? rectangles[t].y_min : rectangles[t].x_min;
251 const IntegerValue x_max =
252 transpose ? rectangles[t].y_max : rectangles[t].x_max;
253 starts.push_back(x_min);
254 task_by_increasing_x_max.push_back({t, x_max});
260 std::sort(task_by_increasing_x_max.begin(), task_by_increasing_x_max.end());
264 IntegerValue max_conflict_height(0);
267 absl::flat_hash_set<std::pair<IntegerValue, IntegerValue>> stripes;
270 std::vector<IntegerValue> energies(starts.size(), IntegerValue(0));
273 std::vector<IntegerValue> energy_at_max_y(starts.size(), IntegerValue(0));
274 std::vector<IntegerValue> energy_at_min_y(starts.size(), IntegerValue(0));
281 const IntegerValue threshold = transpose ? *y_threshold : *x_threshold;
282 for (
int i = 0;
i < task_by_increasing_x_max.size(); ++
i) {
283 const int t = task_by_increasing_x_max[
i].task_index;
285 const IntegerValue energy = rectangle_energies[t];
286 IntegerValue x_min = rectangles[t].x_min;
287 IntegerValue x_max = rectangles[t].x_max;
288 IntegerValue y_min = rectangles[t].y_min;
289 IntegerValue y_max = rectangles[t].y_max;
291 std::swap(x_min, y_min);
292 std::swap(x_max, y_max);
296 while (first_j + 1 < starts.size() && x_max - starts[first_j] > threshold) {
299 for (
int j = first_j; starts[j] <= x_min; ++j) {
300 const IntegerValue old_energy_at_max = energy_at_max_y[j];
301 const IntegerValue old_energy_at_min = energy_at_min_y[j];
303 energies[j] += energy;
305 const bool is_disjoint = y_min >= y_maxs[j] || y_max <= y_mins[j];
307 if (y_min <= y_mins[j]) {
308 if (y_min < y_mins[j]) {
310 energy_at_min_y[j] = energy;
312 energy_at_min_y[j] += energy;
316 if (y_max >= y_maxs[j]) {
317 if (y_max > y_maxs[j]) {
319 energy_at_max_y[j] = energy;
321 energy_at_max_y[j] += energy;
328 if (is_disjoint)
continue;
330 const IntegerValue width = x_max - starts[j];
331 IntegerValue conflict_height =
CeilRatio(energies[j], width) - 1;
332 if (y_max - y_min > conflict_height)
continue;
333 if (conflict_height >= y_maxs[j] - y_mins[j]) {
335 if (conflict !=
nullptr) {
336 *conflict = rectangles[t];
337 for (
int k = 0; k <
i; ++k) {
338 const int task_index = task_by_increasing_x_max[k].task_index;
339 const IntegerValue task_x_min = transpose
340 ? rectangles[task_index].y_min
341 : rectangles[task_index].x_min;
342 if (task_x_min < starts[j])
continue;
351 IntegerValue can_remove = std::min(old_energy_at_min, old_energy_at_max);
352 if (old_energy_at_min < old_energy_at_max) {
353 if (y_maxs[j] - y_min >=
354 CeilRatio(energies[j] - old_energy_at_min, width)) {
357 can_remove = old_energy_at_max;
359 }
else if (old_energy_at_max < old_energy_at_min) {
360 if (y_max - y_mins[j] >=
361 CeilRatio(energies[j] - old_energy_at_max, width)) {
362 can_remove = old_energy_at_min;
365 conflict_height =
CeilRatio(energies[j] - can_remove, width) - 1;
369 if (y_max - y_min > conflict_height)
continue;
371 if (VLOG_IS_ON(2)) stripes.insert({starts[j], x_max});
372 max_conflict_height = std::max(max_conflict_height, conflict_height);
376 VLOG(2) <<
" num_starts: " << starts.size() - 1 <<
"/" << local_boxes.size()
377 <<
" conflict_height: " << max_conflict_height
378 <<
" num_stripes:" << stripes.size() <<
" (<= " << threshold <<
")";
381 *x_threshold = std::min(*x_threshold, max_conflict_height);
383 *y_threshold = std::min(*y_threshold, max_conflict_height);
389 absl::Span<const Rectangle> cached_rectangles, absl::Span<int> boxes,
390 IntegerValue threshold_x, IntegerValue threshold_y,
391 absl::BitGenRef random) {
393 for (
const int b : boxes) {
395 if (dim.
x_max - dim.
x_min > threshold_x)
continue;
396 if (dim.
y_max - dim.
y_min > threshold_y)
continue;
397 boxes[new_size++] =
b;
399 if (new_size == 0)
return {};
400 std::shuffle(&boxes[0], &boxes[0] + new_size, random);
401 return {&boxes[0], new_size};
405 absl::Span<const Rectangle> cached_rectangles,
406 absl::Span<const IntegerValue> energies, absl::Span<int> boxes) {
408 std::sort(boxes.begin(), boxes.end(), [cached_rectangles](
int a,
int b) {
409 return cached_rectangles[a].Area() < cached_rectangles[b].Area();
412 IntegerValue total_energy(0);
413 for (
const int box : boxes) total_energy += energies[box];
417 int new_size = boxes.size();
418 while (new_size > 0 &&
419 cached_rectangles[boxes[new_size - 1]].Area() >= total_energy) {
421 total_energy -= energies[boxes[new_size]];
423 return boxes.subspan(0, new_size);
427 CompactVectorVector<int>* result,
428 absl::Span<const int> order) {
430 DCHECK(std::is_sorted(intervals.begin(), intervals.end(),
438 const int size = intervals.size();
439 for (
int end_index = 0; end_index < size;) {
440 const IntegerValue time = intervals[end_index].start;
445 if (min_end_in_set <= time) {
447 if (start_index + 1 < end_index) {
449 for (
int i = start_index;
i < end_index; ++
i) {
450 result->AppendToLastVector(intervals[
i].index);
456 int new_start = end_index;
457 for (
int i = end_index; --
i >= start_index;) {
458 if (intervals[
i].end > time) {
459 min_end_in_set = std::min(min_end_in_set, intervals[
i].end);
460 intervals[--new_start] = intervals[
i];
463 start_index = new_start;
468 const int old_end = end_index;
469 while (end_index < size && intervals[end_index].start == time) {
470 min_end_in_set = std::min(min_end_in_set, intervals[end_index].end);
476 if (!order.empty() && end_index > old_end) {
477 std::sort(intervals.data() + old_end, intervals.data() + end_index,
479 return order[a.index] < order[b.index];
482 intervals.data() + start_index, intervals.data() + old_end,
483 intervals.data() + end_index,
485 return order[a.index] < order[b.index];
491 if (start_index + 1 < size) {
493 for (
int i = start_index;
i < size; ++
i) {
494 result->AppendToLastVector(intervals[
i].index);
500 std::vector<IndexedInterval>* intervals,
501 std::vector<std::vector<int>>* components) {
503 if (intervals->empty())
return;
504 if (intervals->size() == 1) {
505 components->push_back({intervals->front().index});
516 std::sort(intervals->begin(), intervals->end(),
519 IntegerValue end_max_so_far = (*intervals)[0].end;
520 components->push_back({(*intervals)[0].index});
521 for (
int i = 1;
i < intervals->size(); ++
i) {
523 if (interval.
start >= end_max_so_far) {
524 components->push_back({interval.
index});
526 components->back().push_back(interval.
index);
528 end_max_so_far = std::max(end_max_so_far, interval.
end);
533 std::vector<IndexedInterval>* intervals) {
534 std::vector<int> articulation_points;
535 if (intervals->size() < 3)
return articulation_points;
538 DCHECK_LT(interval.start, interval.end);
542 std::sort(intervals->begin(), intervals->end(),
543 IndexedInterval::ComparatorByStart());
545 IntegerValue end_max_so_far = (*intervals)[0].end;
546 int index_of_max = 0;
548 for (
int i = 1;
i < intervals->size(); ++
i) {
549 const IndexedInterval& interval = (*intervals)[
i];
550 if (interval.start >= end_max_so_far) {
552 end_max_so_far = interval.end;
561 if (articulation_points.empty() ||
562 articulation_points.back() != index_of_max) {
563 articulation_points.push_back(index_of_max);
567 if (interval.end > end_max_so_far) {
568 prev_end_max = end_max_so_far;
569 end_max_so_far = interval.end;
571 }
else if (interval.end > prev_end_max) {
572 prev_end_max = interval.end;
576 for (
int& index : articulation_points) index = (*intervals)[index].index;
577 return articulation_points;
581bool IsZeroOrPowerOfTwo(
int value) {
return (value & (value - 1)) == 0; }
585 std::vector<PairwiseRestriction>* result) {
588 (item1.x.end_min <= item2.x.start_max) +
590 2 * (item2.x.end_min <= item1.x.start_max) +
592 4 * (item1.y.end_min <= item2.y.start_max) +
594 8 * (item2.y.end_min <= item1.y.start_max);
596 if (!IsZeroOrPowerOfTwo(state)) {
604 {.first_index = item1.index,
605 .second_index = item2.index,
610 if (item1.x.end_min > item2.x.start_min ||
611 item2.x.start_max < item1.x.end_max) {
612 result->push_back({.first_index = item1.index,
613 .second_index = item2.index,
614 .type = PairwiseRestriction::
615 PairwiseRestrictionType::FIRST_LEFT_OF_SECOND});
619 if (item2.x.end_min > item1.x.start_min ||
620 item1.x.start_max < item2.x.end_max) {
621 result->push_back({.first_index = item1.index,
622 .second_index = item2.index,
623 .type = PairwiseRestriction::
624 PairwiseRestrictionType::FIRST_RIGHT_OF_SECOND});
629 if (item1.y.end_min > item2.y.start_min ||
630 item2.y.start_max < item1.y.end_max) {
631 result->push_back({.first_index = item1.index,
632 .second_index = item2.index,
633 .type = PairwiseRestriction::
634 PairwiseRestrictionType::FIRST_BELOW_SECOND});
638 if (item2.y.end_min > item1.y.start_min ||
639 item1.y.start_max < item2.y.end_max) {
640 result->push_back({.first_index = item1.index,
641 .second_index = item2.index,
642 .type = PairwiseRestriction::
643 PairwiseRestrictionType::FIRST_ABOVE_SECOND});
653 std::vector<PairwiseRestriction>* result) {
654 for (
int i1 = 0; i1 + 1 < items.size(); ++i1) {
655 for (
int i2 = i1 + 1; i2 < items.size(); ++i2) {
656 AppendPairwiseRestriction(items[i1], items[i2], result);
662 absl::Span<const ItemWithVariableSize> items,
663 absl::Span<const ItemWithVariableSize> other_items,
664 std::vector<PairwiseRestriction>* result) {
665 for (
int i1 = 0; i1 < items.size(); ++i1) {
666 for (
int i2 = 0; i2 < other_items.size(); ++i2) {
667 AppendPairwiseRestriction(items[i1], other_items[i2], result);
674 num_rectangles_added_ = 0;
678 IntegerValue y_min, IntegerValue y_max) {
679 DCHECK_LE(x_min, x_max);
680 if (x_min == x_max)
return;
683 StartRectangleEvent(num_rectangles_added_, x_min, y_min, y_max));
684 events_.push_back(EndRectangleEvent(num_rectangles_added_, x_max));
685 ++num_rectangles_added_;
690 IntegerValue y_height) {
691 DCHECK_LE(x_min, x_max);
692 if (x_min == x_max)
return;
694 events_.push_back(ChangeMandatoryProfileEvent(x_min, y_height));
695 events_.push_back(ChangeMandatoryProfileEvent(x_max, -y_height));
699 std::vector<CapacityProfile::Rectangle>* result) {
700 std::sort(events_.begin(), events_.end());
703 IntegerValue mandatory_capacity(0);
709 for (
int i = 0;
i < events_.size();) {
710 const IntegerValue current_time = events_[
i].time;
711 for (;
i < events_.size(); ++
i) {
712 const Event&
event = events_[
i];
713 if (event.time != current_time)
break;
715 switch (events_[
i].type) {
716 case START_RECTANGLE: {
717 min_pq.Add({
event.index, -
event.y_min});
718 max_pq.Add({
event.index,
event.y_max});
721 case END_RECTANGLE: {
722 min_pq.Remove(event.index);
723 max_pq.Remove(event.index);
726 case CHANGE_MANDATORY_PROFILE: {
727 mandatory_capacity +=
event.y_min;
733 DCHECK(!max_pq.IsEmpty() || mandatory_capacity == 0);
734 const IntegerValue new_height =
737 : max_pq.Top().value + min_pq.Top().value - mandatory_capacity;
738 if (new_height != result->back().height) {
739 result->push_back({current_time, new_height});
744IntegerValue CapacityProfile::GetBoundingArea() {
745 std::sort(events_.begin(), events_.end());
749 IntegerValue area(0);
751 IntegerValue previous_height(0);
753 for (
int i = 0;
i < events_.size();) {
754 const IntegerValue current_time = events_[
i].time;
755 for (;
i < events_.size(); ++
i) {
756 const Event&
event = events_[
i];
757 if (event.time != current_time)
break;
759 switch (event.type) {
760 case START_RECTANGLE: {
761 min_pq.Add({
event.index, -
event.y_min});
762 max_pq.Add({
event.index,
event.y_max});
765 case END_RECTANGLE: {
766 min_pq.Remove(event.index);
767 max_pq.Remove(event.index);
770 case CHANGE_MANDATORY_PROFILE: {
775 const IntegerValue new_height =
776 max_pq.IsEmpty() ? IntegerValue(0)
777 : max_pq.Top().value + min_pq.Top().value;
778 if (previous_height != 0) {
779 area += previous_height * (current_time - previous_time);
781 previous_time = current_time;
782 previous_height = new_height;
788 IntegerValue range_max, IntegerValue size,
789 IntegerValue interval_min,
790 IntegerValue interval_max) {
793 const IntegerValue overlap_on_left =
794 std::min(range_min + size, interval_max) -
795 std::max(range_min, interval_min);
799 const IntegerValue overlap_on_right =
800 std::min(range_max, interval_max) -
801 std::max(range_max - size, interval_min);
803 return std::max(IntegerValue(0), std::min(overlap_on_left, overlap_on_right));
806ProbingRectangle::ProbingRectangle(
807 const std::vector<RectangleInRange>& intervals)
808 : intervals_(intervals) {
810 if (intervals_.empty()) {
813 interval_points_sorted_by_x_.reserve(intervals_.size() * 4 + 2);
814 interval_points_sorted_by_y_.reserve(intervals_.size() * 4 + 2);
816 Rectangle bounding_box = {.x_min = std::numeric_limits<IntegerValue>::max(),
817 .x_max = std::numeric_limits<IntegerValue>::min(),
818 .y_min = std::numeric_limits<IntegerValue>::max(),
819 .y_max = std::numeric_limits<IntegerValue>::min()};
821 for (
int i = 0;
i < intervals_.size(); ++
i) {
835 interval_points_sorted_by_x_.push_back(
837 interval_points_sorted_by_x_.push_back(
842 interval_points_sorted_by_y_.push_back(
844 interval_points_sorted_by_y_.push_back(
849 full_energy_ = minimum_energy_;
852 interval_points_sorted_by_x_.push_back({bounding_box.x_min - 1, -1});
853 interval_points_sorted_by_x_.push_back({bounding_box.x_max + 1, -1});
854 interval_points_sorted_by_y_.push_back({bounding_box.y_min - 1, -1});
855 interval_points_sorted_by_y_.push_back({bounding_box.y_max + 1, -1});
857 auto comparator = [](
const IntervalPoint& a,
const IntervalPoint&
b) {
858 return std::tie(a.value, a.index) < std::tie(
b.value,
b.index);
863 grouped_intervals_sorted_by_x_.reserve(interval_points_sorted_by_x_.size());
864 grouped_intervals_sorted_by_y_.reserve(interval_points_sorted_by_y_.size());
866 while (
i < interval_points_sorted_by_x_.size()) {
868 while (
i < interval_points_sorted_by_x_.size() &&
869 interval_points_sorted_by_x_[
i].value ==
870 interval_points_sorted_by_x_[idx_begin].value) {
873 grouped_intervals_sorted_by_x_.push_back(
874 {interval_points_sorted_by_x_[idx_begin].value,
875 absl::Span<IntervalPoint>(interval_points_sorted_by_x_)
876 .subspan(idx_begin,
i - idx_begin)});
880 while (
i < interval_points_sorted_by_y_.size()) {
882 while (
i < interval_points_sorted_by_y_.size() &&
883 interval_points_sorted_by_y_[
i].value ==
884 interval_points_sorted_by_y_[idx_begin].value) {
887 grouped_intervals_sorted_by_y_.push_back(
888 {interval_points_sorted_by_y_[idx_begin].value,
889 absl::Span<IntervalPoint>(interval_points_sorted_by_y_)
890 .subspan(idx_begin,
i - idx_begin)});
897 indexes_[Edge::LEFT] = 0;
898 indexes_[
Edge::RIGHT] = grouped_intervals_sorted_by_x_.size() - 1;
900 indexes_[
Edge::TOP] = grouped_intervals_sorted_by_y_.size() - 1;
903 next_indexes_[
Edge::RIGHT] = grouped_intervals_sorted_by_x_.size() - 2;
905 next_indexes_[
Edge::TOP] = grouped_intervals_sorted_by_y_.size() - 2;
907 minimum_energy_ = full_energy_;
908 ranges_touching_both_boundaries_[0].clear();
909 ranges_touching_both_boundaries_[1].clear();
911 for (
int i = 0;
i < 4; ++
i) {
912 corner_count_[
i] = 0;
913 intersect_length_[
i] = 0;
914 cached_delta_energy_[
i] = 0;
919 Shrink(Edge::BOTTOM);
924Rectangle ProbingRectangle::GetCurrentRectangle()
const {
926 .x_min = grouped_intervals_sorted_by_x_[indexes_[
Edge::LEFT]].coordinate,
927 .x_max = grouped_intervals_sorted_by_x_[indexes_[
Edge::RIGHT]].coordinate,
929 grouped_intervals_sorted_by_y_[indexes_[
Edge::BOTTOM]].coordinate,
930 .y_max = grouped_intervals_sorted_by_y_[indexes_[
Edge::TOP]].coordinate};
940bool CanConsumeEnergy(
const Rectangle& rectangle,
941 const RectangleInRange& item) {
942 return (rectangle.x_max > item.bounding_area.x_max - item.x_size) &&
943 (rectangle.y_max > item.bounding_area.y_max - item.y_size) &&
944 (rectangle.x_min < item.bounding_area.x_min + item.x_size) &&
945 (rectangle.y_min < item.bounding_area.y_min + item.y_size);
948std::array<bool, 4> GetPossibleEdgeIntersection(
const Rectangle& rectangle,
949 const RectangleInRange& range) {
950 std::array<bool, 4> result;
951 using Edge = ProbingRectangle::Edge;
952 result[Edge::LEFT] = rectangle.x_min >= range.bounding_area.x_min;
953 result[Edge::BOTTOM] = rectangle.y_min >= range.bounding_area.y_min;
954 result[Edge::RIGHT] = rectangle.x_max <= range.bounding_area.x_max;
955 result[Edge::TOP] = rectangle.y_max <= range.bounding_area.y_max;
963void ProbingRectangle::ValidateInvariants()
const {
964 const Rectangle current_rectangle = GetCurrentRectangle();
966 IntegerValue intersect_length[4] = {0, 0, 0, 0};
967 IntegerValue corner_count[4] = {0, 0, 0, 0};
968 IntegerValue energy = 0;
969 CHECK_LE(next_indexes_[Edge::LEFT], indexes_[Edge::RIGHT]);
970 CHECK_LE(next_indexes_[Edge::BOTTOM], indexes_[Edge::TOP]);
971 CHECK_GE(next_indexes_[Edge::TOP], indexes_[Edge::BOTTOM]);
972 CHECK_GE(next_indexes_[Edge::RIGHT], indexes_[Edge::LEFT]);
974 for (
int interval_idx = 0; interval_idx < intervals_.size(); interval_idx++) {
975 const RectangleInRange& range = intervals_[interval_idx];
977 const Rectangle min_intersect =
978 range.GetMinimumIntersection(current_rectangle);
979 CHECK_LE(min_intersect.SizeX(), range.x_size);
980 CHECK_LE(min_intersect.SizeY(), range.y_size);
981 energy += min_intersect.Area();
983 std::array<bool, 4> touching_boundary = {
false,
false,
false,
false};
984 CHECK_EQ(CanConsumeEnergy(current_rectangle, range) &&
985 current_rectangle.Area() != 0,
986 range.GetMinimumIntersectionArea(current_rectangle) != 0);
987 if (CanConsumeEnergy(current_rectangle, range)) {
988 touching_boundary = GetPossibleEdgeIntersection(current_rectangle, range);
992 touching_boundary[Edge::LEFT] && touching_boundary[Edge::RIGHT],
993 ranges_touching_both_boundaries_[Direction::LEFT_AND_RIGHT].contains(
996 touching_boundary[Edge::TOP] && touching_boundary[Edge::BOTTOM],
997 ranges_touching_both_boundaries_[Direction::TOP_AND_BOTTOM].contains(
1000 if (touching_boundary[Edge::LEFT] && !touching_boundary[Edge::RIGHT]) {
1002 range.bounding_area.y_min, range.bounding_area.y_max, range.y_size,
1003 current_rectangle.y_min, current_rectangle.y_max);
1006 if (touching_boundary[Edge::RIGHT] && !touching_boundary[Edge::LEFT]) {
1008 range.bounding_area.y_min, range.bounding_area.y_max, range.y_size,
1009 current_rectangle.y_min, current_rectangle.y_max);
1012 if (touching_boundary[Edge::TOP] && !touching_boundary[Edge::BOTTOM]) {
1014 range.bounding_area.x_min, range.bounding_area.x_max, range.x_size,
1015 current_rectangle.x_min, current_rectangle.x_max);
1018 if (touching_boundary[Edge::BOTTOM] && !touching_boundary[Edge::TOP]) {
1020 range.bounding_area.x_min, range.bounding_area.x_max, range.x_size,
1021 current_rectangle.x_min, current_rectangle.x_max);
1024 if ((touching_boundary[Edge::LEFT] && touching_boundary[Edge::RIGHT]) ||
1025 (touching_boundary[Edge::TOP] && touching_boundary[Edge::BOTTOM])) {
1030 if (touching_boundary[Edge::BOTTOM] && touching_boundary[Edge::LEFT]) {
1031 corner_count[RectangleInRange::Corner::BOTTOM_LEFT]++;
1033 if (touching_boundary[Edge::BOTTOM] && touching_boundary[Edge::RIGHT]) {
1034 corner_count[RectangleInRange::Corner::BOTTOM_RIGHT]++;
1036 if (touching_boundary[Edge::TOP] && touching_boundary[Edge::LEFT]) {
1037 corner_count[RectangleInRange::Corner::TOP_LEFT]++;
1039 if (touching_boundary[Edge::TOP] && touching_boundary[Edge::RIGHT]) {
1040 corner_count[RectangleInRange::Corner::TOP_RIGHT]++;
1044 CHECK_EQ(energy, minimum_energy_);
1045 for (
int i = 0;
i < 4;
i++) {
1046 CHECK_EQ(intersect_length[i], intersect_length_[i]);
1047 CHECK_EQ(corner_count[i], corner_count_[i]);
1054 using Edge = ProbingRectangle::Edge;
1055 using Direction = ProbingRectangle::Direction;
1056 using Corner = RectangleInRange::Corner;
1060 struct OrthogonalInfo {
1062 Corner adjacent_corner;
1065 Direction shrink_direction;
1066 Direction orthogonal_shrink_direction;
1068 OrthogonalInfo orthogonal_edges[2];
1071struct EdgeInfoHolder {
1072 using Edge = ProbingRectangle::Edge;
1073 using Direction = ProbingRectangle::Direction;
1074 using Corner = RectangleInRange::Corner;
1076 static constexpr EdgeInfo kLeft = {
1077 .opposite_edge = Edge::RIGHT,
1078 .shrink_direction = Direction::LEFT_AND_RIGHT,
1079 .orthogonal_shrink_direction = Direction::TOP_AND_BOTTOM,
1080 .orthogonal_edges = {
1081 {.edge = Edge::BOTTOM, .adjacent_corner = Corner::BOTTOM_LEFT},
1082 {.edge = Edge::TOP, .adjacent_corner = Corner::TOP_LEFT}}};
1084 static constexpr EdgeInfo kRight = {
1085 .opposite_edge = Edge::LEFT,
1086 .shrink_direction = Direction::LEFT_AND_RIGHT,
1087 .orthogonal_shrink_direction = Direction::TOP_AND_BOTTOM,
1088 .orthogonal_edges = {
1089 {.edge = Edge::BOTTOM, .adjacent_corner = Corner::BOTTOM_RIGHT},
1090 {.edge = Edge::TOP, .adjacent_corner = Corner::TOP_RIGHT}}};
1091 static constexpr EdgeInfo kBottom = {
1092 .opposite_edge = Edge::TOP,
1093 .shrink_direction = Direction::TOP_AND_BOTTOM,
1094 .orthogonal_shrink_direction = Direction::LEFT_AND_RIGHT,
1095 .orthogonal_edges = {
1096 {.edge = Edge::LEFT, .adjacent_corner = Corner::BOTTOM_LEFT},
1097 {.edge = Edge::RIGHT, .adjacent_corner = Corner::BOTTOM_RIGHT}}};
1098 static constexpr EdgeInfo kTop = {
1099 .opposite_edge = Edge::BOTTOM,
1100 .shrink_direction = Direction::TOP_AND_BOTTOM,
1101 .orthogonal_shrink_direction = Direction::LEFT_AND_RIGHT,
1102 .orthogonal_edges = {
1103 {.edge = Edge::LEFT, .adjacent_corner = Corner::TOP_LEFT},
1104 {.edge = Edge::RIGHT, .adjacent_corner = Corner::TOP_RIGHT}}};
1107constexpr const EdgeInfo& GetEdgeInfo(ProbingRectangle::Edge edge) {
1108 using Edge = ProbingRectangle::Edge;
1111 return EdgeInfoHolder::kLeft;
1113 return EdgeInfoHolder::kRight;
1115 return EdgeInfoHolder::kBottom;
1117 return EdgeInfoHolder::kTop;
1121IntegerValue GetSmallest1DIntersection(ProbingRectangle::Direction direction,
1122 const RectangleInRange& range,
1123 const Rectangle& rectangle) {
1124 switch (direction) {
1125 case ProbingRectangle::Direction::LEFT_AND_RIGHT:
1127 range.bounding_area.x_max, range.x_size,
1128 rectangle.x_min, rectangle.x_max);
1129 case ProbingRectangle::Direction::TOP_AND_BOTTOM:
1131 range.bounding_area.y_max, range.y_size,
1132 rectangle.y_min, rectangle.y_max);
1138template <ProbingRectangle::Edge edge>
1139void ProbingRectangle::ShrinkImpl() {
1140 constexpr EdgeInfo e = GetEdgeInfo(edge);
1142 bool update_next_index[4] = {
false,
false,
false,
false};
1143 update_next_index[edge] =
true;
1145 IntegerValue step_1d_size;
1146 minimum_energy_ -= GetShrinkDeltaEnergy(edge);
1147 const std::vector<PointsForCoordinate>& sorted_intervals =
1148 e.shrink_direction == Direction::LEFT_AND_RIGHT
1149 ? grouped_intervals_sorted_by_x_
1150 : grouped_intervals_sorted_by_y_;
1152 const Rectangle prev_rectangle = GetCurrentRectangle();
1153 indexes_[edge] = next_indexes_[edge];
1154 const Rectangle current_rectangle = GetCurrentRectangle();
1158 step_1d_size = current_rectangle.x_min - prev_rectangle.x_min;
1159 next_indexes_[edge] =
1160 std::min(indexes_[edge] + 1, indexes_[e.opposite_edge]);
1161 next_indexes_[e.opposite_edge] =
1162 std::max(indexes_[edge], next_indexes_[e.opposite_edge]);
1165 step_1d_size = current_rectangle.y_min - prev_rectangle.y_min;
1166 next_indexes_[edge] =
1167 std::min(indexes_[edge] + 1, indexes_[e.opposite_edge]);
1168 next_indexes_[e.opposite_edge] =
1169 std::max(indexes_[edge], next_indexes_[e.opposite_edge]);
1172 step_1d_size = prev_rectangle.x_max - current_rectangle.x_max;
1173 next_indexes_[edge] =
1174 std::max(indexes_[edge] - 1, indexes_[e.opposite_edge]);
1175 next_indexes_[e.opposite_edge] =
1176 std::min(indexes_[edge], next_indexes_[e.opposite_edge]);
1179 step_1d_size = prev_rectangle.y_max - current_rectangle.y_max;
1180 next_indexes_[edge] =
1181 std::max(indexes_[edge] - 1, indexes_[e.opposite_edge]);
1182 next_indexes_[e.opposite_edge] =
1183 std::min(indexes_[edge], next_indexes_[e.opposite_edge]);
1187 absl::Span<ProbingRectangle::IntervalPoint> items_touching_coordinate =
1188 sorted_intervals[indexes_[edge]].items_touching_coordinate;
1190 IntegerValue delta_corner_count[4] = {0, 0, 0, 0};
1191 for (
const auto& item : items_touching_coordinate) {
1192 const RectangleInRange& range = intervals_[item.index];
1193 if (!CanConsumeEnergy(prev_rectangle, range)) {
1198 const std::array<bool, 4> touching_boundary_before =
1199 GetPossibleEdgeIntersection(prev_rectangle, range);
1200 const std::array<bool, 4> touching_boundary_after =
1201 CanConsumeEnergy(current_rectangle, range)
1202 ? GetPossibleEdgeIntersection(current_rectangle, range)
1203 : std::array<bool, 4>({
false,
false,
false,
false});
1205 bool remove_corner[4] = {
false,
false,
false,
false};
1206 auto erase_item = [
this, &prev_rectangle, &range, &touching_boundary_before,
1207 &remove_corner](Edge edge_to_erase) {
1208 const EdgeInfo& erase_info = GetEdgeInfo(edge_to_erase);
1209 intersect_length_[edge_to_erase] -= GetSmallest1DIntersection(
1210 erase_info.orthogonal_shrink_direction, range, prev_rectangle);
1212 if (touching_boundary_before[erase_info.orthogonal_edges[0].edge] &&
1213 touching_boundary_before[erase_info.orthogonal_edges[1].edge]) {
1217 for (
const auto [orthogonal_edge, corner] : erase_info.orthogonal_edges) {
1218 if (touching_boundary_before[orthogonal_edge]) {
1219 remove_corner[corner] =
true;
1224 if (touching_boundary_after[edge] && !touching_boundary_before[edge]) {
1225 if (touching_boundary_before[e.opposite_edge]) {
1226 ranges_touching_both_boundaries_[e.shrink_direction].insert(item.index);
1227 erase_item(e.opposite_edge);
1230 intersect_length_[edge] += GetSmallest1DIntersection(
1231 e.orthogonal_shrink_direction, range, prev_rectangle);
1233 if (!touching_boundary_before[e.orthogonal_edges[0].edge] ||
1234 !touching_boundary_before[e.orthogonal_edges[1].edge]) {
1235 for (
const auto [orthogonal_edge, corner] : e.orthogonal_edges) {
1236 if (touching_boundary_before[orthogonal_edge]) {
1237 delta_corner_count[corner]++;
1244 for (
int i = 0;
i < 4;
i++) {
1245 const Edge edge_to_update = (Edge)i;
1246 const EdgeInfo& info = GetEdgeInfo(edge_to_update);
1247 const bool remove_edge = touching_boundary_before[edge_to_update] &&
1248 !touching_boundary_after[edge_to_update];
1253 update_next_index[edge_to_update] =
true;
1255 if (touching_boundary_before[info.opposite_edge]) {
1256 ranges_touching_both_boundaries_[info.shrink_direction].erase(
1259 erase_item(edge_to_update);
1263 for (
int i = 0;
i < 4;
i++) {
1264 corner_count_[
i] -= remove_corner[
i];
1269 for (
const int idx : ranges_touching_both_boundaries_[e.shrink_direction]) {
1270 const RectangleInRange& range = intervals_[idx];
1271 const std::array<bool, 2> touching_corner =
1272 (e.shrink_direction == Direction::LEFT_AND_RIGHT)
1273 ? std::array<bool, 2>(
1274 {current_rectangle.y_min >= range.bounding_area.y_min,
1275 current_rectangle.y_max <= range.bounding_area.y_max})
1276 : std::array<bool, 2>(
1277 {current_rectangle.x_min >= range.bounding_area.x_min,
1278 current_rectangle.x_max <= range.bounding_area.x_max});
1279 if (touching_corner[0] == touching_corner[1]) {
1286 const IntegerValue incr =
1287 GetSmallest1DIntersection(e.shrink_direction, range, prev_rectangle) -
1288 GetSmallest1DIntersection(e.shrink_direction, range, current_rectangle);
1289 for (
int i = 0;
i < 2;
i++) {
1290 if (touching_corner[i]) {
1291 intersect_length_[e.orthogonal_edges[
i].edge] -= incr;
1296 for (
const auto [orthogonal_edge, corner] : e.orthogonal_edges) {
1297 intersect_length_[orthogonal_edge] -= corner_count_[corner] * step_1d_size;
1300 for (
int i = 0;
i < 4;
i++) {
1301 corner_count_[
i] += delta_corner_count[
i];
1304 auto points_consume_energy =
1306 ¤t_rectangle](absl::Span<ProbingRectangle::IntervalPoint> points) {
1307 for (
const auto& item : points) {
1308 const RectangleInRange& range = intervals_[item.index];
1309 if (CanConsumeEnergy(current_rectangle, range)) {
1315 if (update_next_index[Edge::LEFT]) {
1316 for (; next_indexes_[Edge::LEFT] < indexes_[Edge::RIGHT];
1317 ++next_indexes_[Edge::LEFT]) {
1318 if (points_consume_energy(
1319 grouped_intervals_sorted_by_x_[next_indexes_[Edge::LEFT]]
1320 .items_touching_coordinate)) {
1325 if (update_next_index[Edge::BOTTOM]) {
1326 for (; next_indexes_[Edge::BOTTOM] < indexes_[Edge::TOP];
1327 ++next_indexes_[Edge::BOTTOM]) {
1328 if (points_consume_energy(
1329 grouped_intervals_sorted_by_y_[next_indexes_[Edge::BOTTOM]]
1330 .items_touching_coordinate)) {
1335 if (update_next_index[Edge::RIGHT]) {
1336 for (; next_indexes_[Edge::RIGHT] > indexes_[Edge::LEFT];
1337 --next_indexes_[Edge::RIGHT]) {
1338 if (points_consume_energy(
1339 grouped_intervals_sorted_by_x_[next_indexes_[Edge::RIGHT]]
1340 .items_touching_coordinate)) {
1345 if (update_next_index[Edge::TOP]) {
1346 for (; next_indexes_[Edge::TOP] > indexes_[Edge::BOTTOM];
1347 --next_indexes_[Edge::TOP]) {
1348 if (points_consume_energy(
1349 grouped_intervals_sorted_by_y_[next_indexes_[Edge::TOP]]
1350 .items_touching_coordinate)) {
1356 probe_area_ = current_rectangle.Area();
1357 CacheShrinkDeltaEnergy(0);
1358 CacheShrinkDeltaEnergy(1);
1361void ProbingRectangle::Shrink(Edge edge) {
1364 ShrinkImpl<Edge::LEFT>();
1367 ShrinkImpl<Edge::BOTTOM>();
1370 ShrinkImpl<Edge::RIGHT>();
1373 ShrinkImpl<Edge::TOP>();
1378IntegerValue ProbingRectangle::GetShrinkDeltaArea(Edge edge)
const {
1379 const Rectangle current_rectangle = GetCurrentRectangle();
1380 const std::vector<PointsForCoordinate>& sorted_intervals =
1382 ? grouped_intervals_sorted_by_x_
1383 : grouped_intervals_sorted_by_y_;
1384 const IntegerValue coordinate =
1385 sorted_intervals[next_indexes_[edge]].coordinate;
1388 return (coordinate - current_rectangle.x_min) * current_rectangle.SizeY();
1390 return (coordinate - current_rectangle.y_min) * current_rectangle.SizeX();
1392 return (current_rectangle.x_max - coordinate) * current_rectangle.SizeY();
1394 return (current_rectangle.y_max - coordinate) * current_rectangle.SizeX();
1398void ProbingRectangle::CacheShrinkDeltaEnergy(
int dimension) {
1399 const Rectangle current_rectangle = GetCurrentRectangle();
1400 Rectangle next_rectangle_up = current_rectangle;
1401 Rectangle next_rectangle_down = current_rectangle;
1402 IntegerValue step_1d_size_up, step_1d_size_down;
1403 IntegerValue units_crossed_up, units_crossed_down;
1404 IntegerValue* delta_energy_up_ptr;
1405 IntegerValue* delta_energy_down_ptr;
1407 if (dimension == 0) {
1409 if (!CanShrink(Edge::LEFT)) {
1410 cached_delta_energy_[Edge::LEFT] = 0;
1411 cached_delta_energy_[Edge::RIGHT] = 0;
1415 next_rectangle_up.x_min =
1416 grouped_intervals_sorted_by_x_[next_indexes_[Edge::LEFT]].coordinate;
1417 next_rectangle_down.x_max =
1418 grouped_intervals_sorted_by_x_[next_indexes_[Edge::RIGHT]].coordinate;
1420 step_1d_size_up = next_rectangle_up.x_min - current_rectangle.x_min;
1421 step_1d_size_down = current_rectangle.x_max - next_rectangle_down.x_max;
1422 units_crossed_up = intersect_length_[Edge::LEFT];
1423 units_crossed_down = intersect_length_[Edge::RIGHT];
1424 delta_energy_up_ptr = &cached_delta_energy_[Edge::LEFT];
1425 delta_energy_down_ptr = &cached_delta_energy_[Edge::RIGHT];
1427 if (!CanShrink(Edge::TOP)) {
1428 cached_delta_energy_[Edge::BOTTOM] = 0;
1429 cached_delta_energy_[Edge::TOP] = 0;
1433 next_rectangle_up.y_min =
1434 grouped_intervals_sorted_by_y_[next_indexes_[Edge::BOTTOM]].coordinate;
1435 next_rectangle_down.y_max =
1436 grouped_intervals_sorted_by_y_[next_indexes_[Edge::TOP]].coordinate;
1438 step_1d_size_up = next_rectangle_up.y_min - current_rectangle.y_min;
1439 step_1d_size_down = current_rectangle.y_max - next_rectangle_down.y_max;
1440 units_crossed_up = intersect_length_[Edge::BOTTOM];
1441 units_crossed_down = intersect_length_[Edge::TOP];
1442 delta_energy_up_ptr = &cached_delta_energy_[Edge::BOTTOM];
1443 delta_energy_down_ptr = &cached_delta_energy_[Edge::TOP];
1445 IntegerValue delta_energy_up = 0;
1446 IntegerValue delta_energy_down = 0;
1449 for (
const int idx : ranges_touching_both_boundaries_[dimension]) {
1450 const RectangleInRange& range = intervals_[idx];
1452 range.bounding_area.x_min, range.bounding_area.x_max, range.x_size,
1453 current_rectangle.x_min, current_rectangle.x_max);
1455 range.bounding_area.y_min, range.bounding_area.y_max, range.y_size,
1456 current_rectangle.y_min, current_rectangle.y_max);
1457 const IntegerValue curr = curr_x * curr_y;
1458 delta_energy_up += curr;
1459 delta_energy_down += curr;
1461 if (dimension == 0) {
1463 range.bounding_area.x_min, range.bounding_area.x_max, range.x_size,
1464 next_rectangle_up.x_min, next_rectangle_up.x_max);
1466 range.bounding_area.x_min, range.bounding_area.x_max, range.x_size,
1467 next_rectangle_down.x_min, next_rectangle_down.x_max);
1469 delta_energy_up -= curr_y * up_x;
1470 delta_energy_down -= curr_y * down_x;
1473 range.bounding_area.y_min, range.bounding_area.y_max, range.y_size,
1474 next_rectangle_up.y_min, next_rectangle_up.y_max);
1476 range.bounding_area.y_min, range.bounding_area.y_max, range.y_size,
1477 next_rectangle_down.y_min, next_rectangle_down.y_max);
1479 delta_energy_up -= curr_x * up_y;
1480 delta_energy_down -= curr_x * down_y;
1483 delta_energy_up += units_crossed_up * step_1d_size_up;
1484 delta_energy_down += units_crossed_down * step_1d_size_down;
1485 *delta_energy_up_ptr = delta_energy_up;
1486 *delta_energy_down_ptr = delta_energy_down;
1489bool ProbingRectangle::CanShrink(Edge edge)
const {
1501std::vector<double> GetExpTable() {
1502 std::vector<double> table(101);
1503 for (
int i = 0; i <= 100; ++i) {
1504 table[i] = std::exp(-(i - 50) / 5.0);
1512 const std::vector<RectangleInRange>& intervals, absl::BitGenRef random,
1513 double temperature,
double candidate_energy_usage_factor) {
1517 static const std::vector<double>* cached_probabilities =
1518 new std::vector<double>(GetExpTable());
1520 const double inv_temp = 1.0 / temperature;
1521 absl::InlinedVector<ProbingRectangle::Edge, 4> candidates;
1522 absl::InlinedVector<IntegerValue, 4> energy_deltas;
1523 absl::InlinedVector<double, 4> weights;
1527 if (min_energy > rect_area) {
1529 }
else if (min_energy.value() >
1530 candidate_energy_usage_factor * rect_area.value()) {
1533 if (min_energy == 0) {
1537 energy_deltas.clear();
1539 for (
int border_idx = 0; border_idx < 4; ++border_idx) {
1545 candidates.push_back(border);
1548 energy_deltas.push_back(delta_energy - delta_area);
1550 const IntegerValue min_energy_delta =
1551 *std::min_element(energy_deltas.begin(), energy_deltas.end());
1553 for (
const IntegerValue delta_slack : energy_deltas) {
1554 const int64_t table_lookup =
1555 std::max((int64_t)0,
1556 std::min((int64_t)((delta_slack - min_energy_delta).value() *
1560 weights.push_back((*cached_probabilities)[table_lookup]);
1571std::string RenderDot(std::optional<Rectangle> bb,
1572 absl::Span<const Rectangle>
solution,
1573 std::string_view extra_dot_payload) {
1574 const std::vector<std::string> colors = {
"#0000ff80",
"#ee00ee80",
1575 "#ff000080",
"#eeee0080",
1576 "#00ff0080",
"#00eeee80"};
1577 std::stringstream ss;
1578 ss <<
"digraph {\n";
1579 ss <<
" graph [ bgcolor=lightgray ]\n";
1580 ss <<
" node [style=filled shape=box]\n";
1581 if (bb.has_value()) {
1582 ss <<
" bb [fillcolor=\"grey\" pos=\"" << 2 * bb->x_min + bb->SizeX()
1583 <<
"," << 2 * bb->y_min + bb->SizeY() <<
"!\" width=" << 2 * bb->SizeX()
1584 <<
" height=" << 2 * bb->SizeY() <<
"]\n";
1587 ss <<
" " <<
i <<
" [fillcolor=\"" << colors[
i % colors.size()]
1590 <<
"!\" width=" << 2 *
solution[
i].SizeX()
1591 <<
" height=" << 2 *
solution[
i].SizeY() <<
"]\n";
1593 ss << extra_dot_payload;
1598std::vector<Rectangle> FindEmptySpaces(
1599 const Rectangle& bounding_box, std::vector<Rectangle> ocupied_rectangles) {
1601 std::sort(ocupied_rectangles.begin(), ocupied_rectangles.end(),
1603 return std::tuple(a.x_min, -a.x_max, a.y_min) <
1604 std::tuple(b.x_min, -b.x_max, b.y_min);
1609std::vector<Rectangle> PavedRegionDifference(
1610 std::vector<Rectangle> original_region,
1611 absl::Span<const Rectangle> area_to_remove) {
1612 std::vector<Rectangle> new_area_to_cover;
1613 new_area_to_cover.reserve(original_region.size());
1614 for (
const Rectangle& rectangle : area_to_remove) {
1615 new_area_to_cover.clear();
1616 for (
const Rectangle& r : original_region) {
1617 const auto& new_rectangles = r.RegionDifference(rectangle);
1618 new_area_to_cover.insert(new_area_to_cover.end(), new_rectangles.begin(),
1619 new_rectangles.end());
1621 original_region.swap(new_area_to_cover);
1622 if (original_region.empty())
break;
1624 return original_region;
1633struct BinaryTreeNode {
1664 void RecomputeConnectedComponents(TreeNodeIndex idx) {
1666 if (node.occupying_box_index != -1) {
1667 node.connected_components_descendants = {
1668 union_find.FindRoot(node.occupying_box_index)};
1671 node.connected_components_descendants.clear();
1672 if (tree.IsLeaf(idx))
return;
1673 for (
const TreeNodeIndex child_idx :
1674 {tree.LeftChild(idx), tree.RightChild(idx)}) {
1678 tree_nodes[child_idx].connected_components_descendants) {
1679 node.connected_components_descendants.insert(union_find.FindRoot(c));
1687 void RemoveNodeIfXMaxLowerOrEqual(TreeNodeIndex idx,
int x_threshold) {
1688 BinaryTreeNode& node = tree_nodes[idx];
1689 if (node.occupying_box_index == -1) {
1693 if (node.occupying_box_x_max > x_threshold) {
1697 node.occupying_box_index = -1;
1698 RecomputeConnectedComponents(idx);
1701 void UpdateChildrenIntersecting(TreeNodeIndex idx,
int sweep_line_x_pos,
1702 int component_index,
1703 std::vector<int>* new_connections) {
1704 if (
tree.IsLeaf(idx))
return;
1705 for (
const TreeNodeIndex child_idx :
1706 {
tree.LeftChild(idx),
tree.RightChild(idx)}) {
1709 if (child_node.occupying_box_index != -1) {
1710 if (
union_find.AddEdge(child_node.occupying_box_index,
1712 new_connections->push_back(child_node.occupying_box_index);
1718 const bool had_different_component =
1719 absl::c_any_of(child_node.connected_components_descendants,
1720 [
this, component_index](
const int c) {
1721 return !union_find.Connected(c, component_index);
1725 child_node.connected_components_descendants = {component_index};
1735 if (had_different_component) {
1742 bool UpdateParents(TreeNodeIndex node,
int sweep_line_x_pos,
1743 int component_index, std::vector<int>* new_connections) {
1744 if (node ==
tree.Root())
return false;
1745 for (TreeNodeIndex parent =
tree.Parent(node); parent !=
tree.Root();
1746 parent =
tree.Parent(parent)) {
1749 if (parent_value.occupying_box_index != -1) {
1750 if (
union_find.AddEdge(parent_value.occupying_box_index,
1752 new_connections->push_back(parent_value.occupying_box_index);
1756 parent_value.connected_components_descendants.insert(component_index);
1765 void AddInterval(TreeNodeIndex idx,
int sweep_line_x_pos,
int box_index,
1766 int x_max, std::vector<int>* new_connections) {
1768 int cur_box_component =
union_find.FindRoot(box_index);
1770 if (node.occupying_box_index == -1) {
1771 node.connected_components_descendants = {box_index};
1772 node.occupying_box_index = box_index;
1773 node.occupying_box_x_max = x_max;
1774 const bool had_occupied_parent = UpdateParents(
1775 idx, sweep_line_x_pos, cur_box_component, new_connections);
1778 if (!had_occupied_parent) {
1779 UpdateChildrenIntersecting(idx, sweep_line_x_pos, cur_box_component,
1784 if (union_find.AddEdge(node.occupying_box_index, cur_box_component)) {
1785 new_connections->push_back(node.occupying_box_index);
1786 cur_box_component = union_find.FindRoot(cur_box_component);
1788 node.connected_components_descendants = {cur_box_component};
1789 if (node.occupying_box_x_max < x_max) {
1791 node.occupying_box_index = box_index;
1792 node.occupying_box_x_max = x_max;
1797 FixedShapeBinaryTree tree;
1798 util_intops::StrongVector<TreeNodeIndex, BinaryTreeNode> tree_nodes;
1813 absl::Span<const Rectangle32> rectangles, int32_t
y_max) {
1819 DCHECK(std::is_sorted(rectangles.begin(), rectangles.end(),
1821 return a.x_min < b.x_min;
1826 std::vector<TreeNodeIndex> interval_pieces;
1827 std::vector<int> new_connections;
1828 std::vector<std::pair<int, int>> arcs;
1829 for (
int rectangle_index = 0; rectangle_index < rectangles.size();
1830 ++rectangle_index) {
1831 DCHECK_LT(rectangles[rectangle_index].x_min,
1832 rectangles[rectangle_index].x_max);
1833 DCHECK_LT(rectangles[rectangle_index].y_min,
1834 rectangles[rectangle_index].y_max);
1835 const int sweep_line_x_pos = rectangles[rectangle_index].x_min;
1836 const Rectangle32& r = rectangles[rectangle_index];
1837 interval_pieces.clear();
1838 interval_tree.tree.PartitionIntervalIntoNodes(
1839 LeafIndex(r.
y_min), LeafIndex(r.
y_max - 1), &interval_pieces);
1840 new_connections.clear();
1841 for (
const TreeNodeIndex& node : interval_pieces) {
1842 interval_tree.AddInterval(node, sweep_line_x_pos, rectangle_index,
1843 r.
x_max, &new_connections);
1845 for (
const int new_connection : new_connections) {
1846 arcs.push_back({rectangles[new_connection].index, r.
index});
1853struct PostProcessedResult {
1854 std::vector<Rectangle32> rectangles_sorted_by_x_min;
1855 std::pair<int32_t, int32_t> bounding_box;
1858PostProcessedResult ConvertToRectangle32WithNonZeroSizes(
1859 absl::Span<const Rectangle> rectangles) {
1908 if (rectangles.empty())
return {};
1915 std::vector<std::tuple<IntegerValue, Event, int>> x_events;
1916 std::vector<std::tuple<IntegerValue, Event, int>> y_events;
1917 x_events.reserve(rectangles.size() * 2);
1918 y_events.reserve(rectangles.size() * 2);
1919 for (
int i = 0;
i < rectangles.size(); ++
i) {
1920 const Rectangle& r = rectangles[
i];
1921 DCHECK_GE(r.SizeX(), 0);
1922 DCHECK_GE(r.SizeY(), 0);
1923 if (r.SizeX() == 0) {
1924 x_events.push_back({r.x_min, Event::kPoint,
i});
1926 x_events.push_back({r.x_min, Event::kBegin,
i});
1927 x_events.push_back({r.x_max, Event::kEnd,
i});
1929 if (r.SizeY() == 0) {
1930 y_events.push_back({r.y_min, Event::kPoint,
i});
1932 y_events.push_back({r.y_min, Event::kBegin,
i});
1933 y_events.push_back({r.y_max, Event::kEnd,
i});
1936 std::sort(y_events.begin(), y_events.end());
1938 std::vector<Rectangle32> rectangles32;
1939 rectangles32.resize(rectangles.size());
1940 IntegerValue prev_y = 0;
1941 Event prev_event = Event::kEnd;
1943 for (
int i = 0;
i < y_events.size(); ++
i) {
1944 const auto [y, event, index] = y_events[
i];
1945 if ((prev_event != event && prev_event != Event::kEnd) || prev_y != y ||
1946 event == Event::kPoint || cur_index == -1) {
1952 rectangles32[index].y_min = cur_index;
1953 rectangles32[index].index = index;
1956 rectangles32[index].y_max = cur_index;
1959 rectangles32[index].y_min = cur_index;
1960 rectangles32[index].y_max = cur_index + 1;
1961 rectangles32[index].index = index;
1967 const int max_y_index = cur_index + 1;
1971 std::sort(x_events.begin(), x_events.end());
1972 IntegerValue prev_x = 0;
1973 prev_event = Event::kEnd;
1975 for (
int i = 0;
i < x_events.size(); ++
i) {
1976 const auto [
x, event, index] = x_events[
i];
1977 if ((prev_event != event && prev_event != Event::kEnd) || prev_x != x ||
1978 event == Event::kPoint || cur_index == -1) {
1984 rectangles32[index].x_min = cur_index;
1987 rectangles32[index].x_max = cur_index;
1990 rectangles32[index].x_min = cur_index;
1991 rectangles32[index].x_max = cur_index + 1;
1997 const int max_x_index = cur_index + 1;
1999 std::vector<Rectangle32> sorted_rectangles32;
2000 sorted_rectangles32.reserve(rectangles.size());
2001 for (
int i = 0;
i < x_events.size(); ++
i) {
2002 const auto [
x, event, index] = x_events[
i];
2003 if (event == Event::kBegin || event == Event::kPoint) {
2004 sorted_rectangles32.push_back(rectangles32[index]);
2008 return {sorted_rectangles32, {max_x_index, max_y_index}};
2010template <
typename RectangleT>
2011std::optional<std::pair<int, int>> FindOneIntersectionIfPresentImpl(
2012 absl::Span<const RectangleT> rectangles) {
2013 using CoordinateType = std::decay_t<
decltype(RectangleT::x_min)>;
2014 DCHECK(absl::c_is_sorted(rectangles,
2015 [](
const RectangleT& a,
const RectangleT& b) {
2016 return a.x_min <
b.x_min;
2023 CoordinateType y_min;
2024 bool operator<(
const Element& other)
const {
return y_min < other.y_min; }
2029 absl::btree_set<Element> interval_set;
2031 for (
int i = 0;
i < rectangles.size(); ++
i) {
2032 const CoordinateType
x = rectangles[
i].x_min;
2033 const CoordinateType y_min = rectangles[
i].y_min;
2034 const CoordinateType y_max = rectangles[
i].y_max;
2037 DCHECK_LE(y_min, y_max);
2042 auto [it, inserted] = interval_set.insert({
i, y_min});
2044 if (rectangles[it->index].x_max <= x) {
2049 return {{it->index,
i}};
2054 if (it != interval_set.begin()) {
2055 auto it_before = it;
2059 if (rectangles[it_before->index].x_max <= x) {
2062 it = interval_set.erase(it_before);
2064 DCHECK_LE(it_before->y_min, y_min);
2065 const CoordinateType y_max_before =
2066 rectangles[it_before->index].y_max;
2067 if (y_max_before > y_min) {
2069 return {{it_before->index,
i}};
2078 while (it != interval_set.end()) {
2080 if (rectangles[it->index].x_max <= x) {
2081 it = interval_set.erase(it);
2085 DCHECK_LE(y_min, it->y_min);
2086 if (y_max > it->y_min) {
2088 return {{it->index,
i}};
2100 absl::Span<const Rectangle> rectangles) {
2101 auto postprocessed = ConvertToRectangle32WithNonZeroSizes(rectangles);
2103 postprocessed.rectangles_sorted_by_x_min,
2104 postprocessed.bounding_box.second);
2107std::optional<std::pair<int, int>> FindOneIntersectionIfPresent(
2108 absl::Span<const Rectangle> rectangles) {
2109 return FindOneIntersectionIfPresentImpl(rectangles);
2112std::optional<std::pair<int, int>> FindOneIntersectionIfPresentWithZeroArea(
2113 absl::Span<const Rectangle> rectangles) {
2114 auto postprocessed = ConvertToRectangle32WithNonZeroSizes(rectangles);
2115 std::optional<std::pair<int, int>> result = FindOneIntersectionIfPresentImpl(
2116 absl::MakeConstSpan(postprocessed.rectangles_sorted_by_x_min));
2117 if (!result.has_value())
return {};
2118 return {{postprocessed.rectangles_sorted_by_x_min[result->first].index,
2119 postprocessed.rectangles_sorted_by_x_min[result->second].index}};
A connected components finder that only works on dense ints.
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.
void BuildResidualCapacityProfile(std::vector< Rectangle > *result)
void ResetFromPairs(const Collection &pairs, int minimum_num_nodes=0)
Similar to ResetFromFlatMapping().
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.
Rectangle GetCurrentRectangle() const
bool CanShrink(Edge edge) const
void AddPresenceReason(int t)
IntegerValue ShiftedEndMax(int t) const
IntegerValue ShiftedStartMin(int t) const
void ClearReason()
Functions to clear and then set the current reason.
IntegerValue SizeMin(int t) const
void AddEnergyMinInIntervalReason(int t, IntegerValue min, IntegerValue max)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void STLClearObject(T *obj)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
CompactVectorVector< int > GetOverlappingRectangleComponents(absl::Span< const Rectangle > rectangles)
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)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
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)
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)
int WeightedPick(absl::Span< const double > input, absl::BitGenRef random)
bool ReportEnergyConflict(Rectangle bounding_box, absl::Span< const int > boxes, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y)
std::vector< std::pair< int, int > > FindPartialRectangleIntersectionsImpl(absl::Span< const Rectangle32 > rectangles, int32_t y_max)
FindRectanglesResult FindRectanglesWithEnergyConflictMC(const std::vector< RectangleInRange > &intervals, absl::BitGenRef random, double temperature, double candidate_energy_usage_factor)
std::vector< Rectangle > PavedRegionDifference(std::vector< Rectangle > original_region, absl::Span< const Rectangle > area_to_remove)
bool BoxesAreInEnergyConflict(absl::Span< const Rectangle > rectangles, absl::Span< const IntegerValue > energies, absl::Span< const int > boxes, Rectangle *conflict)
std::vector< std::pair< int, int > > FindPartialRectangleIntersections(absl::Span< const Rectangle > rectangles)
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.
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
void FindStronglyConnectedComponents(NodeIndex num_nodes, const Graph &graph, SccOutput *components)
Simple wrapper function for most usage.
int occupying_box_index
-1 if not occupied.
absl::flat_hash_set< int > connected_components_descendants
Contains exactly one element if occupying_box_index != -1.
std::vector< Rectangle > conflicts
std::vector< Rectangle > candidates
void GrowToInclude(const Rectangle &other)
absl::InlinedVector< Rectangle, 4 > RegionDifference(const Rectangle &other) const
IntegerValue SizeX() const
bool IsDisjoint(const Rectangle &other) const
Rectangle Intersect(const Rectangle &other) const
The methods below are not meant to be used with zero-area rectangles.
DenseConnectedComponentsFinder union_find
void RemoveNodeIfXMaxLowerOrEqual(TreeNodeIndex idx, int x_threshold)
void UpdateChildrenIntersecting(TreeNodeIndex idx, int sweep_line_x_pos, int component_index, std::vector< int > *new_connections)
FixedShapeBinaryTree tree
util_intops::StrongVector< TreeNodeIndex, BinaryTreeNode > tree_nodes