OR-Tools  9.3
diffn_util.cc
Go to the documentation of this file.
1// Copyright 2010-2021 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 <utility>
20#include <vector>
21
22#include "absl/container/flat_hash_set.h"
23#include "absl/random/bit_gen_ref.h"
24#include "absl/types/span.h"
27#include "ortools/sat/integer.h"
31
32namespace operations_research {
33namespace sat {
34
35bool Rectangle::IsDisjoint(const Rectangle& other) const {
36 return x_min >= other.x_max || other.x_min >= x_max || y_min >= other.y_max ||
37 other.y_min >= y_max;
38}
39
40std::vector<absl::Span<int>> GetOverlappingRectangleComponents(
41 const std::vector<Rectangle>& rectangles,
42 absl::Span<int> active_rectangles) {
43 if (active_rectangles.empty()) return {};
44
45 std::vector<absl::Span<int>> result;
46 const int size = active_rectangles.size();
47 for (int start = 0; start < size;) {
48 // Find the component of active_rectangles[start].
49 int end = start + 1;
50 for (int i = start; i < end; i++) {
51 for (int j = end; j < size; ++j) {
52 if (!rectangles[active_rectangles[i]].IsDisjoint(
53 rectangles[active_rectangles[j]])) {
54 std::swap(active_rectangles[end++], active_rectangles[j]);
55 }
56 }
57 }
58 if (end > start + 1) {
59 result.push_back(active_rectangles.subspan(start, end - start));
60 }
61 start = end;
62 }
63 return result;
64}
65
66bool ReportEnergyConflict(Rectangle bounding_box, absl::Span<const int> boxes,
69 x->ClearReason();
70 y->ClearReason();
71 IntegerValue total_energy(0);
72 for (const int b : boxes) {
73 const IntegerValue x_min = x->ShiftedStartMin(b);
74 const IntegerValue x_max = x->ShiftedEndMax(b);
75 if (x_min < bounding_box.x_min || x_max > bounding_box.x_max) continue;
76 const IntegerValue y_min = y->ShiftedStartMin(b);
77 const IntegerValue y_max = y->ShiftedEndMax(b);
78 if (y_min < bounding_box.y_min || y_max > bounding_box.y_max) continue;
79
80 x->AddEnergyMinInIntervalReason(b, bounding_box.x_min, bounding_box.x_max);
81 y->AddEnergyMinInIntervalReason(b, bounding_box.y_min, bounding_box.y_max);
82
85
86 total_energy += x->SizeMin(b) * y->SizeMin(b);
87
88 // We abort early if a subset of boxes is enough.
89 // TODO(user): Also relax the box if possible.
90 if (total_energy > bounding_box.Area()) break;
91 }
92
93 CHECK_GT(total_energy, bounding_box.Area());
94 x->ImportOtherReasons(*y);
95 return x->ReportConflict();
96}
97
98bool BoxesAreInEnergyConflict(const std::vector<Rectangle>& rectangles,
99 const std::vector<IntegerValue>& energies,
100 absl::Span<const int> boxes,
101 Rectangle* conflict) {
102 // First consider all relevant intervals along the x axis.
103 std::vector<IntegerValue> x_starts;
104 std::vector<TaskTime> boxes_by_increasing_x_max;
105 for (const int b : boxes) {
106 x_starts.push_back(rectangles[b].x_min);
107 boxes_by_increasing_x_max.push_back({b, rectangles[b].x_max});
108 }
110 std::sort(boxes_by_increasing_x_max.begin(), boxes_by_increasing_x_max.end());
111
112 std::vector<IntegerValue> y_starts;
113 std::vector<IntegerValue> energy_sum;
114 std::vector<TaskTime> boxes_by_increasing_y_max;
115
116 std::vector<std::vector<int>> stripes(x_starts.size());
117 for (int i = 0; i < boxes_by_increasing_x_max.size(); ++i) {
118 const int b = boxes_by_increasing_x_max[i].task_index;
119 const IntegerValue x_min = rectangles[b].x_min;
120 const IntegerValue x_max = rectangles[b].x_max;
121 for (int j = 0; j < x_starts.size(); ++j) {
122 if (x_starts[j] > x_min) break;
123 stripes[j].push_back(b);
124
125 // Redo the same on the y coordinate for the current x interval
126 // which is [starts[j], x_max].
127 y_starts.clear();
128 boxes_by_increasing_y_max.clear();
129 for (const int b : stripes[j]) {
130 y_starts.push_back(rectangles[b].y_min);
131 boxes_by_increasing_y_max.push_back({b, rectangles[b].y_max});
132 }
134 std::sort(boxes_by_increasing_y_max.begin(),
135 boxes_by_increasing_y_max.end());
136
137 const IntegerValue x_size = x_max - x_starts[j];
138 energy_sum.assign(y_starts.size(), IntegerValue(0));
139 for (int i = 0; i < boxes_by_increasing_y_max.size(); ++i) {
140 const int b = boxes_by_increasing_y_max[i].task_index;
141 const IntegerValue y_min = rectangles[b].y_min;
142 const IntegerValue y_max = rectangles[b].y_max;
143 for (int j = 0; j < y_starts.size(); ++j) {
144 if (y_starts[j] > y_min) break;
145 energy_sum[j] += energies[b];
146 if (energy_sum[j] > x_size * (y_max - y_starts[j])) {
147 if (conflict != nullptr) {
148 *conflict = rectangles[b];
149 for (int k = 0; k < i; ++k) {
150 const int task_index = boxes_by_increasing_y_max[k].task_index;
151 if (rectangles[task_index].y_min >= y_starts[j]) {
152 conflict->TakeUnionWith(rectangles[task_index]);
153 }
154 }
155 }
156 return true;
157 }
158 }
159 }
160 }
161 }
162 return false;
163}
164
165bool AnalyzeIntervals(bool transpose, absl::Span<const int> local_boxes,
166 const std::vector<Rectangle>& rectangles,
167 const std::vector<IntegerValue>& rectangle_energies,
168 IntegerValue* x_threshold, IntegerValue* y_threshold,
169 Rectangle* conflict) {
170 // First, we compute the possible x_min values (removing duplicates).
171 // We also sort the relevant tasks by their x_max.
172 //
173 // TODO(user): If the number of unique x_max is smaller than the number of
174 // unique x_min, it is better to do it the other way around.
175 std::vector<IntegerValue> starts;
176 std::vector<TaskTime> task_by_increasing_x_max;
177 for (const int t : local_boxes) {
178 const IntegerValue x_min =
179 transpose ? rectangles[t].y_min : rectangles[t].x_min;
180 const IntegerValue x_max =
181 transpose ? rectangles[t].y_max : rectangles[t].x_max;
182 starts.push_back(x_min);
183 task_by_increasing_x_max.push_back({t, x_max});
184 }
186
187 // Note that for the same end_max, the order change our heuristic to
188 // evaluate the max_conflict_height.
189 std::sort(task_by_increasing_x_max.begin(), task_by_increasing_x_max.end());
190
191 // The maximum y dimension of a bounding area for which there is a potential
192 // conflict.
193 IntegerValue max_conflict_height(0);
194
195 // This is currently only used for logging.
196 absl::flat_hash_set<std::pair<IntegerValue, IntegerValue>> stripes;
197
198 // All quantities at index j correspond to the interval [starts[j], x_max].
199 std::vector<IntegerValue> energies(starts.size(), IntegerValue(0));
200 std::vector<IntegerValue> y_mins(starts.size(), kMaxIntegerValue);
201 std::vector<IntegerValue> y_maxs(starts.size(), -kMaxIntegerValue);
202 std::vector<IntegerValue> energy_at_max_y(starts.size(), IntegerValue(0));
203 std::vector<IntegerValue> energy_at_min_y(starts.size(), IntegerValue(0));
204
205 // Sentinel.
206 starts.push_back(kMaxIntegerValue);
207
208 // Iterate over all boxes by increasing x_max values.
209 int first_j = 0;
210 const IntegerValue threshold = transpose ? *y_threshold : *x_threshold;
211 for (int i = 0; i < task_by_increasing_x_max.size(); ++i) {
212 const int t = task_by_increasing_x_max[i].task_index;
213
214 const IntegerValue energy = rectangle_energies[t];
215 IntegerValue x_min = rectangles[t].x_min;
216 IntegerValue x_max = rectangles[t].x_max;
217 IntegerValue y_min = rectangles[t].y_min;
218 IntegerValue y_max = rectangles[t].y_max;
219 if (transpose) {
220 std::swap(x_min, y_min);
221 std::swap(x_max, y_max);
222 }
223
224 // Add this box contribution to all the [starts[j], x_max] intervals.
225 while (first_j + 1 < starts.size() && x_max - starts[first_j] > threshold) {
226 ++first_j;
227 }
228 for (int j = first_j; starts[j] <= x_min; ++j) {
229 const IntegerValue old_energy_at_max = energy_at_max_y[j];
230 const IntegerValue old_energy_at_min = energy_at_min_y[j];
231
232 energies[j] += energy;
233
234 const bool is_disjoint = y_min >= y_maxs[j] || y_max <= y_mins[j];
235
236 if (y_min <= y_mins[j]) {
237 if (y_min < y_mins[j]) {
238 y_mins[j] = y_min;
239 energy_at_min_y[j] = energy;
240 } else {
241 energy_at_min_y[j] += energy;
242 }
243 }
244
245 if (y_max >= y_maxs[j]) {
246 if (y_max > y_maxs[j]) {
247 y_maxs[j] = y_max;
248 energy_at_max_y[j] = energy;
249 } else {
250 energy_at_max_y[j] += energy;
251 }
252 }
253
254 // If the new box is disjoint in y from the ones added so far, there
255 // cannot be a new conflict involving this box, so we skip until we add
256 // new boxes.
257 if (is_disjoint) continue;
258
259 const IntegerValue width = x_max - starts[j];
260 IntegerValue conflict_height = CeilRatio(energies[j], width) - 1;
261 if (y_max - y_min > conflict_height) continue;
262 if (conflict_height >= y_maxs[j] - y_mins[j]) {
263 // We have a conflict.
264 if (conflict != nullptr) {
265 *conflict = rectangles[t];
266 for (int k = 0; k < i; ++k) {
267 const int task_index = task_by_increasing_x_max[k].task_index;
268 const IntegerValue task_x_min = transpose
269 ? rectangles[task_index].y_min
270 : rectangles[task_index].x_min;
271 if (task_x_min < starts[j]) continue;
272 conflict->TakeUnionWith(rectangles[task_index]);
273 }
274 }
275 return false;
276 }
277
278 // Because we currently do not have a conflict involving the new box, the
279 // only way to have one is to remove enough energy to reduce the y domain.
280 IntegerValue can_remove = std::min(old_energy_at_min, old_energy_at_max);
281 if (old_energy_at_min < old_energy_at_max) {
282 if (y_maxs[j] - y_min >=
283 CeilRatio(energies[j] - old_energy_at_min, width)) {
284 // In this case, we need to remove at least old_energy_at_max to have
285 // a conflict.
286 can_remove = old_energy_at_max;
287 }
288 } else if (old_energy_at_max < old_energy_at_min) {
289 if (y_max - y_mins[j] >=
290 CeilRatio(energies[j] - old_energy_at_max, width)) {
291 can_remove = old_energy_at_min;
292 }
293 }
294 conflict_height = CeilRatio(energies[j] - can_remove, width) - 1;
295
296 // If the new box height is above the conflict_height, do not count
297 // it now. We only need to consider conflict involving the new box.
298 if (y_max - y_min > conflict_height) continue;
299
300 if (VLOG_IS_ON(2)) stripes.insert({starts[j], x_max});
301 max_conflict_height = std::max(max_conflict_height, conflict_height);
302 }
303 }
304
305 VLOG(2) << " num_starts: " << starts.size() - 1 << "/" << local_boxes.size()
306 << " conflict_height: " << max_conflict_height
307 << " num_stripes:" << stripes.size() << " (<= " << threshold << ")";
308
309 if (transpose) {
310 *x_threshold = std::min(*x_threshold, max_conflict_height);
311 } else {
312 *y_threshold = std::min(*y_threshold, max_conflict_height);
313 }
314 return true;
315}
316
318 const std::vector<Rectangle>& cached_rectangles, absl::Span<int> boxes,
319 IntegerValue threshold_x, IntegerValue threshold_y,
320 absl::BitGenRef random) {
321 size_t new_size = 0;
322 for (const int b : boxes) {
323 const Rectangle& dim = cached_rectangles[b];
324 if (dim.x_max - dim.x_min > threshold_x) continue;
325 if (dim.y_max - dim.y_min > threshold_y) continue;
326 boxes[new_size++] = b;
327 }
328 if (new_size == 0) return {};
329 std::shuffle(&boxes[0], &boxes[0] + new_size, random);
330 return {&boxes[0], new_size};
331}
332
334 const std::vector<Rectangle>& cached_rectangles,
335 const std::vector<IntegerValue>& energies, absl::Span<int> boxes) {
336 // Sort the boxes by increasing area.
337 std::sort(boxes.begin(), boxes.end(), [&cached_rectangles](int a, int b) {
338 return cached_rectangles[a].Area() < cached_rectangles[b].Area();
339 });
340
341 IntegerValue total_energy(0);
342 for (const int box : boxes) total_energy += energies[box];
343
344 // Remove all the large boxes until we have one with area smaller than the
345 // energy of the boxes below.
346 int new_size = boxes.size();
347 while (new_size > 0 &&
348 cached_rectangles[boxes[new_size - 1]].Area() >= total_energy) {
349 --new_size;
350 total_energy -= energies[boxes[new_size]];
351 }
352 return boxes.subspan(0, new_size);
353}
354
355std::ostream& operator<<(std::ostream& out, const IndexedInterval& interval) {
356 return out << "[" << interval.start << ".." << interval.end << " (#"
357 << interval.index << ")]";
358}
359
360void ConstructOverlappingSets(bool already_sorted,
361 std::vector<IndexedInterval>* intervals,
362 std::vector<std::vector<int>>* result) {
363 result->clear();
364 if (already_sorted) {
365 DCHECK(std::is_sorted(intervals->begin(), intervals->end(),
367 } else {
368 std::sort(intervals->begin(), intervals->end(),
370 }
371 IntegerValue min_end_in_set = kMaxIntegerValue;
372 intervals->push_back({-1, kMaxIntegerValue, kMaxIntegerValue}); // Sentinel.
373 const int size = intervals->size();
374
375 // We do a line sweep. The "current" subset crossing the "line" at
376 // (time, time + 1) will be in (*intervals)[start_index, end_index) at the end
377 // of the loop block.
378 int start_index = 0;
379 for (int end_index = 0; end_index < size;) {
380 const IntegerValue time = (*intervals)[end_index].start;
381
382 // First, if there is some deletion, we will push the "old" set to the
383 // result before updating it. Otherwise, we will have a superset later, so
384 // we just continue for now.
385 if (min_end_in_set <= time) {
386 result->push_back({});
387 min_end_in_set = kMaxIntegerValue;
388 for (int i = start_index; i < end_index; ++i) {
389 result->back().push_back((*intervals)[i].index);
390 if ((*intervals)[i].end <= time) {
391 std::swap((*intervals)[start_index++], (*intervals)[i]);
392 } else {
393 min_end_in_set = std::min(min_end_in_set, (*intervals)[i].end);
394 }
395 }
396
397 // Do not output subset of size one.
398 if (result->back().size() == 1) result->pop_back();
399 }
400
401 // Add all the new intervals starting exactly at "time".
402 do {
403 min_end_in_set = std::min(min_end_in_set, (*intervals)[end_index].end);
404 ++end_index;
405 } while (end_index < size && (*intervals)[end_index].start == time);
406 }
407}
408
410 std::vector<IndexedInterval>* intervals,
411 std::vector<std::vector<int>>* components) {
412 components->clear();
413 if (intervals->empty()) return;
414 if (intervals->size() == 1) {
415 components->push_back({intervals->front().index});
416 return;
417 }
418
419 // For correctness, ComparatorByStart is enough, but in unit tests we want to
420 // verify this function against another implementation, and fully defined
421 // sorting with tie-breaking makes that much easier.
422 // If that becomes a performance bottleneck:
423 // - One may want to sort the list outside of this function, and simply
424 // have this function DCHECK that it's sorted by start.
425 // - One may use std::stable_sort() with ComparatorByStart().
426 std::sort(intervals->begin(), intervals->end(),
428
429 IntegerValue end_max_so_far = (*intervals)[0].end;
430 components->push_back({(*intervals)[0].index});
431 for (int i = 1; i < intervals->size(); ++i) {
432 const IndexedInterval& interval = (*intervals)[i];
433 if (interval.start >= end_max_so_far) {
434 components->push_back({interval.index});
435 } else {
436 components->back().push_back(interval.index);
437 }
438 end_max_so_far = std::max(end_max_so_far, interval.end);
439 }
440}
441
443 std::vector<IndexedInterval>* intervals) {
444 std::vector<int> articulation_points;
445 if (intervals->size() < 3) return articulation_points; // Empty.
446 if (DEBUG_MODE) {
447 for (const IndexedInterval& interval : *intervals) {
448 DCHECK_LT(interval.start, interval.end);
449 }
450 }
451
452 std::sort(intervals->begin(), intervals->end(),
454
455 IntegerValue end_max_so_far = (*intervals)[0].end;
456 int index_of_max = 0;
457 IntegerValue prev_end_max = kMinIntegerValue; // Initialized as a sentinel.
458 for (int i = 1; i < intervals->size(); ++i) {
459 const IndexedInterval& interval = (*intervals)[i];
460 if (interval.start >= end_max_so_far) {
461 // New connected component.
462 end_max_so_far = interval.end;
463 index_of_max = i;
464 prev_end_max = kMinIntegerValue;
465 continue;
466 }
467 // Still the same connected component. Was the previous "max" an
468 // articulation point ?
469 if (prev_end_max != kMinIntegerValue && interval.start >= prev_end_max) {
470 // We might be re-inserting the same articulation point: guard against it.
471 if (articulation_points.empty() ||
472 articulation_points.back() != index_of_max) {
473 articulation_points.push_back(index_of_max);
474 }
475 }
476 // Update the max end.
477 if (interval.end > end_max_so_far) {
478 prev_end_max = end_max_so_far;
479 end_max_so_far = interval.end;
480 index_of_max = i;
481 } else if (interval.end > prev_end_max) {
482 prev_end_max = interval.end;
483 }
484 }
485 // Convert articulation point indices to IndexedInterval.index.
486 for (int& index : articulation_points) index = (*intervals)[index].index;
487 return articulation_points;
488}
489
491 events_.clear();
492 num_rectangles_added_ = 0;
493}
494
495void CapacityProfile::AddRectangle(IntegerValue x_min, IntegerValue x_max,
496 IntegerValue y_min, IntegerValue y_max) {
497 DCHECK_LE(x_min, x_max);
498 if (x_min == x_max) return;
499
500 events_.push_back(
501 StartRectangleEvent(num_rectangles_added_, x_min, y_min, y_max));
502 events_.push_back(EndRectangleEvent(num_rectangles_added_, x_max));
503 ++num_rectangles_added_;
504}
505
507 IntegerValue x_max,
508 IntegerValue y_height) {
509 DCHECK_LE(x_min, x_max);
510 if (x_min == x_max) return;
511
512 events_.push_back(ChangeMandatoryProfileEvent(x_min, y_height));
513 events_.push_back(ChangeMandatoryProfileEvent(x_max, -y_height));
514}
515
517 std::vector<CapacityProfile::Rectangle>* result) {
518 std::sort(events_.begin(), events_.end());
519 IntegerPriorityQueue<QueueElement> min_pq(num_rectangles_added_);
520 IntegerPriorityQueue<QueueElement> max_pq(num_rectangles_added_);
521 IntegerValue mandatory_capacity(0);
522
523 result->clear();
524
525 result->push_back({kMinIntegerValue, IntegerValue(0)});
526
527 for (int i = 0; i < events_.size();) {
528 const IntegerValue current_time = events_[i].time;
529 for (; i < events_.size(); ++i) {
530 const Event& event = events_[i];
531 if (event.time != current_time) break;
532
533 switch (events_[i].type) {
534 case START_RECTANGLE: {
535 min_pq.Add({event.index, -event.y_min});
536 max_pq.Add({event.index, event.y_max});
537 break;
538 }
539 case END_RECTANGLE: {
540 min_pq.Remove(event.index);
541 max_pq.Remove(event.index);
542 break;
543 }
544 case CHANGE_MANDATORY_PROFILE: {
545 mandatory_capacity += event.y_min;
546 break;
547 }
548 }
549 }
550
551 DCHECK(!max_pq.IsEmpty() || mandatory_capacity == 0);
552 const IntegerValue new_height =
553 max_pq.IsEmpty()
554 ? IntegerValue(0)
555 : max_pq.Top().value + min_pq.Top().value - mandatory_capacity;
556 if (new_height != result->back().height) {
557 result->push_back({current_time, new_height});
558 }
559 }
560}
561
563 std::sort(events_.begin(), events_.end());
564 IntegerPriorityQueue<QueueElement> min_pq(num_rectangles_added_);
565 IntegerPriorityQueue<QueueElement> max_pq(num_rectangles_added_);
566
567 IntegerValue area(0);
568 IntegerValue previous_time = kMinIntegerValue;
569 IntegerValue previous_height(0);
570
571 for (int i = 0; i < events_.size();) {
572 const IntegerValue current_time = events_[i].time;
573 for (; i < events_.size(); ++i) {
574 const Event& event = events_[i];
575 if (event.time != current_time) break;
576
577 switch (event.type) {
578 case START_RECTANGLE: {
579 min_pq.Add({event.index, -event.y_min});
580 max_pq.Add({event.index, event.y_max});
581 break;
582 }
583 case END_RECTANGLE: {
584 min_pq.Remove(event.index);
585 max_pq.Remove(event.index);
586 break;
587 }
588 case CHANGE_MANDATORY_PROFILE: {
589 break;
590 }
591 }
592 }
593 const IntegerValue new_height =
594 max_pq.IsEmpty() ? IntegerValue(0)
595 : max_pq.Top().value + min_pq.Top().value;
596 if (previous_height != 0) {
597 area += previous_height * (current_time - previous_time);
598 }
599 previous_time = current_time;
600 previous_height = new_height;
601 }
602 return area;
603}
604
605} // namespace sat
606} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:893
#define CHECK_GT(val1, val2)
Definition: base/logging.h:708
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:894
#define DCHECK(condition)
Definition: base/logging.h:890
#define VLOG(verboselevel)
Definition: base/logging.h:984
void AddMandatoryConsumption(IntegerValue x_min, IntegerValue x_max, IntegerValue y_height)
Definition: diffn_util.cc:506
void AddRectangle(IntegerValue x_min, IntegerValue x_max, IntegerValue y_min, IntegerValue y_max)
Definition: diffn_util.cc:495
void BuildResidualCapacityProfile(std::vector< Rectangle > *result)
Definition: diffn_util.cc:516
int index() const
Returns the index of the interval constraint in the model.
Definition: cp_model.h:494
void ImportOtherReasons(const SchedulingConstraintHelper &other_helper)
Definition: intervals.cc:553
void AddEnergyMinInIntervalReason(int t, IntegerValue min, IntegerValue max)
Definition: intervals.h:603
int64_t b
int64_t a
int index
const bool DEBUG_MODE
Definition: macros.h:24
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition: stl_util.h:58
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:262
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::ostream & operator<<(std::ostream &os, const BoolVar &var)
Definition: cp_model.cc:89
void GetOverlappingIntervalComponents(std::vector< IndexedInterval > *intervals, std::vector< std::vector< int > > *components)
Definition: diffn_util.cc:409
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:89
std::vector< int > GetIntervalArticulationPoints(std::vector< IndexedInterval > *intervals)
Definition: diffn_util.cc:442
std::vector< absl::Span< int > > GetOverlappingRectangleComponents(const std::vector< Rectangle > &rectangles, absl::Span< int > active_rectangles)
Definition: diffn_util.cc:40
absl::Span< int > FilterBoxesAndRandomize(const std::vector< Rectangle > &cached_rectangles, absl::Span< int > boxes, IntegerValue threshold_x, IntegerValue threshold_y, absl::BitGenRef random)
Definition: diffn_util.cc:317
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
bool AnalyzeIntervals(bool transpose, absl::Span< const int > local_boxes, const std::vector< Rectangle > &rectangles, const std::vector< IntegerValue > &rectangle_energies, IntegerValue *x_threshold, IntegerValue *y_threshold, Rectangle *conflict)
Definition: diffn_util.cc:165
bool ReportEnergyConflict(Rectangle bounding_box, absl::Span< const int > boxes, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y)
Definition: diffn_util.cc:66
void ConstructOverlappingSets(bool already_sorted, std::vector< IndexedInterval > *intervals, std::vector< std::vector< int > > *result)
Definition: diffn_util.cc:360
bool BoxesAreInEnergyConflict(const std::vector< Rectangle > &rectangles, const std::vector< IntegerValue > &energies, absl::Span< const int > boxes, Rectangle *conflict)
Definition: diffn_util.cc:98
absl::Span< int > FilterBoxesThatAreTooLarge(const std::vector< Rectangle > &cached_rectangles, const std::vector< IntegerValue > &energies, absl::Span< int > boxes)
Definition: diffn_util.cc:333
Collection of objects used to extend the Constraint Solver library.
int64_t energy
Definition: resource.cc:354
int64_t time
Definition: resource.cc:1693
IntervalVar * interval
Definition: resource.cc:100
std::optional< int64_t > end
int64_t start
void TakeUnionWith(const Rectangle &other)
Definition: diffn_util.h:42
bool IsDisjoint(const Rectangle &other) const
Definition: diffn_util.cc:35
#define VLOG_IS_ON(verboselevel)
Definition: vlog_is_on.h:44