Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
intervals.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 <optional>
18#include <utility>
19#include <variant>
20#include <vector>
21
22#include "absl/container/flat_hash_map.h"
23#include "absl/log/check.h"
24#include "absl/types/span.h"
26#include "ortools/sat/clause.h"
27#include "ortools/sat/integer.h"
31#include "ortools/sat/model.h"
38
39namespace operations_research {
40namespace sat {
41
43 : model_(model),
44 assignment_(model->GetOrCreate<Trail>()->Assignment()),
45 sat_solver_(model->GetOrCreate<SatSolver>()),
46 implications_(model->GetOrCreate<BinaryImplicationGraph>()),
47 integer_trail_(model->GetOrCreate<IntegerTrail>()),
48 reified_precedences_(model->GetOrCreate<ReifiedLinear2Bounds>()),
49 root_level_bounds_(model->GetOrCreate<RootLevelLinear2Bounds>()),
50 linear2_bounds_(model->GetOrCreate<Linear2Bounds>()),
51 integer_encoder_(model->GetOrCreate<IntegerEncoder>()) {}
52
53IntervalVariable IntervalsRepository::CreateInterval(IntegerVariable start,
54 IntegerVariable end,
55 IntegerVariable size,
56 IntegerValue fixed_size,
57 LiteralIndex is_present) {
59 size == kNoIntegerVariable
60 ? AffineExpression(fixed_size)
61 : AffineExpression(size),
62 is_present, /*add_linear_relation=*/true);
63}
64
68 LiteralIndex is_present,
69 bool add_linear_relation) {
70 // Create the interval.
71 const IntervalVariable i(starts_.size());
72 starts_.push_back(start);
73 ends_.push_back(end);
74 sizes_.push_back(size);
75 is_present_.push_back(is_present);
76
77 std::vector<Literal> enforcement_literals;
78 if (is_present != kNoLiteralIndex) {
79 enforcement_literals.push_back(Literal(is_present));
80 }
81
82 if (add_linear_relation) {
83 LinearConstraintBuilder builder(model_, IntegerValue(0), IntegerValue(0));
84 builder.AddTerm(Start(i), IntegerValue(1));
85 builder.AddTerm(Size(i), IntegerValue(1));
86 builder.AddTerm(End(i), IntegerValue(-1));
87 LoadConditionalLinearConstraint(enforcement_literals, builder.Build(),
88 model_);
89 }
90
91 return i;
92}
93
95 IntervalVariable a, IntervalVariable b) {
97 IntervalDefinition{.start = Start(a),
98 .end = End(a),
99 .size = Size(a),
100 .is_present = IsOptional(a)
101 ? std::optional(PresenceLiteral(a))
102 : std::nullopt},
103 IntervalDefinition{.start = Start(b),
104 .end = End(b),
105 .size = Size(b),
106 .is_present = IsOptional(b)
107 ? std::optional(PresenceLiteral(b))
108 : std::nullopt});
109}
110
111LiteralIndex
113 const IntervalDefinition& a, const IntervalDefinition& b) {
114 auto it = disjunctive_precedences_.find({a, b});
115 if (it != disjunctive_precedences_.end()) return it->second.Index();
116
117 std::vector<Literal> enforcement_literals;
118 if (a.is_present.has_value()) {
119 enforcement_literals.push_back(a.is_present.value());
120 }
121 if (b.is_present.has_value()) {
122 enforcement_literals.push_back(b.is_present.value());
123 }
124
125 auto remove_fixed_at_root_level =
126 [assignment = &assignment_,
127 sat_solver_ = sat_solver_](std::vector<Literal>& literals) {
128 int new_size = 0;
129 for (const Literal l : literals) {
130 if (assignment->LiteralIsAssigned(l)) {
131 const bool is_fixed =
132 sat_solver_->LiteralTrail().Info(l.Variable()).level == 0;
133 // We can ignore always absent interval, and skip the literal of the
134 // interval that are now always present.
135 if (is_fixed && assignment->LiteralIsTrue(l)) continue;
136 if (is_fixed && assignment->LiteralIsFalse(l)) return false;
137 }
138 literals[new_size++] = l;
139 }
140 literals.resize(new_size);
141 return true;
142 };
143
144 if (!remove_fixed_at_root_level(enforcement_literals)) return kNoLiteralIndex;
145
146 // task_a is currently before task_b ?
147 // Lets not create a literal that will be propagated right away.
148 const auto [expr_b_before_a, ub_b_before_a] =
150 const RelationStatus b_before_a_root_status =
151 root_level_bounds_->GetLevelZeroStatus(expr_b_before_a, kMinIntegerValue,
152 ub_b_before_a);
153
154 // task_b is before task_a ?
155 const auto [expr_a_before_b, ub_a_before_b] =
156 EncodeDifferenceLowerThan(a.end, b.start, 0);
157 const RelationStatus a_before_b_root_status =
158 root_level_bounds_->GetLevelZeroStatus(expr_a_before_b, kMinIntegerValue,
159 ub_a_before_b);
160
161 if (enforcement_literals.empty() &&
162 b_before_a_root_status != RelationStatus::IS_UNKNOWN &&
163 a_before_b_root_status == b_before_a_root_status) {
164 // We have "a before b" and "b before a" at root level (or similarly with
165 // "after"). This is UNSAT.
166 sat_solver_->NotifyThatModelIsUnsat();
167 return kNoLiteralIndex;
168 }
169
170 if (b_before_a_root_status == RelationStatus::IS_FALSE) {
171 if (!enforcement_literals.empty() ||
172 a_before_b_root_status == RelationStatus::IS_UNKNOWN) {
173 AddConditionalAffinePrecedence(enforcement_literals, a.end, b.start,
174 model_);
175 }
176 return kNoLiteralIndex;
177 }
178 const RelationStatus b_before_a_status = linear2_bounds_->GetStatus(
179 expr_b_before_a, kMinIntegerValue, ub_b_before_a);
180 if (b_before_a_status != RelationStatus::IS_UNKNOWN) {
181 // Abort if the relation is already known.
182 return kNoLiteralIndex;
183 }
184
185 if (a_before_b_root_status == RelationStatus::IS_FALSE) {
186 if (!enforcement_literals.empty() ||
187 b_before_a_root_status == RelationStatus::IS_UNKNOWN) {
188 AddConditionalAffinePrecedence(enforcement_literals, b.end, a.start,
189 model_);
190 }
191 return kNoLiteralIndex;
192 }
193 const RelationStatus a_before_b_status = linear2_bounds_->GetStatus(
194 expr_a_before_b, kMinIntegerValue, ub_a_before_b);
195 if (a_before_b_status != RelationStatus::IS_UNKNOWN) {
196 // Abort if the relation is already known.
197 return kNoLiteralIndex;
198 }
199
200 // Create a new literal.
201 //
202 // TODO(user): An alternative solution when it is enforced is to get/create
203 // - s <=> a.end <= b.start
204 // - t <=> b.end <= a.start
205 // and have enforcement => s + t == 1. The later might not even be needed
206 // though, since interval equation should already enforce it.
207 Literal a_before_b;
208 if (enforcement_literals.empty()) {
209 // We don't have any enforcement literal, so we should use the existing
210 // ReifiedLinear2Bounds class.
211 LiteralIndex a_before_b_index = GetPrecedenceLiteral(a.end, b.start);
212 const LiteralIndex b_before_a_index = GetPrecedenceLiteral(b.end, a.start);
213 if (a_before_b_index == kNoLiteralIndex &&
214 b_before_a_index == kNoLiteralIndex) {
216 a_before_b_index = GetPrecedenceLiteral(a.end, b.start);
217 DCHECK_NE(a_before_b_index, kNoLiteralIndex); // We tested not trivial.
218 // Now associate its negation with b.end <= a.start.
219 reified_precedences_->AddBoundEncodingIfNonTrivial(
220 Literal(a_before_b_index).Negated(), expr_b_before_a, ub_b_before_a);
221 } else if (a_before_b_index == kNoLiteralIndex &&
222 b_before_a_index != kNoLiteralIndex) {
223 // We already have a literal for b.end <= a.start.
224 // We can just use the negation of that literal.
225 a_before_b_index = Literal(b_before_a_index).NegatedIndex();
226 reified_precedences_->AddBoundEncodingIfNonTrivial(
227 Literal(a_before_b_index), expr_a_before_b, ub_a_before_b);
228 } else if (a_before_b_index != kNoLiteralIndex &&
229 b_before_a_index == kNoLiteralIndex) {
230 reified_precedences_->AddBoundEncodingIfNonTrivial(
231 Literal(a_before_b_index).Negated(), expr_b_before_a, ub_b_before_a);
232 } else {
233 // We have both literals. One must be the negation of the other.
234 implications_->AddImplication(Literal(a_before_b_index),
235 Literal(b_before_a_index).Negated());
236 implications_->AddImplication(Literal(a_before_b_index).Negated(),
237 Literal(b_before_a_index));
238 }
239 DCHECK_NE(a_before_b_index, kNoLiteralIndex);
240 a_before_b = Literal(a_before_b_index);
241 } else {
242 const BooleanVariable boolean_var = sat_solver_->NewBooleanVariable();
243 a_before_b = Literal(boolean_var, true);
244 }
245
246 disjunctive_precedences_.insert({{a, b}, a_before_b});
247 disjunctive_precedences_.insert({{b, a}, a_before_b.Negated()});
248
249 enforcement_literals.push_back(a_before_b);
250 AddConditionalAffinePrecedence(enforcement_literals, a.end, b.start, model_);
251 enforcement_literals.pop_back();
252
253 enforcement_literals.push_back(a_before_b.Negated());
254 AddConditionalAffinePrecedence(enforcement_literals, b.end, a.start, model_);
255 enforcement_literals.pop_back();
256
257 // The calls to AddConditionalAffinePrecedence() might have fixed some of the
258 // enforcement literals. Remove them if we are at level zero.
259 if (!remove_fixed_at_root_level(enforcement_literals)) return kNoLiteralIndex;
260
261 // Force the value of boolean_var in case the precedence is not active. This
262 // avoids duplicate solutions when enumerating all possible solutions.
263 for (const Literal l : enforcement_literals) {
264 implications_->AddBinaryClause(l, a_before_b);
265 }
266
267 return a_before_b;
268}
269
272 const auto [expr, ub] = EncodeDifferenceLowerThan(x, y, 0);
273 auto reified_bound = reified_precedences_->GetEncodedBound(expr, ub);
274 if (std::holds_alternative<ReifiedLinear2Bounds::ReifiedBoundType>(
275 reified_bound)) {
276 const auto bound_type =
277 std::get<ReifiedLinear2Bounds::ReifiedBoundType>(reified_bound);
280 // Nothing to do, precedence is trivial at level zero.
281 return false;
282 }
283 }
284
285 if (std::holds_alternative<Literal>(reified_bound)) {
286 // Already created.
287 return false;
288 }
289
290 if (std::holds_alternative<IntegerLiteral>(reified_bound)) {
291 if (integer_encoder_->GetAssociatedLiteral(
292 std::get<IntegerLiteral>(reified_bound)) != kNoLiteralIndex) {
293 return false;
294 }
295 // Create a new literal from the IntegerLiteral. This makes sure
296 // GetPrecedenceLiteral() always returns something if this function was
297 // called on a non-trivial precedence.
298 integer_encoder_->GetOrCreateAssociatedLiteral(
299 std::get<IntegerLiteral>(reified_bound));
300 return true;
301 }
302
303 // Create a new literal.
304 const BooleanVariable boolean_var = sat_solver_->NewBooleanVariable();
305 const Literal x_before_y = Literal(boolean_var, true);
306 reified_precedences_->AddBoundEncodingIfNonTrivial(x_before_y, expr, ub);
307
308 AffineExpression y_plus_one = y;
309 y_plus_one.constant += 1;
310 AddConditionalAffinePrecedence({x_before_y}, x, y, model_);
311 AddConditionalAffinePrecedence({x_before_y.Negated()}, y_plus_one, x, model_);
312 return true;
313}
314
317 const auto [expr, ub] = EncodeDifferenceLowerThan(x, y, 0);
318 auto reified_bound = reified_precedences_->GetEncodedBound(expr, ub);
319 if (std::holds_alternative<IntegerLiteral>(reified_bound)) {
320 return integer_encoder_->GetAssociatedLiteral(
321 std::get<IntegerLiteral>(reified_bound));
322 }
323 if (std::holds_alternative<Literal>(reified_bound)) {
324 return std::get<Literal>(reified_bound).Index();
325 }
326 if (std::holds_alternative<ReifiedLinear2Bounds::ReifiedBoundType>(
327 reified_bound)) {
328 const auto bound_type =
329 std::get<ReifiedLinear2Bounds::ReifiedBoundType>(reified_bound);
331 return integer_encoder_->GetTrueLiteral().Index();
332 }
334 return integer_encoder_->GetTrueLiteral().NegatedIndex();
335 }
336 }
337
338 return kNoLiteralIndex;
339}
340
343 {
344 const LiteralIndex index = GetPrecedenceLiteral(x, y);
345 if (index != kNoLiteralIndex) return Literal(index);
346 }
347
349 const LiteralIndex index = GetPrecedenceLiteral(x, y);
350 CHECK_NE(index, kNoLiteralIndex);
351 return Literal(index);
352}
353
354// TODO(user): Ideally we should sort the vector of variables, but right now
355// we cannot since we often use this with a parallel vector of demands. So this
356// "sorting" should happen in the presolver so we can share as much as possible.
358 std::vector<Literal> enforcement_literals,
359 const std::vector<IntervalVariable>& variables,
360 bool register_as_disjunctive_helper) {
361 std::sort(enforcement_literals.begin(), enforcement_literals.end());
362 const auto it = helper_repository_.find({enforcement_literals, variables});
363 if (it != helper_repository_.end()) return it->second;
364 std::vector<AffineExpression> starts;
365 std::vector<AffineExpression> ends;
366 std::vector<AffineExpression> sizes;
367 std::vector<LiteralIndex> reason_for_presence;
368
369 const int num_variables = variables.size();
370 starts.reserve(num_variables);
371 ends.reserve(num_variables);
372 sizes.reserve(num_variables);
373 reason_for_presence.reserve(num_variables);
374
375 for (const IntervalVariable i : variables) {
376 if (IsOptional(i)) {
377 reason_for_presence.push_back(PresenceLiteral(i).Index());
378 } else {
379 reason_for_presence.push_back(kNoLiteralIndex);
380 }
381 sizes.push_back(Size(i));
382 starts.push_back(Start(i));
383 ends.push_back(End(i));
384 }
385
387 std::move(starts), std::move(ends), std::move(sizes),
388 std::move(reason_for_presence), model_);
389 helper->RegisterWith(model_->GetOrCreate<GenericLiteralWatcher>(),
390 enforcement_literals);
391 helper_repository_[{enforcement_literals, variables}] = helper;
392 model_->TakeOwnership(helper);
393 if (register_as_disjunctive_helper) {
394 disjunctive_helpers_.push_back(helper);
395 }
396 return helper;
397}
398
400 std::vector<Literal> enforcement_literals,
401 const std::vector<IntervalVariable>& x_variables,
402 const std::vector<IntervalVariable>& y_variables) {
403 std::sort(enforcement_literals.begin(), enforcement_literals.end());
404 const auto it = no_overlap_2d_helper_repository_.find(
405 {enforcement_literals, x_variables, y_variables});
406 if (it != no_overlap_2d_helper_repository_.end()) return it->second;
407
408 std::vector<AffineExpression> x_starts;
409 std::vector<AffineExpression> x_ends;
410 std::vector<AffineExpression> x_sizes;
411 std::vector<LiteralIndex> x_reason_for_presence;
412
413 for (const IntervalVariable i : x_variables) {
414 if (IsOptional(i)) {
415 x_reason_for_presence.push_back(PresenceLiteral(i).Index());
416 } else {
417 x_reason_for_presence.push_back(kNoLiteralIndex);
418 }
419 x_sizes.push_back(Size(i));
420 x_starts.push_back(Start(i));
421 x_ends.push_back(End(i));
422 }
423 std::vector<AffineExpression> y_starts;
424 std::vector<AffineExpression> y_ends;
425 std::vector<AffineExpression> y_sizes;
426 std::vector<LiteralIndex> y_reason_for_presence;
427
428 for (const IntervalVariable i : y_variables) {
429 if (IsOptional(i)) {
430 y_reason_for_presence.push_back(PresenceLiteral(i).Index());
431 } else {
432 y_reason_for_presence.push_back(kNoLiteralIndex);
433 }
434 y_sizes.push_back(Size(i));
435 y_starts.push_back(Start(i));
436 y_ends.push_back(End(i));
437 }
439 std::move(x_starts), std::move(x_ends), std::move(x_sizes),
440 std::move(x_reason_for_presence), std::move(y_starts), std::move(y_ends),
441 std::move(y_sizes), std::move(y_reason_for_presence), model_);
442 helper->RegisterWith(model_->GetOrCreate<GenericLiteralWatcher>(),
443 enforcement_literals);
444 no_overlap_2d_helper_repository_[{enforcement_literals, x_variables,
445 y_variables}] = helper;
446 model_->TakeOwnership(helper);
447 return helper;
448}
449
452 absl::Span<const AffineExpression> demands) {
453 const std::pair<SchedulingConstraintHelper*, std::vector<AffineExpression>>
454 key = {helper,
455 std::vector<AffineExpression>(demands.begin(), demands.end())};
456 const auto it = demand_helper_repository_.find(key);
457 if (it != demand_helper_repository_.end()) return it->second;
458
459 SchedulingDemandHelper* demand_helper =
460 new SchedulingDemandHelper(demands, helper, model_);
461 model_->TakeOwnership(demand_helper);
462 demand_helper_repository_[key] = demand_helper;
463 return demand_helper;
464}
465
467 for (const auto& it : demand_helper_repository_) {
468 it.second->InitDecomposedEnergies();
469 }
470}
471
472} // namespace sat
473} // namespace operations_research
SchedulingConstraintHelper * GetOrCreateHelper(std::vector< Literal > enforcement_literals, const std::vector< IntervalVariable > &variables, bool register_as_disjunctive_helper=false)
Definition intervals.cc:357
SchedulingDemandHelper * GetOrCreateDemandHelper(SchedulingConstraintHelper *helper, absl::Span< const AffineExpression > demands)
Definition intervals.cc:450
AffineExpression Start(IntervalVariable i) const
Definition intervals.h:94
AffineExpression Size(IntervalVariable i) const
Definition intervals.h:93
AffineExpression End(IntervalVariable i) const
Definition intervals.h:95
Literal GetOrCreatePrecedenceLiteral(AffineExpression x, AffineExpression y)
Definition intervals.cc:341
LiteralIndex GetPrecedenceLiteral(AffineExpression x, AffineExpression y) const
Definition intervals.cc:315
Literal PresenceLiteral(IntervalVariable i) const
Definition intervals.h:74
void CreateDisjunctivePrecedenceLiteral(IntervalVariable a, IntervalVariable b)
Definition intervals.cc:94
bool CreatePrecedenceLiteralIfNonTrivial(AffineExpression x, AffineExpression y)
Definition intervals.cc:270
LiteralIndex GetOrCreateDisjunctivePrecedenceLiteralIfNonTrivial(const IntervalDefinition &a, const IntervalDefinition &b)
Definition intervals.cc:112
bool IsOptional(IntervalVariable i) const
Definition intervals.h:71
NoOverlap2DConstraintHelper * GetOrCreate2DHelper(std::vector< Literal > enforcement_literals, const std::vector< IntervalVariable > &x_variables, const std::vector< IntervalVariable > &y_variables)
Definition intervals.cc:399
IntervalVariable CreateInterval(IntegerVariable start, IntegerVariable end, IntegerVariable size, IntegerValue fixed_size, LiteralIndex is_present)
Definition intervals.cc:53
void AddTerm(IntegerVariable var, IntegerValue coeff)
LiteralIndex NegatedIndex() const
Definition sat_base.h:93
void RegisterWith(GenericLiteralWatcher *watcher, absl::Span< const Literal > enforcement_literals)
void RegisterWith(GenericLiteralWatcher *watcher, absl::Span< const Literal > enforcement_literals)
const LiteralIndex kNoLiteralIndex(-1)
void LoadConditionalLinearConstraint(const absl::Span< const Literal > enforcement_literals, const LinearConstraint &cst, Model *model)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
void AddConditionalAffinePrecedence(const absl::Span< const Literal > enforcement_literals, AffineExpression left, AffineExpression right, Model *model)
std::pair< LinearExpression2, IntegerValue > EncodeDifferenceLowerThan(AffineExpression a, AffineExpression b, IntegerValue ub)
OR-Tools root namespace.
ClosedInterval::Iterator end(ClosedInterval interval)