Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
diffn_util.cc
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <stddef.h>
17
18#include <algorithm>
19#include <array>
20#include <cmath>
21#include <cstddef>
22#include <cstdint>
23#include <limits>
24#include <optional>
25#include <ostream>
26#include <sstream>
27#include <string>
28#include <string_view>
29#include <tuple>
30#include <type_traits>
31#include <utility>
32#include <vector>
33
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/log/log.h"
41#include "absl/log/vlog_is_on.h"
42#include "absl/random/bit_gen_ref.h"
43#include "absl/types/span.h"
50#include "ortools/sat/util.h"
54
55namespace operations_research {
56namespace sat {
57
58bool Rectangle::IsDisjoint(const Rectangle& other) const {
59 return x_min >= other.x_max || other.x_min >= x_max || y_min >= other.y_max ||
60 other.y_min >= y_max;
61}
62
63absl::InlinedVector<Rectangle, 4> Rectangle::RegionDifference(
64 const Rectangle& other) const {
65 const Rectangle intersect = Intersect(other);
66 if (intersect.SizeX() == 0) {
67 return {*this};
68 }
69
70 //-------------------
71 //| | 4 | |
72 //| |---------| |
73 //| 1 | other | 2 |
74 //| |---------| |
75 //| | 3 | |
76 //-------------------
77 absl::InlinedVector<Rectangle, 4> result;
78 if (x_min < intersect.x_min) {
79 // Piece 1
80 result.push_back({.x_min = x_min,
81 .x_max = intersect.x_min,
82 .y_min = y_min,
83 .y_max = y_max});
84 }
85 if (x_max > intersect.x_max) {
86 // Piece 2
87 result.push_back({.x_min = intersect.x_max,
88 .x_max = x_max,
89 .y_min = y_min,
90 .y_max = y_max});
91 }
92 if (y_min < intersect.y_min) {
93 // Piece 3
94 result.push_back({.x_min = intersect.x_min,
95 .x_max = intersect.x_max,
96 .y_min = y_min,
97 .y_max = intersect.y_min});
98 }
99 if (y_max > intersect.y_max) {
100 // Piece 4
101 result.push_back({.x_min = intersect.x_min,
102 .x_max = intersect.x_max,
103 .y_min = intersect.y_max,
104 .y_max = y_max});
105 }
106
107 return result;
108}
109
111 absl::Span<const Rectangle> rectangles) {
112 if (rectangles.empty()) return {};
113
114 std::vector<std::pair<int, int>> intersections =
116 const int num_intersections = intersections.size();
117 intersections.reserve(num_intersections * 2 + 1);
118 for (int i = 0; i < num_intersections; ++i) {
119 intersections.push_back({intersections[i].second, intersections[i].first});
120 }
121
123 view.ResetFromPairs(intersections, /*minimum_num_nodes=*/rectangles.size());
124 CompactVectorVector<int> components;
125 FindStronglyConnectedComponents(static_cast<int>(rectangles.size()), view,
126 &components);
128 for (int i = 0; i < components.size(); ++i) {
129 absl::Span<const int> component = components[i];
130 result.Add({});
131 for (const int r : component) {
132 result.AppendToLastVector(r);
133 }
134 }
135 return result;
136}
137
138bool ReportEnergyConflict(Rectangle bounding_box, absl::Span<const int> boxes,
141 x->ResetReason();
142 y->ResetReason();
143 IntegerValue total_energy(0);
144 for (const int b : boxes) {
145 const IntegerValue x_min = x->ShiftedStartMin(b);
146 const IntegerValue x_max = x->ShiftedEndMax(b);
147 if (x_min < bounding_box.x_min || x_max > bounding_box.x_max) continue;
148 const IntegerValue y_min = y->ShiftedStartMin(b);
149 const IntegerValue y_max = y->ShiftedEndMax(b);
150 if (y_min < bounding_box.y_min || y_max > bounding_box.y_max) continue;
151
152 x->AddEnergyMinInIntervalReason(b, bounding_box.x_min, bounding_box.x_max);
153 y->AddEnergyMinInIntervalReason(b, bounding_box.y_min, bounding_box.y_max);
154
155 x->AddPresenceReason(b);
157
158 total_energy += x->SizeMin(b) * y->SizeMin(b);
159
160 // We abort early if a subset of boxes is enough.
161 // TODO(user): Also relax the box if possible.
162 if (total_energy > bounding_box.Area()) break;
163 }
164
165 CHECK_GT(total_energy, bounding_box.Area());
166 x->ImportReasonsFromOther(*y);
167 return x->ReportConflict();
168}
169
170bool BoxesAreInEnergyConflict(absl::Span<const Rectangle> rectangles,
171 absl::Span<const IntegerValue> energies,
172 absl::Span<const int> boxes,
173 Rectangle* conflict) {
174 // First consider all relevant intervals along the x axis.
175 std::vector<IntegerValue> x_starts;
176 std::vector<TaskTime> boxes_by_increasing_x_max;
177 for (const int b : boxes) {
178 x_starts.push_back(rectangles[b].x_min);
179 boxes_by_increasing_x_max.push_back({b, rectangles[b].x_max});
180 }
182 std::sort(boxes_by_increasing_x_max.begin(), boxes_by_increasing_x_max.end());
183
184 std::vector<IntegerValue> y_starts;
185 std::vector<IntegerValue> energy_sum;
186 std::vector<TaskTime> boxes_by_increasing_y_max;
187
188 std::vector<std::vector<int>> stripes(x_starts.size());
189 for (int i = 0; i < boxes_by_increasing_x_max.size(); ++i) {
190 const int b = boxes_by_increasing_x_max[i].task_index;
191 const IntegerValue x_min = rectangles[b].x_min;
192 const IntegerValue x_max = rectangles[b].x_max;
193 for (int j = 0; j < x_starts.size(); ++j) {
194 if (x_starts[j] > x_min) break;
195 stripes[j].push_back(b);
196
197 // Redo the same on the y coordinate for the current x interval
198 // which is [starts[j], x_max].
199 y_starts.clear();
200 boxes_by_increasing_y_max.clear();
201 for (const int b : stripes[j]) {
202 y_starts.push_back(rectangles[b].y_min);
203 boxes_by_increasing_y_max.push_back({b, rectangles[b].y_max});
204 }
206 std::sort(boxes_by_increasing_y_max.begin(),
207 boxes_by_increasing_y_max.end());
208
209 const IntegerValue x_size = x_max - x_starts[j];
210 energy_sum.assign(y_starts.size(), IntegerValue(0));
211 for (int i = 0; i < boxes_by_increasing_y_max.size(); ++i) {
212 const int b = boxes_by_increasing_y_max[i].task_index;
213 const IntegerValue y_min = rectangles[b].y_min;
214 const IntegerValue y_max = rectangles[b].y_max;
215 for (int j = 0; j < y_starts.size(); ++j) {
216 if (y_starts[j] > y_min) break;
217 energy_sum[j] += energies[b];
218 if (energy_sum[j] > x_size * (y_max - y_starts[j])) {
219 if (conflict != nullptr) {
220 *conflict = rectangles[b];
221 for (int k = 0; k < i; ++k) {
222 const int task_index = boxes_by_increasing_y_max[k].task_index;
223 if (rectangles[task_index].y_min >= y_starts[j]) {
224 conflict->GrowToInclude(rectangles[task_index]);
225 }
226 }
227 }
228 return true;
229 }
230 }
231 }
232 }
233 }
234 return false;
235}
236
237bool AnalyzeIntervals(bool transpose, absl::Span<const int> local_boxes,
238 absl::Span<const Rectangle> rectangles,
239 absl::Span<const IntegerValue> rectangle_energies,
240 IntegerValue* x_threshold, IntegerValue* y_threshold,
241 Rectangle* conflict) {
242 // First, we compute the possible x_min values (removing duplicates).
243 // We also sort the relevant tasks by their x_max.
244 //
245 // TODO(user): If the number of unique x_max is smaller than the number of
246 // unique x_min, it is better to do it the other way around.
247 std::vector<IntegerValue> starts;
248 std::vector<TaskTime> task_by_increasing_x_max;
249 for (const int t : local_boxes) {
250 const IntegerValue x_min =
251 transpose ? rectangles[t].y_min : rectangles[t].x_min;
252 const IntegerValue x_max =
253 transpose ? rectangles[t].y_max : rectangles[t].x_max;
254 starts.push_back(x_min);
255 task_by_increasing_x_max.push_back({t, x_max});
256 }
258
259 // Note that for the same end_max, the order change our heuristic to
260 // evaluate the max_conflict_height.
261 std::sort(task_by_increasing_x_max.begin(), task_by_increasing_x_max.end());
262
263 // The maximum y dimension of a bounding area for which there is a potential
264 // conflict.
265 IntegerValue max_conflict_height(0);
266
267 // This is currently only used for logging.
268 absl::flat_hash_set<std::pair<IntegerValue, IntegerValue>> stripes;
269
270 // All quantities at index j correspond to the interval [starts[j], x_max].
271 std::vector<IntegerValue> energies(starts.size(), IntegerValue(0));
272 std::vector<IntegerValue> y_mins(starts.size(), kMaxIntegerValue);
273 std::vector<IntegerValue> y_maxs(starts.size(), -kMaxIntegerValue);
274 std::vector<IntegerValue> energy_at_max_y(starts.size(), IntegerValue(0));
275 std::vector<IntegerValue> energy_at_min_y(starts.size(), IntegerValue(0));
276
277 // Sentinel.
278 starts.push_back(kMaxIntegerValue);
279
280 // Iterate over all boxes by increasing x_max values.
281 int first_j = 0;
282 const IntegerValue threshold = transpose ? *y_threshold : *x_threshold;
283 for (int i = 0; i < task_by_increasing_x_max.size(); ++i) {
284 const int t = task_by_increasing_x_max[i].task_index;
285
286 const IntegerValue energy = rectangle_energies[t];
287 IntegerValue x_min = rectangles[t].x_min;
288 IntegerValue x_max = rectangles[t].x_max;
289 IntegerValue y_min = rectangles[t].y_min;
290 IntegerValue y_max = rectangles[t].y_max;
291 if (transpose) {
292 std::swap(x_min, y_min);
293 std::swap(x_max, y_max);
294 }
295
296 // Add this box contribution to all the [starts[j], x_max] intervals.
297 while (first_j + 1 < starts.size() && x_max - starts[first_j] > threshold) {
298 ++first_j;
299 }
300 for (int j = first_j; starts[j] <= x_min; ++j) {
301 const IntegerValue old_energy_at_max = energy_at_max_y[j];
302 const IntegerValue old_energy_at_min = energy_at_min_y[j];
303
304 energies[j] += energy;
305
306 const bool is_disjoint = y_min >= y_maxs[j] || y_max <= y_mins[j];
307
308 if (y_min <= y_mins[j]) {
309 if (y_min < y_mins[j]) {
310 y_mins[j] = y_min;
311 energy_at_min_y[j] = energy;
312 } else {
313 energy_at_min_y[j] += energy;
314 }
315 }
316
317 if (y_max >= y_maxs[j]) {
318 if (y_max > y_maxs[j]) {
319 y_maxs[j] = y_max;
320 energy_at_max_y[j] = energy;
321 } else {
322 energy_at_max_y[j] += energy;
323 }
324 }
325
326 // If the new box is disjoint in y from the ones added so far, there
327 // cannot be a new conflict involving this box, so we skip until we add
328 // new boxes.
329 if (is_disjoint) continue;
330
331 const IntegerValue width = x_max - starts[j];
332 IntegerValue conflict_height = CeilRatio(energies[j], width) - 1;
333 if (y_max - y_min > conflict_height) continue;
334 if (conflict_height >= y_maxs[j] - y_mins[j]) {
335 // We have a conflict.
336 if (conflict != nullptr) {
337 *conflict = rectangles[t];
338 for (int k = 0; k < i; ++k) {
339 const int task_index = task_by_increasing_x_max[k].task_index;
340 const IntegerValue task_x_min = transpose
341 ? rectangles[task_index].y_min
342 : rectangles[task_index].x_min;
343 if (task_x_min < starts[j]) continue;
344 conflict->GrowToInclude(rectangles[task_index]);
345 }
346 }
347 return false;
348 }
349
350 // Because we currently do not have a conflict involving the new box, the
351 // only way to have one is to remove enough energy to reduce the y domain.
352 IntegerValue can_remove = std::min(old_energy_at_min, old_energy_at_max);
353 if (old_energy_at_min < old_energy_at_max) {
354 if (y_maxs[j] - y_min >=
355 CeilRatio(energies[j] - old_energy_at_min, width)) {
356 // In this case, we need to remove at least old_energy_at_max to have
357 // a conflict.
358 can_remove = old_energy_at_max;
359 }
360 } else if (old_energy_at_max < old_energy_at_min) {
361 if (y_max - y_mins[j] >=
362 CeilRatio(energies[j] - old_energy_at_max, width)) {
363 can_remove = old_energy_at_min;
364 }
365 }
366 conflict_height = CeilRatio(energies[j] - can_remove, width) - 1;
367
368 // If the new box height is above the conflict_height, do not count
369 // it now. We only need to consider conflict involving the new box.
370 if (y_max - y_min > conflict_height) continue;
371
372 if (VLOG_IS_ON(2)) stripes.insert({starts[j], x_max});
373 max_conflict_height = std::max(max_conflict_height, conflict_height);
374 }
375 }
376
377 VLOG(2) << " num_starts: " << starts.size() - 1 << "/" << local_boxes.size()
378 << " conflict_height: " << max_conflict_height
379 << " num_stripes:" << stripes.size() << " (<= " << threshold << ")";
380
381 if (transpose) {
382 *x_threshold = std::min(*x_threshold, max_conflict_height);
383 } else {
384 *y_threshold = std::min(*y_threshold, max_conflict_height);
385 }
386 return true;
387}
388
389absl::Span<int> FilterBoxesAndRandomize(
390 absl::Span<const Rectangle> cached_rectangles, absl::Span<int> boxes,
391 IntegerValue threshold_x, IntegerValue threshold_y,
392 absl::BitGenRef random) {
393 size_t new_size = 0;
394 for (const int b : boxes) {
395 const Rectangle& dim = cached_rectangles[b];
396 if (dim.x_max - dim.x_min > threshold_x) continue;
397 if (dim.y_max - dim.y_min > threshold_y) continue;
398 boxes[new_size++] = b;
399 }
400 if (new_size == 0) return {};
401 std::shuffle(&boxes[0], &boxes[0] + new_size, random);
402 return {&boxes[0], new_size};
403}
404
405absl::Span<int> FilterBoxesThatAreTooLarge(
406 absl::Span<const Rectangle> cached_rectangles,
407 absl::Span<const IntegerValue> energies, absl::Span<int> boxes) {
408 // Sort the boxes by increasing area.
409 std::sort(boxes.begin(), boxes.end(), [cached_rectangles](int a, int b) {
410 return cached_rectangles[a].Area() < cached_rectangles[b].Area();
411 });
412
413 IntegerValue total_energy(0);
414 for (const int box : boxes) total_energy += energies[box];
415
416 // Remove all the large boxes until we have one with area smaller than the
417 // energy of the boxes below.
418 int new_size = boxes.size();
419 while (new_size > 0 &&
420 cached_rectangles[boxes[new_size - 1]].Area() >= total_energy) {
421 --new_size;
422 total_energy -= energies[boxes[new_size]];
423 }
424 return boxes.subspan(0, new_size);
425}
426
427void ConstructOverlappingSets(absl::Span<IndexedInterval> intervals,
428 CompactVectorVector<int>* result,
429 absl::Span<const int> order) {
430 result->clear();
431 DCHECK(std::is_sorted(intervals.begin(), intervals.end(),
433 IntegerValue min_end_in_set = kMaxIntegerValue;
434
435 // We do a line sweep. The "current" subset crossing the "line" at
436 // (time, time + 1) will be in (*intervals)[start_index, end_index) at the end
437 // of the loop block.
438 int start_index = 0;
439 const int size = intervals.size();
440 for (int end_index = 0; end_index < size;) {
441 const IntegerValue time = intervals[end_index].start;
442
443 // First, if there is some deletion, we will push the "old" set to the
444 // result before updating it. Otherwise, we will have a superset later, so
445 // we just continue for now.
446 if (min_end_in_set <= time) {
447 // Push the current set to result first if its size is > 1.
448 if (start_index + 1 < end_index) {
449 result->Add({});
450 for (int i = start_index; i < end_index; ++i) {
451 result->AppendToLastVector(intervals[i].index);
452 }
453 }
454
455 // Update the set. Note that we keep the order.
456 min_end_in_set = kMaxIntegerValue;
457 int new_start = end_index;
458 for (int i = end_index; --i >= start_index;) {
459 if (intervals[i].end > time) {
460 min_end_in_set = std::min(min_end_in_set, intervals[i].end);
461 intervals[--new_start] = intervals[i];
462 }
463 }
464 start_index = new_start;
465 }
466
467 // Add all the new intervals starting exactly at "time".
468 // Note that we always add at least one here.
469 const int old_end = end_index;
470 while (end_index < size && intervals[end_index].start == time) {
471 min_end_in_set = std::min(min_end_in_set, intervals[end_index].end);
472 ++end_index;
473 }
474
475 // If order is not empty, make sure we maintain the order.
476 // TODO(user): we could only do that when we push a new set.
477 if (!order.empty() && end_index > old_end) {
478 std::sort(intervals.data() + old_end, intervals.data() + end_index,
479 [order](const IndexedInterval& a, const IndexedInterval& b) {
480 return order[a.index] < order[b.index];
481 });
482 std::inplace_merge(
483 intervals.data() + start_index, intervals.data() + old_end,
484 intervals.data() + end_index,
485 [order](const IndexedInterval& a, const IndexedInterval& b) {
486 return order[a.index] < order[b.index];
487 });
488 }
489 }
490
491 // Push final set.
492 if (start_index + 1 < size) {
493 result->Add({});
494 for (int i = start_index; i < size; ++i) {
495 result->AppendToLastVector(intervals[i].index);
496 }
497 }
498}
499
501 std::vector<IndexedInterval>* intervals,
502 std::vector<std::vector<int>>* components) {
503 components->clear();
504 if (intervals->empty()) return;
505 if (intervals->size() == 1) {
506 components->push_back({intervals->front().index});
507 return;
508 }
509
510 // For correctness, ComparatorByStart is enough, but in unit tests we want to
511 // verify this function against another implementation, and fully defined
512 // sorting with tie-breaking makes that much easier.
513 // If that becomes a performance bottleneck:
514 // - One may want to sort the list outside of this function, and simply
515 // have this function DCHECK that it's sorted by start.
516 // - One may use stable_sort() with ComparatorByStart().
517 std::sort(intervals->begin(), intervals->end(),
519
520 IntegerValue end_max_so_far = (*intervals)[0].end;
521 components->push_back({(*intervals)[0].index});
522 for (int i = 1; i < intervals->size(); ++i) {
523 const IndexedInterval& interval = (*intervals)[i];
524 if (interval.start >= end_max_so_far) {
525 components->push_back({interval.index});
526 } else {
527 components->back().push_back(interval.index);
528 }
529 end_max_so_far = std::max(end_max_so_far, interval.end);
530 }
531}
532
533std::vector<int> GetIntervalArticulationPoints(
534 std::vector<IndexedInterval>* intervals) {
535 std::vector<int> articulation_points;
536 if (intervals->size() < 3) return articulation_points; // Empty.
537 if (DEBUG_MODE) {
538 for (const IndexedInterval& interval : *intervals) {
539 DCHECK_LT(interval.start, interval.end);
540 }
541 }
542
543 std::sort(intervals->begin(), intervals->end(),
544 IndexedInterval::ComparatorByStart());
545
546 IntegerValue end_max_so_far = (*intervals)[0].end;
547 int index_of_max = 0;
548 IntegerValue prev_end_max = kMinIntegerValue; // Initialized as a sentinel.
549 for (int i = 1; i < intervals->size(); ++i) {
550 const IndexedInterval& interval = (*intervals)[i];
551 if (interval.start >= end_max_so_far) {
552 // New connected component.
553 end_max_so_far = interval.end;
554 index_of_max = i;
555 prev_end_max = kMinIntegerValue;
556 continue;
557 }
558 // Still the same connected component. Was the previous "max" an
559 // articulation point ?
560 if (prev_end_max != kMinIntegerValue && interval.start >= prev_end_max) {
561 // We might be re-inserting the same articulation point: guard against it.
562 if (articulation_points.empty() ||
563 articulation_points.back() != index_of_max) {
564 articulation_points.push_back(index_of_max);
565 }
566 }
567 // Update the max end.
568 if (interval.end > end_max_so_far) {
569 prev_end_max = end_max_so_far;
570 end_max_so_far = interval.end;
571 index_of_max = i;
572 } else if (interval.end > prev_end_max) {
573 prev_end_max = interval.end;
574 }
575 }
576 // Convert articulation point indices to IndexedInterval.index.
577 for (int& index : articulation_points) index = (*intervals)[index].index;
578 return articulation_points;
579}
580
581namespace {
582bool IsZeroOrPowerOfTwo(int value) { return (value & (value - 1)) == 0; }
583
584void AppendPairwiseRestriction(const ItemWithVariableSize& item1,
585 const ItemWithVariableSize& item2,
586 std::vector<PairwiseRestriction>* result) {
587 const int state =
588 // box1 can be left of box2.
589 (item1.x.end_min <= item2.x.start_max) +
590 // box1 can be right of box2.
591 2 * (item2.x.end_min <= item1.x.start_max) +
592 // box1 can be below box2.
593 4 * (item1.y.end_min <= item2.y.start_max) +
594 // box1 can be up of box2.
595 8 * (item2.y.end_min <= item1.y.start_max);
596
597 if (!IsZeroOrPowerOfTwo(state)) {
598 return;
599 }
600
602 switch (state) {
603 case 0:
604 // Conflict. The two boxes must overlap in both dimensions.
606 break;
607 case 1:
608 // box2 can only be after box1 on x.
609 if (item1.x.end_min > item2.x.start_min ||
610 item2.x.start_max < item1.x.end_max) {
611 type =
613 break;
614 }
615 return;
616 case 2:
617 // box1 can only be after box2 on x.
618 if (item2.x.end_min > item1.x.start_min ||
619 item1.x.start_max < item2.x.end_max) {
620 type =
622 break;
623 }
624 return;
625 case 4:
626 // box2 can only be after box1 on y.
627 if (item1.y.end_min > item2.y.start_min ||
628 item2.y.start_max < item1.y.end_max) {
630 break;
631 }
632 return;
633 case 8:
634 // box1 can only be after box2 on y.
635 if (item2.y.end_min > item1.y.start_min ||
636 item1.y.start_max < item2.y.end_max) {
638 break;
639 }
640 return;
641 default:
642 return;
643 }
644 result->push_back(
645 {.first_index = item1.index, .second_index = item2.index, .type = type});
646}
647} // namespace
648
649void AppendPairwiseRestrictions(absl::Span<const ItemWithVariableSize> items,
650 std::vector<PairwiseRestriction>* result) {
651 for (int i1 = 0; i1 + 1 < items.size(); ++i1) {
652 for (int i2 = i1 + 1; i2 < items.size(); ++i2) {
653 AppendPairwiseRestriction(items[i1], items[i2], result);
654 }
655 }
656}
657
659 absl::Span<const ItemWithVariableSize> items,
660 absl::Span<const ItemWithVariableSize> other_items,
661 std::vector<PairwiseRestriction>* result) {
662 for (int i1 = 0; i1 < items.size(); ++i1) {
663 for (int i2 = 0; i2 < other_items.size(); ++i2) {
664 AppendPairwiseRestriction(items[i1], other_items[i2], result);
665 }
666 }
667}
668
670 events_.clear();
671 num_rectangles_added_ = 0;
672}
673
674void CapacityProfile::AddRectangle(IntegerValue x_min, IntegerValue x_max,
675 IntegerValue y_min, IntegerValue y_max) {
676 DCHECK_LE(x_min, x_max);
677 if (x_min == x_max) return;
678
679 events_.push_back(
680 StartRectangleEvent(num_rectangles_added_, x_min, y_min, y_max));
681 events_.push_back(EndRectangleEvent(num_rectangles_added_, x_max));
682 ++num_rectangles_added_;
683}
684
685void CapacityProfile::AddMandatoryConsumption(IntegerValue x_min,
686 IntegerValue x_max,
687 IntegerValue y_height) {
688 DCHECK_LE(x_min, x_max);
689 if (x_min == x_max) return;
690
691 events_.push_back(ChangeMandatoryProfileEvent(x_min, y_height));
692 events_.push_back(ChangeMandatoryProfileEvent(x_max, -y_height));
693}
694
696 std::vector<CapacityProfile::Rectangle>* result) {
697 std::sort(events_.begin(), events_.end());
698 IntegerPriorityQueue<QueueElement> min_pq(num_rectangles_added_);
699 IntegerPriorityQueue<QueueElement> max_pq(num_rectangles_added_);
700 IntegerValue mandatory_capacity(0);
701
702 result->clear();
703
704 result->push_back({kMinIntegerValue, IntegerValue(0)});
705
706 for (int i = 0; i < events_.size();) {
707 const IntegerValue current_time = events_[i].time;
708 for (; i < events_.size(); ++i) {
709 const Event& event = events_[i];
710 if (event.time != current_time) break;
711
712 switch (events_[i].type) {
713 case START_RECTANGLE: {
714 min_pq.Add({event.index, -event.y_min});
715 max_pq.Add({event.index, event.y_max});
716 break;
717 }
718 case END_RECTANGLE: {
719 min_pq.Remove(event.index);
720 max_pq.Remove(event.index);
721 break;
722 }
723 case CHANGE_MANDATORY_PROFILE: {
724 mandatory_capacity += event.y_min;
725 break;
726 }
727 }
728 }
729
730 DCHECK(!max_pq.IsEmpty() || mandatory_capacity == 0);
731 const IntegerValue new_height =
732 max_pq.IsEmpty()
733 ? IntegerValue(0)
734 : max_pq.Top().value + min_pq.Top().value - mandatory_capacity;
735 if (new_height != result->back().height) {
736 result->push_back({current_time, new_height});
737 }
738 }
739}
740
741IntegerValue CapacityProfile::GetBoundingArea() {
742 std::sort(events_.begin(), events_.end());
743 IntegerPriorityQueue<QueueElement> min_pq(num_rectangles_added_);
744 IntegerPriorityQueue<QueueElement> max_pq(num_rectangles_added_);
745
746 IntegerValue area(0);
747 IntegerValue previous_time = kMinIntegerValue;
748 IntegerValue previous_height(0);
749
750 for (int i = 0; i < events_.size();) {
751 const IntegerValue current_time = events_[i].time;
752 for (; i < events_.size(); ++i) {
753 const Event& event = events_[i];
754 if (event.time != current_time) break;
755
756 switch (event.type) {
757 case START_RECTANGLE: {
758 min_pq.Add({event.index, -event.y_min});
759 max_pq.Add({event.index, event.y_max});
760 break;
761 }
762 case END_RECTANGLE: {
763 min_pq.Remove(event.index);
764 max_pq.Remove(event.index);
765 break;
766 }
767 case CHANGE_MANDATORY_PROFILE: {
768 break;
769 }
770 }
771 }
772 const IntegerValue new_height =
773 max_pq.IsEmpty() ? IntegerValue(0)
774 : max_pq.Top().value + min_pq.Top().value;
775 if (previous_height != 0) {
776 area += previous_height * (current_time - previous_time);
777 }
778 previous_time = current_time;
779 previous_height = new_height;
780 }
781 return area;
782}
783
784IntegerValue Smallest1DIntersection(IntegerValue range_min,
785 IntegerValue range_max, IntegerValue size,
786 IntegerValue interval_min,
787 IntegerValue interval_max) {
788 // If the item is on the left of the range, we get the intersection between
789 // [range_min, range_min + size] and [interval_min, interval_max].
790 const IntegerValue overlap_on_left =
791 std::min(range_min + size, interval_max) -
792 std::max(range_min, interval_min);
793
794 // If the item is on the right of the range, we get the intersection between
795 // [range_max - size, range_max] and [interval_min, interval_max].
796 const IntegerValue overlap_on_right =
797 std::min(range_max, interval_max) -
798 std::max(range_max - size, interval_min);
799
800 return std::max(IntegerValue(0), std::min(overlap_on_left, overlap_on_right));
801}
802
803ProbingRectangle::ProbingRectangle(
804 const std::vector<RectangleInRange>& intervals)
805 : intervals_(intervals) {
806 minimum_energy_ = 0;
807 if (intervals_.empty()) {
808 return;
809 }
810 interval_points_sorted_by_x_.reserve(intervals_.size() * 4 + 2);
811 interval_points_sorted_by_y_.reserve(intervals_.size() * 4 + 2);
812
813 Rectangle bounding_box = {.x_min = std::numeric_limits<IntegerValue>::max(),
814 .x_max = std::numeric_limits<IntegerValue>::min(),
815 .y_min = std::numeric_limits<IntegerValue>::max(),
816 .y_max = std::numeric_limits<IntegerValue>::min()};
817
818 for (int i = 0; i < intervals_.size(); ++i) {
819 const RectangleInRange& interval = intervals_[i];
820 minimum_energy_ += interval.x_size * interval.y_size;
821
822 bounding_box.x_min =
823 std::min(bounding_box.x_min, interval.bounding_area.x_min);
824 bounding_box.x_max =
825 std::max(bounding_box.x_max, interval.bounding_area.x_max);
826 bounding_box.y_min =
827 std::min(bounding_box.y_min, interval.bounding_area.y_min);
828 bounding_box.y_max =
829 std::max(bounding_box.y_max, interval.bounding_area.y_max);
830
831 interval_points_sorted_by_x_.push_back({interval.bounding_area.x_min, i});
832 interval_points_sorted_by_x_.push_back(
833 {interval.bounding_area.x_min + interval.x_size, i});
834 interval_points_sorted_by_x_.push_back(
835 {interval.bounding_area.x_max - interval.x_size, i});
836 interval_points_sorted_by_x_.push_back({interval.bounding_area.x_max, i});
837
838 interval_points_sorted_by_y_.push_back({interval.bounding_area.y_min, i});
839 interval_points_sorted_by_y_.push_back(
840 {interval.bounding_area.y_min + interval.y_size, i});
841 interval_points_sorted_by_y_.push_back(
842 {interval.bounding_area.y_max - interval.y_size, i});
843 interval_points_sorted_by_y_.push_back({interval.bounding_area.y_max, i});
844 }
845
846 full_energy_ = minimum_energy_;
847 // Add four bogus points in the extremities so we can delegate setting up all
848 // internal state to Shrink().
849 interval_points_sorted_by_x_.push_back({bounding_box.x_min - 1, -1});
850 interval_points_sorted_by_x_.push_back({bounding_box.x_max + 1, -1});
851 interval_points_sorted_by_y_.push_back({bounding_box.y_min - 1, -1});
852 interval_points_sorted_by_y_.push_back({bounding_box.y_max + 1, -1});
853
854 auto comparator = [](const IntervalPoint& a, const IntervalPoint& b) {
855 return std::tie(a.value, a.index) < std::tie(b.value, b.index);
856 };
857 gtl::STLSortAndRemoveDuplicates(&interval_points_sorted_by_x_, comparator);
858 gtl::STLSortAndRemoveDuplicates(&interval_points_sorted_by_y_, comparator);
859
860 grouped_intervals_sorted_by_x_.reserve(interval_points_sorted_by_x_.size());
861 grouped_intervals_sorted_by_y_.reserve(interval_points_sorted_by_y_.size());
862 int i = 0;
863 while (i < interval_points_sorted_by_x_.size()) {
864 int idx_begin = i;
865 while (i < interval_points_sorted_by_x_.size() &&
866 interval_points_sorted_by_x_[i].value ==
867 interval_points_sorted_by_x_[idx_begin].value) {
868 i++;
869 }
870 grouped_intervals_sorted_by_x_.push_back(
871 {interval_points_sorted_by_x_[idx_begin].value,
872 absl::Span<IntervalPoint>(interval_points_sorted_by_x_)
873 .subspan(idx_begin, i - idx_begin)});
874 }
875
876 i = 0;
877 while (i < interval_points_sorted_by_y_.size()) {
878 int idx_begin = i;
879 while (i < interval_points_sorted_by_y_.size() &&
880 interval_points_sorted_by_y_[i].value ==
881 interval_points_sorted_by_y_[idx_begin].value) {
882 i++;
883 }
884 grouped_intervals_sorted_by_y_.push_back(
885 {interval_points_sorted_by_y_[idx_begin].value,
886 absl::Span<IntervalPoint>(interval_points_sorted_by_y_)
887 .subspan(idx_begin, i - idx_begin)});
888 }
889
890 Reset();
891}
892
894 indexes_[Edge::LEFT] = 0;
895 indexes_[Edge::RIGHT] = grouped_intervals_sorted_by_x_.size() - 1;
896 indexes_[Edge::BOTTOM] = 0;
897 indexes_[Edge::TOP] = grouped_intervals_sorted_by_y_.size() - 1;
898
899 next_indexes_[Edge::LEFT] = 1;
900 next_indexes_[Edge::RIGHT] = grouped_intervals_sorted_by_x_.size() - 2;
901 next_indexes_[Edge::BOTTOM] = 1;
902 next_indexes_[Edge::TOP] = grouped_intervals_sorted_by_y_.size() - 2;
903
904 minimum_energy_ = full_energy_;
905 ranges_touching_both_boundaries_[0].clear();
906 ranges_touching_both_boundaries_[1].clear();
907
908 for (int i = 0; i < 4; ++i) {
909 corner_count_[i] = 0;
910 intersect_length_[i] = 0;
911 cached_delta_energy_[i] = 0;
912 }
913
914 // Remove the four bogus points we added.
915 Shrink(Edge::LEFT);
916 Shrink(Edge::BOTTOM);
917 Shrink(Edge::RIGHT);
918 Shrink(Edge::TOP);
919}
920
921Rectangle ProbingRectangle::GetCurrentRectangle() const {
922 return {
923 .x_min = grouped_intervals_sorted_by_x_[indexes_[Edge::LEFT]].coordinate,
924 .x_max = grouped_intervals_sorted_by_x_[indexes_[Edge::RIGHT]].coordinate,
925 .y_min =
926 grouped_intervals_sorted_by_y_[indexes_[Edge::BOTTOM]].coordinate,
927 .y_max = grouped_intervals_sorted_by_y_[indexes_[Edge::TOP]].coordinate};
928}
929
930namespace {
931// Intersects `rectangle` with the largest rectangle that must intersect with
932// the range in some way. To visualize this largest rectangle, imagine the four
933// possible extreme positions for the item in range (the four corners). This
934// rectangle is the one defined by the interior points of each position. This
935// don't use IsDisjoint() because it also works when the rectangle would be
936// malformed (it's bounding box less than twice the size).
937bool CanConsumeEnergy(const Rectangle& rectangle,
938 const RectangleInRange& item) {
939 return (rectangle.x_max > item.bounding_area.x_max - item.x_size) &&
940 (rectangle.y_max > item.bounding_area.y_max - item.y_size) &&
941 (rectangle.x_min < item.bounding_area.x_min + item.x_size) &&
942 (rectangle.y_min < item.bounding_area.y_min + item.y_size);
943}
944
945std::array<bool, 4> GetPossibleEdgeIntersection(const Rectangle& rectangle,
946 const RectangleInRange& range) {
947 std::array<bool, 4> result;
948 using Edge = ProbingRectangle::Edge;
949 result[Edge::LEFT] = rectangle.x_min >= range.bounding_area.x_min;
950 result[Edge::BOTTOM] = rectangle.y_min >= range.bounding_area.y_min;
951 result[Edge::RIGHT] = rectangle.x_max <= range.bounding_area.x_max;
952 result[Edge::TOP] = rectangle.y_max <= range.bounding_area.y_max;
953 return result;
954}
955
956} // namespace
957
958// NOMUTANTS -- This is a sanity check, it is hard to corrupt the data in an
959// unit test to check it will fail.
960void ProbingRectangle::ValidateInvariants() const {
961 const Rectangle current_rectangle = GetCurrentRectangle();
963 IntegerValue intersect_length[4] = {0, 0, 0, 0};
964 IntegerValue corner_count[4] = {0, 0, 0, 0};
965 IntegerValue energy = 0;
966 CHECK_LE(next_indexes_[Edge::LEFT], indexes_[Edge::RIGHT]);
967 CHECK_LE(next_indexes_[Edge::BOTTOM], indexes_[Edge::TOP]);
968 CHECK_GE(next_indexes_[Edge::TOP], indexes_[Edge::BOTTOM]);
969 CHECK_GE(next_indexes_[Edge::RIGHT], indexes_[Edge::LEFT]);
970
971 for (int interval_idx = 0; interval_idx < intervals_.size(); interval_idx++) {
972 const RectangleInRange& range = intervals_[interval_idx];
973
974 const Rectangle min_intersect =
975 range.GetMinimumIntersection(current_rectangle);
976 CHECK_LE(min_intersect.SizeX(), range.x_size);
977 CHECK_LE(min_intersect.SizeY(), range.y_size);
978 energy += min_intersect.Area();
979
980 std::array<bool, 4> touching_boundary = {false, false, false, false};
981 CHECK_EQ(CanConsumeEnergy(current_rectangle, range) &&
982 current_rectangle.Area() != 0,
983 range.GetMinimumIntersectionArea(current_rectangle) != 0);
984 if (CanConsumeEnergy(current_rectangle, range)) {
985 touching_boundary = GetPossibleEdgeIntersection(current_rectangle, range);
986 }
987
988 CHECK_EQ(
989 touching_boundary[Edge::LEFT] && touching_boundary[Edge::RIGHT],
990 ranges_touching_both_boundaries_[Direction::LEFT_AND_RIGHT].contains(
991 interval_idx));
992 CHECK_EQ(
993 touching_boundary[Edge::TOP] && touching_boundary[Edge::BOTTOM],
994 ranges_touching_both_boundaries_[Direction::TOP_AND_BOTTOM].contains(
995 interval_idx));
996
997 if (touching_boundary[Edge::LEFT] && !touching_boundary[Edge::RIGHT]) {
998 intersect_length[Edge::LEFT] += Smallest1DIntersection(
999 range.bounding_area.y_min, range.bounding_area.y_max, range.y_size,
1000 current_rectangle.y_min, current_rectangle.y_max);
1001 }
1002
1003 if (touching_boundary[Edge::RIGHT] && !touching_boundary[Edge::LEFT]) {
1004 intersect_length[Edge::RIGHT] += Smallest1DIntersection(
1005 range.bounding_area.y_min, range.bounding_area.y_max, range.y_size,
1006 current_rectangle.y_min, current_rectangle.y_max);
1007 }
1008
1009 if (touching_boundary[Edge::TOP] && !touching_boundary[Edge::BOTTOM]) {
1010 intersect_length[Edge::TOP] += Smallest1DIntersection(
1011 range.bounding_area.x_min, range.bounding_area.x_max, range.x_size,
1012 current_rectangle.x_min, current_rectangle.x_max);
1013 }
1014
1015 if (touching_boundary[Edge::BOTTOM] && !touching_boundary[Edge::TOP]) {
1016 intersect_length[Edge::BOTTOM] += Smallest1DIntersection(
1017 range.bounding_area.x_min, range.bounding_area.x_max, range.x_size,
1018 current_rectangle.x_min, current_rectangle.x_max);
1019 }
1020
1021 if ((touching_boundary[Edge::LEFT] && touching_boundary[Edge::RIGHT]) ||
1022 (touching_boundary[Edge::TOP] && touching_boundary[Edge::BOTTOM])) {
1023 // We account separately for the problematic items that touches both
1024 // sides.
1025 continue;
1026 }
1027 if (touching_boundary[Edge::BOTTOM] && touching_boundary[Edge::LEFT]) {
1028 corner_count[RectangleInRange::Corner::BOTTOM_LEFT]++;
1029 }
1030 if (touching_boundary[Edge::BOTTOM] && touching_boundary[Edge::RIGHT]) {
1031 corner_count[RectangleInRange::Corner::BOTTOM_RIGHT]++;
1032 }
1033 if (touching_boundary[Edge::TOP] && touching_boundary[Edge::LEFT]) {
1034 corner_count[RectangleInRange::Corner::TOP_LEFT]++;
1035 }
1036 if (touching_boundary[Edge::TOP] && touching_boundary[Edge::RIGHT]) {
1037 corner_count[RectangleInRange::Corner::TOP_RIGHT]++;
1038 }
1039 }
1040
1041 CHECK_EQ(energy, minimum_energy_);
1042 for (int i = 0; i < 4; i++) {
1043 CHECK_EQ(intersect_length[i], intersect_length_[i]);
1044 CHECK_EQ(corner_count[i], corner_count_[i]);
1045 }
1046}
1047
1048namespace {
1049
1050struct EdgeInfo {
1051 using Edge = ProbingRectangle::Edge;
1052 using Direction = ProbingRectangle::Direction;
1053 using Corner = RectangleInRange::Corner;
1054
1055 Edge opposite_edge;
1056
1057 struct OrthogonalInfo {
1058 Edge edge;
1059 Corner adjacent_corner;
1060 };
1061
1062 Direction shrink_direction;
1063 Direction orthogonal_shrink_direction;
1064 // Lower coordinate one first (ie., BOTTOM before TOP, LEFT before RIGHT).
1065 OrthogonalInfo orthogonal_edges[2];
1066};
1067
1068struct EdgeInfoHolder {
1069 using Edge = ProbingRectangle::Edge;
1070 using Direction = ProbingRectangle::Direction;
1071 using Corner = RectangleInRange::Corner;
1072
1073 static constexpr EdgeInfo kLeft = {
1074 .opposite_edge = Edge::RIGHT,
1075 .shrink_direction = Direction::LEFT_AND_RIGHT,
1076 .orthogonal_shrink_direction = Direction::TOP_AND_BOTTOM,
1077 .orthogonal_edges = {
1078 {.edge = Edge::BOTTOM, .adjacent_corner = Corner::BOTTOM_LEFT},
1079 {.edge = Edge::TOP, .adjacent_corner = Corner::TOP_LEFT}}};
1080
1081 static constexpr EdgeInfo kRight = {
1082 .opposite_edge = Edge::LEFT,
1083 .shrink_direction = Direction::LEFT_AND_RIGHT,
1084 .orthogonal_shrink_direction = Direction::TOP_AND_BOTTOM,
1085 .orthogonal_edges = {
1086 {.edge = Edge::BOTTOM, .adjacent_corner = Corner::BOTTOM_RIGHT},
1087 {.edge = Edge::TOP, .adjacent_corner = Corner::TOP_RIGHT}}};
1088 static constexpr EdgeInfo kBottom = {
1089 .opposite_edge = Edge::TOP,
1090 .shrink_direction = Direction::TOP_AND_BOTTOM,
1091 .orthogonal_shrink_direction = Direction::LEFT_AND_RIGHT,
1092 .orthogonal_edges = {
1093 {.edge = Edge::LEFT, .adjacent_corner = Corner::BOTTOM_LEFT},
1094 {.edge = Edge::RIGHT, .adjacent_corner = Corner::BOTTOM_RIGHT}}};
1095 static constexpr EdgeInfo kTop = {
1096 .opposite_edge = Edge::BOTTOM,
1097 .shrink_direction = Direction::TOP_AND_BOTTOM,
1098 .orthogonal_shrink_direction = Direction::LEFT_AND_RIGHT,
1099 .orthogonal_edges = {
1100 {.edge = Edge::LEFT, .adjacent_corner = Corner::TOP_LEFT},
1101 {.edge = Edge::RIGHT, .adjacent_corner = Corner::TOP_RIGHT}}};
1102};
1103
1104constexpr const EdgeInfo& GetEdgeInfo(ProbingRectangle::Edge edge) {
1105 using Edge = ProbingRectangle::Edge;
1106 switch (edge) {
1107 case Edge::LEFT:
1108 return EdgeInfoHolder::kLeft;
1109 case Edge::RIGHT:
1110 return EdgeInfoHolder::kRight;
1111 case Edge::BOTTOM:
1112 return EdgeInfoHolder::kBottom;
1113 case Edge::TOP:
1114 return EdgeInfoHolder::kTop;
1115 }
1116 LOG(FATAL) << "Invalid edge: " << static_cast<int>(edge);
1117}
1118
1119IntegerValue GetSmallest1DIntersection(ProbingRectangle::Direction direction,
1120 const RectangleInRange& range,
1121 const Rectangle& rectangle) {
1122 switch (direction) {
1123 case ProbingRectangle::Direction::LEFT_AND_RIGHT:
1124 return Smallest1DIntersection(range.bounding_area.x_min,
1125 range.bounding_area.x_max, range.x_size,
1126 rectangle.x_min, rectangle.x_max);
1127 case ProbingRectangle::Direction::TOP_AND_BOTTOM:
1128 return Smallest1DIntersection(range.bounding_area.y_min,
1129 range.bounding_area.y_max, range.y_size,
1130 rectangle.y_min, rectangle.y_max);
1131 }
1132 LOG(FATAL) << "Invalid direction: " << static_cast<int>(direction);
1133}
1134
1135} // namespace
1136
1137template <ProbingRectangle::Edge edge>
1138void ProbingRectangle::ShrinkImpl() {
1139 constexpr EdgeInfo e = GetEdgeInfo(edge);
1140
1141 bool update_next_index[4] = {false, false, false, false};
1142 update_next_index[edge] = true;
1143
1144 IntegerValue step_1d_size;
1145 minimum_energy_ -= GetShrinkDeltaEnergy(edge);
1146 const std::vector<PointsForCoordinate>& sorted_intervals =
1147 e.shrink_direction == Direction::LEFT_AND_RIGHT
1148 ? grouped_intervals_sorted_by_x_
1149 : grouped_intervals_sorted_by_y_;
1150
1151 const Rectangle prev_rectangle = GetCurrentRectangle();
1152 indexes_[edge] = next_indexes_[edge];
1153 const Rectangle current_rectangle = GetCurrentRectangle();
1154
1155 switch (edge) {
1156 case Edge::LEFT:
1157 step_1d_size = current_rectangle.x_min - prev_rectangle.x_min;
1158 next_indexes_[edge] =
1159 std::min(indexes_[edge] + 1, indexes_[e.opposite_edge]);
1160 next_indexes_[e.opposite_edge] =
1161 std::max(indexes_[edge], next_indexes_[e.opposite_edge]);
1162 break;
1163 case Edge::BOTTOM:
1164 step_1d_size = current_rectangle.y_min - prev_rectangle.y_min;
1165 next_indexes_[edge] =
1166 std::min(indexes_[edge] + 1, indexes_[e.opposite_edge]);
1167 next_indexes_[e.opposite_edge] =
1168 std::max(indexes_[edge], next_indexes_[e.opposite_edge]);
1169 break;
1170 case Edge::RIGHT:
1171 step_1d_size = prev_rectangle.x_max - current_rectangle.x_max;
1172 next_indexes_[edge] =
1173 std::max(indexes_[edge] - 1, indexes_[e.opposite_edge]);
1174 next_indexes_[e.opposite_edge] =
1175 std::min(indexes_[edge], next_indexes_[e.opposite_edge]);
1176 break;
1177 case Edge::TOP:
1178 step_1d_size = prev_rectangle.y_max - current_rectangle.y_max;
1179 next_indexes_[edge] =
1180 std::max(indexes_[edge] - 1, indexes_[e.opposite_edge]);
1181 next_indexes_[e.opposite_edge] =
1182 std::min(indexes_[edge], next_indexes_[e.opposite_edge]);
1183 break;
1184 }
1185
1186 absl::Span<ProbingRectangle::IntervalPoint> items_touching_coordinate =
1187 sorted_intervals[indexes_[edge]].items_touching_coordinate;
1188
1189 IntegerValue delta_corner_count[4] = {0, 0, 0, 0};
1190 for (const auto& item : items_touching_coordinate) {
1191 const RectangleInRange& range = intervals_[item.index];
1192 if (!CanConsumeEnergy(prev_rectangle, range)) {
1193 // This item is out of our area of interest, skip.
1194 continue;
1195 }
1196
1197 const std::array<bool, 4> touching_boundary_before =
1198 GetPossibleEdgeIntersection(prev_rectangle, range);
1199 const std::array<bool, 4> touching_boundary_after =
1200 CanConsumeEnergy(current_rectangle, range)
1201 ? GetPossibleEdgeIntersection(current_rectangle, range)
1202 : std::array<bool, 4>({false, false, false, false});
1203
1204 bool remove_corner[4] = {false, false, false, false};
1205 auto erase_item = [this, &prev_rectangle, &range, &touching_boundary_before,
1206 &remove_corner](Edge edge_to_erase) {
1207 const EdgeInfo& erase_info = GetEdgeInfo(edge_to_erase);
1208 intersect_length_[edge_to_erase] -= GetSmallest1DIntersection(
1209 erase_info.orthogonal_shrink_direction, range, prev_rectangle);
1210
1211 if (touching_boundary_before[erase_info.orthogonal_edges[0].edge] &&
1212 touching_boundary_before[erase_info.orthogonal_edges[1].edge]) {
1213 // Ignore touching both corners
1214 return;
1215 }
1216 for (const auto [orthogonal_edge, corner] : erase_info.orthogonal_edges) {
1217 if (touching_boundary_before[orthogonal_edge]) {
1218 remove_corner[corner] = true;
1219 }
1220 }
1221 };
1222
1223 if (touching_boundary_after[edge] && !touching_boundary_before[edge]) {
1224 if (touching_boundary_before[e.opposite_edge]) {
1225 ranges_touching_both_boundaries_[e.shrink_direction].insert(item.index);
1226 erase_item(e.opposite_edge);
1227 } else {
1228 // Do the opposite of remove_item().
1229 intersect_length_[edge] += GetSmallest1DIntersection(
1230 e.orthogonal_shrink_direction, range, prev_rectangle);
1231 // Update the corner count unless it is touching both.
1232 if (!touching_boundary_before[e.orthogonal_edges[0].edge] ||
1233 !touching_boundary_before[e.orthogonal_edges[1].edge]) {
1234 for (const auto [orthogonal_edge, corner] : e.orthogonal_edges) {
1235 if (touching_boundary_before[orthogonal_edge]) {
1236 delta_corner_count[corner]++;
1237 }
1238 }
1239 }
1240 }
1241 }
1242
1243 for (int i = 0; i < 4; i++) {
1244 const Edge edge_to_update = (Edge)i;
1245 const EdgeInfo& info = GetEdgeInfo(edge_to_update);
1246 const bool remove_edge = touching_boundary_before[edge_to_update] &&
1247 !touching_boundary_after[edge_to_update];
1248 if (!remove_edge) {
1249 continue;
1250 }
1251
1252 update_next_index[edge_to_update] = true;
1253
1254 if (touching_boundary_before[info.opposite_edge]) {
1255 ranges_touching_both_boundaries_[info.shrink_direction].erase(
1256 item.index);
1257 } else {
1258 erase_item(edge_to_update);
1259 }
1260 }
1261
1262 for (int i = 0; i < 4; i++) {
1263 corner_count_[i] -= remove_corner[i];
1264 }
1265 }
1266
1267 // Update the intersection length for items touching both sides.
1268 for (const int idx : ranges_touching_both_boundaries_[e.shrink_direction]) {
1269 const RectangleInRange& range = intervals_[idx];
1270 const std::array<bool, 2> touching_corner =
1271 (e.shrink_direction == Direction::LEFT_AND_RIGHT)
1272 ? std::array<bool, 2>(
1273 {current_rectangle.y_min >= range.bounding_area.y_min,
1274 current_rectangle.y_max <= range.bounding_area.y_max})
1275 : std::array<bool, 2>(
1276 {current_rectangle.x_min >= range.bounding_area.x_min,
1277 current_rectangle.x_max <= range.bounding_area.x_max});
1278 if (touching_corner[0] == touching_corner[1]) {
1279 // Either it is not touching neither corners (so no length to update) or
1280 // it is touching both corners, which will be handled by the "both
1281 // sides" code and should not contribute to intersect_length_.
1282 continue;
1283 }
1284
1285 const IntegerValue incr =
1286 GetSmallest1DIntersection(e.shrink_direction, range, prev_rectangle) -
1287 GetSmallest1DIntersection(e.shrink_direction, range, current_rectangle);
1288 for (int i = 0; i < 2; i++) {
1289 if (touching_corner[i]) {
1290 intersect_length_[e.orthogonal_edges[i].edge] -= incr;
1291 }
1292 }
1293 }
1294
1295 for (const auto [orthogonal_edge, corner] : e.orthogonal_edges) {
1296 intersect_length_[orthogonal_edge] -= corner_count_[corner] * step_1d_size;
1297 }
1298
1299 for (int i = 0; i < 4; i++) {
1300 corner_count_[i] += delta_corner_count[i];
1301 }
1302
1303 auto points_consume_energy =
1304 [this,
1305 &current_rectangle](absl::Span<ProbingRectangle::IntervalPoint> points) {
1306 for (const auto& item : points) {
1307 const RectangleInRange& range = intervals_[item.index];
1308 if (CanConsumeEnergy(current_rectangle, range)) {
1309 return true;
1310 }
1311 }
1312 return false;
1313 };
1314 if (update_next_index[Edge::LEFT]) {
1315 for (; next_indexes_[Edge::LEFT] < indexes_[Edge::RIGHT];
1316 ++next_indexes_[Edge::LEFT]) {
1317 if (points_consume_energy(
1318 grouped_intervals_sorted_by_x_[next_indexes_[Edge::LEFT]]
1319 .items_touching_coordinate)) {
1320 break;
1321 }
1322 }
1323 }
1324 if (update_next_index[Edge::BOTTOM]) {
1325 for (; next_indexes_[Edge::BOTTOM] < indexes_[Edge::TOP];
1326 ++next_indexes_[Edge::BOTTOM]) {
1327 if (points_consume_energy(
1328 grouped_intervals_sorted_by_y_[next_indexes_[Edge::BOTTOM]]
1329 .items_touching_coordinate)) {
1330 break;
1331 }
1332 }
1333 }
1334 if (update_next_index[Edge::RIGHT]) {
1335 for (; next_indexes_[Edge::RIGHT] > indexes_[Edge::LEFT];
1336 --next_indexes_[Edge::RIGHT]) {
1337 if (points_consume_energy(
1338 grouped_intervals_sorted_by_x_[next_indexes_[Edge::RIGHT]]
1339 .items_touching_coordinate)) {
1340 break;
1341 }
1342 }
1343 }
1344 if (update_next_index[Edge::TOP]) {
1345 for (; next_indexes_[Edge::TOP] > indexes_[Edge::BOTTOM];
1346 --next_indexes_[Edge::TOP]) {
1347 if (points_consume_energy(
1348 grouped_intervals_sorted_by_y_[next_indexes_[Edge::TOP]]
1349 .items_touching_coordinate)) {
1350 break;
1351 }
1352 }
1353 }
1354
1355 probe_area_ = current_rectangle.Area();
1356 CacheShrinkDeltaEnergy(0);
1357 CacheShrinkDeltaEnergy(1);
1358}
1359
1360void ProbingRectangle::Shrink(Edge edge) {
1361 switch (edge) {
1363 ShrinkImpl<Edge::LEFT>();
1364 return;
1365 case Edge::BOTTOM:
1366 ShrinkImpl<Edge::BOTTOM>();
1367 return;
1368 case Edge::RIGHT:
1369 ShrinkImpl<Edge::RIGHT>();
1370 return;
1371 case Edge::TOP:
1372 ShrinkImpl<Edge::TOP>();
1373 return;
1374 }
1375}
1376
1377IntegerValue ProbingRectangle::GetShrinkDeltaArea(Edge edge) const {
1378 const Rectangle current_rectangle = GetCurrentRectangle();
1379 const std::vector<PointsForCoordinate>& sorted_intervals =
1380 (edge == Edge::LEFT || edge == Edge::RIGHT)
1381 ? grouped_intervals_sorted_by_x_
1382 : grouped_intervals_sorted_by_y_;
1383 const IntegerValue coordinate =
1384 sorted_intervals[next_indexes_[edge]].coordinate;
1385 switch (edge) {
1386 case Edge::LEFT:
1387 return (coordinate - current_rectangle.x_min) * current_rectangle.SizeY();
1388 case Edge::BOTTOM:
1389 return (coordinate - current_rectangle.y_min) * current_rectangle.SizeX();
1390 case Edge::RIGHT:
1391 return (current_rectangle.x_max - coordinate) * current_rectangle.SizeY();
1392 case Edge::TOP:
1393 return (current_rectangle.y_max - coordinate) * current_rectangle.SizeX();
1394 }
1395 LOG(FATAL) << "Invalid edge: " << static_cast<int>(edge);
1396}
1397
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;
1406
1407 if (dimension == 0) {
1408 // CanShrink(Edge::RIGHT) and CanShrink(Edge::LEFT) are equivalent
1409 if (!CanShrink(Edge::LEFT)) {
1410 cached_delta_energy_[Edge::LEFT] = 0;
1411 cached_delta_energy_[Edge::RIGHT] = 0;
1412 return;
1413 }
1414
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;
1419
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];
1426 } else {
1427 if (!CanShrink(Edge::TOP)) {
1428 cached_delta_energy_[Edge::BOTTOM] = 0;
1429 cached_delta_energy_[Edge::TOP] = 0;
1430 return;
1431 }
1432
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;
1437
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];
1444 }
1445 IntegerValue delta_energy_up = 0;
1446 IntegerValue delta_energy_down = 0;
1447
1448 // Note that the non-deterministic iteration order is fine here.
1449 for (const int idx : ranges_touching_both_boundaries_[dimension]) {
1450 const RectangleInRange& range = intervals_[idx];
1451 const IntegerValue curr_x = Smallest1DIntersection(
1452 range.bounding_area.x_min, range.bounding_area.x_max, range.x_size,
1453 current_rectangle.x_min, current_rectangle.x_max);
1454 const IntegerValue curr_y = Smallest1DIntersection(
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;
1460
1461 if (dimension == 0) {
1462 const IntegerValue up_x = Smallest1DIntersection(
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);
1465 const IntegerValue down_x = Smallest1DIntersection(
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);
1468
1469 delta_energy_up -= curr_y * up_x;
1470 delta_energy_down -= curr_y * down_x;
1471 } else {
1472 const IntegerValue up_y = Smallest1DIntersection(
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);
1475 const IntegerValue down_y = Smallest1DIntersection(
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);
1478
1479 delta_energy_up -= curr_x * up_y;
1480 delta_energy_down -= curr_x * down_y;
1481 }
1482 }
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;
1487}
1488
1489bool ProbingRectangle::CanShrink(Edge edge) const {
1490 switch (edge) {
1492 case Edge::RIGHT:
1493 return (next_indexes_[Edge::RIGHT] > indexes_[Edge::LEFT]);
1494 case Edge::BOTTOM:
1495 case Edge::TOP:
1496 return (indexes_[Edge::TOP] > next_indexes_[Edge::BOTTOM]);
1497 }
1498 LOG(FATAL) << "Invalid edge: " << static_cast<int>(edge);
1499}
1500
1501namespace {
1502std::vector<double> GetExpTable() {
1503 std::vector<double> table(101);
1504 for (int i = 0; i <= 100; ++i) {
1505 table[i] = std::exp(-(i - 50) / 5.0);
1506 }
1507 return table;
1508}
1509
1510} // namespace
1511
1512FindRectanglesResult FindRectanglesWithEnergyConflictMC(
1513 const std::vector<RectangleInRange>& intervals, absl::BitGenRef random,
1514 double temperature, double candidate_energy_usage_factor) {
1515 FindRectanglesResult result;
1516 ProbingRectangle ranges(intervals);
1517
1518 static const std::vector<double>* cached_probabilities =
1519 new std::vector<double>(GetExpTable());
1520
1521 const double inv_temp = 1.0 / temperature;
1522 absl::InlinedVector<ProbingRectangle::Edge, 4> candidates;
1523 absl::InlinedVector<IntegerValue, 4> energy_deltas;
1524 absl::InlinedVector<double, 4> weights;
1525 while (!ranges.IsMinimal()) {
1526 const IntegerValue rect_area = ranges.GetCurrentRectangleArea();
1527 const IntegerValue min_energy = ranges.GetMinimumEnergy();
1528 if (min_energy > rect_area) {
1529 result.conflicts.push_back(ranges.GetCurrentRectangle());
1530 } else if (min_energy.value() >
1531 candidate_energy_usage_factor * rect_area.value()) {
1532 result.candidates.push_back(ranges.GetCurrentRectangle());
1533 }
1534 if (min_energy == 0) {
1535 break;
1536 }
1537 candidates.clear();
1538 energy_deltas.clear();
1539
1540 for (int border_idx = 0; border_idx < 4; ++border_idx) {
1541 const ProbingRectangle::Edge border =
1542 static_cast<ProbingRectangle::Edge>(border_idx);
1543 if (!ranges.CanShrink(border)) {
1544 continue;
1545 }
1546 candidates.push_back(border);
1547 const IntegerValue delta_area = ranges.GetShrinkDeltaArea(border);
1548 const IntegerValue delta_energy = ranges.GetShrinkDeltaEnergy(border);
1549 energy_deltas.push_back(delta_energy - delta_area);
1550 }
1551 const IntegerValue min_energy_delta =
1552 *std::min_element(energy_deltas.begin(), energy_deltas.end());
1553 weights.clear();
1554 for (const IntegerValue delta_slack : energy_deltas) {
1555 const int64_t table_lookup =
1556 std::max((int64_t)0,
1557 std::min((int64_t)((delta_slack - min_energy_delta).value() *
1558 5 * inv_temp +
1559 50),
1560 (int64_t)100));
1561 weights.push_back((*cached_probabilities)[table_lookup]);
1562 }
1563 // Pick a change with a probability proportional to exp(- delta_E / Temp)
1564 ranges.Shrink(candidates[WeightedPick(weights, random)]);
1565 }
1566 if (ranges.GetMinimumEnergy() > ranges.GetCurrentRectangleArea()) {
1567 result.conflicts.push_back(ranges.GetCurrentRectangle());
1568 }
1569 return result;
1570}
1571
1572std::string RenderDot(std::optional<Rectangle> bb,
1573 absl::Span<const Rectangle> solution,
1574 std::string_view extra_dot_payload) {
1575 const std::vector<std::string> colors = {"#0000ff80", "#ee00ee80",
1576 "#ff000080", "#eeee0080",
1577 "#00ff0080", "#00eeee80"};
1578 std::stringstream ss;
1579 ss << "digraph {\n";
1580 ss << " graph [ bgcolor=lightgray ]\n";
1581 ss << " node [style=filled shape=box]\n";
1582 if (bb.has_value()) {
1583 ss << " bb [fillcolor=\"grey\" pos=\"" << 2 * bb->x_min + bb->SizeX()
1584 << "," << 2 * bb->y_min + bb->SizeY() << "!\" width=" << 2 * bb->SizeX()
1585 << " height=" << 2 * bb->SizeY() << "]\n";
1586 }
1587 for (int i = 0; i < solution.size(); ++i) {
1588 ss << " " << i << " [fillcolor=\"" << colors[i % colors.size()]
1589 << "\" pos=\"" << 2 * solution[i].x_min + solution[i].SizeX() << ","
1590 << 2 * solution[i].y_min + solution[i].SizeY()
1591 << "!\" width=" << 2 * solution[i].SizeX()
1592 << " height=" << 2 * solution[i].SizeY() << "]\n";
1593 }
1594 ss << extra_dot_payload;
1595 ss << "}\n";
1596 return ss.str();
1597}
1598
1599std::vector<Rectangle> FindEmptySpaces(
1600 const Rectangle& bounding_box, std::vector<Rectangle> ocupied_rectangles) {
1601 // Sorting is not necessary for correctness but makes it faster.
1602 std::sort(ocupied_rectangles.begin(), ocupied_rectangles.end(),
1603 [](const Rectangle& a, const Rectangle& b) {
1604 return std::tuple(a.x_min, -a.x_max, a.y_min) <
1605 std::tuple(b.x_min, -b.x_max, b.y_min);
1606 });
1607 return PavedRegionDifference({bounding_box}, ocupied_rectangles);
1608}
1609
1610std::vector<Rectangle> PavedRegionDifference(
1611 std::vector<Rectangle> original_region,
1612 absl::Span<const Rectangle> area_to_remove) {
1613 std::vector<Rectangle> new_area_to_cover;
1614 new_area_to_cover.reserve(original_region.size());
1615 for (const Rectangle& rectangle : area_to_remove) {
1616 new_area_to_cover.clear();
1617 for (const Rectangle& r : original_region) {
1618 const auto& new_rectangles = r.RegionDifference(rectangle);
1619 new_area_to_cover.insert(new_area_to_cover.end(), new_rectangles.begin(),
1620 new_rectangles.end());
1621 }
1622 original_region.swap(new_area_to_cover);
1623 if (original_region.empty()) break;
1624 }
1625 return original_region;
1626}
1627
1628// Each node in the tree will hold either a single box that is covering the
1629// whole interval represented by the node, or, if no such box exists, a superset
1630// of all the connected components of boxes that are overlapping the interval.
1631// It is a superset and not the exact set of connected components because we
1632// don't delete nodes that became stale, as explained in the class comment
1633// below.
1634struct BinaryTreeNode {
1635 // Contains exactly one element if occupying_box_index != -1.
1636 absl::flat_hash_set<int> connected_components_descendants;
1637 // Hold the x_max of the box that is currently occupying this node (if any) to
1638 // know when it is stale.
1640 // -1 if not occupied.
1642};
1644// A data structure to store which boxes are overlapping the current sweep line.
1645// This uses a binary tree in a slight non-standard way: in a typical use of a
1646// binary tree the actual values are stored in the leaves and the intermediate
1647// nodes are there just to make finding the right leaf efficient. Here we do the
1648// opposite: the values are stored as high up in the tree as possible.
1649// For example, for a tree of size 8 a box that occupies the y interval [0, 7]
1650// will be stored as a single node at the root. In the same tree, a box that
1651// occupies [3, 7] will be stored with the nodes representing the [3, 4), [4, 6)
1652// and [6, 8) intervals. There is no difference on what is stored in the
1653// intermediate nodes or on the leaves. When the sweep line moves, we don't
1654// update the existing nodes on the tree. Thus, some nodes will become stale and
1655// will represent boxes that no longer overlap the sweep line. Those stale nodes
1656// get removed lazily.
1657struct SweepLineIntervalTree {
1658 explicit SweepLineIntervalTree(int max_y, int num_boxes)
1659 : tree(LeafIndex(max_y + 1)), tree_nodes(tree.StorageSize()) {
1660 union_find.SetNumberOfNodes(num_boxes);
1661 }
1662
1663 // Recompute the connected components of a given node, by simply setting it to
1664 // {self} + left.connected_components + right.connected_components.
1665 void RecomputeConnectedComponents(TreeNodeIndex idx) {
1666 BinaryTreeNode& node = tree_nodes[idx];
1667 if (node.occupying_box_index != -1) {
1668 node.connected_components_descendants = {
1669 union_find.FindRoot(node.occupying_box_index)};
1670 return;
1671 }
1672 node.connected_components_descendants.clear();
1673 if (tree.IsLeaf(idx)) return;
1674 for (const TreeNodeIndex child_idx :
1675 {tree.LeftChild(idx), tree.RightChild(idx)}) {
1676 // The order is non-deterministic, but since this is doing the union of
1677 // hash sets the result is deterministic.
1678 for (const int c :
1679 tree_nodes[child_idx].connected_components_descendants) {
1680 node.connected_components_descendants.insert(union_find.FindRoot(c));
1681 }
1682 }
1683 }
1684
1685 // We don't have global deletion method in this class, but this method
1686 // checks if a single interval is fully to the left of the sweep line and
1687 // removes it if so, also updating its connected components.
1688 void RemoveNodeIfXMaxLowerOrEqual(TreeNodeIndex idx, int x_threshold) {
1689 BinaryTreeNode& node = tree_nodes[idx];
1690 if (node.occupying_box_index == -1) {
1691 // Node is already empty.
1692 return;
1693 }
1694 if (node.occupying_box_x_max > x_threshold) {
1695 // Node is still overlapping the sweep line.
1696 return;
1697 }
1698 node.occupying_box_index = -1;
1699 RecomputeConnectedComponents(idx);
1700 }
1701
1702 void UpdateChildrenIntersecting(TreeNodeIndex idx, int sweep_line_x_pos,
1703 int component_index,
1704 std::vector<int>* new_connections) {
1705 if (tree.IsLeaf(idx)) return;
1706 for (const TreeNodeIndex child_idx :
1707 {tree.LeftChild(idx), tree.RightChild(idx)}) {
1708 RemoveNodeIfXMaxLowerOrEqual(child_idx, sweep_line_x_pos);
1709 BinaryTreeNode& child_node = tree_nodes[child_idx];
1710 if (child_node.occupying_box_index != -1) {
1711 if (union_find.AddEdge(child_node.occupying_box_index,
1712 component_index)) {
1713 new_connections->push_back(child_node.occupying_box_index);
1714 }
1715 // No need to recurse here: we already connected everything on this
1716 // branch to the new box.
1717 continue;
1718 }
1719 const bool had_different_component =
1720 absl::c_any_of(child_node.connected_components_descendants,
1721 [this, component_index](const int c) {
1722 return !union_find.Connected(c, component_index);
1723 });
1724 // Since everything is intersecting the current box, all descendants
1725 // must be in one single component.
1726 child_node.connected_components_descendants = {component_index};
1727
1728 // Only go down on the tree if we have below either:
1729 // - a different component to connect.
1730 // - a box to remove that is in a different component.
1731 // In any case, we will visit O(c + d) terminals, where c is the number of
1732 // components we are connecting and d is the number of boxes that we will
1733 // delete. Since a box can only be deleted log N times (one per interval
1734 // it was cut into) and we can only connect O(N) components in total, the
1735 // amortized cost of a call to UpdateChildrenIntersecting is O((log N)^2).
1736 if (had_different_component) {
1737 UpdateChildrenIntersecting(child_idx, sweep_line_x_pos, component_index,
1738 new_connections);
1739 }
1740 }
1741 }
1742
1743 bool UpdateParents(TreeNodeIndex node, int sweep_line_x_pos,
1744 int component_index, std::vector<int>* new_connections) {
1745 if (node == tree.Root()) return false;
1746 for (TreeNodeIndex parent = tree.Parent(node); parent != tree.Root();
1747 parent = tree.Parent(parent)) {
1748 RemoveNodeIfXMaxLowerOrEqual(parent, sweep_line_x_pos);
1749 BinaryTreeNode& parent_value = tree_nodes[parent];
1750 if (parent_value.occupying_box_index != -1) {
1751 if (union_find.AddEdge(parent_value.occupying_box_index,
1752 component_index)) {
1753 new_connections->push_back(parent_value.occupying_box_index);
1754 return true;
1755 }
1756 }
1757 parent_value.connected_components_descendants.insert(component_index);
1758 }
1759 return false;
1760 }
1761
1762 // Add a new box to the sweep line. This will store it in the tree (split in
1763 // log N intervals) check if it connects to one or more existing connected
1764 // components, and for each case it does, add the box that it is overlapping
1765 // to new_connections.
1766 void AddInterval(TreeNodeIndex idx, int sweep_line_x_pos, int box_index,
1767 int x_max, std::vector<int>* new_connections) {
1768 RemoveNodeIfXMaxLowerOrEqual(idx, sweep_line_x_pos);
1769 int cur_box_component = union_find.FindRoot(box_index);
1770 BinaryTreeNode& node = tree_nodes[idx];
1771 if (node.occupying_box_index == -1) {
1772 node.connected_components_descendants = {box_index};
1773 node.occupying_box_index = box_index;
1774 node.occupying_box_x_max = x_max;
1775 const bool had_occupied_parent = UpdateParents(
1776 idx, sweep_line_x_pos, cur_box_component, new_connections);
1777 // We can only be connecting children if it is not already connect via
1778 // something above on the tree.
1779 if (!had_occupied_parent) {
1780 UpdateChildrenIntersecting(idx, sweep_line_x_pos, cur_box_component,
1781 new_connections);
1782 }
1783 } else {
1784 // We have already something fully occupying this interval.
1785 if (union_find.AddEdge(node.occupying_box_index, cur_box_component)) {
1786 new_connections->push_back(node.occupying_box_index);
1787 cur_box_component = union_find.FindRoot(cur_box_component);
1788 }
1789 node.connected_components_descendants = {cur_box_component};
1790 if (node.occupying_box_x_max < x_max) {
1791 // Replace the existing box by the new one.
1792 node.occupying_box_index = box_index;
1793 node.occupying_box_x_max = x_max;
1794 }
1795 }
1796 }
1797
1798 FixedShapeBinaryTree tree;
1799 util_intops::StrongVector<TreeNodeIndex, BinaryTreeNode> tree_nodes;
1803struct Rectangle32 {
1804 int x_min;
1811// Requires that rectangles are sorted by x_min and that sizes on both
1812// dimensions are > 0.
1813std::vector<std::pair<int, int>> FindPartialRectangleIntersectionsImpl(
1814 absl::Span<const Rectangle32> rectangles, int32_t y_max) {
1815 // We are going to use a sweep line algorithm to find the intersections.
1816 // First, we sort the rectangles by their x coordinates, then consider a sweep
1817 // line that goes from the left to the right. See the comment on the
1818 // SweepLineIntervalTree class for more details about what we store for each
1819 // line.
1820 DCHECK(std::is_sorted(rectangles.begin(), rectangles.end(),
1821 [](const Rectangle32& a, const Rectangle32& b) {
1822 return a.x_min < b.x_min;
1823 }));
1824
1825 SweepLineIntervalTree interval_tree(y_max, rectangles.size());
1826
1827 std::vector<TreeNodeIndex> interval_pieces;
1828 std::vector<int> new_connections;
1829 std::vector<std::pair<int, int>> arcs;
1830 for (int rectangle_index = 0; rectangle_index < rectangles.size();
1831 ++rectangle_index) {
1832 DCHECK_LT(rectangles[rectangle_index].x_min,
1833 rectangles[rectangle_index].x_max);
1834 DCHECK_LT(rectangles[rectangle_index].y_min,
1835 rectangles[rectangle_index].y_max);
1836 const int sweep_line_x_pos = rectangles[rectangle_index].x_min;
1837 const Rectangle32& r = rectangles[rectangle_index];
1838 interval_pieces.clear();
1839 interval_tree.tree.PartitionIntervalIntoNodes(
1840 LeafIndex(r.y_min), LeafIndex(r.y_max - 1), &interval_pieces);
1841 new_connections.clear();
1842 for (const TreeNodeIndex& node : interval_pieces) {
1843 interval_tree.AddInterval(node, sweep_line_x_pos, rectangle_index,
1844 r.x_max, &new_connections);
1845 }
1846 for (const int new_connection : new_connections) {
1847 arcs.push_back({rectangles[new_connection].index, r.index});
1848 }
1849 }
1850 return arcs;
1851}
1852
1853namespace {
1854struct PostProcessedResult {
1855 std::vector<Rectangle32> rectangles_sorted_by_x_min;
1856 std::pair<int32_t, int32_t> bounding_box; // Always starting at (0,0).
1857};
1858
1859PostProcessedResult ConvertToRectangle32WithNonZeroSizes(
1860 absl::Span<const Rectangle> rectangles) {
1861 // This function is a preprocessing function for algorithms that find overlap
1862 // between rectangles. It does the following:
1863 // - It converts the arbitrary int64_t coordinates into a small integer by
1864 // sorting the possible values and assigning them consecutive integers.
1865 // - It grows zero size intervals to make them size one. This simplifies
1866 // things considerably, since it is hard to reason about degenerated
1867 // rectangles in the general algorithm.
1868 //
1869 // Note that the last point need to be done with care. Imagine the following
1870 // example:
1871 // +----------+
1872 // | |
1873 // | +--------------+
1874 // | | |
1875 // | | p,q r |
1876 // | +----*-----*-+-+
1877 // | | |
1878 // | | |
1879 // | | |
1880 // | +------------+
1881 // | |
1882 // | |
1883 // +----------+
1884 // Where p,q and r are points (ie, boxes of size 0x0) and p and q have the
1885 // same coordinates. We replace them by the following:
1886 // +----------+
1887 // | |
1888 // | +----------------------+
1889 // | | |
1890 // | | |
1891 // | +----+-+---------------+
1892 // | | |p|
1893 // | | +-+-+
1894 // | | |q|
1895 // | | +-+ +-+
1896 // | | |r|
1897 // | +--------------+-+---+
1898 // | | |
1899 // | | |
1900 // | | |
1901 // | +--------------------+
1902 // | |
1903 // | |
1904 // +----------+
1905 //
1906 // That is a pretty radical deformation of the original shape, but it retains
1907 // the property of whether a pair of rectangles intersect or not.
1908
1909 if (rectangles.empty()) return {};
1910
1911 enum class Event {
1912 kEnd = 0,
1913 kPoint = 1,
1914 kBegin = 2,
1915 };
1916 std::vector<std::tuple<IntegerValue, Event, int>> x_events;
1917 std::vector<std::tuple<IntegerValue, Event, int>> y_events;
1918 x_events.reserve(rectangles.size() * 2);
1919 y_events.reserve(rectangles.size() * 2);
1920 for (int i = 0; i < rectangles.size(); ++i) {
1921 const Rectangle& r = rectangles[i];
1922 DCHECK_GE(r.SizeX(), 0);
1923 DCHECK_GE(r.SizeY(), 0);
1924 if (r.SizeX() == 0) {
1925 x_events.push_back({r.x_min, Event::kPoint, i});
1926 } else {
1927 x_events.push_back({r.x_min, Event::kBegin, i});
1928 x_events.push_back({r.x_max, Event::kEnd, i});
1929 }
1930 if (r.SizeY() == 0) {
1931 y_events.push_back({r.y_min, Event::kPoint, i});
1932 } else {
1933 y_events.push_back({r.y_min, Event::kBegin, i});
1934 y_events.push_back({r.y_max, Event::kEnd, i});
1935 }
1936 }
1937 std::sort(y_events.begin(), y_events.end());
1938
1939 std::vector<Rectangle32> rectangles32;
1940 rectangles32.resize(rectangles.size());
1941 IntegerValue prev_y = 0;
1942 Event prev_event = Event::kEnd;
1943 int cur_index = -1;
1944 for (const auto [y, event, index] : y_events) {
1945 if ((prev_event != event && prev_event != Event::kEnd) || prev_y != y ||
1946 event == Event::kPoint || cur_index == -1) {
1947 ++cur_index;
1948 }
1949
1950 switch (event) {
1951 case Event::kBegin:
1952 rectangles32[index].y_min = cur_index;
1953 rectangles32[index].index = index;
1954 break;
1955 case Event::kEnd:
1956 rectangles32[index].y_max = cur_index;
1957 break;
1958 case Event::kPoint:
1959 rectangles32[index].y_min = cur_index;
1960 rectangles32[index].y_max = cur_index + 1;
1961 rectangles32[index].index = index;
1962 break;
1963 }
1964 prev_event = event;
1965 prev_y = y;
1966 }
1967 const int max_y_index = cur_index + 1;
1968
1969 gtl::STLClearObject(&y_events);
1970
1971 std::sort(x_events.begin(), x_events.end());
1972 IntegerValue prev_x = 0;
1973 prev_event = Event::kEnd;
1974 cur_index = -1;
1975 for (const auto [x, event, index] : x_events) {
1976 if ((prev_event != event && prev_event != Event::kEnd) || prev_x != x ||
1977 event == Event::kPoint || cur_index == -1) {
1978 ++cur_index;
1979 }
1980
1981 switch (event) {
1982 case Event::kBegin:
1983 rectangles32[index].x_min = cur_index;
1984 break;
1985 case Event::kEnd:
1986 rectangles32[index].x_max = cur_index;
1987 break;
1988 case Event::kPoint:
1989 rectangles32[index].x_min = cur_index;
1990 rectangles32[index].x_max = cur_index + 1;
1991 break;
1992 }
1993 prev_event = event;
1994 prev_x = x;
1995 }
1996 const int max_x_index = cur_index + 1;
1997
1998 std::vector<Rectangle32> sorted_rectangles32;
1999 sorted_rectangles32.reserve(rectangles.size());
2000 for (const auto [x, event, index] : x_events) {
2001 if (event == Event::kBegin || event == Event::kPoint) {
2002 sorted_rectangles32.push_back(rectangles32[index]);
2003 }
2004 }
2005
2006 return {sorted_rectangles32, {max_x_index, max_y_index}};
2007}
2008template <typename RectangleT>
2009std::optional<std::pair<int, int>> FindOneIntersectionIfPresentImpl(
2010 absl::Span<const RectangleT> rectangles) {
2011 using CoordinateType = std::decay_t<decltype(RectangleT::x_min)>;
2012 DCHECK(absl::c_is_sorted(rectangles,
2013 [](const RectangleT& a, const RectangleT& b) {
2014 return a.x_min < b.x_min;
2015 }));
2016
2017 // Set of box intersection the sweep line. We only store y_min, other
2018 // coordinates can be accessed via rectangles[index].coordinate.
2019 struct Element {
2020 mutable int index;
2021 CoordinateType y_min;
2022 bool operator<(const Element& other) const { return y_min < other.y_min; }
2023 };
2024
2025 // Note: To use btree_set that has no iterator stability, we have to be
2026 // a bit careful below.
2027 absl::btree_set<Element> interval_set;
2028
2029 for (int i = 0; i < rectangles.size(); ++i) {
2030 const CoordinateType x = rectangles[i].x_min;
2031 const CoordinateType y_min = rectangles[i].y_min;
2032 const CoordinateType y_max = rectangles[i].y_max;
2033
2034 // TODO(user): We can handle that, but it require some changes below.
2035 DCHECK_LE(y_min, y_max);
2036
2037 // Try to add this rectangle to the set, if there is an intersection, lazily
2038 // remove it if its x_max is already passed, otherwise report the
2039 // intersection.
2040 auto [it, inserted] = interval_set.insert({i, y_min});
2041 if (!inserted) {
2042 if (rectangles[it->index].x_max <= x) {
2043 // We just replace if the rectangle at position i is stale.
2044 it->index = i;
2045 } else {
2046 // Intersection.
2047 return {{it->index, i}};
2048 }
2049 } else {
2050 // If there was no element at position y_min, we need to test if the
2051 // interval before is stale or if it overlap with the new one.
2052 if (it != interval_set.begin()) {
2053 auto it_before = it;
2054 --it_before;
2055
2056 // Lazy erase stale entry.
2057 if (rectangles[it_before->index].x_max <= x) {
2058 // For absl::btree_set we don't have iterator stability, so we do need
2059 // to re-assign 'it' to the element just after the one we erased.
2060 it = interval_set.erase(it_before);
2061 } else {
2062 DCHECK_LE(it_before->y_min, y_min);
2063 const CoordinateType y_max_before =
2064 rectangles[it_before->index].y_max;
2065 if (y_max_before > y_min) {
2066 // Intersection.
2067 return {{it_before->index, i}};
2068 }
2069 }
2070 }
2071 }
2072
2073 // We handled the part before, now we need to deal with the interval that
2074 // starts after y_min.
2075 ++it;
2076 while (it != interval_set.end()) {
2077 // Lazy erase stale entry.
2078 if (rectangles[it->index].x_max <= x) {
2079 it = interval_set.erase(it);
2080 continue;
2081 }
2082
2083 DCHECK_LE(y_min, it->y_min);
2084 if (y_max > it->y_min) {
2085 // Intersection.
2086 return {{it->index, i}};
2087 }
2088 break;
2089 }
2090 }
2091
2092 return {};
2093}
2094
2095} // namespace
2096
2097std::vector<std::pair<int, int>> FindPartialRectangleIntersections(
2098 absl::Span<const Rectangle> rectangles) {
2099 auto postprocessed = ConvertToRectangle32WithNonZeroSizes(rectangles);
2101 postprocessed.rectangles_sorted_by_x_min,
2102 postprocessed.bounding_box.second);
2103}
2104
2105std::optional<std::pair<int, int>> FindOneIntersectionIfPresent(
2106 absl::Span<const Rectangle> rectangles) {
2107 return FindOneIntersectionIfPresentImpl(rectangles);
2108}
2109
2110std::optional<std::pair<int, int>> FindOneIntersectionIfPresentWithZeroArea(
2111 absl::Span<const Rectangle> rectangles) {
2112 auto postprocessed = ConvertToRectangle32WithNonZeroSizes(rectangles);
2113 std::optional<std::pair<int, int>> result = FindOneIntersectionIfPresentImpl(
2114 absl::MakeConstSpan(postprocessed.rectangles_sorted_by_x_min));
2115 if (!result.has_value()) return {};
2116 return {{postprocessed.rectangles_sorted_by_x_min[result->first].index,
2117 postprocessed.rectangles_sorted_by_x_min[result->second].index}};
2118}
2119
2120} // namespace sat
2121} // namespace operations_research
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)
void BuildResidualCapacityProfile(std::vector< Rectangle > *result)
void ResetFromPairs(const Collection &pairs, int minimum_num_nodes=0)
Definition util.h:1089
IntegerValue GetShrinkDeltaEnergy(Edge edge) const
Definition diffn_util.h:599
IntegerValue GetShrinkDeltaArea(Edge edge) const
void AddEnergyMinInIntervalReason(int t, IntegerValue min, IntegerValue max)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition stl_util.h:55
void STLClearObject(T *obj)
Definition stl_util.h:120
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)
Definition util.cc:415
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)
OR-Tools root namespace.
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
ClosedInterval::Iterator end(ClosedInterval interval)
const bool DEBUG_MODE
Definition radix_sort.h:266
void FindStronglyConnectedComponents(NodeIndex num_nodes, const Graph &graph, SccOutput *components)
absl::flat_hash_set< int > connected_components_descendants
void GrowToInclude(const Rectangle &other)
Definition diffn_util.h:49
absl::InlinedVector< Rectangle, 4 > RegionDifference(const Rectangle &other) const
Definition diffn_util.cc:63
bool IsDisjoint(const Rectangle &other) const
Definition diffn_util.cc:58
Rectangle Intersect(const Rectangle &other) const
Definition diffn_util.h:104
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)
util_intops::StrongVector< TreeNodeIndex, BinaryTreeNode > tree_nodes