23 #include "absl/container/flat_hash_set.h"
24 #include "absl/random/bit_gen_ref.h"
25 #include "absl/types/span.h"
36 bool Rectangle::IsDisjoint(
const Rectangle& other)
const {
37 return x_min >= other.x_max || other.x_min >= x_max || y_min >= other.y_max ||
42 const std::vector<Rectangle>& rectangles,
43 absl::Span<int> active_rectangles) {
44 if (active_rectangles.empty())
return {};
46 std::vector<absl::Span<int>> result;
47 const int size = active_rectangles.size();
52 for (
int j =
end; j < size; ++j) {
53 if (!rectangles[active_rectangles[i]].IsDisjoint(
54 rectangles[active_rectangles[j]])) {
55 std::swap(active_rectangles[
end++], active_rectangles[j]);
60 result.push_back(active_rectangles.subspan(
start,
end -
start));
72 IntegerValue total_energy(0);
73 for (
const int b : boxes) {
76 if (x_min < bounding_box.x_min || x_max > bounding_box.x_max)
continue;
79 if (y_min < bounding_box.y_min || y_max > bounding_box.y_max)
continue;
91 if (total_energy > bounding_box.Area())
break;
94 CHECK_GT(total_energy, bounding_box.Area());
100 const std::vector<IntegerValue>& energies,
101 absl::Span<const int> boxes,
102 Rectangle* conflict) {
104 std::vector<IntegerValue> x_starts;
105 std::vector<TaskTime> boxes_by_increasing_x_max;
106 for (
const int b : boxes) {
107 x_starts.push_back(rectangles[
b].x_min);
108 boxes_by_increasing_x_max.push_back({
b, rectangles[
b].x_max});
111 std::sort(boxes_by_increasing_x_max.begin(), boxes_by_increasing_x_max.end());
113 std::vector<IntegerValue> y_starts;
114 std::vector<IntegerValue> energy_sum;
115 std::vector<TaskTime> boxes_by_increasing_y_max;
117 std::vector<std::vector<int>> stripes(x_starts.size());
118 for (
int i = 0; i < boxes_by_increasing_x_max.size(); ++i) {
119 const int b = boxes_by_increasing_x_max[i].task_index;
120 const IntegerValue x_min = rectangles[
b].x_min;
121 const IntegerValue x_max = rectangles[
b].x_max;
122 for (
int j = 0; j < x_starts.size(); ++j) {
123 if (x_starts[j] > x_min)
break;
124 stripes[j].push_back(
b);
129 boxes_by_increasing_y_max.clear();
130 for (
const int b : stripes[j]) {
131 y_starts.push_back(rectangles[
b].y_min);
132 boxes_by_increasing_y_max.push_back({
b, rectangles[
b].y_max});
135 std::sort(boxes_by_increasing_y_max.begin(),
136 boxes_by_increasing_y_max.end());
138 const IntegerValue x_size = x_max - x_starts[j];
139 energy_sum.assign(y_starts.size(), IntegerValue(0));
140 for (
int i = 0; i < boxes_by_increasing_y_max.size(); ++i) {
141 const int b = boxes_by_increasing_y_max[i].task_index;
142 const IntegerValue y_min = rectangles[
b].y_min;
143 const IntegerValue y_max = rectangles[
b].y_max;
144 for (
int j = 0; j < y_starts.size(); ++j) {
145 if (y_starts[j] > y_min)
break;
146 energy_sum[j] += energies[
b];
147 if (energy_sum[j] > x_size * (y_max - y_starts[j])) {
148 if (conflict !=
nullptr) {
149 *conflict = rectangles[
b];
150 for (
int k = 0; k < i; ++k) {
151 const int task_index = boxes_by_increasing_y_max[k].task_index;
152 if (rectangles[task_index].y_min >= y_starts[j]) {
153 conflict->TakeUnionWith(rectangles[task_index]);
167 const std::vector<Rectangle>& rectangles,
168 const std::vector<IntegerValue>& rectangle_energies,
169 IntegerValue* x_threshold, IntegerValue* y_threshold,
170 Rectangle* conflict) {
176 std::vector<IntegerValue> starts;
177 std::vector<TaskTime> task_by_increasing_x_max;
178 for (
const int t : local_boxes) {
179 const IntegerValue x_min =
180 transpose ? rectangles[t].y_min : rectangles[t].x_min;
181 const IntegerValue x_max =
182 transpose ? rectangles[t].y_max : rectangles[t].x_max;
183 starts.push_back(x_min);
184 task_by_increasing_x_max.push_back({t, x_max});
190 std::sort(task_by_increasing_x_max.begin(), task_by_increasing_x_max.end());
194 IntegerValue max_conflict_height(0);
197 absl::flat_hash_set<std::pair<IntegerValue, IntegerValue>> stripes;
200 std::vector<IntegerValue> energies(starts.size(), IntegerValue(0));
203 std::vector<IntegerValue> energy_at_max_y(starts.size(), IntegerValue(0));
204 std::vector<IntegerValue> energy_at_min_y(starts.size(), IntegerValue(0));
211 const IntegerValue threshold = transpose ? *y_threshold : *x_threshold;
212 for (
int i = 0; i < task_by_increasing_x_max.size(); ++i) {
213 const int t = task_by_increasing_x_max[i].task_index;
215 const IntegerValue
energy = rectangle_energies[t];
216 IntegerValue x_min = rectangles[t].x_min;
217 IntegerValue x_max = rectangles[t].x_max;
218 IntegerValue y_min = rectangles[t].y_min;
219 IntegerValue y_max = rectangles[t].y_max;
226 while (first_j + 1 < starts.size() && x_max - starts[first_j] > threshold) {
229 for (
int j = first_j; starts[j] <= x_min; ++j) {
230 const IntegerValue old_energy_at_max = energy_at_max_y[j];
231 const IntegerValue old_energy_at_min = energy_at_min_y[j];
235 const bool is_disjoint = y_min >= y_maxs[j] || y_max <= y_mins[j];
237 if (y_min <= y_mins[j]) {
238 if (y_min < y_mins[j]) {
240 energy_at_min_y[j] =
energy;
242 energy_at_min_y[j] +=
energy;
246 if (y_max >= y_maxs[j]) {
247 if (y_max > y_maxs[j]) {
249 energy_at_max_y[j] =
energy;
251 energy_at_max_y[j] +=
energy;
258 if (is_disjoint)
continue;
260 const IntegerValue
width = x_max - starts[j];
262 if (y_max - y_min > conflict_height)
continue;
263 if (conflict_height >= y_maxs[j] - y_mins[j]) {
265 if (conflict !=
nullptr) {
266 *conflict = rectangles[t];
267 for (
int k = 0; k < i; ++k) {
268 const int task_index = task_by_increasing_x_max[k].task_index;
269 const IntegerValue task_x_min = transpose
270 ? rectangles[task_index].y_min
271 : rectangles[task_index].x_min;
272 if (task_x_min < starts[j])
continue;
273 conflict->TakeUnionWith(rectangles[task_index]);
281 IntegerValue can_remove =
std::min(old_energy_at_min, old_energy_at_max);
282 if (old_energy_at_min < old_energy_at_max) {
283 if (y_maxs[j] - y_min >=
287 can_remove = old_energy_at_max;
289 }
else if (old_energy_at_max < old_energy_at_min) {
290 if (y_max - y_mins[j] >=
292 can_remove = old_energy_at_min;
299 if (y_max - y_min > conflict_height)
continue;
301 if (
VLOG_IS_ON(2)) stripes.insert({starts[j], x_max});
302 max_conflict_height =
std::max(max_conflict_height, conflict_height);
306 VLOG(2) <<
" num_starts: " << starts.size() - 1 <<
"/" << local_boxes.size()
307 <<
" conflict_height: " << max_conflict_height
308 <<
" num_stripes:" << stripes.size() <<
" (<= " << threshold <<
")";
311 *x_threshold =
std::min(*x_threshold, max_conflict_height);
313 *y_threshold =
std::min(*y_threshold, max_conflict_height);
319 const std::vector<Rectangle>& cached_rectangles, absl::Span<int> boxes,
320 IntegerValue threshold_x, IntegerValue threshold_y,
321 absl::BitGenRef random) {
323 for (
const int b : boxes) {
324 const Rectangle& dim = cached_rectangles[
b];
325 if (dim.x_max - dim.x_min > threshold_x)
continue;
326 if (dim.y_max - dim.y_min > threshold_y)
continue;
327 boxes[new_size++] =
b;
329 if (new_size == 0)
return {};
330 std::shuffle(&boxes[0], &boxes[0] + new_size, random);
331 return {&boxes[0], new_size};
335 const std::vector<Rectangle>& cached_rectangles,
336 const std::vector<IntegerValue>& energies, absl::Span<int> boxes) {
338 std::sort(boxes.begin(), boxes.end(), [&cached_rectangles](
int a,
int b) {
339 return cached_rectangles[a].Area() < cached_rectangles[b].Area();
342 IntegerValue total_energy(0);
343 for (
const int box : boxes) total_energy += energies[box];
347 int new_size = boxes.size();
348 while (new_size > 0 &&
349 cached_rectangles[boxes[new_size - 1]].Area() >= total_energy) {
351 total_energy -= energies[boxes[new_size]];
353 return boxes.subspan(0, new_size);
362 std::vector<IndexedInterval>* intervals,
363 std::vector<std::vector<int>>* result) {
365 if (already_sorted) {
366 DCHECK(std::is_sorted(intervals->begin(), intervals->end(),
369 std::sort(intervals->begin(), intervals->end(),
374 const int size = intervals->size();
380 for (
int end_index = 0; end_index < size;) {
381 const IntegerValue
time = (*intervals)[end_index].start;
386 if (min_end_in_set <=
time) {
387 result->push_back({});
389 for (
int i = start_index; i < end_index; ++i) {
390 result->back().push_back((*intervals)[i].
index);
391 if ((*intervals)[i].
end <=
time) {
392 std::swap((*intervals)[start_index++], (*intervals)[i]);
394 min_end_in_set =
std::min(min_end_in_set, (*intervals)[i].
end);
399 if (result->back().size() == 1) result->pop_back();
404 min_end_in_set =
std::min(min_end_in_set, (*intervals)[end_index].
end);
406 }
while (end_index < size && (*intervals)[end_index].
start ==
time);
411 std::vector<IndexedInterval>* intervals,
412 std::vector<std::vector<int>>* components) {
414 if (intervals->empty())
return;
415 if (intervals->size() == 1) {
416 components->push_back({intervals->front().
index});
427 std::sort(intervals->begin(), intervals->end(),
430 IntegerValue end_max_so_far = (*intervals)[0].end;
431 components->push_back({(*intervals)[0].index});
432 for (
int i = 1; i < intervals->size(); ++i) {
434 if (
interval.start >= end_max_so_far) {
444 std::vector<IndexedInterval>* intervals) {
445 std::vector<int> articulation_points;
446 if (intervals->size() < 3)
return articulation_points;
453 std::sort(intervals->begin(), intervals->end(),
456 IntegerValue end_max_so_far = (*intervals)[0].end;
457 int index_of_max = 0;
459 for (
int i = 1; i < intervals->size(); ++i) {
461 if (
interval.start >= end_max_so_far) {
472 if (articulation_points.empty() ||
473 articulation_points.back() != index_of_max) {
474 articulation_points.push_back(index_of_max);
478 if (
interval.end > end_max_so_far) {
479 prev_end_max = end_max_so_far;
482 }
else if (
interval.end > prev_end_max) {
487 for (
int&
index : articulation_points)
index = (*intervals)[
index].index;
488 return articulation_points;
493 num_rectangles_added_ = 0;
497 IntegerValue y_min, IntegerValue y_max) {
498 DCHECK_LE(x_min, x_max);
499 if (x_min == x_max)
return;
502 StartRectangleEvent(num_rectangles_added_, x_min, y_min, y_max));
503 events_.push_back(EndRectangleEvent(num_rectangles_added_, x_max));
504 ++num_rectangles_added_;
509 IntegerValue y_height) {
510 DCHECK_LE(x_min, x_max);
511 if (x_min == x_max)
return;
513 events_.push_back(ChangeMandatoryProfileEvent(x_min, y_height));
514 events_.push_back(ChangeMandatoryProfileEvent(x_max, -y_height));
518 std::vector<CapacityProfile::Rectangle>* result) {
519 std::sort(events_.begin(), events_.end());
522 IntegerValue mandatory_capacity(0);
528 for (
int i = 0; i < events_.size();) {
529 const IntegerValue current_time = events_[i].time;
530 for (; i < events_.size(); ++i) {
531 const Event&
event = events_[i];
532 if (event.time != current_time)
break;
534 switch (events_[i].type) {
535 case START_RECTANGLE: {
536 min_pq.
Add({
event.index, -
event.y_min});
537 max_pq.
Add({
event.index,
event.y_max});
540 case END_RECTANGLE: {
541 min_pq.
Remove(event.index);
542 max_pq.
Remove(event.index);
545 case CHANGE_MANDATORY_PROFILE: {
546 mandatory_capacity +=
event.y_min;
552 DCHECK(!max_pq.
IsEmpty() || mandatory_capacity == 0);
553 const IntegerValue new_height =
556 : max_pq.
Top().value + min_pq.
Top().value - mandatory_capacity;
557 if (new_height != result->back().height) {
558 result->push_back({current_time, new_height});
564 std::sort(events_.begin(), events_.end());
568 IntegerValue area(0);
570 IntegerValue previous_height(0);
572 for (
int i = 0; i < events_.size();) {
573 const IntegerValue current_time = events_[i].time;
574 for (; i < events_.size(); ++i) {
575 const Event&
event = events_[i];
576 if (event.time != current_time)
break;
578 switch (event.type) {
579 case START_RECTANGLE: {
580 min_pq.
Add({
event.index, -
event.y_min});
581 max_pq.
Add({
event.index,
event.y_max});
584 case END_RECTANGLE: {
585 min_pq.
Remove(event.index);
586 max_pq.
Remove(event.index);
589 case CHANGE_MANDATORY_PROFILE: {
594 const IntegerValue new_height =
595 max_pq.
IsEmpty() ? IntegerValue(0)
596 : max_pq.
Top().value + min_pq.
Top().value;
597 if (previous_height != 0) {
598 area += previous_height * (current_time - previous_time);
600 previous_time = current_time;
601 previous_height = new_height;
void Add(Element element)
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)
IntegerValue GetBoundingArea()
int index() const
Returns the index of the interval constraint in the model.
IntegerValue ShiftedStartMin(int t) const
void AddPresenceReason(int t)
IntegerValue ShiftedEndMax(int t) const
ABSL_MUST_USE_RESULT bool ReportConflict()
void ImportOtherReasons(const SchedulingConstraintHelper &other_helper)
void AddEnergyMinInIntervalReason(int t, IntegerValue min, IntegerValue max)
IntegerValue SizeMin(int t) const
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::ostream & operator<<(std::ostream &os, const BoolVar &var)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
std::vector< int > GetIntervalArticulationPoints(std::vector< IndexedInterval > *intervals)
void GetOverlappingIntervalComponents(std::vector< IndexedInterval > *intervals, std::vector< std::vector< int >> *components)
std::vector< absl::Span< int > > GetOverlappingRectangleComponents(const std::vector< Rectangle > &rectangles, absl::Span< int > active_rectangles)
absl::Span< int > FilterBoxesAndRandomize(const std::vector< Rectangle > &cached_rectangles, absl::Span< int > boxes, IntegerValue threshold_x, IntegerValue threshold_y, absl::BitGenRef random)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
void ConstructOverlappingSets(bool already_sorted, std::vector< IndexedInterval > *intervals, std::vector< std::vector< int >> *result)
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)
bool ReportEnergyConflict(Rectangle bounding_box, absl::Span< const int > boxes, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y)
bool BoxesAreInEnergyConflict(const std::vector< Rectangle > &rectangles, const std::vector< IntegerValue > &energies, absl::Span< const int > boxes, Rectangle *conflict)
absl::Span< int > FilterBoxesThatAreTooLarge(const std::vector< Rectangle > &cached_rectangles, const std::vector< IntegerValue > &energies, absl::Span< int > boxes)
Collection of objects used to extend the Constraint Solver library.
std::optional< int64_t > end
#define VLOG(verboselevel)
#define VLOG_IS_ON(verboselevel)