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