20#include "absl/container/flat_hash_map.h"
21#include "absl/strings/str_join.h"
40void AddIsEqualToMinOf(IntegerVariable min_var,
41 const std::vector<AffineExpression>& exprs,
43 std::vector<LinearExpression> converted;
44 for (
const AffineExpression& affine : exprs) {
46 e.offset = affine.constant;
48 e.vars.push_back(affine.var);
49 e.coeffs.push_back(affine.coeff);
51 converted.push_back(e);
53 LinearExpression target;
54 target.vars.push_back(min_var);
55 target.coeffs.push_back(IntegerValue(1));
59void AddIsEqualToMaxOf(IntegerVariable max_var,
60 const std::vector<AffineExpression>& exprs,
62 std::vector<LinearExpression> converted;
63 for (
const AffineExpression& affine : exprs) {
65 e.offset = affine.constant;
67 e.vars.push_back(affine.var);
68 e.coeffs.push_back(affine.coeff);
72 LinearExpression target;
74 target.coeffs.push_back(IntegerValue(1));
85 std::vector<AffineExpression> sizes;
86 for (
int box = 0; box < y->
NumTasks(); ++box) {
89 sizes.push_back(y->
Sizes()[box]);
92 const IntegerVariable min_start_var =
94 AddIsEqualToMinOf(min_start_var, y->
Starts(),
model);
96 const IntegerVariable max_end_var =
98 AddIsEqualToMaxOf(max_end_var, y->
Ends(),
model);
103 const std::vector<int64_t> coeffs = {-
capacity.coeff.value(), -1, 1};
106 coeffs,
capacity.constant.value()));
111#define RETURN_IF_FALSE(f) \
112 if (!(f)) return false;
118 const int num_boxes = x_.
NumTasks();
122 active_boxes_.clear();
123 cached_energies_.resize(num_boxes);
124 cached_rectangles_.resize(num_boxes);
125 for (
int box = 0; box < num_boxes; ++box) {
127 if (cached_energies_[box] == 0)
continue;
135 Rectangle& rectangle = cached_rectangles_[box];
141 active_boxes_.push_back(box);
145 cached_rectangles_, cached_energies_, absl::MakeSpan(active_boxes_));
146 if (initial_boxes.size() <= 1)
return true;
149 std::vector<absl::Span<int>> components =
151 for (absl::Span<int> boxes : components) {
157 for (
int i = 0; i < 3; ++i) {
159 cached_energies_, &threshold_x_, &threshold_y_,
160 &conflicting_rectangle)) {
164 threshold_y_, *random_);
165 if (boxes.size() <= 1)
break;
167 if (boxes.size() <= 1)
continue;
170 const std::vector<absl::Span<int>> local_components =
172 for (absl::Span<int> local_boxes : local_components) {
173 IntegerValue total_sum_of_areas(0);
174 for (
const int box : local_boxes) {
175 total_sum_of_areas += cached_energies_[box];
177 for (
const int box : local_boxes) {
179 FailWhenEnergyIsTooLarge(box, local_boxes, total_sum_of_areas));
189 const int id = watcher->
Register(
this);
197void NonOverlappingRectanglesEnergyPropagator::SortBoxesIntoNeighbors(
198 int box, absl::Span<const int> local_boxes,
199 IntegerValue total_sum_of_areas) {
200 const Rectangle& box_dim = cached_rectangles_[box];
203 for (
const int other_box : local_boxes) {
204 if (other_box == box)
continue;
205 const Rectangle& other_dim = cached_rectangles_[other_box];
208 if (span_x > threshold_x_)
continue;
211 if (span_y > threshold_y_)
continue;
212 const IntegerValue bounding_area = span_x * span_y;
213 if (bounding_area < total_sum_of_areas) {
214 neighbors_.push_back({other_box, bounding_area});
217 std::sort(neighbors_.begin(), neighbors_.end());
220bool NonOverlappingRectanglesEnergyPropagator::FailWhenEnergyIsTooLarge(
221 int box, absl::Span<const int> local_boxes,
222 IntegerValue total_sum_of_areas) {
223 SortBoxesIntoNeighbors(box, local_boxes, total_sum_of_areas);
225 Rectangle area = cached_rectangles_[box];
226 IntegerValue sum_of_areas = cached_energies_[box];
228 const auto add_box_energy_in_rectangle_reason = [&](
int b) {
235 for (
int i = 0; i < neighbors_.size(); ++i) {
236 const int other_box = neighbors_[i].box;
237 CHECK_GT(cached_energies_[other_box], 0);
240 area.TakeUnionWith(cached_rectangles_[other_box]);
241 if (area.x_max - area.x_min > threshold_x_)
break;
242 if (area.y_max - area.y_min > threshold_y_)
break;
245 sum_of_areas += cached_energies_[other_box];
246 const IntegerValue bounding_area =
247 (area.x_max - area.x_min) * (area.y_max - area.y_min);
248 if (bounding_area >= total_sum_of_areas) {
253 if (sum_of_areas > bounding_area) {
256 add_box_energy_in_rectangle_reason(box);
257 for (
int j = 0; j <= i; ++j) {
258 add_box_energy_in_rectangle_reason(neighbors_[j].box);
273IntegerValue FindCanonicalValue(IntegerValue lb, IntegerValue ub) {
274 if (lb == ub)
return lb;
275 if (lb <= 0 && ub > 0)
return IntegerValue(0);
276 if (lb < 0 && ub <= 0) {
277 return -FindCanonicalValue(-ub, -lb);
281 IntegerValue candidate = ub;
282 for (
int o = 0; o < 62; ++o) {
284 const IntegerValue masked_ub(ub.value() & ~mask);
285 if (masked_ub >= lb) {
286 candidate = masked_ub;
294void SplitDisjointBoxes(
const SchedulingConstraintHelper& x,
295 absl::Span<int> boxes,
296 std::vector<absl::Span<int>>* result) {
298 std::sort(boxes.begin(), boxes.end(), [&x](
int a,
int b) {
299 return x.ShiftedStartMin(a) < x.ShiftedStartMin(b);
301 int current_start = 0;
302 std::size_t current_length = 1;
303 IntegerValue current_max_end = x.EndMax(boxes[0]);
305 for (
int b = 1;
b < boxes.size(); ++
b) {
306 const int box = boxes[
b];
307 if (x.ShiftedStartMin(box) < current_max_end) {
310 current_max_end =
std::max(current_max_end, x.EndMax(box));
312 if (current_length > 1) {
313 result->emplace_back(&boxes[current_start], current_length);
317 current_max_end = x.EndMax(box);
322 if (current_length > 1) {
323 result->emplace_back(&boxes[current_start], current_length);
338 x_(x->NumTasks(),
model),
341 overload_checker_(&x_),
342 forward_detectable_precedences_(true, &x_),
343 backward_detectable_precedences_(false, &x_),
344 forward_not_last_(true, &x_),
345 backward_not_last_(false, &x_),
346 forward_edge_finding_(true, &x_),
347 backward_edge_finding_(false, &x_) {}
353 int fast_priority,
int slow_priority) {
354 fast_id_ = watcher_->
Register(
this);
363 const int slow_id = watcher_->
Register(
this);
369bool NonOverlappingRectanglesDisjunctivePropagator::
370 FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
372 std::function<
bool()> inner_propagate) {
379 indexed_intervals_.clear();
381 for (
int i = temp.size(); --i >= 0;) {
382 const int box = temp[i].task_index;
383 if (!strict_ && (x.
SizeMin(box) == 0 || y->
SizeMin(box) == 0))
continue;
389 const IntegerValue
start_max = temp[i].time;
397 if (indexed_intervals_.size() < 2)
return true;
399 &events_overlapping_boxes_);
402 boxes_to_propagate_.clear();
403 reduced_overlapping_boxes_.clear();
404 for (
int i = 0; i < events_overlapping_boxes_.size(); ++i) {
405 SplitDisjointBoxes(x, absl::MakeSpan(events_overlapping_boxes_[i]),
407 for (absl::Span<int> sub_boxes : disjoint_boxes_) {
411 const auto& insertion = reduced_overlapping_boxes_.insert(sub_boxes);
412 if (insertion.second) boxes_to_propagate_.push_back(sub_boxes);
419 for (
const absl::Span<const int> boxes : boxes_to_propagate_) {
426 for (
const int b : boxes) {
440 const IntegerValue line_to_use_for_reason = FindCanonicalValue(lb, ub);
455 std::function<bool()> inner_propagate;
457 inner_propagate = [
this]() {
471 inner_propagate = [
this]() {
472 if (x_.
NumTasks() <= 2)
return true;
482 global_x_, &global_y_, inner_propagate));
486 global_y_, &global_x_, inner_propagate));
496 const int num_boxes = global_x_.
NumTasks();
497 for (
int box1 = 0; box1 < num_boxes; ++box1) {
498 if (!global_x_.
IsPresent(box1))
continue;
499 for (
int box2 = box1 + 1; box2 < num_boxes; ++box2) {
500 if (!global_x_.
IsPresent(box2))
continue;
528bool NonOverlappingRectanglesDisjunctivePropagator::PropagateTwoBoxes() {
533 const auto left_box_before_right_box = [
this](
int left,
int right) {
535 const IntegerValue left_end_min = x_.
EndMin(left);
536 if (left_end_min > x_.
StartMin(right)) {
546 const IntegerValue right_start_max = x_.
StartMax(right);
547 if (right_start_max < x_.
EndMax(left)) {
569 return left_box_before_right_box(0, 1);
572 return left_box_before_right_box(1, 0);
580#undef RETURN_IF_FALSE
#define CHECK_GT(val1, val2)
#define DCHECK_GE(val1, val2)
#define CHECK_LE(val1, val2)
void SetPropagatorPriority(int id, int priority)
int Register(PropagatorInterface *propagator)
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)
Class that owns everything related to a particular optimization model.
void Register(int fast_priority, int slow_priority)
~NonOverlappingRectanglesDisjunctivePropagator() override
NonOverlappingRectanglesDisjunctivePropagator(bool strict, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y, Model *model)
int RegisterWith(GenericLiteralWatcher *watcher)
~NonOverlappingRectanglesEnergyPropagator() override
IntegerValue ShiftedStartMin(int t) const
IntegerValue EndMin(int t) const
const std::vector< AffineExpression > & Starts() const
void WatchAllTasks(int id, GenericLiteralWatcher *watcher, bool watch_start_max=true, bool watch_end_max=true) const
void AddPresenceReason(int t)
ABSL_MUST_USE_RESULT bool ResetFromSubset(const SchedulingConstraintHelper &other, absl::Span< const int > tasks)
bool IsPresent(int t) const
IntegerValue ShiftedEndMax(int t) const
void AddEndMinReason(int t, IntegerValue lower_bound)
ABSL_MUST_USE_RESULT bool IncreaseStartMin(int t, IntegerValue new_start_min)
const std::vector< AffineExpression > & Sizes() const
IntegerValue EndMax(int t) const
ABSL_MUST_USE_RESULT bool ReportConflict()
void ImportOtherReasons(const SchedulingConstraintHelper &other_helper)
void SetOtherHelper(SchedulingConstraintHelper *other_helper, absl::Span< const int > map_to_other_helper, IntegerValue event)
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
IntegerValue StartMin(int t) const
const std::vector< TaskTime > & TaskByDecreasingStartMax()
void AddEnergyMinInIntervalReason(int t, IntegerValue min, IntegerValue max)
IntegerValue StartMax(int t) const
void AddReasonForBeingBefore(int before, int after)
void AddStartMaxReason(int t, IntegerValue upper_bound)
void SetTimeDirection(bool is_forward)
ABSL_MUST_USE_RESULT bool DecreaseEndMax(int t, IntegerValue new_end_max)
const std::vector< AffineExpression > & Ends() const
IntegerValue SizeMin(int t) const
std::function< void(Model *)> WeightedSumGreaterOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
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)
const IntegerVariable kNoIntegerVariable(-1)
std::function< void(Model *)> Cumulative(const std::vector< IntervalVariable > &vars, const std::vector< AffineExpression > &demands, AffineExpression capacity, SchedulingConstraintHelper *helper)
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)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
std::function< void(Model *)> IsEqualToMinOf(IntegerVariable min_var, const std::vector< IntegerVariable > &vars)
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
bool ReportEnergyConflict(Rectangle bounding_box, absl::Span< const int > boxes, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y)
void ConstructOverlappingSets(bool already_sorted, std::vector< IndexedInterval > *intervals, std::vector< std::vector< int > > *result)
void AddCumulativeRelaxation(const std::vector< IntervalVariable > &x_intervals, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y, Model *model)
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.
int64_t CapSub(int64_t x, int64_t y)
#define RETURN_IF_FALSE(f)