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