22#include "absl/container/flat_hash_set.h"
23#include "absl/types/span.h"
34#include "ortools/sat/sat_parameters.pb.h"
47void AddIsEqualToMinOf(IntegerVariable min_var,
48 const std::vector<AffineExpression>& exprs,
50 std::vector<LinearExpression> converted;
51 for (
const AffineExpression& affine : exprs) {
53 e.offset = affine.constant;
55 e.vars.push_back(affine.var);
56 e.coeffs.push_back(affine.coeff);
58 converted.push_back(e);
60 LinearExpression target;
61 target.vars.push_back(min_var);
62 target.coeffs.push_back(IntegerValue(1));
66void AddIsEqualToMaxOf(IntegerVariable max_var,
67 const std::vector<AffineExpression>& exprs,
69 std::vector<LinearExpression> converted;
70 for (
const AffineExpression& affine : exprs) {
72 e.offset = affine.constant;
74 e.vars.push_back(affine.var);
75 e.coeffs.push_back(affine.coeff);
79 LinearExpression target;
81 target.coeffs.push_back(IntegerValue(1));
88 const std::vector<IntervalVariable>& x_intervals,
93 std::vector<AffineExpression> sizes;
94 for (
int box = 0; box < y->
NumTasks(); ++box) {
97 sizes.push_back(y->
Sizes()[box]);
100 const IntegerVariable min_start_var =
102 AddIsEqualToMinOf(min_start_var, y->
Starts(),
model);
104 const IntegerVariable max_end_var =
106 AddIsEqualToMaxOf(max_end_var, y->
Ends(),
model);
111 const std::vector<int64_t> coeffs = {-
capacity.coeff.value(), -1, 1};
114 coeffs,
capacity.constant.value()));
124 time_tabling->RegisterWith(watcher);
125 model->TakeOwnership(time_tabling);
129 if (
model->GetOrCreate<SatParameters>()
130 ->use_overload_checker_in_cumulative_constraint()) {
135#define RETURN_IF_FALSE(f) \
136 if (!(f)) return false;
142 const int num_boxes = x_.
NumTasks();
146 active_boxes_.clear();
147 cached_energies_.resize(num_boxes);
148 cached_rectangles_.resize(num_boxes);
149 for (
int box = 0; box < num_boxes; ++box) {
151 if (cached_energies_[box] == 0)
continue;
159 Rectangle& rectangle = cached_rectangles_[box];
165 active_boxes_.push_back(box);
169 cached_rectangles_, cached_energies_, absl::MakeSpan(active_boxes_));
170 if (initial_boxes.size() <= 1)
return true;
173 std::vector<absl::Span<int>> components =
175 for (absl::Span<int> boxes : components) {
181 for (
int i = 0; i < 3; ++i) {
183 cached_energies_, &threshold_x_, &threshold_y_,
184 &conflicting_rectangle)) {
188 threshold_y_, *random_);
189 if (boxes.size() <= 1)
break;
191 if (boxes.size() <= 1)
continue;
194 const std::vector<absl::Span<int>> local_components =
196 for (absl::Span<int> local_boxes : local_components) {
197 IntegerValue total_sum_of_areas(0);
198 for (
const int box : local_boxes) {
199 total_sum_of_areas += cached_energies_[box];
201 for (
const int box : local_boxes) {
203 FailWhenEnergyIsTooLarge(box, local_boxes, total_sum_of_areas));
213 const int id = watcher->
Register(
this);
221void NonOverlappingRectanglesEnergyPropagator::SortBoxesIntoNeighbors(
222 int box, absl::Span<const int> local_boxes,
223 IntegerValue total_sum_of_areas) {
224 const Rectangle& box_dim = cached_rectangles_[box];
227 for (
const int other_box : local_boxes) {
228 if (other_box == box)
continue;
229 const Rectangle& other_dim = cached_rectangles_[other_box];
232 if (span_x > threshold_x_)
continue;
235 if (span_y > threshold_y_)
continue;
236 const IntegerValue bounding_area = span_x * span_y;
237 if (bounding_area < total_sum_of_areas) {
238 neighbors_.push_back({other_box, bounding_area});
241 std::sort(neighbors_.begin(), neighbors_.end());
244bool NonOverlappingRectanglesEnergyPropagator::FailWhenEnergyIsTooLarge(
245 int box, absl::Span<const int> local_boxes,
246 IntegerValue total_sum_of_areas) {
247 SortBoxesIntoNeighbors(box, local_boxes, total_sum_of_areas);
249 CapacityProfile bounding_area;
250 IntegerValue sum_of_areas = cached_energies_[box];
251 Rectangle area = cached_rectangles_[box];
252 bounding_area.AddRectangle(
253 cached_rectangles_[box].x_min, cached_rectangles_[box].x_max,
254 cached_rectangles_[box].y_min, cached_rectangles_[box].y_max);
256 const auto add_box_energy_in_rectangle_reason = [&](
int b) {
264 const auto add_precise_box_energy_reason = [&](
int b) {
275 for (
int i = 0; i < neighbors_.size(); ++i) {
276 const int other_box = neighbors_[i].box;
277 const Rectangle& other = cached_rectangles_[other_box];
278 CHECK_GT(cached_energies_[other_box], 0);
281 area.TakeUnionWith(other);
282 if (area.x_max - area.x_min > threshold_x_)
break;
283 if (area.y_max - area.y_min > threshold_y_)
break;
287 bounding_area.AddRectangle(other.x_min, other.x_max, other.y_min,
291 sum_of_areas += cached_energies_[other_box];
292 const IntegerValue bounding_rectangle_area =
293 (area.x_max - area.x_min) * (area.y_max - area.y_min);
294 const IntegerValue precise_area = bounding_area.GetBoundingArea();
296 if (precise_area >= total_sum_of_areas) {
301 if (sum_of_areas > precise_area) {
304 if (sum_of_areas > bounding_rectangle_area) {
306 add_box_energy_in_rectangle_reason(box);
307 for (
int j = 0; j <= i; ++j) {
308 add_box_energy_in_rectangle_reason(neighbors_[j].box);
311 add_precise_box_energy_reason(box);
312 for (
int j = 0; j <= i; ++j) {
313 add_precise_box_energy_reason(neighbors_[j].box);
329IntegerValue FindCanonicalValue(IntegerValue lb, IntegerValue ub) {
330 if (lb == ub)
return lb;
331 if (lb <= 0 && ub > 0)
return IntegerValue(0);
332 if (lb < 0 && ub <= 0) {
333 return -FindCanonicalValue(-ub, -lb);
337 IntegerValue candidate = ub;
338 for (
int o = 0; o < 62; ++o) {
340 const IntegerValue masked_ub(ub.value() & ~mask);
341 if (masked_ub >= lb) {
342 candidate = masked_ub;
350void SplitDisjointBoxes(
const SchedulingConstraintHelper& x,
351 absl::Span<int> boxes,
352 std::vector<absl::Span<int>>* result) {
354 std::sort(boxes.begin(), boxes.end(), [&x](
int a,
int b) {
355 return x.ShiftedStartMin(a) < x.ShiftedStartMin(b);
357 int current_start = 0;
358 std::size_t current_length = 1;
359 IntegerValue current_max_end = x.EndMax(boxes[0]);
361 for (
int b = 1;
b < boxes.size(); ++
b) {
362 const int box = boxes[
b];
363 if (x.ShiftedStartMin(box) < current_max_end) {
366 current_max_end =
std::max(current_max_end, x.EndMax(box));
368 if (current_length > 1) {
369 result->emplace_back(&boxes[current_start], current_length);
373 current_max_end = x.EndMax(box);
378 if (current_length > 1) {
379 result->emplace_back(&boxes[current_start], current_length);
394 x_(x->NumTasks(),
model),
397 overload_checker_(&x_),
398 forward_detectable_precedences_(true, &x_),
399 backward_detectable_precedences_(false, &x_),
400 forward_not_last_(true, &x_),
401 backward_not_last_(false, &x_),
402 forward_edge_finding_(true, &x_),
403 backward_edge_finding_(false, &x_) {}
409 int fast_priority,
int slow_priority) {
410 fast_id_ = watcher_->
Register(
this);
419 const int slow_id = watcher_->
Register(
this);
425bool NonOverlappingRectanglesDisjunctivePropagator::
426 FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
435 indexed_intervals_.clear();
437 for (
int i = temp.size(); --i >= 0;) {
438 const int box = temp[i].task_index;
439 if (!strict_ && (x.
SizeMin(box) == 0 || y->
SizeMin(box) == 0))
continue;
452 const IntegerValue
start_max = temp[i].time;
460 if (indexed_intervals_.size() < 2)
return true;
462 &events_overlapping_boxes_);
465 boxes_to_propagate_.clear();
466 reduced_overlapping_boxes_.clear();
467 for (
int i = 0; i < events_overlapping_boxes_.size(); ++i) {
468 SplitDisjointBoxes(x, absl::MakeSpan(events_overlapping_boxes_[i]),
470 for (absl::Span<int> sub_boxes : disjoint_boxes_) {
474 const auto& insertion = reduced_overlapping_boxes_.insert(sub_boxes);
475 if (insertion.second) boxes_to_propagate_.push_back(sub_boxes);
482 for (
const absl::Span<const int> boxes : boxes_to_propagate_) {
485 if (!fast_propagation && boxes.size() <= 2)
continue;
493 for (
const int b : boxes) {
507 const IntegerValue line_to_use_for_reason = FindCanonicalValue(lb, ub);
512 if (fast_propagation) {
542 const bool fast_propagation = watcher_->
GetCurrentId() == fast_id_;
544 fast_propagation, global_x_, &global_y_));
548 fast_propagation, global_y_, &global_x_));
558 const int num_boxes = global_x_.
NumTasks();
559 for (
int box1 = 0; box1 < num_boxes; ++box1) {
560 if (!global_x_.
IsPresent(box1))
continue;
561 for (
int box2 = box1 + 1; box2 < num_boxes; ++box2) {
562 if (!global_x_.
IsPresent(box2))
continue;
590bool NonOverlappingRectanglesDisjunctivePropagator::PropagateTwoBoxes() {
597 const auto left_box_before_right_box = [
this](
int left,
int right) {
599 const IntegerValue left_end_min = x_.
EndMin(left);
600 if (left_end_min > x_.
StartMin(right)) {
610 const IntegerValue right_start_max = x_.
StartMax(right);
611 if (right_start_max < x_.
EndMax(left)) {
633 return left_box_before_right_box(0, 1);
636 return left_box_before_right_box(1, 0);
644#undef RETURN_IF_FALSE
#define CHECK_GT(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK_GT(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 AddSizeMinReason(int t)
void AddStartMinReason(int t, IntegerValue lower_bound)
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
bool IsAbsent(int t) 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()
Literal PresenceLiteral(int index) const
void AddEndMaxReason(int t, IntegerValue upper_bound)
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
void AddCumulativeOverloadChecker(const std::vector< AffineExpression > &demands, AffineExpression capacity, SchedulingConstraintHelper *helper, Model *model)
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)
void AddDiffnCumulativeRelationOnX(const std::vector< IntervalVariable > &x_intervals, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y, Model *model)
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)
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)
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)