OR-Tools  9.1
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);
231 };
232
233 for (int i = 0; i < neighbors_.size(); ++i) {
234 const int other_box = neighbors_[i].box;
235 CHECK_GT(cached_energies_[other_box], 0);
236
237 // Update Bounding box.
238 area.TakeUnionWith(cached_rectangles_[other_box]);
239 if (area.x_max - area.x_min > threshold_x_) break;
240 if (area.y_max - area.y_min > threshold_y_) break;
241
242 // Update sum of areas.
243 sum_of_areas += cached_energies_[other_box];
244 const IntegerValue bounding_area =
245 (area.x_max - area.x_min) * (area.y_max - area.y_min);
246 if (bounding_area >= total_sum_of_areas) {
247 // Nothing will be deduced. Exiting.
248 return true;
249 }
250
251 if (sum_of_areas > bounding_area) {
252 x_.ClearReason();
253 y_.ClearReason();
254 add_box_energy_in_rectangle_reason(box);
255 for (int j = 0; j <= i; ++j) {
256 add_box_energy_in_rectangle_reason(neighbors_[j].box);
257 }
258 x_.ImportOtherReasons(y_);
259 return x_.ReportConflict();
260 }
261 }
262 return true;
263}
264
265namespace {
266
267// We want for different propagation to reuse as much as possible the same
268// line. The idea behind this is to compute the 'canonical' line to use
269// when explaining that boxes overlap on the 'y_dim' dimension. We compute
270// the multiple of the biggest power of two that is common to all boxes.
271IntegerValue FindCanonicalValue(IntegerValue lb, IntegerValue ub) {
272 if (lb == ub) return lb;
273 if (lb <= 0 && ub > 0) return IntegerValue(0);
274 if (lb < 0 && ub <= 0) {
275 return -FindCanonicalValue(-ub, -lb);
276 }
277
278 int64_t mask = 0;
279 IntegerValue candidate = ub;
280 for (int o = 0; o < 62; ++o) {
281 mask = 2 * mask + 1;
282 const IntegerValue masked_ub(ub.value() & ~mask);
283 if (masked_ub >= lb) {
284 candidate = masked_ub;
285 } else {
286 break;
287 }
288 }
289 return candidate;
290}
291
292void SplitDisjointBoxes(const SchedulingConstraintHelper& x,
293 absl::Span<int> boxes,
294 std::vector<absl::Span<int>>* result) {
295 result->clear();
296 std::sort(boxes.begin(), boxes.end(), [&x](int a, int b) {
297 return x.ShiftedStartMin(a) < x.ShiftedStartMin(b);
298 });
299 int current_start = 0;
300 std::size_t current_length = 1;
301 IntegerValue current_max_end = x.EndMax(boxes[0]);
302
303 for (int b = 1; b < boxes.size(); ++b) {
304 const int box = boxes[b];
305 if (x.ShiftedStartMin(box) < current_max_end) {
306 // Merge.
307 current_length++;
308 current_max_end = std::max(current_max_end, x.EndMax(box));
309 } else {
310 if (current_length > 1) { // Ignore lists of size 1.
311 result->emplace_back(&boxes[current_start], current_length);
312 }
313 current_start = b;
314 current_length = 1;
315 current_max_end = x.EndMax(box);
316 }
317 }
318
319 // Push last span.
320 if (current_length > 1) {
321 result->emplace_back(&boxes[current_start], current_length);
322 }
323}
324
325} // namespace
326
327// Note that x_ and y_ must be initialized with enough intervals when passed
328// to the disjunctive propagators.
333 Model* model)
334 : global_x_(*x),
335 global_y_(*y),
336 x_(x->NumTasks(), model),
337 strict_(strict),
338 watcher_(model->GetOrCreate<GenericLiteralWatcher>()),
339 overload_checker_(&x_),
340 forward_detectable_precedences_(true, &x_),
341 backward_detectable_precedences_(false, &x_),
342 forward_not_last_(true, &x_),
343 backward_not_last_(false, &x_),
344 forward_edge_finding_(true, &x_),
345 backward_edge_finding_(false, &x_) {}
346
349
351 int fast_priority, int slow_priority) {
352 fast_id_ = watcher_->Register(this);
353 watcher_->SetPropagatorPriority(fast_id_, fast_priority);
354 global_x_.WatchAllTasks(fast_id_, watcher_);
355 global_y_.WatchAllTasks(fast_id_, watcher_);
356
357 // This propagator is the one making sure our propagation is complete, so
358 // we do need to make sure it is called again if it modified some bounds.
360
361 const int slow_id = watcher_->Register(this);
362 watcher_->SetPropagatorPriority(slow_id, slow_priority);
363 global_x_.WatchAllTasks(slow_id, watcher_);
364 global_y_.WatchAllTasks(slow_id, watcher_);
365}
366
367bool NonOverlappingRectanglesDisjunctivePropagator::
368 FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
370 std::function<bool()> inner_propagate) {
371 // Note that since we only push bounds on x, we cache the value for y just
372 // once.
373 if (!y->SynchronizeAndSetTimeDirection(true)) return false;
374
375 // Compute relevant boxes, the one with a mandatory part of y. Because we will
376 // need to sort it this way, we consider them by increasing start max.
377 indexed_intervals_.clear();
378 const std::vector<TaskTime>& temp = y->TaskByDecreasingStartMax();
379 for (int i = temp.size(); --i >= 0;) {
380 const int box = temp[i].task_index;
381 if (!strict_ && (x.SizeMin(box) == 0 || y->SizeMin(box) == 0)) continue;
382
383 // Ignore a box if its x interval and its y interval are not present at
384 // the same time.
385 if (!x.IsPresent(box) || !y->IsPresent(box)) continue;
386
387 const IntegerValue start_max = temp[i].time;
388 const IntegerValue end_min = y->EndMin(box);
389 if (start_max < end_min) {
390 indexed_intervals_.push_back({box, start_max, end_min});
391 }
392 }
393
394 // Less than 2 boxes, no propagation.
395 if (indexed_intervals_.size() < 2) return true;
396 ConstructOverlappingSets(/*already_sorted=*/true, &indexed_intervals_,
397 &events_overlapping_boxes_);
398
399 // Split lists of boxes into disjoint set of boxes (w.r.t. overlap).
400 boxes_to_propagate_.clear();
401 reduced_overlapping_boxes_.clear();
402 for (int i = 0; i < events_overlapping_boxes_.size(); ++i) {
403 SplitDisjointBoxes(x, absl::MakeSpan(events_overlapping_boxes_[i]),
404 &disjoint_boxes_);
405 for (absl::Span<int> sub_boxes : disjoint_boxes_) {
406 // Boxes are sorted in a stable manner in the Split method.
407 // Note that we do not use reduced_overlapping_boxes_ directly so that
408 // the order of iteration is deterministic.
409 const auto& insertion = reduced_overlapping_boxes_.insert(sub_boxes);
410 if (insertion.second) boxes_to_propagate_.push_back(sub_boxes);
411 }
412 }
413
414 // And finally propagate.
415 //
416 // TODO(user): Sorting of boxes seems influential on the performance. Test.
417 for (const absl::Span<const int> boxes : boxes_to_propagate_) {
418 x_.ClearOtherHelper();
419 if (!x_.ResetFromSubset(x, boxes)) return false;
420
421 // Collect the common overlapping coordinates of all boxes.
422 IntegerValue lb(std::numeric_limits<int64_t>::min());
423 IntegerValue ub(std::numeric_limits<int64_t>::max());
424 for (const int b : boxes) {
425 lb = std::max(lb, y->StartMax(b));
426 ub = std::min(ub, y->EndMin(b) - 1);
427 }
428 CHECK_LE(lb, ub);
429
430 // TODO(user): We should scan the integer trail to find the oldest
431 // non-empty common interval. Then we can pick the canonical value within
432 // it.
433
434 // We want for different propagation to reuse as much as possible the same
435 // line. The idea behind this is to compute the 'canonical' line to use
436 // when explaining that boxes overlap on the 'y_dim' dimension. We compute
437 // the multiple of the biggest power of two that is common to all boxes.
438 const IntegerValue line_to_use_for_reason = FindCanonicalValue(lb, ub);
439
440 // Setup x_dim for propagation.
441 x_.SetOtherHelper(y, boxes, line_to_use_for_reason);
442
443 RETURN_IF_FALSE(inner_propagate());
444 }
445
446 return true;
447}
448
450 global_x_.SetTimeDirection(true);
451 global_y_.SetTimeDirection(true);
452
453 std::function<bool()> inner_propagate;
454 if (watcher_->GetCurrentId() == fast_id_) {
455 inner_propagate = [this]() {
456 if (x_.NumTasks() == 2) {
457 // In that case, we can use simpler algorithms.
458 // Note that this case happens frequently (~30% of all calls to this
459 // method according to our tests).
460 RETURN_IF_FALSE(PropagateTwoBoxes());
461 } else {
462 RETURN_IF_FALSE(overload_checker_.Propagate());
463 RETURN_IF_FALSE(forward_detectable_precedences_.Propagate());
464 RETURN_IF_FALSE(backward_detectable_precedences_.Propagate());
465 }
466 return true;
467 };
468 } else {
469 inner_propagate = [this]() {
470 if (x_.NumTasks() <= 2) return true;
471 RETURN_IF_FALSE(forward_not_last_.Propagate());
472 RETURN_IF_FALSE(backward_not_last_.Propagate());
473 RETURN_IF_FALSE(backward_edge_finding_.Propagate());
474 RETURN_IF_FALSE(forward_edge_finding_.Propagate());
475 return true;
476 };
477 }
478
479 RETURN_IF_FALSE(FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
480 global_x_, &global_y_, inner_propagate));
481
482 // We can actually swap dimensions to propagate vertically.
483 RETURN_IF_FALSE(FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
484 global_y_, &global_x_, inner_propagate));
485
486 // If two boxes must overlap but do not have a mandatory line/column that
487 // crosses both of them, then the code above do not see it. So we manually
488 // propagate this case.
489 //
490 // TODO(user): Since we are at it, do more propagation even if no conflict?
491 // This rarely propagate, so disabled for now. Investigate if it is worth
492 // it.
493 if (/*DISABLES CODE*/ (false) && watcher_->GetCurrentId() == fast_id_) {
494 const int num_boxes = global_x_.NumTasks();
495 for (int box1 = 0; box1 < num_boxes; ++box1) {
496 if (!global_x_.IsPresent(box1)) continue;
497 for (int box2 = box1 + 1; box2 < num_boxes; ++box2) {
498 if (!global_x_.IsPresent(box2)) continue;
499 if (global_x_.EndMin(box1) <= global_x_.StartMax(box2)) continue;
500 if (global_x_.EndMin(box2) <= global_x_.StartMax(box1)) continue;
501 if (global_y_.EndMin(box1) <= global_y_.StartMax(box2)) continue;
502 if (global_y_.EndMin(box2) <= global_y_.StartMax(box1)) continue;
503
504 // X and Y must overlap. This is a conflict.
505 global_x_.ClearReason();
506 global_x_.AddPresenceReason(box1);
507 global_x_.AddPresenceReason(box2);
508 global_x_.AddReasonForBeingBefore(box1, box2);
509 global_x_.AddReasonForBeingBefore(box2, box1);
510 global_y_.ClearReason();
511 global_y_.AddReasonForBeingBefore(box1, box2);
512 global_y_.AddReasonForBeingBefore(box2, box1);
513 global_x_.ImportOtherReasons(global_y_);
514 return global_x_.ReportConflict();
515 }
516 }
517 }
518
519 return true;
520}
521
522// Specialized propagation on only two boxes that must intersect with the
523// given y_line_for_reason.
524bool NonOverlappingRectanglesDisjunctivePropagator::PropagateTwoBoxes() {
525 // For each direction and each order, we test if the boxes can be disjoint.
526 const int state =
527 (x_.EndMin(0) <= x_.StartMax(1)) + 2 * (x_.EndMin(1) <= x_.StartMax(0));
528
529 const auto left_box_before_right_box = [this](int left, int right) {
530 // left box pushes right box.
531 const IntegerValue left_end_min = x_.EndMin(left);
532 if (left_end_min > x_.StartMin(right)) {
533 x_.ClearReason();
534 x_.AddReasonForBeingBefore(left, right);
535 x_.AddEndMinReason(left, left_end_min);
536 RETURN_IF_FALSE(x_.IncreaseStartMin(right, left_end_min));
537 }
538
539 // right box pushes left box.
540 const IntegerValue right_start_max = x_.StartMax(right);
541 if (right_start_max < x_.EndMax(left)) {
542 x_.ClearReason();
543 x_.AddReasonForBeingBefore(left, right);
544 x_.AddStartMaxReason(right, right_start_max);
545 RETURN_IF_FALSE(x_.DecreaseEndMax(left, right_start_max));
546 }
547
548 return true;
549 };
550
551 switch (state) {
552 case 0: { // Conflict.
553 x_.ClearReason();
556 return x_.ReportConflict();
557 }
558 case 1: { // b1 is left of b2.
559 return left_box_before_right_box(0, 1);
560 }
561 case 2: { // b2 is left of b1.
562 return left_box_before_right_box(1, 0);
563 }
564 default: { // Nothing to deduce.
565 return true;
566 }
567 }
568}
569
570#undef RETURN_IF_FALSE
571} // namespace sat
572} // 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:703
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:890
#define CHECK_LE(val1, val2)
Definition: base/logging.h:700
void SetPropagatorPriority(int id, int priority)
Definition: integer.cc:2019
int Register(PropagatorInterface *propagator)
Definition: integer.cc:1996
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:330
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:405
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:300
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:148
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:29
std::function< void(Model *)> IsEqualToMinOf(IntegerVariable min_var, const std::vector< IntegerVariable > &vars)
Definition: integer_expr.h:673
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
Definition: integer.h:1483
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:343
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:316
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