OR-Tools  9.2
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
20#include "absl/container/flat_hash_map.h"
21#include "absl/strings/str_join.h"
31#include "ortools/util/sort.h"
32
33namespace operations_research {
34namespace sat {
35
36namespace {
37
38// TODO(user): Use the faster variable only version if all expressions reduce
39// to a single variable?
40void AddIsEqualToMinOf(IntegerVariable min_var,
41 const std::vector<AffineExpression>& exprs,
42 Model* model) {
43 std::vector<LinearExpression> converted;
44 for (const AffineExpression& affine : exprs) {
45 LinearExpression e;
46 e.offset = affine.constant;
47 if (affine.var != kNoIntegerVariable) {
48 e.vars.push_back(affine.var);
49 e.coeffs.push_back(affine.coeff);
50 }
51 converted.push_back(e);
52 }
53 LinearExpression target;
54 target.vars.push_back(min_var);
55 target.coeffs.push_back(IntegerValue(1));
56 model->Add(IsEqualToMinOf(target, converted));
57}
58
59void AddIsEqualToMaxOf(IntegerVariable max_var,
60 const std::vector<AffineExpression>& exprs,
61 Model* model) {
62 std::vector<LinearExpression> converted;
63 for (const AffineExpression& affine : exprs) {
64 LinearExpression e;
65 e.offset = affine.constant;
66 if (affine.var != kNoIntegerVariable) {
67 e.vars.push_back(affine.var);
68 e.coeffs.push_back(affine.coeff);
69 }
70 converted.push_back(NegationOf(e));
71 }
72 LinearExpression target;
73 target.vars.push_back(NegationOf(max_var));
74 target.coeffs.push_back(IntegerValue(1));
75 model->Add(IsEqualToMinOf(target, converted));
76}
77
78} // namespace
79
80void AddCumulativeRelaxation(const std::vector<IntervalVariable>& x_intervals,
83 int64_t min_starts = std::numeric_limits<int64_t>::max();
84 int64_t max_ends = std::numeric_limits<int64_t>::min();
85 std::vector<AffineExpression> sizes;
86 for (int box = 0; box < y->NumTasks(); ++box) {
87 min_starts = std::min(min_starts, y->StartMin(box).value());
88 max_ends = std::max(max_ends, y->EndMax(box).value());
89 sizes.push_back(y->Sizes()[box]);
90 }
91
92 const IntegerVariable min_start_var =
93 model->Add(NewIntegerVariable(min_starts, max_ends));
94 AddIsEqualToMinOf(min_start_var, y->Starts(), model);
95
96 const IntegerVariable max_end_var =
97 model->Add(NewIntegerVariable(min_starts, max_ends));
98 AddIsEqualToMaxOf(max_end_var, y->Ends(), model);
99
100 // (max_end - min_start) >= capacity.
102 model->Add(NewIntegerVariable(0, CapSub(max_ends, min_starts))));
103 const std::vector<int64_t> coeffs = {-capacity.coeff.value(), -1, 1};
104 model->Add(
105 WeightedSumGreaterOrEqual({capacity.var, min_start_var, max_end_var},
106 coeffs, capacity.constant.value()));
107
108 model->Add(Cumulative(x_intervals, sizes, capacity, x));
109}
110
111#define RETURN_IF_FALSE(f) \
112 if (!(f)) return false;
113
116
118 const int num_boxes = x_.NumTasks();
119 if (!x_.SynchronizeAndSetTimeDirection(true)) return false;
120 if (!y_.SynchronizeAndSetTimeDirection(true)) return false;
121
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) {
126 cached_energies_[box] = x_.SizeMin(box) * y_.SizeMin(box);
127 if (cached_energies_[box] == 0) continue;
128 if (!x_.IsPresent(box) || !y_.IsPresent(box)) continue;
129
130 // The code needs the size min to be larger or equal to the mandatory part
131 // for it to works correctly. This is always enforced by the helper.
132 DCHECK_GE(x_.SizeMin(box), x_.EndMin(box) - x_.StartMax(box));
133 DCHECK_GE(y_.SizeMin(box), y_.EndMin(box) - y_.StartMax(box));
134
135 Rectangle& rectangle = cached_rectangles_[box];
136 rectangle.x_min = x_.ShiftedStartMin(box);
137 rectangle.x_max = x_.ShiftedEndMax(box);
138 rectangle.y_min = y_.ShiftedStartMin(box);
139 rectangle.y_max = y_.ShiftedEndMax(box);
140
141 active_boxes_.push_back(box);
142 }
143
144 absl::Span<int> initial_boxes = FilterBoxesThatAreTooLarge(
145 cached_rectangles_, cached_energies_, absl::MakeSpan(active_boxes_));
146 if (initial_boxes.size() <= 1) return true;
147
148 Rectangle conflicting_rectangle;
149 std::vector<absl::Span<int>> components =
150 GetOverlappingRectangleComponents(cached_rectangles_, initial_boxes);
151 for (absl::Span<int> boxes : components) {
152 // Computes the size on x and y past which there is no point doing any
153 // energetic reasonning. We do a few iterations since as we filter one size,
154 // we can potentially filter more on the transposed dimension.
155 threshold_x_ = kMaxIntegerValue;
156 threshold_y_ = kMaxIntegerValue;
157 for (int i = 0; i < 3; ++i) {
158 if (!AnalyzeIntervals(/*transpose=*/i == 1, boxes, cached_rectangles_,
159 cached_energies_, &threshold_x_, &threshold_y_,
160 &conflicting_rectangle)) {
161 return ReportEnergyConflict(conflicting_rectangle, boxes, &x_, &y_);
162 }
163 boxes = FilterBoxesAndRandomize(cached_rectangles_, boxes, threshold_x_,
164 threshold_y_, *random_);
165 if (boxes.size() <= 1) break;
166 }
167 if (boxes.size() <= 1) continue;
168
169 // Now that we removed boxes with a large domain, resplit everything.
170 const std::vector<absl::Span<int>> local_components =
171 GetOverlappingRectangleComponents(cached_rectangles_, boxes);
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];
176 }
177 for (const int box : local_boxes) {
179 FailWhenEnergyIsTooLarge(box, local_boxes, total_sum_of_areas));
180 }
181 }
182 }
183
184 return true;
185}
186
188 GenericLiteralWatcher* watcher) {
189 const int id = watcher->Register(this);
190 x_.WatchAllTasks(id, watcher, /*watch_start_max=*/true,
191 /*watch_end_max=*/true);
192 y_.WatchAllTasks(id, watcher, /*watch_start_max=*/true,
193 /*watch_end_max=*/true);
194 return id;
195}
196
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];
201
202 neighbors_.clear();
203 for (const int other_box : local_boxes) {
204 if (other_box == box) continue;
205 const Rectangle& other_dim = cached_rectangles_[other_box];
206 const IntegerValue span_x = std::max(box_dim.x_max, other_dim.x_max) -
207 std::min(box_dim.x_min, other_dim.x_min);
208 if (span_x > threshold_x_) continue;
209 const IntegerValue span_y = std::max(box_dim.y_max, other_dim.y_max) -
210 std::min(box_dim.y_min, other_dim.y_min);
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});
215 }
216 }
217 std::sort(neighbors_.begin(), neighbors_.end());
218}
219
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);
224
225 Rectangle area = cached_rectangles_[box];
226 IntegerValue sum_of_areas = cached_energies_[box];
227
228 const auto add_box_energy_in_rectangle_reason = [&](int b) {
229 x_.AddEnergyMinInIntervalReason(b, area.x_min, area.x_max);
230 y_.AddEnergyMinInIntervalReason(b, area.y_min, area.y_max);
233 };
234
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);
238
239 // Update Bounding box.
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;
243
244 // Update sum of areas.
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) {
249 // Nothing will be deduced. Exiting.
250 return true;
251 }
252
253 if (sum_of_areas > bounding_area) {
254 x_.ClearReason();
255 y_.ClearReason();
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);
259 }
260 x_.ImportOtherReasons(y_);
261 return x_.ReportConflict();
262 }
263 }
264 return true;
265}
266
267namespace {
268
269// We want for different propagation to reuse as much as possible the same
270// line. The idea behind this is to compute the 'canonical' line to use
271// when explaining that boxes overlap on the 'y_dim' dimension. We compute
272// the multiple of the biggest power of two that is common to all boxes.
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);
278 }
279
280 int64_t mask = 0;
281 IntegerValue candidate = ub;
282 for (int o = 0; o < 62; ++o) {
283 mask = 2 * mask + 1;
284 const IntegerValue masked_ub(ub.value() & ~mask);
285 if (masked_ub >= lb) {
286 candidate = masked_ub;
287 } else {
288 break;
289 }
290 }
291 return candidate;
292}
293
294void SplitDisjointBoxes(const SchedulingConstraintHelper& x,
295 absl::Span<int> boxes,
296 std::vector<absl::Span<int>>* result) {
297 result->clear();
298 std::sort(boxes.begin(), boxes.end(), [&x](int a, int b) {
299 return x.ShiftedStartMin(a) < x.ShiftedStartMin(b);
300 });
301 int current_start = 0;
302 std::size_t current_length = 1;
303 IntegerValue current_max_end = x.EndMax(boxes[0]);
304
305 for (int b = 1; b < boxes.size(); ++b) {
306 const int box = boxes[b];
307 if (x.ShiftedStartMin(box) < current_max_end) {
308 // Merge.
309 current_length++;
310 current_max_end = std::max(current_max_end, x.EndMax(box));
311 } else {
312 if (current_length > 1) { // Ignore lists of size 1.
313 result->emplace_back(&boxes[current_start], current_length);
314 }
315 current_start = b;
316 current_length = 1;
317 current_max_end = x.EndMax(box);
318 }
319 }
320
321 // Push last span.
322 if (current_length > 1) {
323 result->emplace_back(&boxes[current_start], current_length);
324 }
325}
326
327} // namespace
328
329// Note that x_ and y_ must be initialized with enough intervals when passed
330// to the disjunctive propagators.
335 Model* model)
336 : global_x_(*x),
337 global_y_(*y),
338 x_(x->NumTasks(), model),
339 strict_(strict),
340 watcher_(model->GetOrCreate<GenericLiteralWatcher>()),
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_) {}
348
351
353 int fast_priority, int slow_priority) {
354 fast_id_ = watcher_->Register(this);
355 watcher_->SetPropagatorPriority(fast_id_, fast_priority);
356 global_x_.WatchAllTasks(fast_id_, watcher_);
357 global_y_.WatchAllTasks(fast_id_, watcher_);
358
359 // This propagator is the one making sure our propagation is complete, so
360 // we do need to make sure it is called again if it modified some bounds.
362
363 const int slow_id = watcher_->Register(this);
364 watcher_->SetPropagatorPriority(slow_id, slow_priority);
365 global_x_.WatchAllTasks(slow_id, watcher_);
366 global_y_.WatchAllTasks(slow_id, watcher_);
367}
368
369bool NonOverlappingRectanglesDisjunctivePropagator::
370 FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
372 std::function<bool()> inner_propagate) {
373 // Note that since we only push bounds on x, we cache the value for y just
374 // once.
375 if (!y->SynchronizeAndSetTimeDirection(true)) return false;
376
377 // Compute relevant boxes, the one with a mandatory part of y. Because we will
378 // need to sort it this way, we consider them by increasing start max.
379 indexed_intervals_.clear();
380 const std::vector<TaskTime>& temp = y->TaskByDecreasingStartMax();
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;
384
385 // Ignore a box if its x interval and its y interval are not present at
386 // the same time.
387 if (!x.IsPresent(box) || !y->IsPresent(box)) continue;
388
389 const IntegerValue start_max = temp[i].time;
390 const IntegerValue end_min = y->EndMin(box);
391 if (start_max < end_min) {
392 indexed_intervals_.push_back({box, start_max, end_min});
393 }
394 }
395
396 // Less than 2 boxes, no propagation.
397 if (indexed_intervals_.size() < 2) return true;
398 ConstructOverlappingSets(/*already_sorted=*/true, &indexed_intervals_,
399 &events_overlapping_boxes_);
400
401 // Split lists of boxes into disjoint set of boxes (w.r.t. overlap).
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]),
406 &disjoint_boxes_);
407 for (absl::Span<int> sub_boxes : disjoint_boxes_) {
408 // Boxes are sorted in a stable manner in the Split method.
409 // Note that we do not use reduced_overlapping_boxes_ directly so that
410 // the order of iteration is deterministic.
411 const auto& insertion = reduced_overlapping_boxes_.insert(sub_boxes);
412 if (insertion.second) boxes_to_propagate_.push_back(sub_boxes);
413 }
414 }
415
416 // And finally propagate.
417 //
418 // TODO(user): Sorting of boxes seems influential on the performance. Test.
419 for (const absl::Span<const int> boxes : boxes_to_propagate_) {
420 x_.ClearOtherHelper();
421 if (!x_.ResetFromSubset(x, boxes)) return false;
422
423 // Collect the common overlapping coordinates of all boxes.
424 IntegerValue lb(std::numeric_limits<int64_t>::min());
425 IntegerValue ub(std::numeric_limits<int64_t>::max());
426 for (const int b : boxes) {
427 lb = std::max(lb, y->StartMax(b));
428 ub = std::min(ub, y->EndMin(b) - 1);
429 }
430 CHECK_LE(lb, ub);
431
432 // TODO(user): We should scan the integer trail to find the oldest
433 // non-empty common interval. Then we can pick the canonical value within
434 // it.
435
436 // We want for different propagation to reuse as much as possible the same
437 // line. The idea behind this is to compute the 'canonical' line to use
438 // when explaining that boxes overlap on the 'y_dim' dimension. We compute
439 // the multiple of the biggest power of two that is common to all boxes.
440 const IntegerValue line_to_use_for_reason = FindCanonicalValue(lb, ub);
441
442 // Setup x_dim for propagation.
443 x_.SetOtherHelper(y, boxes, line_to_use_for_reason);
444
445 RETURN_IF_FALSE(inner_propagate());
446 }
447
448 return true;
449}
450
452 global_x_.SetTimeDirection(true);
453 global_y_.SetTimeDirection(true);
454
455 std::function<bool()> inner_propagate;
456 if (watcher_->GetCurrentId() == fast_id_) {
457 inner_propagate = [this]() {
458 if (x_.NumTasks() == 2) {
459 // In that case, we can use simpler algorithms.
460 // Note that this case happens frequently (~30% of all calls to this
461 // method according to our tests).
462 RETURN_IF_FALSE(PropagateTwoBoxes());
463 } else {
464 RETURN_IF_FALSE(overload_checker_.Propagate());
465 RETURN_IF_FALSE(forward_detectable_precedences_.Propagate());
466 RETURN_IF_FALSE(backward_detectable_precedences_.Propagate());
467 }
468 return true;
469 };
470 } else {
471 inner_propagate = [this]() {
472 if (x_.NumTasks() <= 2) return true;
473 RETURN_IF_FALSE(forward_not_last_.Propagate());
474 RETURN_IF_FALSE(backward_not_last_.Propagate());
475 RETURN_IF_FALSE(backward_edge_finding_.Propagate());
476 RETURN_IF_FALSE(forward_edge_finding_.Propagate());
477 return true;
478 };
479 }
480
481 RETURN_IF_FALSE(FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
482 global_x_, &global_y_, inner_propagate));
483
484 // We can actually swap dimensions to propagate vertically.
485 RETURN_IF_FALSE(FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
486 global_y_, &global_x_, inner_propagate));
487
488 // If two boxes must overlap but do not have a mandatory line/column that
489 // crosses both of them, then the code above do not see it. So we manually
490 // propagate this case.
491 //
492 // TODO(user): Since we are at it, do more propagation even if no conflict?
493 // This rarely propagate, so disabled for now. Investigate if it is worth
494 // it.
495 if (/*DISABLES CODE*/ (false) && watcher_->GetCurrentId() == fast_id_) {
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;
501 if (global_x_.EndMin(box1) <= global_x_.StartMax(box2)) continue;
502 if (global_x_.EndMin(box2) <= global_x_.StartMax(box1)) continue;
503 if (global_y_.EndMin(box1) <= global_y_.StartMax(box2)) continue;
504 if (global_y_.EndMin(box2) <= global_y_.StartMax(box1)) continue;
505
506 // X and Y must overlap. This is a conflict.
507 global_x_.ClearReason();
508 global_x_.AddPresenceReason(box1);
509 global_x_.AddPresenceReason(box2);
510 global_x_.AddReasonForBeingBefore(box1, box2);
511 global_x_.AddReasonForBeingBefore(box2, box1);
512 global_y_.ClearReason();
513 global_y_.AddPresenceReason(box1);
514 global_y_.AddPresenceReason(box2);
515 global_y_.AddReasonForBeingBefore(box1, box2);
516 global_y_.AddReasonForBeingBefore(box2, box1);
517 global_x_.ImportOtherReasons(global_y_);
518 return global_x_.ReportConflict();
519 }
520 }
521 }
522
523 return true;
524}
525
526// Specialized propagation on only two boxes that must intersect with the
527// given y_line_for_reason.
528bool NonOverlappingRectanglesDisjunctivePropagator::PropagateTwoBoxes() {
529 // For each direction and each order, we test if the boxes can be disjoint.
530 const int state =
531 (x_.EndMin(0) <= x_.StartMax(1)) + 2 * (x_.EndMin(1) <= x_.StartMax(0));
532
533 const auto left_box_before_right_box = [this](int left, int right) {
534 // left box pushes right box.
535 const IntegerValue left_end_min = x_.EndMin(left);
536 if (left_end_min > x_.StartMin(right)) {
537 x_.ClearReason();
538 x_.AddPresenceReason(left);
539 x_.AddPresenceReason(right);
540 x_.AddReasonForBeingBefore(left, right);
541 x_.AddEndMinReason(left, left_end_min);
542 RETURN_IF_FALSE(x_.IncreaseStartMin(right, left_end_min));
543 }
544
545 // right box pushes left box.
546 const IntegerValue right_start_max = x_.StartMax(right);
547 if (right_start_max < x_.EndMax(left)) {
548 x_.ClearReason();
549 x_.AddPresenceReason(left);
550 x_.AddPresenceReason(right);
551 x_.AddReasonForBeingBefore(left, right);
552 x_.AddStartMaxReason(right, right_start_max);
553 RETURN_IF_FALSE(x_.DecreaseEndMax(left, right_start_max));
554 }
555
556 return true;
557 };
558
559 switch (state) {
560 case 0: { // Conflict.
561 x_.ClearReason();
562 x_.AddPresenceReason(0);
563 x_.AddPresenceReason(1);
566 return x_.ReportConflict();
567 }
568 case 1: { // b1 is left of b2.
569 return left_box_before_right_box(0, 1);
570 }
571 case 2: { // b2 is left of b1.
572 return left_box_before_right_box(1, 0);
573 }
574 default: { // Nothing to deduce.
575 return true;
576 }
577 }
578}
579
580#undef RETURN_IF_FALSE
581} // namespace sat
582} // 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:704
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:891
#define CHECK_LE(val1, val2)
Definition: base/logging.h:701
void SetPropagatorPriority(int id, int priority)
Definition: integer.cc:2018
int Register(PropagatorInterface *propagator)
Definition: integer.cc:1995
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
NonOverlappingRectanglesDisjunctivePropagator(bool strict, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y, Model *model)
Definition: sat/diffn.cc:332
const std::vector< AffineExpression > & Starts() const
Definition: intervals.h:336
void WatchAllTasks(int id, GenericLiteralWatcher *watcher, bool watch_start_max=true, bool watch_end_max=true) const
Definition: intervals.cc:508
ABSL_MUST_USE_RESULT bool ResetFromSubset(const SchedulingConstraintHelper &other, absl::Span< const int > tasks)
Definition: intervals.cc:218
void AddEndMinReason(int t, IntegerValue lower_bound)
Definition: intervals.h:575
ABSL_MUST_USE_RESULT bool IncreaseStartMin(int t, IntegerValue new_start_min)
Definition: intervals.cc:453
const std::vector< AffineExpression > & Sizes() const
Definition: intervals.h:338
void ImportOtherReasons(const SchedulingConstraintHelper &other_helper)
Definition: intervals.cc:541
void SetOtherHelper(SchedulingConstraintHelper *other_helper, absl::Span< const int > map_to_other_helper, IntegerValue event)
Definition: intervals.h:355
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
Definition: intervals.cc:295
const std::vector< TaskTime > & TaskByDecreasingStartMax()
Definition: intervals.cc:337
void AddEnergyMinInIntervalReason(int t, IntegerValue min, IntegerValue max)
Definition: intervals.h:600
void AddReasonForBeingBefore(int before, int after)
Definition: intervals.cc:382
void AddStartMaxReason(int t, IntegerValue upper_bound)
Definition: intervals.h:568
ABSL_MUST_USE_RESULT bool DecreaseEndMax(int t, IntegerValue new_end_max)
Definition: intervals.cc:462
const std::vector< AffineExpression > & Ends() const
Definition: intervals.h:337
int64_t b
int64_t a
GRBmodel * model
std::function< void(Model *)> WeightedSumGreaterOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
Definition: integer_expr.h:468
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)
Definition: diffn_util.cc:26
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:303
const IntegerVariable kNoIntegerVariable(-1)
std::function< void(Model *)> Cumulative(const std::vector< IntervalVariable > &vars, const std::vector< AffineExpression > &demands, AffineExpression capacity, SchedulingConstraintHelper *helper)
Definition: cumulative.cc:35
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:151
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:30
std::function< void(Model *)> IsEqualToMinOf(IntegerVariable min_var, const std::vector< IntegerVariable > &vars)
Definition: integer_expr.h:737
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
Definition: integer.h:1612
bool ReportEnergyConflict(Rectangle bounding_box, absl::Span< const int > boxes, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y)
Definition: diffn_util.cc:52
void ConstructOverlappingSets(bool already_sorted, std::vector< IndexedInterval > *intervals, std::vector< std::vector< int > > *result)
Definition: diffn_util.cc:346
void AddCumulativeRelaxation(const std::vector< IntervalVariable > &x_intervals, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y, Model *model)
Definition: sat/diffn.cc:80
absl::Span< int > FilterBoxesThatAreTooLarge(const std::vector< Rectangle > &cached_rectangles, const std::vector< IntegerValue > &energies, absl::Span< int > boxes)
Definition: diffn_util.cc:319
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:111
Rev< int64_t > start_max
Rev< int64_t > end_min