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"
23 #include "ortools/base/map_util.h"
24 #include "ortools/base/stl_util.h"
25 #include "ortools/sat/cumulative.h"
26 #include "ortools/sat/diffn_util.h"
28 #include "ortools/sat/intervals.h"
29 #include "ortools/sat/sat_solver.h"
30 #include "ortools/sat/theta_tree.h"
31 #include "ortools/util/sort.h"
32 
33 namespace operations_research {
34 namespace sat {
35 
36 namespace {
37 
38 // TODO(user): Use the faster variable only version if all expressions reduce
39 // to a single variable?
40 void 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 
59 void 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 
80 void 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 
197 void 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 
220 bool 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  x_.AddPresenceReason(b);
232  y_.AddPresenceReason(b);
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 
267 namespace {
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.
273 IntegerValue 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 
294 void 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 
369 bool NonOverlappingRectanglesDisjunctivePropagator::
370  FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
371  bool fast_propagation, const SchedulingConstraintHelper& x,
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  // The case of two boxes should be taken care of during "fast" propagation,
421  // so we can skip it here.
422  if (!fast_propagation && boxes.size() <= 2) continue;
423 
424  x_.ClearOtherHelper();
425  if (!x_.ResetFromSubset(x, boxes)) return false;
426 
427  // Collect the common overlapping coordinates of all boxes.
428  IntegerValue lb(std::numeric_limits<int64_t>::min());
429  IntegerValue ub(std::numeric_limits<int64_t>::max());
430  for (const int b : boxes) {
431  lb = std::max(lb, y->StartMax(b));
432  ub = std::min(ub, y->EndMin(b) - 1);
433  }
434  CHECK_LE(lb, ub);
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  //
441  // TODO(user): We should scan the integer trail to find the oldest
442  // non-empty common interval. Then we can pick the canonical value within
443  // it.
444  const IntegerValue line_to_use_for_reason = FindCanonicalValue(lb, ub);
445 
446  // Setup x_dim for propagation.
447  x_.SetOtherHelper(y, boxes, line_to_use_for_reason);
448 
449  if (fast_propagation) {
450  if (x_.NumTasks() == 2) {
451  // In that case, we can use simpler algorithms.
452  // Note that this case happens frequently (~30% of all calls to this
453  // method according to our tests).
454  RETURN_IF_FALSE(PropagateTwoBoxes());
455  } else {
456  RETURN_IF_FALSE(overload_checker_.Propagate());
457  RETURN_IF_FALSE(forward_detectable_precedences_.Propagate());
458  RETURN_IF_FALSE(backward_detectable_precedences_.Propagate());
459  }
460  } else {
461  DCHECK_GT(x_.NumTasks(), 2);
462  RETURN_IF_FALSE(forward_not_last_.Propagate());
463  RETURN_IF_FALSE(backward_not_last_.Propagate());
464  RETURN_IF_FALSE(backward_edge_finding_.Propagate());
465  RETURN_IF_FALSE(forward_edge_finding_.Propagate());
466  }
467  }
468 
469  return true;
470 }
471 
473  global_x_.SetTimeDirection(true);
474  global_y_.SetTimeDirection(true);
475 
476  // Note that the code assumes that this was registered twice in fast and slow
477  // mode. So we will not redo some propagation in slow mode that was already
478  // done by the fast mode.
479  const bool fast_propagation = watcher_->GetCurrentId() == fast_id_;
480  RETURN_IF_FALSE(FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
481  fast_propagation, global_x_, &global_y_));
482 
483  // We can actually swap dimensions to propagate vertically.
484  RETURN_IF_FALSE(FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
485  fast_propagation, global_y_, &global_x_));
486 
487  // If two boxes must overlap but do not have a mandatory line/column that
488  // crosses both of them, then the code above do not see it. So we manually
489  // propagate this case.
490  //
491  // TODO(user): Since we are at it, do more propagation even if no conflict?
492  // This rarely propagate, so disabled for now. Investigate if it is worth
493  // it.
494  if (/*DISABLES CODE*/ (false) && watcher_->GetCurrentId() == fast_id_) {
495  const int num_boxes = global_x_.NumTasks();
496  for (int box1 = 0; box1 < num_boxes; ++box1) {
497  if (!global_x_.IsPresent(box1)) continue;
498  for (int box2 = box1 + 1; box2 < num_boxes; ++box2) {
499  if (!global_x_.IsPresent(box2)) continue;
500  if (global_x_.EndMin(box1) <= global_x_.StartMax(box2)) continue;
501  if (global_x_.EndMin(box2) <= global_x_.StartMax(box1)) continue;
502  if (global_y_.EndMin(box1) <= global_y_.StartMax(box2)) continue;
503  if (global_y_.EndMin(box2) <= global_y_.StartMax(box1)) continue;
504 
505  // X and Y must overlap. This is a conflict.
506  global_x_.ClearReason();
507  global_x_.AddPresenceReason(box1);
508  global_x_.AddPresenceReason(box2);
509  global_x_.AddReasonForBeingBefore(box1, box2);
510  global_x_.AddReasonForBeingBefore(box2, box1);
511  global_y_.ClearReason();
512  global_y_.AddPresenceReason(box1);
513  global_y_.AddPresenceReason(box2);
514  global_y_.AddReasonForBeingBefore(box1, box2);
515  global_y_.AddReasonForBeingBefore(box2, box1);
516  global_x_.ImportOtherReasons(global_y_);
517  return global_x_.ReportConflict();
518  }
519  }
520  }
521 
522  return true;
523 }
524 
525 // Specialized propagation on only two boxes that must intersect with the
526 // given y_line_for_reason.
527 bool NonOverlappingRectanglesDisjunctivePropagator::PropagateTwoBoxes() {
528  // For each direction and each order, we test if the boxes can be disjoint.
529  const int state =
530  (x_.EndMin(0) <= x_.StartMax(1)) + 2 * (x_.EndMin(1) <= x_.StartMax(0));
531 
532  const auto left_box_before_right_box = [this](int left, int right) {
533  // left box pushes right box.
534  const IntegerValue left_end_min = x_.EndMin(left);
535  if (left_end_min > x_.StartMin(right)) {
536  x_.ClearReason();
537  x_.AddPresenceReason(left);
538  x_.AddPresenceReason(right);
539  x_.AddReasonForBeingBefore(left, right);
540  x_.AddEndMinReason(left, left_end_min);
541  RETURN_IF_FALSE(x_.IncreaseStartMin(right, left_end_min));
542  }
543 
544  // right box pushes left box.
545  const IntegerValue right_start_max = x_.StartMax(right);
546  if (right_start_max < x_.EndMax(left)) {
547  x_.ClearReason();
548  x_.AddPresenceReason(left);
549  x_.AddPresenceReason(right);
550  x_.AddReasonForBeingBefore(left, right);
551  x_.AddStartMaxReason(right, right_start_max);
552  RETURN_IF_FALSE(x_.DecreaseEndMax(left, right_start_max));
553  }
554 
555  return true;
556  };
557 
558  switch (state) {
559  case 0: { // Conflict.
560  x_.ClearReason();
561  x_.AddPresenceReason(0);
562  x_.AddPresenceReason(1);
563  x_.AddReasonForBeingBefore(0, 1);
564  x_.AddReasonForBeingBefore(1, 0);
565  return x_.ReportConflict();
566  }
567  case 1: { // b1 is left of b2.
568  return left_box_before_right_box(0, 1);
569  }
570  case 2: { // b2 is left of b1.
571  return left_box_before_right_box(1, 0);
572  }
573  default: { // Nothing to deduce.
574  return true;
575  }
576  }
577 }
578 
579 #undef RETURN_IF_FALSE
580 } // namespace sat
581 } // namespace operations_research
int64_t CapSub(int64_t x, int64_t y)
int64_t min
Definition: alldiff_cst.cc:139
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
Definition: intervals.cc:295
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
#define CHECK_GT(val1, val2)
Definition: base/logging.h:707
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
void SetOtherHelper(SchedulingConstraintHelper *other_helper, absl::Span< const int > map_to_other_helper, IntegerValue event)
Definition: intervals.h:355
void AddReasonForBeingBefore(int before, int after)
Definition: intervals.cc:382
GRBmodel * model
void WatchAllTasks(int id, GenericLiteralWatcher *watcher, bool watch_start_max=true, bool watch_end_max=true) const
Definition: intervals.cc:508
void ImportOtherReasons(const SchedulingConstraintHelper &other_helper)
Definition: intervals.cc:541
ABSL_MUST_USE_RESULT bool IncreaseStartMin(int t, IntegerValue new_start_min)
Definition: intervals.cc:453
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:895
void AddStartMaxReason(int t, IntegerValue upper_bound)
Definition: intervals.h:568
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
void AddCumulativeRelaxation(const std::vector< IntervalVariable > &x_intervals, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y, Model *model)
Definition: sat/diffn.cc:80
void AddEndMinReason(int t, IntegerValue lower_bound)
Definition: intervals.h:575
int64_t b
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
Definition: integer.h:1612
int64_t max
Definition: alldiff_cst.cc:140
std::function< void(Model *)> IsEqualToMinOf(IntegerVariable min_var, const std::vector< IntegerVariable > &vars)
Definition: integer_expr.h:737
Rev< int64_t > start_max
ABSL_MUST_USE_RESULT bool DecreaseEndMax(int t, IntegerValue new_end_max)
Definition: intervals.cc:462
#define CHECK_LE(val1, val2)
Definition: base/logging.h:704
Rev< int64_t > end_min
void AddEnergyMinInIntervalReason(int t, IntegerValue min, IntegerValue max)
Definition: intervals.h:600
void SetPropagatorPriority(int id, int priority)
Definition: integer.cc:2018
int64_t capacity
void ConstructOverlappingSets(bool already_sorted, std::vector< IndexedInterval > *intervals, std::vector< std::vector< int >> *result)
Definition: diffn_util.cc:346
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:894
std::vector< absl::Span< int > > GetOverlappingRectangleComponents(const std::vector< Rectangle > &rectangles, absl::Span< int > active_rectangles)
Definition: diffn_util.cc:26
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
const std::vector< AffineExpression > & Ends() const
Definition: intervals.h:337
#define RETURN_IF_FALSE(f)
Definition: sat/diffn.cc:111
bool ReportEnergyConflict(Rectangle bounding_box, absl::Span< const int > boxes, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y)
Definition: diffn_util.cc:52
const std::vector< AffineExpression > & Starts() const
Definition: intervals.h:336
int Register(PropagatorInterface *propagator)
Definition: integer.cc:1995
NonOverlappingRectanglesDisjunctivePropagator(bool strict, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y, Model *model)
Definition: sat/diffn.cc:332
Collection of objects used to extend the Constraint Solver library.
const IntegerVariable kNoIntegerVariable(-1)
absl::Span< int > FilterBoxesThatAreTooLarge(const std::vector< Rectangle > &cached_rectangles, const std::vector< IntegerValue > &energies, absl::Span< int > boxes)
Definition: diffn_util.cc:319
std::function< void(Model *)> WeightedSumGreaterOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
Definition: integer_expr.h:468
ABSL_MUST_USE_RESULT bool ResetFromSubset(const SchedulingConstraintHelper &other, absl::Span< const int > tasks)
Definition: intervals.cc:218
const std::vector< TaskTime > & TaskByDecreasingStartMax()
Definition: intervals.cc:337
std::function< void(Model *)> Cumulative(const std::vector< IntervalVariable > &vars, const std::vector< AffineExpression > &demands, AffineExpression capacity, SchedulingConstraintHelper *helper=nullptr)
Definition: cumulative.h:45
const std::vector< AffineExpression > & Sizes() const
Definition: intervals.h:338
int64_t a