OR-Tools  9.3
sat/diffn.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
14#include "ortools/sat/diffn.h"
15
16#include <algorithm>
17#include <cstdint>
18#include <limits>
19#include <utility>
20#include <vector>
21
22#include "absl/container/flat_hash_set.h"
23#include "absl/types/span.h"
28#include "ortools/sat/integer.h"
32#include "ortools/sat/model.h"
34#include "ortools/sat/sat_parameters.pb.h"
36#include "ortools/sat/util.h"
39
40namespace operations_research {
41namespace sat {
42
43namespace {
44
45// TODO(user): Use the faster variable only version if all expressions reduce
46// to a single variable?
47void AddIsEqualToMinOf(IntegerVariable min_var,
48 const std::vector<AffineExpression>& exprs,
49 Model* model) {
50 std::vector<LinearExpression> converted;
51 for (const AffineExpression& affine : exprs) {
52 LinearExpression e;
53 e.offset = affine.constant;
54 if (affine.var != kNoIntegerVariable) {
55 e.vars.push_back(affine.var);
56 e.coeffs.push_back(affine.coeff);
57 }
58 converted.push_back(e);
59 }
60 LinearExpression target;
61 target.vars.push_back(min_var);
62 target.coeffs.push_back(IntegerValue(1));
63 model->Add(IsEqualToMinOf(target, converted));
64}
65
66void AddIsEqualToMaxOf(IntegerVariable max_var,
67 const std::vector<AffineExpression>& exprs,
68 Model* model) {
69 std::vector<LinearExpression> converted;
70 for (const AffineExpression& affine : exprs) {
71 LinearExpression e;
72 e.offset = affine.constant;
73 if (affine.var != kNoIntegerVariable) {
74 e.vars.push_back(affine.var);
75 e.coeffs.push_back(affine.coeff);
76 }
77 converted.push_back(NegationOf(e));
78 }
79 LinearExpression target;
80 target.vars.push_back(NegationOf(max_var));
81 target.coeffs.push_back(IntegerValue(1));
82 model->Add(IsEqualToMinOf(target, converted));
83}
84
85} // namespace
86
88 const std::vector<IntervalVariable>& x_intervals,
90 Model* model) {
91 int64_t min_starts = std::numeric_limits<int64_t>::max();
92 int64_t max_ends = std::numeric_limits<int64_t>::min();
93 std::vector<AffineExpression> sizes;
94 for (int box = 0; box < y->NumTasks(); ++box) {
95 min_starts = std::min(min_starts, y->StartMin(box).value());
96 max_ends = std::max(max_ends, y->EndMax(box).value());
97 sizes.push_back(y->Sizes()[box]);
98 }
99
100 const IntegerVariable min_start_var =
101 model->Add(NewIntegerVariable(min_starts, max_ends));
102 AddIsEqualToMinOf(min_start_var, y->Starts(), model);
103
104 const IntegerVariable max_end_var =
105 model->Add(NewIntegerVariable(min_starts, max_ends));
106 AddIsEqualToMaxOf(max_end_var, y->Ends(), model);
107
108 // (max_end - min_start) >= capacity.
110 model->Add(NewIntegerVariable(0, CapSub(max_ends, min_starts))));
111 const std::vector<int64_t> coeffs = {-capacity.coeff.value(), -1, 1};
112 model->Add(
113 WeightedSumGreaterOrEqual({capacity.var, min_start_var, max_end_var},
114 coeffs, capacity.constant.value()));
115
116 auto* integer_trail = model->GetOrCreate<IntegerTrail>();
117 auto* watcher = model->GetOrCreate<GenericLiteralWatcher>();
118
119 // Propagator responsible for applying Timetabling filtering rule. It
120 // increases the minimum of the start variables, decrease the maximum of the
121 // end variables, and increase the minimum of the capacity variable.
122 TimeTablingPerTask* time_tabling =
123 new TimeTablingPerTask(sizes, capacity, integer_trail, x);
124 time_tabling->RegisterWith(watcher);
125 model->TakeOwnership(time_tabling);
126
127 // Propagator responsible for applying the Overload Checking filtering rule.
128 // It increases the minimum of the capacity variable.
129 if (model->GetOrCreate<SatParameters>()
130 ->use_overload_checker_in_cumulative_constraint()) {
132 }
133}
134
135#define RETURN_IF_FALSE(f) \
136 if (!(f)) return false;
137
140
142 const int num_boxes = x_.NumTasks();
143 if (!x_.SynchronizeAndSetTimeDirection(true)) return false;
144 if (!y_.SynchronizeAndSetTimeDirection(true)) return false;
145
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) {
150 cached_energies_[box] = x_.SizeMin(box) * y_.SizeMin(box);
151 if (cached_energies_[box] == 0) continue;
152 if (!x_.IsPresent(box) || !y_.IsPresent(box)) continue;
153
154 // The code needs the size min to be larger or equal to the mandatory part
155 // for it to works correctly. This is always enforced by the helper.
156 DCHECK_GE(x_.SizeMin(box), x_.EndMin(box) - x_.StartMax(box));
157 DCHECK_GE(y_.SizeMin(box), y_.EndMin(box) - y_.StartMax(box));
158
159 Rectangle& rectangle = cached_rectangles_[box];
160 rectangle.x_min = x_.ShiftedStartMin(box);
161 rectangle.x_max = x_.ShiftedEndMax(box);
162 rectangle.y_min = y_.ShiftedStartMin(box);
163 rectangle.y_max = y_.ShiftedEndMax(box);
164
165 active_boxes_.push_back(box);
166 }
167
168 absl::Span<int> initial_boxes = FilterBoxesThatAreTooLarge(
169 cached_rectangles_, cached_energies_, absl::MakeSpan(active_boxes_));
170 if (initial_boxes.size() <= 1) return true;
171
172 Rectangle conflicting_rectangle;
173 std::vector<absl::Span<int>> components =
174 GetOverlappingRectangleComponents(cached_rectangles_, initial_boxes);
175 for (absl::Span<int> boxes : components) {
176 // Computes the size on x and y past which there is no point doing any
177 // energetic reasonning. We do a few iterations since as we filter one size,
178 // we can potentially filter more on the transposed dimension.
179 threshold_x_ = kMaxIntegerValue;
180 threshold_y_ = kMaxIntegerValue;
181 for (int i = 0; i < 3; ++i) {
182 if (!AnalyzeIntervals(/*transpose=*/i == 1, boxes, cached_rectangles_,
183 cached_energies_, &threshold_x_, &threshold_y_,
184 &conflicting_rectangle)) {
185 return ReportEnergyConflict(conflicting_rectangle, boxes, &x_, &y_);
186 }
187 boxes = FilterBoxesAndRandomize(cached_rectangles_, boxes, threshold_x_,
188 threshold_y_, *random_);
189 if (boxes.size() <= 1) break;
190 }
191 if (boxes.size() <= 1) continue;
192
193 // Now that we removed boxes with a large domain, resplit everything.
194 const std::vector<absl::Span<int>> local_components =
195 GetOverlappingRectangleComponents(cached_rectangles_, boxes);
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];
200 }
201 for (const int box : local_boxes) {
203 FailWhenEnergyIsTooLarge(box, local_boxes, total_sum_of_areas));
204 }
205 }
206 }
207
208 return true;
209}
210
212 GenericLiteralWatcher* watcher) {
213 const int id = watcher->Register(this);
214 x_.WatchAllTasks(id, watcher, /*watch_start_max=*/true,
215 /*watch_end_max=*/true);
216 y_.WatchAllTasks(id, watcher, /*watch_start_max=*/true,
217 /*watch_end_max=*/true);
218 return id;
219}
220
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];
225
226 neighbors_.clear();
227 for (const int other_box : local_boxes) {
228 if (other_box == box) continue;
229 const Rectangle& other_dim = cached_rectangles_[other_box];
230 const IntegerValue span_x = std::max(box_dim.x_max, other_dim.x_max) -
231 std::min(box_dim.x_min, other_dim.x_min);
232 if (span_x > threshold_x_) continue;
233 const IntegerValue span_y = std::max(box_dim.y_max, other_dim.y_max) -
234 std::min(box_dim.y_min, other_dim.y_min);
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});
239 }
240 }
241 std::sort(neighbors_.begin(), neighbors_.end());
242}
243
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);
248
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);
255
256 const auto add_box_energy_in_rectangle_reason = [&](int b) {
257 x_.AddEnergyMinInIntervalReason(b, area.x_min, area.x_max);
258 y_.AddEnergyMinInIntervalReason(b, area.y_min, area.y_max);
261 };
262
263 // TODO(user): using the bounding area profile we can relax this reason.
264 const auto add_precise_box_energy_reason = [&](int b) {
265 x_.AddStartMinReason(b, x_.StartMin(b));
266 x_.AddEndMaxReason(b, x_.EndMax(b));
267 x_.AddSizeMinReason(b, x_.SizeMin(b));
268 y_.AddStartMinReason(b, y_.StartMin(b));
269 y_.AddEndMaxReason(b, y_.EndMax(b));
270 y_.AddSizeMinReason(b, y_.SizeMin(b));
273 };
274
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);
279
280 // Update Bounding box.
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;
284
285 // Update precise bounding area.
286 // TODO(user): speed up (cache, maintain profile, ...)
287 bounding_area.AddRectangle(other.x_min, other.x_max, other.y_min,
288 other.y_max);
289
290 // Update sum of areas.
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();
295
296 if (precise_area >= total_sum_of_areas) {
297 // Nothing will be deduced. Exiting.
298 return true;
299 }
300
301 if (sum_of_areas > precise_area) {
302 x_.ClearReason();
303 y_.ClearReason();
304 if (sum_of_areas > bounding_rectangle_area) {
305 // Use the more general relaxation.
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);
309 }
310 } else {
311 add_precise_box_energy_reason(box);
312 for (int j = 0; j <= i; ++j) {
313 add_precise_box_energy_reason(neighbors_[j].box);
314 }
315 }
316 x_.ImportOtherReasons(y_);
317 return x_.ReportConflict();
318 }
319 }
320 return true;
321}
322
323namespace {
324
325// We want for different propagation to reuse as much as possible the same
326// line. The idea behind this is to compute the 'canonical' line to use
327// when explaining that boxes overlap on the 'y_dim' dimension. We compute
328// the multiple of the biggest power of two that is common to all boxes.
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);
334 }
335
336 int64_t mask = 0;
337 IntegerValue candidate = ub;
338 for (int o = 0; o < 62; ++o) {
339 mask = 2 * mask + 1;
340 const IntegerValue masked_ub(ub.value() & ~mask);
341 if (masked_ub >= lb) {
342 candidate = masked_ub;
343 } else {
344 break;
345 }
346 }
347 return candidate;
348}
349
350void SplitDisjointBoxes(const SchedulingConstraintHelper& x,
351 absl::Span<int> boxes,
352 std::vector<absl::Span<int>>* result) {
353 result->clear();
354 std::sort(boxes.begin(), boxes.end(), [&x](int a, int b) {
355 return x.ShiftedStartMin(a) < x.ShiftedStartMin(b);
356 });
357 int current_start = 0;
358 std::size_t current_length = 1;
359 IntegerValue current_max_end = x.EndMax(boxes[0]);
360
361 for (int b = 1; b < boxes.size(); ++b) {
362 const int box = boxes[b];
363 if (x.ShiftedStartMin(box) < current_max_end) {
364 // Merge.
365 current_length++;
366 current_max_end = std::max(current_max_end, x.EndMax(box));
367 } else {
368 if (current_length > 1) { // Ignore lists of size 1.
369 result->emplace_back(&boxes[current_start], current_length);
370 }
371 current_start = b;
372 current_length = 1;
373 current_max_end = x.EndMax(box);
374 }
375 }
376
377 // Push last span.
378 if (current_length > 1) {
379 result->emplace_back(&boxes[current_start], current_length);
380 }
381}
382
383} // namespace
384
385// Note that x_ and y_ must be initialized with enough intervals when passed
386// to the disjunctive propagators.
391 Model* model)
392 : global_x_(*x),
393 global_y_(*y),
394 x_(x->NumTasks(), model),
395 strict_(strict),
396 watcher_(model->GetOrCreate<GenericLiteralWatcher>()),
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_) {}
404
407
409 int fast_priority, int slow_priority) {
410 fast_id_ = watcher_->Register(this);
411 watcher_->SetPropagatorPriority(fast_id_, fast_priority);
412 global_x_.WatchAllTasks(fast_id_, watcher_);
413 global_y_.WatchAllTasks(fast_id_, watcher_);
414
415 // This propagator is the one making sure our propagation is complete, so
416 // we do need to make sure it is called again if it modified some bounds.
418
419 const int slow_id = watcher_->Register(this);
420 watcher_->SetPropagatorPriority(slow_id, slow_priority);
421 global_x_.WatchAllTasks(slow_id, watcher_);
422 global_y_.WatchAllTasks(slow_id, watcher_);
423}
424
425bool NonOverlappingRectanglesDisjunctivePropagator::
426 FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
427 bool fast_propagation, const SchedulingConstraintHelper& x,
429 // Note that since we only push bounds on x, we cache the value for y just
430 // once.
431 if (!y->SynchronizeAndSetTimeDirection(true)) return false;
432
433 // Compute relevant boxes, the one with a mandatory part of y. Because we will
434 // need to sort it this way, we consider them by increasing start max.
435 indexed_intervals_.clear();
436 const std::vector<TaskTime>& temp = y->TaskByDecreasingStartMax();
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;
440
441 // Ignore absent boxes.
442 if (x.IsAbsent(box) || y->IsAbsent(box)) continue;
443
444 // Ignore boxes where the relevant presence literal is only on the y
445 // dimension, or if both intervals are optionals with different literals.
446 if (x.IsPresent(box) && !y->IsPresent(box)) continue;
447 if (!x.IsPresent(box) && !y->IsPresent(box) &&
448 x.PresenceLiteral(box) != y->PresenceLiteral(box)) {
449 continue;
450 }
451
452 const IntegerValue start_max = temp[i].time;
453 const IntegerValue end_min = y->EndMin(box);
454 if (start_max < end_min) {
455 indexed_intervals_.push_back({box, start_max, end_min});
456 }
457 }
458
459 // Less than 2 boxes, no propagation.
460 if (indexed_intervals_.size() < 2) return true;
461 ConstructOverlappingSets(/*already_sorted=*/true, &indexed_intervals_,
462 &events_overlapping_boxes_);
463
464 // Split lists of boxes into disjoint set of boxes (w.r.t. overlap).
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]),
469 &disjoint_boxes_);
470 for (absl::Span<int> sub_boxes : disjoint_boxes_) {
471 // Boxes are sorted in a stable manner in the Split method.
472 // Note that we do not use reduced_overlapping_boxes_ directly so that
473 // the order of iteration is deterministic.
474 const auto& insertion = reduced_overlapping_boxes_.insert(sub_boxes);
475 if (insertion.second) boxes_to_propagate_.push_back(sub_boxes);
476 }
477 }
478
479 // And finally propagate.
480 //
481 // TODO(user): Sorting of boxes seems influential on the performance. Test.
482 for (const absl::Span<const int> boxes : boxes_to_propagate_) {
483 // The case of two boxes should be taken care of during "fast" propagation,
484 // so we can skip it here.
485 if (!fast_propagation && boxes.size() <= 2) continue;
486
487 x_.ClearOtherHelper();
488 if (!x_.ResetFromSubset(x, boxes)) return false;
489
490 // Collect the common overlapping coordinates of all boxes.
491 IntegerValue lb(std::numeric_limits<int64_t>::min());
492 IntegerValue ub(std::numeric_limits<int64_t>::max());
493 for (const int b : boxes) {
494 lb = std::max(lb, y->StartMax(b));
495 ub = std::min(ub, y->EndMin(b) - 1);
496 }
497 CHECK_LE(lb, ub);
498
499 // We want for different propagation to reuse as much as possible the same
500 // line. The idea behind this is to compute the 'canonical' line to use
501 // when explaining that boxes overlap on the 'y_dim' dimension. We compute
502 // the multiple of the biggest power of two that is common to all boxes.
503 //
504 // TODO(user): We should scan the integer trail to find the oldest
505 // non-empty common interval. Then we can pick the canonical value within
506 // it.
507 const IntegerValue line_to_use_for_reason = FindCanonicalValue(lb, ub);
508
509 // Setup x_dim for propagation.
510 x_.SetOtherHelper(y, boxes, line_to_use_for_reason);
511
512 if (fast_propagation) {
513 if (x_.NumTasks() == 2) {
514 // In that case, we can use simpler algorithms.
515 // Note that this case happens frequently (~30% of all calls to this
516 // method according to our tests).
517 RETURN_IF_FALSE(PropagateTwoBoxes());
518 } else {
519 RETURN_IF_FALSE(overload_checker_.Propagate());
520 RETURN_IF_FALSE(forward_detectable_precedences_.Propagate());
521 RETURN_IF_FALSE(backward_detectable_precedences_.Propagate());
522 }
523 } else {
524 DCHECK_GT(x_.NumTasks(), 2);
525 RETURN_IF_FALSE(forward_not_last_.Propagate());
526 RETURN_IF_FALSE(backward_not_last_.Propagate());
527 RETURN_IF_FALSE(backward_edge_finding_.Propagate());
528 RETURN_IF_FALSE(forward_edge_finding_.Propagate());
529 }
530 }
531
532 return true;
533}
534
536 global_x_.SetTimeDirection(true);
537 global_y_.SetTimeDirection(true);
538
539 // Note that the code assumes that this was registered twice in fast and slow
540 // mode. So we will not redo some propagation in slow mode that was already
541 // done by the fast mode.
542 const bool fast_propagation = watcher_->GetCurrentId() == fast_id_;
543 RETURN_IF_FALSE(FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
544 fast_propagation, global_x_, &global_y_));
545
546 // We can actually swap dimensions to propagate vertically.
547 RETURN_IF_FALSE(FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
548 fast_propagation, global_y_, &global_x_));
549
550 // If two boxes must overlap but do not have a mandatory line/column that
551 // crosses both of them, then the code above do not see it. So we manually
552 // propagate this case.
553 //
554 // TODO(user): Since we are at it, do more propagation even if no conflict?
555 // This rarely propagate, so disabled for now. Investigate if it is worth
556 // it.
557 if (/*DISABLES CODE*/ (false) && watcher_->GetCurrentId() == fast_id_) {
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;
563 if (global_x_.EndMin(box1) <= global_x_.StartMax(box2)) continue;
564 if (global_x_.EndMin(box2) <= global_x_.StartMax(box1)) continue;
565 if (global_y_.EndMin(box1) <= global_y_.StartMax(box2)) continue;
566 if (global_y_.EndMin(box2) <= global_y_.StartMax(box1)) continue;
567
568 // X and Y must overlap. This is a conflict.
569 global_x_.ClearReason();
570 global_x_.AddPresenceReason(box1);
571 global_x_.AddPresenceReason(box2);
572 global_x_.AddReasonForBeingBefore(box1, box2);
573 global_x_.AddReasonForBeingBefore(box2, box1);
574 global_y_.ClearReason();
575 global_y_.AddPresenceReason(box1);
576 global_y_.AddPresenceReason(box2);
577 global_y_.AddReasonForBeingBefore(box1, box2);
578 global_y_.AddReasonForBeingBefore(box2, box1);
579 global_x_.ImportOtherReasons(global_y_);
580 return global_x_.ReportConflict();
581 }
582 }
583 }
584
585 return true;
586}
587
588// Specialized propagation on only two boxes that must intersect with the
589// given y_line_for_reason.
590bool NonOverlappingRectanglesDisjunctivePropagator::PropagateTwoBoxes() {
591 if (!x_.IsPresent(0) || !x_.IsPresent(1)) return true;
592
593 // For each direction and each order, we test if the boxes can be disjoint.
594 const int state =
595 (x_.EndMin(0) <= x_.StartMax(1)) + 2 * (x_.EndMin(1) <= x_.StartMax(0));
596
597 const auto left_box_before_right_box = [this](int left, int right) {
598 // left box pushes right box.
599 const IntegerValue left_end_min = x_.EndMin(left);
600 if (left_end_min > x_.StartMin(right)) {
601 x_.ClearReason();
602 x_.AddPresenceReason(left);
603 x_.AddPresenceReason(right);
604 x_.AddReasonForBeingBefore(left, right);
605 x_.AddEndMinReason(left, left_end_min);
606 RETURN_IF_FALSE(x_.IncreaseStartMin(right, left_end_min));
607 }
608
609 // right box pushes left box.
610 const IntegerValue right_start_max = x_.StartMax(right);
611 if (right_start_max < x_.EndMax(left)) {
612 x_.ClearReason();
613 x_.AddPresenceReason(left);
614 x_.AddPresenceReason(right);
615 x_.AddReasonForBeingBefore(left, right);
616 x_.AddStartMaxReason(right, right_start_max);
617 RETURN_IF_FALSE(x_.DecreaseEndMax(left, right_start_max));
618 }
619
620 return true;
621 };
622
623 switch (state) {
624 case 0: { // Conflict.
625 x_.ClearReason();
626 x_.AddPresenceReason(0);
627 x_.AddPresenceReason(1);
630 return x_.ReportConflict();
631 }
632 case 1: { // b1 is left of b2.
633 return left_box_before_right_box(0, 1);
634 }
635 case 2: { // b2 is left of b1.
636 return left_box_before_right_box(1, 0);
637 }
638 default: { // Nothing to deduce.
639 return true;
640 }
641 }
642}
643
644#undef RETURN_IF_FALSE
645} // namespace sat
646} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK_GT(val1, val2)
Definition: base/logging.h:708
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:895
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:896
#define CHECK_LE(val1, val2)
Definition: base/logging.h:705
void SetPropagatorPriority(int id, int priority)
Definition: integer.cc:2051
int Register(PropagatorInterface *propagator)
Definition: integer.cc:2028
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
NonOverlappingRectanglesDisjunctivePropagator(bool strict, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y, Model *model)
Definition: sat/diffn.cc:388
const std::vector< AffineExpression > & Starts() const
Definition: intervals.h:339
void AddStartMinReason(int t, IntegerValue lower_bound)
Definition: intervals.h:564
void WatchAllTasks(int id, GenericLiteralWatcher *watcher, bool watch_start_max=true, bool watch_end_max=true) const
Definition: intervals.cc:520
ABSL_MUST_USE_RESULT bool ResetFromSubset(const SchedulingConstraintHelper &other, absl::Span< const int > tasks)
Definition: intervals.cc:230
void AddEndMinReason(int t, IntegerValue lower_bound)
Definition: intervals.h:578
ABSL_MUST_USE_RESULT bool IncreaseStartMin(int t, IntegerValue new_start_min)
Definition: intervals.cc:465
const std::vector< AffineExpression > & Sizes() const
Definition: intervals.h:341
void ImportOtherReasons(const SchedulingConstraintHelper &other_helper)
Definition: intervals.cc:553
void SetOtherHelper(SchedulingConstraintHelper *other_helper, absl::Span< const int > map_to_other_helper, IntegerValue event)
Definition: intervals.h:358
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
Definition: intervals.cc:307
const std::vector< TaskTime > & TaskByDecreasingStartMax()
Definition: intervals.cc:349
void AddEndMaxReason(int t, IntegerValue upper_bound)
Definition: intervals.h:586
void AddEnergyMinInIntervalReason(int t, IntegerValue min, IntegerValue max)
Definition: intervals.h:603
void AddReasonForBeingBefore(int before, int after)
Definition: intervals.cc:394
void AddStartMaxReason(int t, IntegerValue upper_bound)
Definition: intervals.h:571
ABSL_MUST_USE_RESULT bool DecreaseEndMax(int t, IntegerValue new_end_max)
Definition: intervals.cc:474
const std::vector< AffineExpression > & Ends() const
Definition: intervals.h:340
int64_t b
int64_t a
GRBmodel * model
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)
Definition: integer_expr.h:474
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
void AddDiffnCumulativeRelationOnX(const std::vector< IntervalVariable > &x_intervals, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y, Model *model)
Definition: sat/diffn.cc:87
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
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)
Definition: diffn_util.cc:165
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:47
std::function< void(Model *)> IsEqualToMinOf(IntegerVariable min_var, const std::vector< IntegerVariable > &vars)
Definition: integer_expr.h:743
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
Definition: integer.h:1622
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
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 CapSub(int64_t x, int64_t y)
int64_t capacity
#define RETURN_IF_FALSE(f)
Definition: sat/diffn.cc:135
Rev< int64_t > start_max
Rev< int64_t > end_min