Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
no_overlap_2d_helper.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 OR_TOOLS_SAT_NO_OVERLAP_2D_HELPER_H_
15#define OR_TOOLS_SAT_NO_OVERLAP_2D_HELPER_H_
16
17#include <cstdint>
18#include <memory>
19#include <utility>
20#include <vector>
21
22#include "absl/types/span.h"
24#include "ortools/sat/integer.h"
26#include "ortools/sat/model.h"
29#include "ortools/sat/util.h"
30
31namespace operations_research {
32namespace sat {
33
34// Helper class shared by the propagators that handle no_overlap_2d constraints.
35//
36// Having a helper class like this one makes much easier to do in-processing and
37// to share pre-computed data between the two propagators.
39 public:
40 NoOverlap2DConstraintHelper(std::vector<AffineExpression> x_starts,
41 std::vector<AffineExpression> x_ends,
42 std::vector<AffineExpression> x_sizes,
43 std::vector<LiteralIndex> x_reason_for_presence,
44 std::vector<AffineExpression> y_starts,
45 std::vector<AffineExpression> y_ends,
46 std::vector<AffineExpression> y_sizes,
47 std::vector<LiteralIndex> y_reason_for_presence,
48 Model* model)
49 : axes_are_swapped_(false),
50 x_helper_(std::make_unique<SchedulingConstraintHelper>(
51 x_starts, x_ends, x_sizes, x_reason_for_presence, model)),
52 y_helper_(std::make_unique<SchedulingConstraintHelper>(
53 y_starts, y_ends, y_sizes, y_reason_for_presence, model)),
54 model_(model),
55 watcher_(model->GetOrCreate<GenericLiteralWatcher>()) {
56 const int num_boxes = x_helper_->NumTasks();
57 connected_components_.reserve(1, num_boxes);
58 connected_components_.Add({});
59 for (int i = 0; i < x_helper_->NumTasks(); ++i) {
60 if (!x_helper_->IsAbsent(i) && !y_helper_->IsAbsent(i)) {
61 connected_components_.AppendToLastVector(i);
62 }
63 }
64 }
65
67
68 bool SynchronizeAndSetDirection(bool x_is_forward_after_swap = true,
69 bool y_is_forward_after_swap = true,
70 bool swap_x_and_y = false);
71
72 bool IsOptional(int index) const {
73 return x_helper_->IsOptional(index) || y_helper_->IsOptional(index);
74 }
75
76 bool IsPresent(int index) const {
77 return x_helper_->IsPresent(index) && y_helper_->IsPresent(index);
78 }
79
80 bool IsAbsent(int index) const {
81 return x_helper_->IsAbsent(index) || y_helper_->IsAbsent(index);
82 }
83
85 return {.x_min = x_helper_->StartMin(index),
86 .x_max = x_helper_->EndMax(index),
87 .y_min = y_helper_->StartMin(index),
88 .y_max = y_helper_->EndMax(index)};
89 }
90
92 return {.x_min = x_helper_->LevelZeroStartMin(index),
93 .x_max = x_helper_->LevelZeroEndMax(index),
94 .y_min = y_helper_->LevelZeroStartMin(index),
95 .y_max = y_helper_->LevelZeroEndMax(index)};
96 }
97
98 bool IsFixed(int index) const {
99 return x_helper_->StartIsFixed(index) && x_helper_->EndIsFixed(index) &&
100 y_helper_->StartIsFixed(index) && y_helper_->EndIsFixed(index);
101 }
102
103 std::pair<IntegerValue, IntegerValue> GetBoxSizesMax(int index) const {
104 return {x_helper_->SizeMax(index), y_helper_->SizeMax(index)};
105 }
106
107 std::pair<IntegerValue, IntegerValue> GetLevelZeroBoxSizesMin(
108 int index) const {
109 return {x_helper_->LevelZeroSizeMin(index),
110 y_helper_->LevelZeroSizeMin(index)};
111 }
112
113 void ClearReason() {
114 x_helper_->ClearReason();
115 y_helper_->ClearReason();
116 }
117
118 void WatchAllBoxes(int id) { propagators_watching_.push_back(id); }
119
120 // Propagate a relationship between two boxes (ie., first must be to the left
121 // of the second, etc).
123 int first, int second, PairwiseRestriction::PairwiseRestrictionType type);
124
125 // Returns a "fixed size projection" of the item of the item `index`. More
126 // precisely, returns item of index `index` with its sizes fixed to their
127 // minimum value alongside a bounding box that contains the item.
129
130 // Returns a {start_min, start_max, end_min, end_max} view of the item of
131 // the index `index`.
133
134 // If there is no possible placement for the two mandatory boxes (they will
135 // necessarily overlap), call this function to report this as a conflict.
136 // Returns true.
137 bool ReportConflictFromTwoBoxes(int box1, int box2);
138
139 // Reports a conflict due to a (potentially relaxed) infeasible subproblem of
140 // the no_overlap_2d constraint. More concretely, this function takes a list
141 // of fixed-size rectangles and their placement domains in `ranges` that
142 // satisfy:
143 // - the problem of placing all the rectangles in their domain is
144 // infeasible;
145 // - the x and y sizes of each box in `ranges` are smaller or equal than
146 // the corresponding current minimum sizes of the boxes;
147 // - for each range in `ranges`, range.box_index.bounding_box is fully
148 // contained inside GetItemRangeForSizeMin(range.box_index).bounding_box.
149 // In other terms, each element is infeasible in a domain at least as
150 // large as the current one.
152 absl::Span<const RectangleInRange> ranges);
153
154 void AddXSizeMinReason(int index) { x_helper_->AddSizeMinReason(index); }
155 void AddYSizeMinReason(int index) { y_helper_->AddSizeMinReason(index); }
156 void AddSizeMinReason(int index) {
157 AddXSizeMinReason(index);
158 AddYSizeMinReason(index);
159 }
160
161 // Push the explanation that the left edge of this box is to the right of the
162 // vertical line x=lower_bound.
163 //
164 // | => |
165 // | => \/
166 // | => +---+
167 // | => | |
168 // | => +---+
169 // |
170 void AddLeftMinReason(int index, IntegerValue lower_bound) {
171 x_helper_->AddStartMinReason(index, lower_bound);
172 }
173
174 // Push the explanation that the left edge of this box is to the left of the
175 // vertical line x=upper_bound.
176 //
177 // | <= |
178 // \/ <= |
179 // +---------|---+
180 // | <= | |
181 // | <= | |
182 // +---------|---|
183 // |
184 void AddLeftMaxReason(int index, IntegerValue upper_bound) {
185 x_helper_->AddStartMaxReason(index, upper_bound);
186 }
187
188 // Push the explanation that the bottom edge of this box is to the top of the
189 // horizontal line y=lower_bound.
190 void AddBottomMinReason(int index, IntegerValue lower_bound) {
191 y_helper_->AddStartMinReason(index, lower_bound);
192 }
193
194 // Push the explanation that the bottom edge of this box is to the bottom of
195 // the vertical line y=upper_bound.
196 void AddBottomMaxReason(int index, IntegerValue upper_bound) {
197 y_helper_->AddStartMaxReason(index, upper_bound);
198 }
199
200 void AddPresenceReason(int index) {
201 x_helper_->AddPresenceReason(index);
202 y_helper_->AddPresenceReason(index);
203 }
204
205 bool IncreaseLeftMin(int index, IntegerValue new_lower_bound) {
206 x_helper_->ImportOtherReasons(*y_helper_);
207 return x_helper_->IncreaseStartMin(index, new_lower_bound);
208 }
209
211 x_helper_->ImportOtherReasons(*y_helper_);
212 return x_helper_->ReportConflict();
213 }
214
215 int NumBoxes() const { return x_helper_->NumTasks(); }
216
217 bool Propagate() override;
218
219 // Note that the helpers are only valid until the next call to
220 // Propagate().
221 SchedulingConstraintHelper& x_helper() const { return *x_helper_; }
222 SchedulingConstraintHelper& y_helper() const { return *y_helper_; }
223
226
228 return connected_components_;
229 }
230
231 int64_t InProcessingCount() const { return inprocessing_count_; }
232 int64_t LastLevelZeroChangeIdx() const {
233 return level_zero_bound_change_idx_;
234 }
235
236 private:
237 void Reset(absl::Span<const Rectangle> fixed_boxes,
238 absl::Span<const int> non_fixed_box_indexes);
239
240 CompactVectorVector<int> connected_components_;
241
242 bool axes_are_swapped_;
243 std::unique_ptr<SchedulingConstraintHelper> x_helper_;
244 std::unique_ptr<SchedulingConstraintHelper> y_helper_;
245 std::unique_ptr<SchedulingDemandHelper> x_demands_helper_;
246 std::unique_ptr<SchedulingDemandHelper> y_demands_helper_;
247 Model* model_;
248 GenericLiteralWatcher* watcher_;
249 std::vector<int> propagators_watching_;
250 int64_t inprocessing_count_ = 0;
251 int64_t level_zero_bound_change_idx_ = 0;
252};
253
254} // namespace sat
255} // namespace operations_research
256
257#endif // OR_TOOLS_SAT_NO_OVERLAP_2D_HELPER_H_
const CompactVectorVector< int > & connected_components() const
bool SynchronizeAndSetDirection(bool x_is_forward_after_swap=true, bool y_is_forward_after_swap=true, bool swap_x_and_y=false)
void AddLeftMaxReason(int index, IntegerValue upper_bound)
bool ReportConflictFromInfeasibleBoxRanges(absl::Span< const RectangleInRange > ranges)
std::pair< IntegerValue, IntegerValue > GetLevelZeroBoxSizesMin(int index) const
ItemWithVariableSize GetItemWithVariableSize(int index) const
void AddLeftMinReason(int index, IntegerValue lower_bound)
NoOverlap2DConstraintHelper(std::vector< AffineExpression > x_starts, std::vector< AffineExpression > x_ends, std::vector< AffineExpression > x_sizes, std::vector< LiteralIndex > x_reason_for_presence, std::vector< AffineExpression > y_starts, std::vector< AffineExpression > y_ends, std::vector< AffineExpression > y_sizes, std::vector< LiteralIndex > y_reason_for_presence, Model *model)
bool PropagateRelativePosition(int first, int second, PairwiseRestriction::PairwiseRestrictionType type)
void AddBottomMaxReason(int index, IntegerValue upper_bound)
void AddBottomMinReason(int index, IntegerValue lower_bound)
bool IncreaseLeftMin(int index, IntegerValue new_lower_bound)
std::pair< IntegerValue, IntegerValue > GetBoxSizesMax(int index) const
In SWIG mode, we don't want anything besides these top-level includes.
STL namespace.