OR-Tools  9.0
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"
27 #include "ortools/sat/intervals.h"
28 #include "ortools/sat/sat_solver.h"
29 #include "ortools/sat/theta_tree.h"
30 #include "ortools/util/sort.h"
31 
32 namespace operations_research {
33 namespace sat {
34 
35 namespace {
36 
37 // TODO(user): Use the faster variable only version if all expressions reduce
38 // to a single variable?
39 void AddIsEqualToMinOf(IntegerVariable min_var,
40  const std::vector<AffineExpression>& exprs,
41  Model* model) {
42  std::vector<LinearExpression> converted;
43  for (const AffineExpression& affine : exprs) {
44  LinearExpression e;
45  e.offset = affine.constant;
46  if (affine.var != kNoIntegerVariable) {
47  e.vars.push_back(affine.var);
48  e.coeffs.push_back(affine.coeff);
49  }
50  converted.push_back(e);
51  }
52  LinearExpression target;
53  target.vars.push_back(min_var);
54  target.coeffs.push_back(IntegerValue(1));
55  model->Add(IsEqualToMinOf(target, converted));
56 }
57 
58 void AddIsEqualToMaxOf(IntegerVariable max_var,
59  const std::vector<AffineExpression>& exprs,
60  Model* model) {
61  std::vector<LinearExpression> converted;
62  for (const AffineExpression& affine : exprs) {
63  LinearExpression e;
64  e.offset = affine.constant;
65  if (affine.var != kNoIntegerVariable) {
66  e.vars.push_back(affine.var);
67  e.coeffs.push_back(affine.coeff);
68  }
69  converted.push_back(NegationOf(e));
70  }
71  LinearExpression target;
72  target.vars.push_back(NegationOf(max_var));
73  target.coeffs.push_back(IntegerValue(1));
74  model->Add(IsEqualToMinOf(target, converted));
75 }
76 
77 } // namespace
78 
79 void AddCumulativeRelaxation(const std::vector<IntervalVariable>& x_intervals,
82  int64_t min_starts = std::numeric_limits<int64_t>::max();
83  int64_t max_ends = std::numeric_limits<int64_t>::min();
84  std::vector<AffineExpression> sizes;
85  for (int box = 0; box < y->NumTasks(); ++box) {
86  min_starts = std::min(min_starts, y->StartMin(box).value());
87  max_ends = std::max(max_ends, y->EndMax(box).value());
88  sizes.push_back(y->Sizes()[box]);
89  }
90 
91  const IntegerVariable min_start_var =
92  model->Add(NewIntegerVariable(min_starts, max_ends));
93  AddIsEqualToMinOf(min_start_var, y->Starts(), model);
94 
95  const IntegerVariable max_end_var =
96  model->Add(NewIntegerVariable(min_starts, max_ends));
97  AddIsEqualToMaxOf(max_end_var, y->Ends(), model);
98 
99  // (max_end - min_start) >= capacity.
101  model->Add(NewIntegerVariable(0, CapSub(max_ends, min_starts))));
102  const std::vector<int64_t> coeffs = {-capacity.coeff.value(), -1, 1};
103  model->Add(
104  WeightedSumGreaterOrEqual({capacity.var, min_start_var, max_end_var},
105  coeffs, capacity.constant.value()));
106 
107  model->Add(Cumulative(x_intervals, sizes, capacity, x));
108 }
109 
110 namespace {
111 
112 // We want for different propagation to reuse as much as possible the same
113 // line. The idea behind this is to compute the 'canonical' line to use
114 // when explaining that boxes overlap on the 'y_dim' dimension. We compute
115 // the multiple of the biggest power of two that is common to all boxes.
116 IntegerValue FindCanonicalValue(IntegerValue lb, IntegerValue ub) {
117  if (lb == ub) return lb;
118  if (lb <= 0 && ub > 0) return IntegerValue(0);
119  if (lb < 0 && ub <= 0) {
120  return -FindCanonicalValue(-ub, -lb);
121  }
122 
123  int64_t mask = 0;
124  IntegerValue candidate = ub;
125  for (int o = 0; o < 62; ++o) {
126  mask = 2 * mask + 1;
127  const IntegerValue masked_ub(ub.value() & ~mask);
128  if (masked_ub >= lb) {
129  candidate = masked_ub;
130  } else {
131  break;
132  }
133  }
134  return candidate;
135 }
136 
137 void SplitDisjointBoxes(const SchedulingConstraintHelper& x,
138  absl::Span<int> boxes,
139  std::vector<absl::Span<int>>* result) {
140  result->clear();
141  std::sort(boxes.begin(), boxes.end(),
142  [&x](int a, int b) { return x.StartMin(a) < x.StartMin(b); });
143  int current_start = 0;
144  std::size_t current_length = 1;
145  IntegerValue current_max_end = x.EndMax(boxes[0]);
146 
147  for (int b = 1; b < boxes.size(); ++b) {
148  const int box = boxes[b];
149  if (x.StartMin(box) < current_max_end) {
150  // Merge.
151  current_length++;
152  current_max_end = std::max(current_max_end, x.EndMax(box));
153  } else {
154  if (current_length > 1) { // Ignore lists of size 1.
155  result->emplace_back(&boxes[current_start], current_length);
156  }
157  current_start = b;
158  current_length = 1;
159  current_max_end = x.EndMax(box);
160  }
161  }
162 
163  // Push last span.
164  if (current_length > 1) {
165  result->emplace_back(&boxes[current_start], current_length);
166  }
167 }
168 
169 } // namespace
170 
171 #define RETURN_IF_FALSE(f) \
172  if (!(f)) return false;
173 
176 
178  const int num_boxes = x_.NumTasks();
181 
182  active_boxes_.clear();
183  cached_areas_.resize(num_boxes);
184  cached_dimensions_.resize(num_boxes);
185  for (int box = 0; box < num_boxes; ++box) {
186  cached_areas_[box] = x_.SizeMin(box) * y_.SizeMin(box);
187  if (cached_areas_[box] == 0) continue;
188 
189  // TODO(user): Also consider shifted end max.
190  Dimension& dimension = cached_dimensions_[box];
191  dimension.x_min = x_.ShiftedStartMin(box);
192  dimension.x_max = x_.EndMax(box);
193  dimension.y_min = y_.ShiftedStartMin(box);
194  dimension.y_max = y_.EndMax(box);
195 
196  active_boxes_.push_back(box);
197  }
198  if (active_boxes_.size() <= 1) return true;
199 
200  SplitDisjointBoxes(x_, absl::MakeSpan(active_boxes_), &x_split_);
201  for (absl::Span<int> x_boxes : x_split_) {
202  SplitDisjointBoxes(y_, x_boxes, &y_split_);
203  for (absl::Span<int> y_boxes : y_split_) {
204  IntegerValue total_sum_of_areas(0);
205  for (const int box : y_boxes) {
206  total_sum_of_areas += cached_areas_[box];
207  }
208  for (const int box : y_boxes) {
210  FailWhenEnergyIsTooLarge(box, y_boxes, total_sum_of_areas));
211  }
212  }
213  }
214 
215  return true;
216 }
217 
219  GenericLiteralWatcher* watcher) {
220  const int id = watcher->Register(this);
221  x_.WatchAllTasks(id, watcher, /*watch_start_max=*/false,
222  /*watch_end_max=*/true);
223  y_.WatchAllTasks(id, watcher, /*watch_start_max=*/false,
224  /*watch_end_max=*/true);
225  return id;
226 }
227 
228 void NonOverlappingRectanglesEnergyPropagator::SortBoxesIntoNeighbors(
229  int box, absl::Span<const int> local_boxes,
230  IntegerValue total_sum_of_areas) {
231  const Dimension& box_dim = cached_dimensions_[box];
232 
233  neighbors_.clear();
234  for (const int other_box : local_boxes) {
235  if (other_box == box) continue;
236  const Dimension& other_dim = cached_dimensions_[other_box];
237  const IntegerValue span_x = std::max(box_dim.x_max, other_dim.x_max) -
238  std::min(box_dim.x_min, other_dim.x_min);
239  const IntegerValue span_y = std::max(box_dim.y_max, other_dim.y_max) -
240  std::min(box_dim.y_min, other_dim.y_min);
241  const IntegerValue bounding_area = span_x * span_y;
242  if (bounding_area < total_sum_of_areas) {
243  neighbors_.push_back({other_box, bounding_area});
244  }
245  }
246  std::sort(neighbors_.begin(), neighbors_.end());
247 }
248 
249 bool NonOverlappingRectanglesEnergyPropagator::FailWhenEnergyIsTooLarge(
250  int box, absl::Span<const int> local_boxes,
251  IntegerValue total_sum_of_areas) {
252  SortBoxesIntoNeighbors(box, local_boxes, total_sum_of_areas);
253 
254  Dimension area = cached_dimensions_[box];
255  IntegerValue sum_of_areas = cached_areas_[box];
256 
257  const auto add_box_energy_in_rectangle_reason = [&](int b) {
258  x_.AddEnergyAfterReason(b, x_.SizeMin(b), area.x_min);
259  x_.AddEndMaxReason(b, area.x_max);
260  y_.AddEnergyAfterReason(b, y_.SizeMin(b), area.y_min);
261  y_.AddEndMaxReason(b, area.y_max);
262  };
263 
264  for (int i = 0; i < neighbors_.size(); ++i) {
265  const int other_box = neighbors_[i].box;
266  CHECK_GT(cached_areas_[other_box], 0);
267 
268  // Update Bounding box.
269  area.TakeUnionWith(cached_dimensions_[other_box]);
270 
271  // Update sum of areas.
272  sum_of_areas += cached_areas_[other_box];
273  const IntegerValue bounding_area =
274  (area.x_max - area.x_min) * (area.y_max - area.y_min);
275  if (bounding_area >= total_sum_of_areas) {
276  // Nothing will be deduced. Exiting.
277  return true;
278  }
279 
280  if (sum_of_areas > bounding_area) {
281  x_.ClearReason();
282  y_.ClearReason();
283  add_box_energy_in_rectangle_reason(box);
284  for (int j = 0; j <= i; ++j) {
285  add_box_energy_in_rectangle_reason(neighbors_[j].box);
286  }
287  x_.ImportOtherReasons(y_);
288  return x_.ReportConflict();
289  }
290  }
291  return true;
292 }
293 
294 // Note that x_ and y_ must be initialized with enough intervals when passed
295 // to the disjunctive propagators.
300  Model* model)
301  : global_x_(*x),
302  global_y_(*y),
303  x_(x->NumTasks(), model),
304  y_(y->NumTasks(), model),
305  strict_(strict),
306  watcher_(model->GetOrCreate<GenericLiteralWatcher>()),
307  overload_checker_(&x_),
308  forward_detectable_precedences_(true, &x_),
309  backward_detectable_precedences_(false, &x_),
310  forward_not_last_(true, &x_),
311  backward_not_last_(false, &x_),
312  forward_edge_finding_(true, &x_),
313  backward_edge_finding_(false, &x_) {}
314 
317 
319  int fast_priority, int slow_priority) {
320  fast_id_ = watcher_->Register(this);
321  watcher_->SetPropagatorPriority(fast_id_, fast_priority);
322  global_x_.WatchAllTasks(fast_id_, watcher_);
323  global_y_.WatchAllTasks(fast_id_, watcher_);
324 
325  // This propagator is the one making sure our propagation is complete, so
326  // we do need to make sure it is called again if it modified some bounds.
328 
329  const int slow_id = watcher_->Register(this);
330  watcher_->SetPropagatorPriority(slow_id, slow_priority);
331  global_x_.WatchAllTasks(slow_id, watcher_);
332  global_y_.WatchAllTasks(slow_id, watcher_);
333 }
334 
335 bool NonOverlappingRectanglesDisjunctivePropagator::
336  FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
339  std::function<bool()> inner_propagate) {
340  // Compute relevant events (line in the y dimension).
341  active_boxes_.clear();
342  events_time_.clear();
343  for (int box = 0; box < x.NumTasks(); ++box) {
344  if (!strict_ && (x.SizeMin(box) == 0 || y.SizeMin(box) == 0)) {
345  continue;
346  }
347 
348  const IntegerValue start_max = y.StartMax(box);
349  const IntegerValue end_min = y.EndMin(box);
350  if (start_max < end_min) {
351  events_time_.push_back(start_max);
352  active_boxes_.push_back(box);
353  }
354  }
355 
356  // Less than 2 boxes, no propagation.
357  if (active_boxes_.size() < 2) return true;
358 
359  // Add boxes to the event lists they always overlap with.
360  gtl::STLSortAndRemoveDuplicates(&events_time_);
361  events_overlapping_boxes_.resize(events_time_.size());
362  for (int i = 0; i < events_time_.size(); ++i) {
363  events_overlapping_boxes_[i].clear();
364  }
365  for (const int box : active_boxes_) {
366  const IntegerValue start_max = y.StartMax(box);
367  const IntegerValue end_min = y.EndMin(box);
368 
369  for (int i = 0; i < events_time_.size(); ++i) {
370  const IntegerValue t = events_time_[i];
371  if (t < start_max) continue;
372  if (t >= end_min) break;
373  events_overlapping_boxes_[i].push_back(box);
374  }
375  }
376 
377  // Scan events chronologically to remove events where there is only one
378  // mandatory box, or dominated events lists.
379  //
380  // Optimization: We do not resize the events_overlapping_boxes_ vector so that
381  // we do not free/realloc the memory of the inner vector from one propagate to
382  // the next. This save a bit more than 1%.
383  int new_size = 0;
384  {
385  for (std::vector<int>& overlapping_boxes : events_overlapping_boxes_) {
386  if (overlapping_boxes.size() < 2) {
387  continue; // Remove current event.
388  }
389  if (new_size > 0) {
390  const std::vector<int>& previous_overlapping_boxes =
391  events_overlapping_boxes_[new_size - 1];
392 
393  // If the previous set of boxes is included in the current one, replace
394  // the old one by the new one.
395  //
396  // Note that because the events correspond to new boxes, there is no
397  // need to check for the other side (current set included in previous
398  // set).
399  if (std::includes(overlapping_boxes.begin(), overlapping_boxes.end(),
400  previous_overlapping_boxes.begin(),
401  previous_overlapping_boxes.end())) {
402  --new_size;
403  }
404  }
405 
406  std::swap(events_overlapping_boxes_[new_size], overlapping_boxes);
407  ++new_size;
408  }
409  }
410 
411  // Split lists of boxes into disjoint set of boxes (w.r.t. overlap).
412  boxes_to_propagate_.clear();
413  reduced_overlapping_boxes_.clear();
414  for (int i = 0; i < new_size; ++i) {
415  SplitDisjointBoxes(x, absl::MakeSpan(events_overlapping_boxes_[i]),
416  &disjoint_boxes_);
417  for (absl::Span<int> sub_boxes : disjoint_boxes_) {
418  // Boxes are sorted in a stable manner in the Split method.
419  // Note that we do not use reduced_overlapping_boxes_ directly so that
420  // the order of iteration is deterministic.
421  const auto& insertion = reduced_overlapping_boxes_.insert(sub_boxes);
422  if (insertion.second) boxes_to_propagate_.push_back(sub_boxes);
423  }
424  }
425 
426  // And finally propagate.
427  // TODO(user): Sorting of boxes seems influential on the performance. Test.
428  for (const absl::Span<const int> boxes : boxes_to_propagate_) {
429  x_.ResetFromSubset(x, boxes);
430  y_.ResetFromSubset(y, boxes);
431 
432  // Collect the common overlapping coordinates of all boxes.
433  IntegerValue lb(std::numeric_limits<int64_t>::min());
434  IntegerValue ub(std::numeric_limits<int64_t>::max());
435  for (int i = 0; i < y_.NumTasks(); ++i) {
436  lb = std::max(lb, y_.StartMax(i));
437  ub = std::min(ub, y_.EndMin(i) - 1);
438  }
439  CHECK_LE(lb, ub);
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 
445  // We want for different propagation to reuse as much as possible the same
446  // line. The idea behind this is to compute the 'canonical' line to use
447  // when explaining that boxes overlap on the 'y_dim' dimension. We compute
448  // the multiple of the biggest power of two that is common to all boxes.
449  const IntegerValue line_to_use_for_reason = FindCanonicalValue(lb, ub);
450 
451  // Setup x_dim for propagation.
452  x_.SetOtherHelper(&y_, line_to_use_for_reason);
453 
454  RETURN_IF_FALSE(inner_propagate());
455  }
456 
457  return true;
458 }
459 
461  global_x_.SynchronizeAndSetTimeDirection(true);
462  global_y_.SynchronizeAndSetTimeDirection(true);
463 
464  std::function<bool()> inner_propagate;
465  if (watcher_->GetCurrentId() == fast_id_) {
466  inner_propagate = [this]() {
467  if (x_.NumTasks() == 2) {
468  // In that case, we can use simpler algorithms.
469  // Note that this case happens frequently (~30% of all calls to this
470  // method according to our tests).
471  RETURN_IF_FALSE(PropagateTwoBoxes());
472  } else {
473  RETURN_IF_FALSE(overload_checker_.Propagate());
474  RETURN_IF_FALSE(forward_detectable_precedences_.Propagate());
475  RETURN_IF_FALSE(backward_detectable_precedences_.Propagate());
476  }
477  return true;
478  };
479  } else {
480  inner_propagate = [this]() {
481  if (x_.NumTasks() <= 2) return true;
482  RETURN_IF_FALSE(forward_not_last_.Propagate());
483  RETURN_IF_FALSE(backward_not_last_.Propagate());
484  RETURN_IF_FALSE(backward_edge_finding_.Propagate());
485  RETURN_IF_FALSE(forward_edge_finding_.Propagate());
486  return true;
487  };
488  }
489 
490  RETURN_IF_FALSE(FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
491  global_x_, global_y_, inner_propagate));
492 
493  // We can actually swap dimensions to propagate vertically.
494  RETURN_IF_FALSE(FindBoxesThatMustOverlapAHorizontalLineAndPropagate(
495  global_y_, global_x_, inner_propagate));
496 
497  // If two boxes must overlap but do not have a mandatory line/column that
498  // crosses both of them, then the code above do not see it. So we manually
499  // propagate this case.
500  //
501  // TODO(user): Since we are at it, do more propagation even if no conflict?
502  // This rarely propagate, so disabled for now. Investigate if it is worth
503  // it.
504  if (/*DISABLES CODE*/ (false) && watcher_->GetCurrentId() == fast_id_) {
505  const int num_boxes = global_x_.NumTasks();
506  for (int box1 = 0; box1 < num_boxes; ++box1) {
507  if (!global_x_.IsPresent(box1)) continue;
508  for (int box2 = box1 + 1; box2 < num_boxes; ++box2) {
509  if (!global_x_.IsPresent(box2)) continue;
510  if (global_x_.EndMin(box1) <= global_x_.StartMax(box2)) continue;
511  if (global_x_.EndMin(box2) <= global_x_.StartMax(box1)) continue;
512  if (global_y_.EndMin(box1) <= global_y_.StartMax(box2)) continue;
513  if (global_y_.EndMin(box2) <= global_y_.StartMax(box1)) continue;
514 
515  // X and Y must overlap. This is a conflict.
516  global_x_.ClearReason();
517  global_x_.AddPresenceReason(box1);
518  global_x_.AddPresenceReason(box2);
519  global_x_.AddReasonForBeingBefore(box1, box2);
520  global_x_.AddReasonForBeingBefore(box2, box1);
521  global_y_.ClearReason();
522  global_y_.AddReasonForBeingBefore(box1, box2);
523  global_y_.AddReasonForBeingBefore(box2, box1);
524  global_x_.ImportOtherReasons(global_y_);
525  return global_x_.ReportConflict();
526  }
527  }
528  }
529 
530  return true;
531 }
532 
533 // Specialized propagation on only two boxes that must intersect with the
534 // given y_line_for_reason.
535 bool NonOverlappingRectanglesDisjunctivePropagator::PropagateTwoBoxes() {
536  // For each direction and each order, we test if the boxes can be disjoint.
537  const int state =
538  (x_.EndMin(0) <= x_.StartMax(1)) + 2 * (x_.EndMin(1) <= x_.StartMax(0));
539 
540  const auto left_box_before_right_box = [this](int left, int right) {
541  // left box pushes right box.
542  const IntegerValue left_end_min = x_.EndMin(left);
543  if (left_end_min > x_.StartMin(right)) {
544  x_.ClearReason();
545  x_.AddReasonForBeingBefore(left, right);
546  x_.AddEndMinReason(left, left_end_min);
547  RETURN_IF_FALSE(x_.IncreaseStartMin(right, left_end_min));
548  }
549 
550  // right box pushes left box.
551  const IntegerValue right_start_max = x_.StartMax(right);
552  if (right_start_max < x_.EndMax(left)) {
553  x_.ClearReason();
554  x_.AddReasonForBeingBefore(left, right);
555  x_.AddStartMaxReason(right, right_start_max);
556  RETURN_IF_FALSE(x_.DecreaseEndMax(left, right_start_max));
557  }
558 
559  return true;
560  };
561 
562  switch (state) {
563  case 0: { // Conflict.
564  x_.ClearReason();
565  x_.AddReasonForBeingBefore(0, 1);
566  x_.AddReasonForBeingBefore(1, 0);
567  return x_.ReportConflict();
568  }
569  case 1: { // b1 is left of b2.
570  return left_box_before_right_box(0, 1);
571  }
572  case 2: { // b2 is left of b1.
573  return left_box_before_right_box(1, 0);
574  }
575  default: { // Nothing to deduce.
576  return true;
577  }
578  }
579 }
580 
581 #undef RETURN_IF_FALSE
582 } // namespace sat
583 } // 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:710
#define CHECK_LE(val1, val2)
Definition: base/logging.h:707
void SetPropagatorPriority(int id, int priority)
Definition: integer.cc:1968
int Register(PropagatorInterface *propagator)
Definition: integer.cc:1945
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:297
void WatchAllTasks(int id, GenericLiteralWatcher *watcher, bool watch_start_max=true, bool watch_end_max=true) const
Definition: intervals.cc:460
void ResetFromSubset(const SchedulingConstraintHelper &other, absl::Span< const int > tasks)
Definition: intervals.cc:176
void AddEnergyAfterReason(int t, IntegerValue energy_min, IntegerValue time)
Definition: intervals.h:567
void AddEndMinReason(int t, IntegerValue lower_bound)
Definition: intervals.h:536
ABSL_MUST_USE_RESULT bool IncreaseStartMin(int t, IntegerValue new_start_min)
Definition: intervals.cc:405
const std::vector< AffineExpression > & Starts() const
Definition: intervals.h:320
void ImportOtherReasons(const SchedulingConstraintHelper &other_helper)
Definition: intervals.cc:492
ABSL_MUST_USE_RESULT bool DecreaseEndMax(int t, IntegerValue new_start_max)
Definition: intervals.cc:414
void AddEndMaxReason(int t, IntegerValue upper_bound)
Definition: intervals.h:558
void SetOtherHelper(SchedulingConstraintHelper *other_helper, IntegerValue event)
Definition: intervals.h:339
void AddReasonForBeingBefore(int before, int after)
Definition: intervals.cc:334
const std::vector< AffineExpression > & Sizes() const
Definition: intervals.h:322
void AddStartMaxReason(int t, IntegerValue upper_bound)
Definition: intervals.h:514
const std::vector< AffineExpression > & Ends() const
Definition: intervals.h:321
int64_t b
int64_t a
GRBmodel * model
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition: stl_util.h:58
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:263
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
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
Definition: integer.h:1431
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:675
void AddCumulativeRelaxation(const std::vector< IntervalVariable > &x_intervals, SchedulingConstraintHelper *x, SchedulingConstraintHelper *y, Model *model)
Definition: sat/diffn.cc:79
std::function< void(Model *)> WeightedSumGreaterOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
Definition: integer_expr.h:407
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:171
Rev< int64_t > start_max
Rev< int64_t > end_min