Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
2d_orthogonal_packing_testing.cc
Go to the documentation of this file.
1// Copyright 2010-2025 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
15
16#include <algorithm>
17#include <cmath>
18#include <cstdint>
19#include <random>
20#include <utility>
21#include <vector>
22
23#include "absl/log/check.h"
24#include "absl/random/bit_gen_ref.h"
25#include "absl/random/distributions.h"
26#include "absl/types/span.h"
29
30namespace operations_research {
31namespace sat {
32
33std::vector<Rectangle> GenerateNonConflictingRectangles(
34 int num_rectangles, absl::BitGenRef random) {
35 static constexpr int kSizeMax = 1'000'000;
36 std::vector<Rectangle> rectangles;
37 rectangles.reserve(num_rectangles);
38 rectangles.push_back(
39 {.x_min = 0, .x_max = kSizeMax, .y_min = 0, .y_max = kSizeMax});
40 while (rectangles.size() < num_rectangles) {
41 std::swap(rectangles.back(),
42 rectangles[absl::Uniform(random, 0ull, rectangles.size() - 1)]);
43 const Rectangle& rec = rectangles.back();
44 if (absl::Bernoulli(random, 0.5)) {
45 const IntegerValue cut = IntegerValue(
46 absl::Uniform(random, rec.x_min.value(), rec.x_max.value()));
47 const Rectangle new_range = {.x_min = rec.x_min,
48 .x_max = cut,
49 .y_min = rec.y_min,
50 .y_max = rec.y_max};
51 const Rectangle new_range2 = {.x_min = cut,
52 .x_max = rec.x_max,
53 .y_min = rec.y_min,
54 .y_max = rec.y_max};
55 if (new_range.Area() == 0 || new_range2.Area() == 0) {
56 continue;
57 }
58 rectangles.pop_back();
59 rectangles.push_back(new_range);
60 rectangles.push_back(new_range2);
61 } else {
62 const IntegerValue cut = IntegerValue(
63 absl::Uniform(random, rec.y_min.value(), rec.y_max.value()));
64 const Rectangle new_range = {.x_min = rec.x_min,
65 .x_max = rec.x_max,
66 .y_min = rec.y_min,
67 .y_max = cut};
68 const Rectangle new_range2 = {.x_min = rec.x_min,
69 .x_max = rec.x_max,
70 .y_min = cut,
71 .y_max = rec.y_max};
72 if (new_range.Area() == 0 || new_range2.Area() == 0) {
73 continue;
74 }
75 rectangles.pop_back();
76 rectangles.push_back(new_range);
77 rectangles.push_back(new_range2);
78 }
79 }
80 return rectangles;
81}
82
84 std::pair<IntegerValue, IntegerValue> bb, int average_num_boxes,
85 absl::BitGenRef random) {
86 const double p = 0.01;
87 std::vector<Rectangle> rectangles;
88 int num_retries = 0;
89 double average_size =
90 std::sqrt(bb.first.value() * bb.second.value() / average_num_boxes);
91 const int64_t n_x = static_cast<int64_t>(average_size / p);
92 const int64_t n_y = static_cast<int64_t>(average_size / p);
93 while (num_retries < 4) {
94 num_retries++;
95
96 std::pair<IntegerValue, IntegerValue> sizes;
97 do {
98 sizes.first = std::binomial_distribution<>(n_x, p)(random);
99 } while (sizes.first == 0 || sizes.first > bb.first);
100 do {
101 sizes.second = std::binomial_distribution<>(n_y, p)(random);
102 } while (sizes.second == 0 || sizes.second > bb.second);
103
104 std::vector<IntegerValue> possible_x_positions = {0};
105 std::vector<IntegerValue> possible_y_positions = {0};
106 for (const Rectangle& rec : rectangles) {
107 possible_x_positions.push_back(rec.x_max);
108 possible_y_positions.push_back(rec.y_max);
109 }
110 std::sort(possible_x_positions.begin(), possible_x_positions.end());
111 std::sort(possible_y_positions.begin(), possible_y_positions.end());
112 bool found_position = false;
113 for (const IntegerValue x : possible_x_positions) {
114 for (const IntegerValue y : possible_y_positions) {
115 if (x + sizes.first > bb.first || y + sizes.second > bb.second) {
116 continue;
117 }
118 const Rectangle rec = {.x_min = x,
119 .x_max = x + sizes.first,
120 .y_min = y,
121 .y_max = y + sizes.second};
122 bool conflict = false;
123 for (const Rectangle r : rectangles) {
124 if (!r.IsDisjoint(rec)) {
125 conflict = true;
126 break;
127 }
128 }
129 if (conflict) {
130 continue;
131 } else {
132 rectangles.push_back(rec);
133 found_position = true;
134 break;
135 }
136 }
137 if (found_position) {
138 break;
139 }
140 }
141 if (found_position) {
142 num_retries = 0;
143 }
144 }
145 return rectangles;
146}
147
148std::vector<RectangleInRange> MakeItemsFromRectangles(
149 absl::Span<const Rectangle> rectangles, double slack_factor,
150 absl::BitGenRef random) {
151 IntegerValue size_max_x = 0;
152 IntegerValue size_max_y = 0;
153 for (const Rectangle& rec : rectangles) {
154 size_max_x = std::max(size_max_x, rec.SizeX());
155 size_max_y = std::max(size_max_y, rec.SizeY());
156 }
157 std::vector<RectangleInRange> ranges;
158 ranges.reserve(rectangles.size());
159 const int max_slack_x = slack_factor * size_max_x.value();
160 const int max_slack_y = slack_factor * size_max_y.value();
161 int count = 0;
162 for (const Rectangle& rec : rectangles) {
163 RectangleInRange range;
164 range.x_size = rec.x_max - rec.x_min;
165 range.y_size = rec.y_max - rec.y_min;
166 range.box_index = count++;
167 range.bounding_area = {
168 .x_min =
169 rec.x_min - IntegerValue(absl::Uniform(random, 0, max_slack_x)),
170 .x_max =
171 rec.x_max + IntegerValue(absl::Uniform(random, 0, max_slack_x)),
172 .y_min =
173 rec.y_min - IntegerValue(absl::Uniform(random, 0, max_slack_y)),
174 .y_max =
175 rec.y_max + IntegerValue(absl::Uniform(random, 0, max_slack_y))};
176 ranges.push_back(range);
177 }
178 return ranges;
179}
180
181std::vector<ItemWithVariableSize> GenerateItemsRectanglesWithNoPairwiseConflict(
182 absl::Span<const Rectangle> rectangles, double slack_factor,
183 absl::BitGenRef random) {
184 const std::vector<RectangleInRange> range_items =
185 MakeItemsFromRectangles(rectangles, slack_factor, random);
186 std::vector<ItemWithVariableSize> items;
187 items.reserve(rectangles.size());
188 for (int i = 0; i < range_items.size(); ++i) {
189 const RectangleInRange& rec = range_items[i];
190 items.push_back({.index = i,
191 .x = {.start_min = rec.bounding_area.x_min,
192 .start_max = rec.bounding_area.x_max - rec.x_size,
193 .end_min = rec.bounding_area.x_min + rec.x_size,
194 .end_max = rec.bounding_area.x_max},
195 .y = {.start_min = rec.bounding_area.y_min,
196 .start_max = rec.bounding_area.y_max - rec.y_size,
197 .end_min = rec.bounding_area.y_min + rec.y_size,
198 .end_max = rec.bounding_area.y_max}});
199 }
200 return items;
201}
202
203std::vector<ItemWithVariableSize>
205 double slack_factor,
206 absl::BitGenRef random) {
207 const std::vector<Rectangle> rectangles =
208 GenerateNonConflictingRectangles(num_rectangles, random);
209 std::vector<ItemWithVariableSize> items =
210 GenerateItemsRectanglesWithNoPairwiseConflict(rectangles, slack_factor,
211 random);
212 bool done = false;
213 // Now run the propagator until there is no more pairwise conditions.
214 do {
215 std::vector<PairwiseRestriction> results;
216 AppendPairwiseRestrictions(items, &results);
217 for (const PairwiseRestriction& restriction : results) {
218 CHECK(restriction.type !=
220 // Remove the slack we added
221 for (int index : {restriction.first_index, restriction.second_index}) {
222 const auto& rec = rectangles[index];
223 items[index] = {.index = index,
224 .x = {.start_min = rec.x_min,
225 .start_max = rec.x_min,
226 .end_min = rec.x_max,
227 .end_max = rec.x_max},
228 .y = {.start_min = rec.y_min,
229 .start_max = rec.y_min,
230 .end_min = rec.y_max,
231 .end_max = rec.y_max}};
232 }
233 }
234 done = results.empty();
235 } while (!done);
236 return items;
237}
238
239} // namespace sat
240} // namespace operations_research
std::vector< Rectangle > GenerateNonConflictingRectangles(int num_rectangles, absl::BitGenRef random)
std::vector< Rectangle > GenerateNonConflictingRectanglesWithPacking(std::pair< IntegerValue, IntegerValue > bb, int average_num_boxes, absl::BitGenRef random)
void AppendPairwiseRestrictions(absl::Span< const ItemWithVariableSize > items, std::vector< PairwiseRestriction > *result)
std::vector< ItemWithVariableSize > GenerateItemsRectanglesWithNoPairwisePropagation(int num_rectangles, double slack_factor, absl::BitGenRef random)
std::vector< ItemWithVariableSize > GenerateItemsRectanglesWithNoPairwiseConflict(absl::Span< const Rectangle > rectangles, double slack_factor, absl::BitGenRef random)
std::vector< RectangleInRange > MakeItemsFromRectangles(absl::Span< const Rectangle > rectangles, double slack_factor, absl::BitGenRef random)
In SWIG mode, we don't want anything besides these top-level includes.