Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
2d_rectangle_presolve.h
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
14#ifndef ORTOOLS_SAT_2D_RECTANGLE_PRESOLVE_H_
15#define ORTOOLS_SAT_2D_RECTANGLE_PRESOLVE_H_
16
17#include <tuple>
18#include <utility>
19#include <vector>
20
21#include "absl/algorithm/container.h"
22#include "absl/container/flat_hash_map.h"
23#include "absl/container/inlined_vector.h"
24#include "absl/log/log.h"
25#include "absl/types/span.h"
28
29namespace operations_research {
30namespace sat {
31
32// Given a set of fixed boxes and a set of boxes that are not yet
33// fixed (but attributed a range), look for a more optimal set of fixed
34// boxes that are equivalent to the initial set of fixed boxes. This
35// uses "equivalent" in the sense that a placement of the non-fixed boxes will
36// be non-overlapping with all other boxes if and only if it was with the
37// original set of fixed boxes too.
39 absl::Span<const RectangleInRange> non_fixed_boxes,
40 std::vector<Rectangle>* fixed_boxes);
41
42// Detect whether the fixed boxes of a no_overlap_2d constraint are splitting
43// the space into separate components and thus can be replaced by one
44// no_overlap_2d constraint per component. If this is not possible, return an
45// empty result. Otherwise, return a struct containing what boxes (fixed and
46// non-fixed) are needed in each new constraint.
47//
48// Note that for this to be correct, we need to introduce new boxes to "fill"
49// the space occupied by the other components. For example, if we have a
50// no_overlap_2d constraint with the fixed boxes and the bounding box of the
51// non-fixed boxes as follows:
52// +---------------------+
53// | |
54// | |
55// | |
56// | |
57// | |
58// | ++++++++++++++++++|
59// | ++++++++++++++++++|
60// | ++++++++++++++++++|
61// |***** |
62// |***** |
63// |***** |
64// | |
65// | |
66// | |
67// +---------------------+
68// and we are building the new no_overlap_2d constraint for the space below, we
69// would need to add two new fixed boxes (the '.' and the 'o' one):
70// +---------------------+
71// |ooooooooooooooooooooo|
72// |ooooooooooooooooooooo|
73// |ooooooooooooooooooooo|
74// |ooooooooooooooooooooo|
75// |ooooooooooooooooooooo|
76// |...++++++++++++++++++|
77// |...++++++++++++++++++|
78// |...++++++++++++++++++|
79// |***** |
80// |***** |
81// |***** |
82// | |
83// | |
84// | |
85// +---------------------+
86// This ensures that the new no_overlap_2d constraint will impose the box to be
87// in that component as it should. Note that in the example above, the number of
88// boxes won't really increase after a presolve pass, since the presolve for
89// fixed boxes will simplify it to two fixed boxes again.
91 struct Bin {
92 // Fixed boxes that the non-fixed boxes in this bin cannot overlap with.
93 std::vector<Rectangle> fixed_boxes;
94 // Non-fixed boxes on the original problem to copy to this new constraint.
95 std::vector<int> non_fixed_box_indexes;
96 // Area that is covered by the connected component bin represents encoded as
97 // a non-overlapping set of rectangles.
98 std::vector<Rectangle> bin_area;
99 };
100 std::vector<Bin> bins;
101};
103 absl::Span<const RectangleInRange> non_fixed_boxes,
104 absl::Span<const Rectangle> fixed_boxes, int max_num_components);
105
106// Given two vectors of non-overlapping rectangles defining two regions of the
107// space: one mandatory region that must be occupied and one optional region
108// that can be occupied, try to build a vector of as few non-overlapping
109// rectangles as possible defining a region R that satisfy:
110// - R \subset (mandatory \union optional);
111// - mandatory \subset R.
112//
113// The function updates the vector of `mandatory_rectangles` with `R` and
114// `optional_rectangles` with `optional_rectangles \setdiff R`. It returns
115// true if the `mandatory_rectangles` was updated.
116//
117// This function uses a greedy algorithm that merge rectangles that share an
118// edge.
119bool ReduceNumberofBoxesGreedy(std::vector<Rectangle>* mandatory_rectangles,
120 std::vector<Rectangle>* optional_rectangles);
121
122// Same as above, but this implementation returns the optimal solution in
123// minimizing the number of boxes if `optional_rectangles` is empty. On the
124// other hand, its handling of optional boxes is rather limited. It simply fills
125// the holes in the mandatory boxes with optional boxes, if possible.
127 std::vector<Rectangle>* mandatory_rectangles,
128 std::vector<Rectangle>* optional_rectangles);
129
130enum EdgePosition { TOP = 0, RIGHT = 1, BOTTOM = 2, LEFT = 3 };
131
132template <typename Sink>
133void AbslStringify(Sink& sink, EdgePosition e) {
134 switch (e) {
136 sink.Append("TOP");
137 break;
139 sink.Append("RIGHT");
140 break;
142 sink.Append("BOTTOM");
143 break;
145 sink.Append("LEFT");
146 break;
147 }
148}
149
150// Given a set of non-overlapping rectangles, precompute a data-structure that
151// allow for each rectangle to find the adjacent rectangle along an edge.
152//
153// Note that it only consider adjacent rectangles whose segments has a
154// intersection of non-zero size. In particular, rectangles as following are not
155// considered adjacent:
156//
157// ********
158// ********
159// ********
160// ********
161// +++++++++
162// +++++++++
163// +++++++++
164// +++++++++
165//
166// Precondition: All rectangles must be disjoint.
168 public:
170 public:
171 explicit CompareClockwise(EdgePosition edge) : edge_(edge) {}
172
173 bool operator()(const Rectangle& a, const Rectangle& b) const {
174 switch (edge_) {
176 return std::tie(a.x_min, a.x_max) > std::tie(b.x_min, b.x_max);
178 return std::tie(a.x_min, a.x_max) < std::tie(b.x_min, b.x_max);
180 return std::tie(a.y_min, a.y_max) < std::tie(b.y_min, b.y_max);
182 return std::tie(a.y_min, a.y_max) > std::tie(b.y_min, b.y_max);
183 }
184 LOG(FATAL) << "Invalid edge position: " << static_cast<int>(edge_);
185 }
187 };
188
189 explicit Neighbours(
190 absl::Span<const Rectangle> rectangles,
191 absl::Span<const std::tuple<int, EdgePosition, int>> neighbors)
192 : size_(rectangles.size()) {
193 for (const auto& [box_index, edge, neighbor] : neighbors) {
194 neighbors_[edge][box_index].push_back(neighbor);
195 }
196 for (int edge = 0; edge < 4; ++edge) {
197 for (auto& [box_index, neighbors] : neighbors_[edge]) {
198 absl::c_sort(neighbors, [&rectangles, edge](int a, int b) {
199 return CompareClockwise(static_cast<EdgePosition>(edge))(
200 rectangles[a], rectangles[b]);
201 });
202 }
203 }
204 }
205
206 int NumRectangles() const { return size_; }
207
208 // Neighbors are sorted in the clockwise order.
209 absl::Span<const int> GetSortedNeighbors(int rectangle_index,
210 EdgePosition edge) const {
211 if (auto it = neighbors_[edge].find(rectangle_index);
212 it != neighbors_[edge].end()) {
213 return it->second;
214 } else {
215 return {};
216 }
217 }
218
219 private:
220 absl::flat_hash_map<int, absl::InlinedVector<int, 3>> neighbors_[4];
221 int size_;
222};
223
224Neighbours BuildNeighboursGraph(absl::Span<const Rectangle> rectangles);
225
226std::vector<std::vector<int>> SplitInConnectedComponents(
227 const Neighbours& neighbours);
228
229// Generally, given a set of non-overlapping rectangles and a path that doesn't
230// cross itself, the path can be cut into segments that touch only one single
231// rectangle in the interior of the region delimited by the path. This struct
232// holds a path cut into such segments. In particular, for the contour of an
233// union of rectangles, the path is a subset of the union of all the rectangle's
234// edges.
235struct ShapePath {
236 // The two vectors should have exactly the same size.
237 std::vector<std::pair<IntegerValue, IntegerValue>> step_points;
238 // touching_box_index[i] contains the index of the unique interior rectangle
239 // touching the segment step_points[i]->step_points[(i+1)%size].
240 std::vector<int> touching_box_index;
241};
242
245 std::vector<ShapePath> holes;
246};
247
248// Given a set of rectangles, split it into connected components and transform
249// each individual set into a shape described by its boundary and holes paths.
250std::vector<SingleShape> BoxesToShapes(absl::Span<const Rectangle> rectangles,
251 const Neighbours& neighbours);
252
253std::vector<Rectangle> CutShapeIntoRectangles(SingleShape shapes);
254
255} // namespace sat
256} // namespace operations_research
257
258#endif // ORTOOLS_SAT_2D_RECTANGLE_PRESOLVE_H_
bool operator()(const Rectangle &a, const Rectangle &b) const
absl::Span< const int > GetSortedNeighbors(int rectangle_index, EdgePosition edge) const
Neighbours(absl::Span< const Rectangle > rectangles, absl::Span< const std::tuple< int, EdgePosition, int > > neighbors)
bool ReduceNumberOfBoxesExactMandatory(std::vector< Rectangle > *mandatory_rectangles, std::vector< Rectangle > *optional_rectangles)
std::vector< std::vector< int > > SplitInConnectedComponents(const Neighbours &neighbours)
std::vector< SingleShape > BoxesToShapes(absl::Span< const Rectangle > rectangles, const Neighbours &neighbours)
Disjoint2dPackingResult DetectDisjointRegionIn2dPacking(absl::Span< const RectangleInRange > non_fixed_boxes, absl::Span< const Rectangle > fixed_boxes, int max_num_components)
bool ReduceNumberofBoxesGreedy(std::vector< Rectangle > *mandatory_rectangles, std::vector< Rectangle > *optional_rectangles)
std::vector< Rectangle > CutShapeIntoRectangles(SingleShape shape)
void AbslStringify(Sink &sink, EdgePosition e)
Neighbours BuildNeighboursGraph(absl::Span< const Rectangle > rectangles)
bool PresolveFixed2dRectangles(absl::Span< const RectangleInRange > non_fixed_boxes, std::vector< Rectangle > *fixed_boxes)
OR-Tools root namespace.
std::vector< std::pair< IntegerValue, IntegerValue > > step_points