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