OR-Tools  9.0
constraint_solver/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 <algorithm>
15 #include <cstdint>
16 #include <limits>
17 #include <string>
18 #include <vector>
19 
20 #include "absl/strings/str_format.h"
21 #include "ortools/base/hash.h"
22 #include "ortools/base/int_type.h"
24 #include "ortools/base/logging.h"
28 
29 namespace operations_research {
30 // Diffn constraint, Non overlapping boxs.
31 namespace {
32 DEFINE_INT_TYPE(Box, int);
33 class Diffn : public Constraint {
34  public:
35  Diffn(Solver* const solver, const std::vector<IntVar*>& x_vars,
36  const std::vector<IntVar*>& y_vars, const std::vector<IntVar*>& x_size,
37  const std::vector<IntVar*>& y_size, bool strict)
38  : Constraint(solver),
39  x_(x_vars),
40  y_(y_vars),
41  dx_(x_size),
42  dy_(y_size),
43  strict_(strict),
44  size_(x_vars.size()),
45  fail_stamp_(0) {
46  CHECK_EQ(x_vars.size(), y_vars.size());
47  CHECK_EQ(x_vars.size(), x_size.size());
48  CHECK_EQ(x_vars.size(), y_size.size());
49  }
50 
51  ~Diffn() override {}
52 
53  void Post() override {
54  Solver* const s = solver();
55  for (int i = 0; i < size_; ++i) {
56  Demon* const demon = MakeConstraintDemon1(
57  s, this, &Diffn::OnBoxRangeChange, "OnBoxRangeChange", i);
58  x_[i]->WhenRange(demon);
59  y_[i]->WhenRange(demon);
60  dx_[i]->WhenRange(demon);
61  dy_[i]->WhenRange(demon);
62  }
63  delayed_demon_ = MakeDelayedConstraintDemon0(s, this, &Diffn::PropagateAll,
64  "PropagateAll");
65  if (solver()->parameters().diffn_use_cumulative() &&
66  IsArrayInRange<int64_t>(x_, 0, std::numeric_limits<int64_t>::max()) &&
67  IsArrayInRange<int64_t>(y_, 0, std::numeric_limits<int64_t>::max())) {
68  Constraint* ct1 = nullptr;
69  Constraint* ct2 = nullptr;
70  {
71  // We can add redundant cumulative constraints. This is done
72  // inside a c++ block to avoid leaking memory if adding the
73  // constraints leads to a failure. A cumulative constraint is
74  // a scheduling constraint that will perform finer energy
75  // based reasoning to do more propagation. (see Solver::MakeCumulative).
76  const int64_t min_x = MinVarArray(x_);
77  const int64_t max_x = MaxVarArray(x_);
78  const int64_t max_size_x = MaxVarArray(dx_);
79  const int64_t min_y = MinVarArray(y_);
80  const int64_t max_y = MaxVarArray(y_);
81  const int64_t max_size_y = MaxVarArray(dy_);
82  if (AreAllBound(dx_)) {
83  std::vector<int64_t> size_x;
84  FillValues(dx_, &size_x);
85  ct1 = MakeCumulativeConstraint(x_, size_x, dy_,
86  max_size_y + max_y - min_y);
87  }
88  if (AreAllBound(dy_)) {
89  std::vector<int64_t> size_y;
90  FillValues(dy_, &size_y);
91  ct2 = MakeCumulativeConstraint(y_, size_y, dx_,
92  max_size_x + max_x - min_x);
93  }
94  }
95  if (ct1 != nullptr) {
96  s->AddConstraint(ct1);
97  }
98  if (ct2 != nullptr) {
99  s->AddConstraint(ct2);
100  }
101  }
102  }
103 
104  void InitialPropagate() override {
105  // All sizes should be >= 0.
106  for (int i = 0; i < size_; ++i) {
107  dx_[i]->SetMin(0);
108  dy_[i]->SetMin(0);
109  }
110 
111  // Force propagation on all boxes.
112  to_propagate_.clear();
113  for (int i = 0; i < size_; i++) {
114  to_propagate_.insert(i);
115  }
116  PropagateAll();
117  }
118 
119  std::string DebugString() const override {
120  return absl::StrFormat(
121  "Diffn(x = [%s], y = [%s], dx = [%s], dy = [%s]))",
122  JoinDebugStringPtr(x_, ", "), JoinDebugStringPtr(y_, ", "),
123  JoinDebugStringPtr(dx_, ", "), JoinDebugStringPtr(dy_, ", "));
124  }
125 
126  void Accept(ModelVisitor* const visitor) const override {
127  visitor->BeginVisitConstraint(ModelVisitor::kDisjunctive, this);
128  visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kPositionXArgument,
129  x_);
130  visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kPositionYArgument,
131  y_);
132  visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kSizeXArgument,
133  dx_);
134  visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kSizeYArgument,
135  dy_);
136  visitor->EndVisitConstraint(ModelVisitor::kDisjunctive, this);
137  }
138 
139  private:
140  void PropagateAll() {
141  for (const int box : to_propagate_) {
142  FillNeighbors(box);
143  FailWhenEnergyIsTooLarge(box);
144  PushOverlappingBoxes(box);
145  }
146  to_propagate_.clear();
147  fail_stamp_ = solver()->fail_stamp();
148  }
149 
150  void OnBoxRangeChange(int box) {
151  if (solver()->fail_stamp() > fail_stamp_ && !to_propagate_.empty()) {
152  // We have failed in the last propagation and the to_propagate_
153  // was not cleared.
154  fail_stamp_ = solver()->fail_stamp();
155  to_propagate_.clear();
156  }
157  to_propagate_.insert(box);
158  EnqueueDelayedDemon(delayed_demon_);
159  }
160 
161  bool CanBoxedOverlap(int i, int j) const {
162  if (AreBoxedDisjoingHorizontallyForSure(i, j) ||
163  AreBoxedDisjoingVerticallyForSure(i, j)) {
164  return false;
165  }
166  return true;
167  }
168 
169  bool AreBoxedDisjoingHorizontallyForSure(int i, int j) const {
170  return (x_[i]->Min() >= x_[j]->Max() + dx_[j]->Max()) ||
171  (x_[j]->Min() >= x_[i]->Max() + dx_[i]->Max()) ||
172  (!strict_ && (dx_[i]->Min() == 0 || dx_[j]->Min() == 0));
173  }
174 
175  bool AreBoxedDisjoingVerticallyForSure(int i, int j) const {
176  return (y_[i]->Min() >= y_[j]->Max() + dy_[j]->Max()) ||
177  (y_[j]->Min() >= y_[i]->Max() + dy_[i]->Max()) ||
178  (!strict_ && (dy_[i]->Min() == 0 || dy_[j]->Min() == 0));
179  }
180 
181  // Fill neighbors_ with all boxes that can overlap the given box.
182  void FillNeighbors(int box) {
183  // TODO(user): We could maintain a non reversible list of
184  // neighbors and clean it after each failure.
185  neighbors_.clear();
186  for (int other = 0; other < size_; ++other) {
187  if (other != box && CanBoxedOverlap(other, box)) {
188  neighbors_.push_back(other);
189  }
190  }
191  }
192 
193  // Fails if the minimum area of the given box plus the area of its neighbors
194  // (that must already be computed in neighbors_) is greater than the area of a
195  // bounding box that necessarily contains all these boxes.
196  void FailWhenEnergyIsTooLarge(int box) {
197  int64_t area_min_x = x_[box]->Min();
198  int64_t area_max_x = x_[box]->Max() + dx_[box]->Max();
199  int64_t area_min_y = y_[box]->Min();
200  int64_t area_max_y = y_[box]->Max() + dy_[box]->Max();
201  int64_t sum_of_areas = dx_[box]->Min() * dy_[box]->Min();
202  // TODO(user): Is there a better order, maybe sort by distance
203  // with the current box.
204  for (int i = 0; i < neighbors_.size(); ++i) {
205  const int other = neighbors_[i];
206  // Update Bounding box.
207  area_min_x = std::min(area_min_x, x_[other]->Min());
208  area_max_x = std::max(area_max_x, x_[other]->Max() + dx_[other]->Max());
209  area_min_y = std::min(area_min_y, y_[other]->Min());
210  area_max_y = std::max(area_max_y, y_[other]->Max() + dy_[other]->Max());
211  // Update sum of areas.
212  sum_of_areas += dx_[other]->Min() * dy_[other]->Min();
213  const int64_t bounding_area =
214  (area_max_x - area_min_x) * (area_max_y - area_min_y);
215  if (sum_of_areas > bounding_area) {
216  solver()->Fail();
217  }
218  }
219  }
220 
221  // Changes the domain of all the neighbors of a given box (that must
222  // already be computed in neighbors_) so that they can't overlap the
223  // mandatory part of the given box.
224  void PushOverlappingBoxes(int box) {
225  for (int i = 0; i < neighbors_.size(); ++i) {
226  PushOneBox(box, neighbors_[i]);
227  }
228  }
229 
230  // Changes the domain of the two given box by excluding the value that
231  // make them overlap for sure. Note that this function is symmetric in
232  // the sense that its argument can be swapped for the same result.
233  void PushOneBox(int box, int other) {
234  const int state =
235  (x_[box]->Min() + dx_[box]->Min() <= x_[other]->Max()) +
236  2 * (x_[other]->Min() + dx_[other]->Min() <= x_[box]->Max()) +
237  4 * (y_[box]->Min() + dy_[box]->Min() <= y_[other]->Max()) +
238  8 * (y_[other]->Min() + dy_[other]->Min() <= y_[box]->Max());
239  // This is an "hack" to be able to easily test for none or for one
240  // and only one of the conditions below.
241  switch (state) {
242  case 0: {
243  solver()->Fail();
244  break;
245  }
246  case 1: { // We push other left (x increasing).
247  x_[other]->SetMin(x_[box]->Min() + dx_[box]->Min());
248  x_[box]->SetMax(x_[other]->Max() - dx_[box]->Min());
249  dx_[box]->SetMax(x_[other]->Max() - x_[box]->Min());
250  break;
251  }
252  case 2: { // We push other right (x decreasing).
253  x_[box]->SetMin(x_[other]->Min() + dx_[other]->Min());
254  x_[other]->SetMax(x_[box]->Max() - dx_[other]->Min());
255  dx_[other]->SetMax(x_[box]->Max() - x_[other]->Min());
256  break;
257  }
258  case 4: { // We push other up (y increasing).
259  y_[other]->SetMin(y_[box]->Min() + dy_[box]->Min());
260  y_[box]->SetMax(y_[other]->Max() - dy_[box]->Min());
261  dy_[box]->SetMax(y_[other]->Max() - y_[box]->Min());
262  break;
263  }
264  case 8: { // We push other down (y decreasing).
265  y_[box]->SetMin(y_[other]->Min() + dy_[other]->Min());
266  y_[other]->SetMax(y_[box]->Max() - dy_[other]->Min());
267  dy_[other]->SetMax(y_[box]->Max() - y_[other]->Min());
268  break;
269  }
270  default: {
271  break;
272  }
273  }
274  }
275 
276  Constraint* MakeCumulativeConstraint(const std::vector<IntVar*>& positions,
277  const std::vector<int64_t>& sizes,
278  const std::vector<IntVar*>& demands,
279  int64_t capacity) {
280  std::vector<IntervalVar*> intervals;
281  solver()->MakeFixedDurationIntervalVarArray(positions, sizes, "interval",
282  &intervals);
283  return solver()->MakeCumulative(intervals, demands, capacity, "cumul");
284  }
285 
286  std::vector<IntVar*> x_;
287  std::vector<IntVar*> y_;
288  std::vector<IntVar*> dx_;
289  std::vector<IntVar*> dy_;
290  const bool strict_;
291  const int64_t size_;
292  Demon* delayed_demon_;
293  absl::flat_hash_set<int> to_propagate_;
294  std::vector<int> neighbors_;
295  uint64_t fail_stamp_;
296 };
297 } // namespace
298 
300  const std::vector<IntVar*>& x_vars, const std::vector<IntVar*>& y_vars,
301  const std::vector<IntVar*>& x_size, const std::vector<IntVar*>& y_size) {
302  return RevAlloc(new Diffn(this, x_vars, y_vars, x_size, y_size, true));
303 }
304 
306  const std::vector<IntVar*>& x_vars, const std::vector<IntVar*>& y_vars,
307  const std::vector<int64_t>& x_size, const std::vector<int64_t>& y_size) {
308  std::vector<IntVar*> dx(x_size.size());
309  std::vector<IntVar*> dy(y_size.size());
310  for (int i = 0; i < x_size.size(); ++i) {
311  dx[i] = MakeIntConst(x_size[i]);
312  dy[i] = MakeIntConst(y_size[i]);
313  }
314  return RevAlloc(new Diffn(this, x_vars, y_vars, dx, dy, true));
315 }
316 
318  const std::vector<IntVar*>& x_vars, const std::vector<IntVar*>& y_vars,
319  const std::vector<int>& x_size, const std::vector<int>& y_size) {
320  std::vector<IntVar*> dx(x_size.size());
321  std::vector<IntVar*> dy(y_size.size());
322  for (int i = 0; i < x_size.size(); ++i) {
323  dx[i] = MakeIntConst(x_size[i]);
324  dy[i] = MakeIntConst(y_size[i]);
325  }
326  return RevAlloc(new Diffn(this, x_vars, y_vars, dx, dy, true));
327 }
328 
330  const std::vector<IntVar*>& x_vars, const std::vector<IntVar*>& y_vars,
331  const std::vector<IntVar*>& x_size, const std::vector<IntVar*>& y_size) {
332  return RevAlloc(new Diffn(this, x_vars, y_vars, x_size, y_size, false));
333 }
334 
336  const std::vector<IntVar*>& x_vars, const std::vector<IntVar*>& y_vars,
337  const std::vector<int64_t>& x_size, const std::vector<int64_t>& y_size) {
338  std::vector<IntVar*> dx(x_size.size());
339  std::vector<IntVar*> dy(y_size.size());
340  for (int i = 0; i < x_size.size(); ++i) {
341  dx[i] = MakeIntConst(x_size[i]);
342  dy[i] = MakeIntConst(y_size[i]);
343  }
344  return RevAlloc(new Diffn(this, x_vars, y_vars, dx, dy, false));
345 }
346 
348  const std::vector<IntVar*>& x_vars, const std::vector<IntVar*>& y_vars,
349  const std::vector<int>& x_size, const std::vector<int>& y_size) {
350  std::vector<IntVar*> dx(x_size.size());
351  std::vector<IntVar*> dy(y_size.size());
352  for (int i = 0; i < x_size.size(); ++i) {
353  dx[i] = MakeIntConst(x_size[i]);
354  dy[i] = MakeIntConst(y_size[i]);
355  }
356  return RevAlloc(new Diffn(this, x_vars, y_vars, dx, dy, false));
357 }
358 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:705
A constraint is the main modeling object.
Constraint * MakeNonOverlappingBoxesConstraint(const std::vector< IntVar * > &x_vars, const std::vector< IntVar * > &y_vars, const std::vector< IntVar * > &x_size, const std::vector< IntVar * > &y_size)
This constraint states that all the boxes must not overlap.
T * RevAlloc(T *object)
Registers the given object as being reversible.
Constraint * MakeNonOverlappingNonStrictBoxesConstraint(const std::vector< IntVar * > &x_vars, const std::vector< IntVar * > &y_vars, const std::vector< IntVar * > &x_size, const std::vector< IntVar * > &y_size)
This constraint states that all the boxes must not overlap.
IntVar * MakeIntConst(int64_t val, const std::string &name)
IntConst will create a constant expression.
SatParameters parameters
Collection of objects used to extend the Constraint Solver library.
Demon * MakeConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
int64_t MaxVarArray(const std::vector< IntVar * > &vars)
void FillValues(const std::vector< IntVar * > &vars, std::vector< int64_t > *const values)
Demon * MakeDelayedConstraintDemon0(Solver *const s, T *const ct, void(T::*method)(), const std::string &name)
int64_t MinVarArray(const std::vector< IntVar * > &vars)
DEFINE_INT_TYPE(RoutingNodeIndex, int)
Defining common types used in the routing library outside the main RoutingModel class has several pur...
std::string JoinDebugStringPtr(const std::vector< T > &v, const std::string &separator)
Definition: string_array.h:45
bool AreAllBound(const std::vector< IntVar * > &vars)
int64_t capacity