Google OR-Tools v9.14
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
2d_distances_propagator.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 <cstdint>
17#include <string>
18#include <utility>
19#include <vector>
20
21#include "absl/container/flat_hash_map.h"
22#include "absl/log/check.h"
23#include "absl/log/log.h"
24#include "absl/types/span.h"
27#include "ortools/sat/integer.h"
30#include "ortools/sat/model.h"
35
36namespace operations_research {
37namespace sat {
38
40 NoOverlap2DConstraintHelper* helper, Model* model)
41 : helper_(*helper),
42 binary_relations_maps_(model->GetOrCreate<BinaryRelationsMaps>()),
43 shared_stats_(model->GetOrCreate<SharedStatistics>()) {
45}
46
47void Precedences2DPropagator::CollectPairsOfBoxesWithNonTrivialDistance() {
49 non_trivial_pairs_.clear();
50
51 struct VarUsage {
52 // boxes[0=x, 1=y][0=start, 1=end]
53 std::vector<int> boxes[2][2];
54 };
55 absl::flat_hash_map<IntegerVariable, VarUsage> var_to_box_and_coeffs;
56
57 for (int dim = 0; dim < 2; ++dim) {
58 const SchedulingConstraintHelper& dim_helper =
59 dim == 0 ? helper_.x_helper() : helper_.y_helper();
60 for (int j = 0; j < 2; ++j) {
61 const absl::Span<const AffineExpression> interval_points =
62 j == 0 ? dim_helper.Starts() : dim_helper.Ends();
63 for (int i = 0; i < helper_.NumBoxes(); ++i) {
64 if (interval_points[i].var != kNoIntegerVariable) {
65 var_to_box_and_coeffs[PositiveVariable(interval_points[i].var)]
66 .boxes[dim][j]
67 .push_back(i);
68 }
69 }
70 }
71 }
72
73 VLOG(2) << "CollectPairsOfBoxesWithNonTrivialDistance called, num_exprs: "
74 << binary_relations_maps_->GetAllExpressionsWithAffineBounds().size();
75 for (const LinearExpression2& expr :
76 binary_relations_maps_->GetAllExpressionsWithAffineBounds()) {
77 auto it1 = var_to_box_and_coeffs.find(PositiveVariable(expr.vars[0]));
78 auto it2 = var_to_box_and_coeffs.find(PositiveVariable(expr.vars[1]));
79 if (it1 == var_to_box_and_coeffs.end() ||
80 it2 == var_to_box_and_coeffs.end()) {
81 continue;
82 }
83
84 const VarUsage& usage1 = it1->second;
85 const VarUsage& usage2 = it2->second;
86 for (int dim = 0; dim < 2; ++dim) {
87 const SchedulingConstraintHelper& dim_helper =
88 dim == 0 ? helper_.x_helper() : helper_.y_helper();
89 for (const int box1 : usage1.boxes[dim][0 /* start */]) {
90 for (const int box2 : usage2.boxes[dim][1 /* end */]) {
91 if (box1 == box2) continue;
92 const AffineExpression& start = dim_helper.Starts()[box1];
93 const AffineExpression& end = dim_helper.Ends()[box2];
94 LinearExpression2 expr2;
95 expr2.vars[0] = start.var;
96 expr2.vars[1] = end.var;
97 expr2.coeffs[0] = start.coeff;
98 expr2.coeffs[1] = -end.coeff;
99 expr2.SimpleCanonicalization();
100 expr2.DivideByGcd();
101 if (expr == expr2) {
102 if (box1 < box2) {
103 non_trivial_pairs_.push_back({box1, box2});
104 } else {
105 non_trivial_pairs_.push_back({box2, box1});
106 }
107 }
108 }
109 }
110 }
111 }
112
113 gtl::STLSortAndRemoveDuplicates(&non_trivial_pairs_);
114}
115
117 if (!helper_.SynchronizeAndSetDirection()) return false;
118 if (last_helper_inprocessing_count_ != helper_.InProcessingCount() ||
119 helper_.x_helper().CurrentDecisionLevel() == 0 ||
120 last_num_expressions_ !=
121 binary_relations_maps_->NumExpressionsWithAffineBounds()) {
122 last_helper_inprocessing_count_ = helper_.InProcessingCount();
123 last_num_expressions_ =
124 binary_relations_maps_->NumExpressionsWithAffineBounds();
125 CollectPairsOfBoxesWithNonTrivialDistance();
126 }
127
128 num_calls_++;
129
130 SchedulingConstraintHelper* helpers[2] = {&helper_.x_helper(),
131 &helper_.y_helper()};
132
133 for (const auto& [box1, box2] : non_trivial_pairs_) {
134 DCHECK(box1 < helper_.NumBoxes());
135 DCHECK(box2 < helper_.NumBoxes());
136 DCHECK_NE(box1, box2);
137 if (!helper_.IsPresent(box1) || !helper_.IsPresent(box2)) {
138 continue;
139 }
140
141 bool is_unfeasible = true;
142 for (int dim = 0; dim < 2; dim++) {
143 const SchedulingConstraintHelper* helper = helpers[dim];
144 for (int j = 0; j < 2; j++) {
145 int b1 = box1;
146 int b2 = box2;
147 if (j == 1) {
148 std::swap(b1, b2);
149 }
151 expr.vars[0] = helper->Starts()[b1].var;
152 expr.vars[1] = helper->Ends()[b2].var;
153 expr.coeffs[0] = helper->Starts()[b1].coeff;
154 expr.coeffs[1] = -helper->Ends()[b2].coeff;
155 const IntegerValue ub_of_start_minus_end_value =
156 binary_relations_maps_->UpperBound(expr) +
157 helper->Starts()[b1].constant - helper->Ends()[b2].constant;
158 if (ub_of_start_minus_end_value >= 0) {
159 is_unfeasible = false;
160 break;
161 }
162 }
163 if (!is_unfeasible) break;
164 }
165 if (!is_unfeasible) continue;
166
167 // We have a mandatory overlap on both x and y! Explain and propagate.
168
169 helper_.ClearReason();
170 num_conflicts_++;
171
172 for (int dim = 0; dim < 2; dim++) {
173 SchedulingConstraintHelper* helper = helpers[dim];
174 for (int j = 0; j < 2; j++) {
175 int b1 = box1;
176 int b2 = box2;
177 if (j == 1) {
178 std::swap(b1, b2);
179 }
181 expr.vars[0] = helper->Starts()[b1].var;
182 expr.vars[1] = helper->Ends()[b2].var;
183 expr.coeffs[0] = helper->Starts()[b1].coeff;
184 expr.coeffs[1] = -helper->Ends()[b2].coeff;
185 binary_relations_maps_->AddReasonForUpperBoundLowerThan(
186 expr,
187 -(helper->Starts()[b1].constant - helper->Ends()[b2].constant) - 1,
188 helper_.x_helper().MutableLiteralReason(),
189 helper_.x_helper().MutableIntegerReason());
190 }
191 }
192 helper_.AddPresenceReason(box1);
193 helper_.AddPresenceReason(box2);
194 return helper_.ReportConflict();
195 }
196 return true;
197}
198
200 const int id = watcher->Register(this);
201 helper_.WatchAllBoxes(id);
202 binary_relations_maps_->WatchAllLinearExpressions2(id);
203 return id;
204}
205
207 if (!VLOG_IS_ON(1)) return;
208 std::vector<std::pair<std::string, int64_t>> stats;
209 stats.push_back({"Precedences2DPropagator/called", num_calls_});
210 stats.push_back({"Precedences2DPropagator/conflicts", num_conflicts_});
211 stats.push_back({"Precedences2DPropagator/pairs", non_trivial_pairs_.size()});
212
213 shared_stats_->AddStats(stats);
214}
215
216} // namespace sat
217} // namespace operations_research
int Register(PropagatorInterface *propagator)
Registers a propagator and returns its unique ids.
Definition integer.cc:2326
bool SynchronizeAndSetDirection(bool x_is_forward_after_swap=true, bool y_is_forward_after_swap=true, bool swap_x_and_y=false)
Precedences2DPropagator(NoOverlap2DConstraintHelper *helper, Model *model)
absl::Span< const AffineExpression > Starts() const
absl::Span< const AffineExpression > Ends() const
Simple class to add statistics by name and print them at the end.
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition stl_util.h:55
const IntegerVariable kNoIntegerVariable(-1)
IntegerVariable PositiveVariable(IntegerVariable i)
In SWIG mode, we don't want anything besides these top-level includes.
ClosedInterval::Iterator end(ClosedInterval interval)