21#include "absl/base/log_severity.h"
22#include "absl/log/check.h"
23#include "absl/types/span.h"
37 bool x_is_forward_after_swap,
bool y_is_forward_after_swap,
39 if (axes_are_swapped_ != swap_x_and_y) {
40 std::swap(x_helper_, y_helper_);
41 std::swap(x_demands_helper_, y_demands_helper_);
42 axes_are_swapped_ = !axes_are_swapped_;
44 if (!x_helper_->SynchronizeAndSetTimeDirection(x_is_forward_after_swap))
46 if (!y_helper_->SynchronizeAndSetTimeDirection(y_is_forward_after_swap))
52 if (x_demands_helper_ ==
nullptr) {
53 x_demands_helper_ = std::make_unique<SchedulingDemandHelper>(
54 x_helper_->Sizes(), y_helper_.get(), model_);
56 return *x_demands_helper_;
60 if (y_demands_helper_ ==
nullptr) {
61 y_demands_helper_ = std::make_unique<SchedulingDemandHelper>(
62 y_helper_->Sizes(), x_helper_.get(), model_);
64 return *y_demands_helper_;
71 .bounding_area = {.x_min = x_helper_->StartMin(index),
72 .x_max = x_helper_->StartMax(index) +
73 x_helper_->SizeMin(index),
74 .y_min = y_helper_->StartMin(index),
75 .y_max = y_helper_->StartMax(index) +
76 y_helper_->SizeMin(index)},
77 .x_size = x_helper_->SizeMin(index),
78 .y_size = y_helper_->SizeMin(index)};
84 .x = {.start_min = x_helper_->StartMin(index),
85 .start_max = x_helper_->StartMax(index),
86 .end_min = x_helper_->EndMin(index),
87 .end_max = x_helper_->EndMax(index)},
88 .y = {.start_min = y_helper_->StartMin(index),
89 .start_max = y_helper_->StartMax(index),
90 .end_min = y_helper_->EndMin(index),
91 .end_max = y_helper_->EndMax(index)}};
95void ClearAndAddMandatoryOverlapReason(
int box1,
int box2,
107 DCHECK_NE(box1, box2);
109 std::vector<PairwiseRestriction> restrictions;
112 DCHECK_EQ(restrictions.size(), 1);
113 DCHECK(restrictions[0].type ==
116 ClearAndAddMandatoryOverlapReason(box1, box2, x_helper_.get());
117 ClearAndAddMandatoryOverlapReason(box1, box2, y_helper_.get());
118 x_helper_->ImportOtherReasons(*y_helper_);
119 return x_helper_->ReportConflict();
123 absl::Span<const RectangleInRange> ranges) {
124 if (ranges.size() == 2) {
127 x_helper_->ClearReason();
128 y_helper_->ClearReason();
129 for (
const auto& range : ranges) {
130 const int b = range.box_index;
132 x_helper_->AddStartMinReason(
b, range.bounding_area.x_min);
133 y_helper_->AddStartMinReason(
b, range.bounding_area.y_min);
135 x_helper_->AddStartMaxReason(
b, range.bounding_area.x_max - range.x_size);
136 y_helper_->AddStartMaxReason(
b, range.bounding_area.y_max - range.y_size);
138 x_helper_->AddSizeMinReason(
b);
139 y_helper_->AddSizeMinReason(
b);
141 x_helper_->AddPresenceReason(
b);
142 y_helper_->AddPresenceReason(
b);
144 x_helper_->ImportOtherReasons(*y_helper_);
145 return x_helper_->ReportConflict();
156bool LeftBoxBeforeRightBoxOnFirstDimension(
int left,
int right,
160 const IntegerValue left_end_min = x->EndMin(left);
161 if (left_end_min > x->StartMin(right)) {
163 x->AddPresenceReason(left);
164 x->AddPresenceReason(right);
165 x->AddReasonForBeingBefore(left, right);
166 x->AddEndMinReason(left, left_end_min);
168 ClearAndAddMandatoryOverlapReason(left, right, y);
170 x->ImportOtherReasons(*y);
171 if (!x->IncreaseStartMin(right, left_end_min))
return false;
175 const IntegerValue right_start_max = x->StartMax(right);
176 if (right_start_max < x->EndMax(left)) {
178 x->AddPresenceReason(left);
179 x->AddPresenceReason(right);
180 x->AddReasonForBeingBefore(left, right);
181 x->AddStartMaxReason(right, right_start_max);
183 ClearAndAddMandatoryOverlapReason(left, right, y);
185 x->ImportOtherReasons(*y);
186 if (!x->DecreaseEndMax(left, right_start_max))
return false;
200 return LeftBoxBeforeRightBoxOnFirstDimension(
201 first, second, x_helper_.get(), y_helper_.get());
203 return LeftBoxBeforeRightBoxOnFirstDimension(
204 second, first, x_helper_.get(), y_helper_.get());
206 return LeftBoxBeforeRightBoxOnFirstDimension(
207 first, second, y_helper_.get(), x_helper_.get());
209 return LeftBoxBeforeRightBoxOnFirstDimension(
210 second, first, y_helper_.get(), x_helper_.get());
214void NoOverlap2DConstraintHelper::Reset(
215 absl::Span<const Rectangle> fixed_boxes,
216 absl::Span<const int> non_fixed_box_indexes) {
217 inprocessing_count_++;
218 std::vector<AffineExpression> x_starts;
219 std::vector<AffineExpression> x_ends;
220 std::vector<AffineExpression> x_sizes;
221 std::vector<LiteralIndex> x_reason_for_presence;
222 std::vector<AffineExpression> y_starts;
223 std::vector<AffineExpression> y_ends;
224 std::vector<AffineExpression> y_sizes;
225 std::vector<LiteralIndex> y_reason_for_presence;
227 auto add_non_fixed_box = [&](
int box_index) {
228 x_starts.push_back(x_helper_->Starts()[box_index]);
229 x_ends.push_back(x_helper_->Ends()[box_index]);
230 x_sizes.push_back(x_helper_->Sizes()[box_index]);
231 if (x_helper_->IsOptional(box_index)) {
232 x_reason_for_presence.push_back(x_helper_->PresenceLiteral(box_index));
237 y_starts.push_back(y_helper_->Starts()[box_index]);
238 y_ends.push_back(y_helper_->Ends()[box_index]);
239 y_sizes.push_back(y_helper_->Sizes()[box_index]);
240 if (y_helper_->IsOptional(box_index)) {
241 y_reason_for_presence.push_back(y_helper_->PresenceLiteral(box_index));
246 return x_starts.size() - 1;
249 auto add_fixed_box = [&](
const Rectangle& box) {
250 x_starts.push_back(AffineExpression(box.x_min));
251 x_ends.push_back(AffineExpression(box.x_max));
252 x_sizes.push_back(box.SizeX());
255 y_starts.push_back(AffineExpression(box.y_min));
256 y_ends.push_back(AffineExpression(box.y_max));
257 y_sizes.push_back(box.SizeY());
260 return x_starts.size() - 1;
263 std::vector<Rectangle> active_bounding_boxes;
264 std::vector<std::tuple<int, bool>> active_box_indexes;
265 int new_num_boxes = fixed_boxes.size() + non_fixed_box_indexes.size();
266 active_bounding_boxes.reserve(new_num_boxes);
267 active_box_indexes.reserve(new_num_boxes);
268 for (
int box : non_fixed_box_indexes) {
270 active_box_indexes.push_back({box,
false});
272 for (
int box = 0; box < fixed_boxes.size(); ++box) {
273 active_bounding_boxes.push_back(fixed_boxes[box]);
274 active_box_indexes.push_back({box,
true});
276 CompactVectorVector<int> components =
278 connected_components_.clear();
279 for (absl::Span<const int> component : components.AsVectorOfSpan()) {
280 if (component.size() < 2)
continue;
281 connected_components_.Add({});
282 for (
int idx : component) {
283 const auto [box_idx, is_fixed] = active_box_indexes[idx];
286 new_index = add_fixed_box(fixed_boxes[box_idx]);
288 new_index = add_non_fixed_box(box_idx);
290 connected_components_.AppendToLastVector(new_index);
293 const int old_num_boxes =
NumBoxes();
295 if (old_num_boxes != connected_components_.num_entries()) {
296 VLOG_EVERY_N_SEC(1, 1) <<
"Total boxes: "
297 << connected_components_.num_entries()
298 <<
" connected components: "
299 << connected_components_.size()
300 <<
" dropped single box components: "
302 connected_components_.num_entries();
304 VLOG_EVERY_N_SEC(1, 2) <<
"No_overlap_2d helper inprocessing: "
305 << connected_components_.size() <<
" components and "
306 << connected_components_.num_entries() <<
" boxes";
308 x_helper_ = std::make_unique<SchedulingConstraintHelper>(
309 std::move(x_starts), std::move(x_ends), std::move(x_sizes),
310 std::move(x_reason_for_presence), model_);
311 y_helper_ = std::make_unique<SchedulingConstraintHelper>(
312 std::move(y_starts), std::move(y_ends), std::move(y_sizes),
313 std::move(y_reason_for_presence), model_);
314 x_demands_helper_ =
nullptr;
315 y_demands_helper_ =
nullptr;
319 for (
const int id : propagators_watching_) {
320 watcher_->CallOnNextPropagate(
id);
322 if (!x_helper_->Propagate() || !y_helper_->Propagate())
return false;
324 if (x_helper_->CurrentDecisionLevel() == 0) {
325 ++level_zero_bound_change_idx_;
333 std::vector<Rectangle> fixed_boxes;
334 std::vector<int> non_fixed_box_indexes;
335 std::vector<RectangleInRange> non_fixed_boxes;
336 fixed_boxes.reserve(num_boxes);
337 non_fixed_box_indexes.reserve(num_boxes);
338 non_fixed_boxes.reserve(num_boxes);
339 bool has_zero_area_boxes =
false;
340 for (
int box_index = 0; box_index < num_boxes; ++box_index) {
341 if (x_helper_->IsAbsent(box_index) || y_helper_->IsAbsent(box_index)) {
344 if (x_helper_->SizeMin(box_index) == 0 ||
345 y_helper_->SizeMin(box_index) == 0) {
346 has_zero_area_boxes =
true;
349 non_fixed_boxes.push_back(
351 .x_size = x_helper_->SizeMin(box_index),
352 .y_size = y_helper_->SizeMin(box_index)});
353 non_fixed_box_indexes.push_back(box_index);
359 const int original_num_fixed_boxes = fixed_boxes.size();
360 if (!non_fixed_boxes.empty() && !has_zero_area_boxes) {
366 if (fixed_boxes.size() != original_num_fixed_boxes) {
367 VLOG_EVERY_N_SEC(1, 1) <<
"Num_boxes: " << num_boxes
368 <<
" fixed_before: " << original_num_fixed_boxes
369 <<
" fixed_after: " << fixed_boxes.size();
371 Reset(fixed_boxes, non_fixed_box_indexes);
377 const int id = watcher->
Register(
this);
379 for (
int b = 0;
b < num_boxes; ++
b) {
380 if (x_helper_->IsOptional(
b)) {
383 if (y_helper_->IsOptional(
b)) {
void WatchLiteral(Literal l, int id, int watch_index=-1)
void SetPropagatorPriority(int id, int priority)
void WatchIntegerVariable(IntegerVariable i, int id, int watch_index=-1)
int Register(PropagatorInterface *propagator)
Registers a propagator and returns its unique ids.
void RegisterWith(GenericLiteralWatcher *watcher)
bool ReportConflictFromTwoBoxes(int box1, int box2)
SchedulingDemandHelper & y_demands_helper()
bool SynchronizeAndSetDirection(bool x_is_forward_after_swap=true, bool y_is_forward_after_swap=true, bool swap_x_and_y=false)
bool IsFixed(int index) const
bool ReportConflictFromInfeasibleBoxRanges(absl::Span< const RectangleInRange > ranges)
bool IsOptional(int index) const
bool Propagate() override
ItemWithVariableSize GetItemWithVariableSize(int index) const
Rectangle GetBoundingRectangle(int index) const
bool PropagateRelativePosition(int first, int second, PairwiseRestriction::PairwiseRestrictionType type)
SchedulingDemandHelper & x_demands_helper()
RectangleInRange GetItemRangeForSizeMin(int index) const
void AddPresenceReason(int t)
void AddReasonForBeingBefore(int before, int after)
Produces a relaxed reason for StartMax(before) < EndMin(after).
void ClearReason()
Functions to clear and then set the current reason.
const LiteralIndex kNoLiteralIndex(-1)
CompactVectorVector< int > GetOverlappingRectangleComponents(absl::Span< const Rectangle > rectangles)
void AppendPairwiseRestrictions(absl::Span< const ItemWithVariableSize > items, std::vector< PairwiseRestriction > *result)
std::vector< std::pair< int, int > > FindPartialRectangleIntersections(absl::Span< const Rectangle > rectangles)
bool PresolveFixed2dRectangles(absl::Span< const RectangleInRange > non_fixed_boxes, std::vector< Rectangle > *fixed_boxes)
In SWIG mode, we don't want anything besides these top-level includes.