Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
intervals.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_INTERVALS_H_
15#define ORTOOLS_SAT_INTERVALS_H_
16
17#include <cstdint>
18#include <functional>
19#include <tuple>
20#include <utility>
21#include <vector>
22
23#include "absl/container/flat_hash_map.h"
24#include "absl/log/check.h"
25#include "absl/types/span.h"
27#include "ortools/sat/clause.h"
28#include "ortools/sat/integer.h"
30#include "ortools/sat/model.h"
37
38namespace operations_research {
39namespace sat {
40
41// This class maintains a set of intervals which correspond to three integer
42// variables (start, end and size). It automatically registers with the
43// PrecedencesPropagator the relation between the bounds of each interval and
44// provides many helper functions to add precedences relation between intervals.
46 public:
47 explicit IntervalsRepository(Model* model);
48
49 // This type is neither copyable nor movable.
52
53 // Returns the current number of intervals in the repository.
54 // The interval will always be identified by an integer in [0, num_intervals).
55 int NumIntervals() const { return starts_.size(); }
56
57 // Functions to add a new interval to the repository.
58 // If add_linear_relation is true, then we also link start, size and end.
59 //
60 // - If size == kNoIntegerVariable, then the size is fixed to fixed_size.
61 // - If is_present != kNoLiteralIndex, then this is an optional interval.
62 IntervalVariable CreateInterval(IntegerVariable start, IntegerVariable end,
63 IntegerVariable size, IntegerValue fixed_size,
64 LiteralIndex is_present);
67 LiteralIndex is_present = kNoLiteralIndex,
68 bool add_linear_relation = false);
69
70 // Returns whether or not a interval is optional and the associated literal.
71 bool IsOptional(IntervalVariable i) const {
72 return is_present_[i] != kNoLiteralIndex;
73 }
74 Literal PresenceLiteral(IntervalVariable i) const {
75 return Literal(is_present_[i]);
76 }
77 bool IsPresent(IntervalVariable i) const {
78 if (!IsOptional(i)) return true;
79 return assignment_.LiteralIsTrue(PresenceLiteral(i));
80 }
81 bool IsAbsent(IntervalVariable i) const {
82 if (!IsOptional(i)) return false;
83 return assignment_.LiteralIsFalse(PresenceLiteral(i));
84 }
85
86 // The 3 integer variables associated to a interval.
87 // Fixed size intervals will have a kNoIntegerVariable as size.
88 //
89 // Note: For an optional interval, the start/end variables are propagated
90 // assuming the interval is present. Because of that, these variables can
91 // cross each other or have an empty domain. If any of this happen, then the
92 // PresenceLiteral() of this interval will be propagated to false.
93 AffineExpression Size(IntervalVariable i) const { return sizes_[i]; }
94 AffineExpression Start(IntervalVariable i) const { return starts_[i]; }
95 AffineExpression End(IntervalVariable i) const { return ends_[i]; }
96
97 // Return the minimum size of the given IntervalVariable.
98 IntegerValue MinSize(IntervalVariable i) const {
99 return integer_trail_->LowerBound(sizes_[i]);
100 }
101
102 // Return the maximum size of the given IntervalVariable.
103 IntegerValue MaxSize(IntervalVariable i) const {
104 return integer_trail_->UpperBound(sizes_[i]);
105 }
106
107 // Utility function that returns a vector will all intervals.
108 std::vector<IntervalVariable> AllIntervals() const {
109 std::vector<IntervalVariable> result;
110 for (IntervalVariable i(0); i < NumIntervals(); ++i) {
111 result.push_back(i);
112 }
113 return result;
114 }
115
116 // Returns a SchedulingConstraintHelper corresponding to the given variables.
117 // Note that the order of interval in the helper will be the same.
118 //
119 // It is possible to indicate that this correspond to a disjunctive constraint
120 // by setting the Boolean to true. This is used by our scheduling heuristic
121 // based on precedences.
123 std::vector<Literal> enforcement_literals,
124 const std::vector<IntervalVariable>& variables,
125 bool register_as_disjunctive_helper = false);
126
128 std::vector<Literal> enforcement_literals,
129 const std::vector<IntervalVariable>& x_variables,
130 const std::vector<IntervalVariable>& y_variables);
131
132 // Returns a SchedulingDemandHelper corresponding to the given helper and
133 // demands. Note that the order of interval in the helper and the order of
134 // demands must be the compatible.
137 absl::Span<const AffineExpression> demands);
138
139 // Calls InitDecomposedEnergies on all SchedulingDemandHelper created.
141
142 // Assuming a and b cannot overlap if they are present, this create a new
143 // literal such that:
144 // - literal & presences => a is before b.
145 // - not(literal) & presences => b is before a.
146 // - not present => literal @ true for disallowing multiple solutions.
147 //
148 // If such literal already exists this returns it.
149 void CreateDisjunctivePrecedenceLiteral(IntervalVariable a,
150 IntervalVariable b);
152 const IntervalDefinition& a, const IntervalDefinition& b);
153
154 // Creates a literal l <=> y >= x.
155 // Returns true if such literal is "non-trivial" and was created.
158
159 // Returns a literal l <=> y >= x if it exist or kNoLiteralIndex
160 // otherwise. This could be the one created by
161 // CreateDisjunctivePrecedenceLiteral() or
162 // CreatePrecedenceLiteralIfNonTrivial().
164 AffineExpression y) const;
165
166 // Combines the two calls. Note that we will only create literals when the
167 // relation is not known.
169
170 const std::vector<SchedulingConstraintHelper*>& AllDisjunctiveHelpers()
171 const {
172 return disjunctive_helpers_;
173 }
174
175 // We register cumulative at load time so that our search heuristic can loop
176 // over all cumulative constraints easily.
183 cumulative_helpers_.push_back(helper);
184 }
185 const std::vector<CumulativeHelper>& AllCumulativeHelpers() const {
186 return cumulative_helpers_;
187 }
188
189 private:
190 // External classes needed.
191 Model* model_;
192 const VariablesAssignment& assignment_;
193 SatSolver* sat_solver_;
194 BinaryImplicationGraph* implications_;
195 IntegerTrail* integer_trail_;
196 ReifiedLinear2Bounds* reified_precedences_;
197 RootLevelLinear2Bounds* root_level_bounds_;
198 Linear2Bounds* linear2_bounds_;
199 IntegerEncoder* integer_encoder_;
200
201 // Literal indicating if the tasks is executed. Tasks that are always executed
202 // will have a kNoLiteralIndex entry in this vector.
204
205 // The integer variables for each tasks.
209
210 // We can share the helper for all the propagators that work on the same set
211 // of intervals, and with the same enforcement literals.
212 absl::flat_hash_map<
213 std::pair<std::vector<Literal>, std::vector<IntervalVariable>>,
215 helper_repository_;
216 absl::flat_hash_map<
217 std::tuple<std::vector<Literal>, std::vector<IntervalVariable>,
218 std::vector<IntervalVariable>>,
220 no_overlap_2d_helper_repository_;
221 absl::flat_hash_map<
222 std::pair<SchedulingConstraintHelper*, std::vector<AffineExpression>>,
224 demand_helper_repository_;
225
226 // Disjunctive precedences.
227 absl::flat_hash_map<std::pair<IntervalDefinition, IntervalDefinition>,
228 Literal>
229 disjunctive_precedences_;
230
231 // Disjunctive/Cumulative helpers_.
232 std::vector<SchedulingConstraintHelper*> disjunctive_helpers_;
233 std::vector<CumulativeHelper> cumulative_helpers_;
234};
235
236// =============================================================================
237// Model based functions.
238// =============================================================================
239
240inline std::function<int64_t(const Model&)> MinSize(IntervalVariable v) {
241 return [=](const Model& model) {
242 return model.Get<IntervalsRepository>()->MinSize(v).value();
243 };
244}
245
246inline std::function<int64_t(const Model&)> MaxSize(IntervalVariable v) {
247 return [=](const Model& model) {
248 return model.Get<IntervalsRepository>()->MaxSize(v).value();
249 };
250}
251
252inline std::function<bool(const Model&)> IsOptional(IntervalVariable v) {
253 return [=](const Model& model) {
254 return model.Get<IntervalsRepository>()->IsOptional(v);
255 };
256}
257
258inline std::function<Literal(const Model&)> IsPresentLiteral(
259 IntervalVariable v) {
260 return [=](const Model& model) {
261 return model.Get<IntervalsRepository>()->PresenceLiteral(v);
262 };
263}
264
265inline std::function<IntervalVariable(Model*)> NewInterval(int64_t min_start,
266 int64_t max_end,
267 int64_t size) {
268 return [=](Model* model) {
269 CHECK_LE(min_start + size, max_end);
270 const IntegerVariable start =
271 model->Add(NewIntegerVariable(min_start, max_end - size));
272 return model->GetOrCreate<IntervalsRepository>()->CreateInterval(
273 AffineExpression(start),
274 AffineExpression(start, IntegerValue(1), IntegerValue(size)),
275 AffineExpression(IntegerValue(size)), kNoLiteralIndex,
276 /*add_linear_relation=*/false);
277 };
278}
279
280inline std::function<IntervalVariable(Model*)> NewInterval(
281 IntegerVariable start, IntegerVariable end, IntegerVariable size) {
282 return [=](Model* model) {
283 return model->GetOrCreate<IntervalsRepository>()->CreateInterval(
284 start, end, size, IntegerValue(0), kNoLiteralIndex);
285 };
286}
287
288inline std::function<IntervalVariable(Model*)> NewIntervalWithVariableSize(
289 int64_t min_start, int64_t max_end, int64_t min_size, int64_t max_size) {
290 return [=](Model* model) {
291 return model->GetOrCreate<IntervalsRepository>()->CreateInterval(
292 model->Add(NewIntegerVariable(min_start, max_end)),
293 model->Add(NewIntegerVariable(min_start, max_end)),
294 model->Add(NewIntegerVariable(min_size, max_size)), IntegerValue(0),
296 };
297}
298
299// Note that this should only be used in tests.
300inline std::function<IntervalVariable(Model*)> NewOptionalInterval(
301 int64_t min_start, int64_t max_end, int64_t size, Literal is_present) {
302 return [=](Model* model) {
303 CHECK_LE(min_start + size, max_end);
304 const IntegerVariable start =
305 model->Add(NewIntegerVariable(min_start, max_end - size));
306 const IntervalVariable interval =
307 model->GetOrCreate<IntervalsRepository>()->CreateInterval(
308 AffineExpression(start),
309 AffineExpression(start, IntegerValue(1), IntegerValue(size)),
310 AffineExpression(IntegerValue(size)), is_present.Index(),
311 /*add_linear_relation=*/false);
312
313 // To not have too many solutions during enumeration, we force the
314 // start at its min value for absent interval.
315 model->Add(Implication({is_present.Negated()},
316 IntegerLiteral::LowerOrEqual(start, min_start)));
317 return interval;
318 };
319}
320
321inline std::function<IntervalVariable(Model*)> NewOptionalInterval(
322 IntegerVariable start, IntegerVariable end, IntegerVariable size,
323 Literal is_present) {
324 return [=](Model* model) {
325 return model->GetOrCreate<IntervalsRepository>()->CreateInterval(
326 start, end, size, IntegerValue(0), is_present.Index());
327 };
328}
329
330inline std::function<IntervalVariable(Model*)>
331NewOptionalIntervalWithVariableSize(int64_t min_start, int64_t max_end,
332 int64_t min_size, int64_t max_size,
333 Literal is_present) {
334 return [=](Model* model) {
335 return model->GetOrCreate<IntervalsRepository>()->CreateInterval(
336 model->Add(NewIntegerVariable(min_start, max_end)),
337 model->Add(NewIntegerVariable(min_start, max_end)),
338 model->Add(NewIntegerVariable(min_size, max_size)), IntegerValue(0),
339 is_present.Index());
340 };
341}
342
344 const AffineExpression& capacity, SchedulingDemandHelper* demands_helper,
345 Model* model, std::vector<IntegerVariable>* vars);
346
347} // namespace sat
348} // namespace operations_research
349
350#endif // ORTOOLS_SAT_INTERVALS_H_
Definition model.h:345
IntervalsRepository(const IntervalsRepository &)=delete
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
bool IsAbsent(IntervalVariable i) const
Definition intervals.h:81
const std::vector< CumulativeHelper > & AllCumulativeHelpers() const
Definition intervals.h:185
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
const std::vector< SchedulingConstraintHelper * > & AllDisjunctiveHelpers() const
Definition intervals.h:170
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
void RegisterCumulative(CumulativeHelper helper)
Definition intervals.h:182
IntervalVariable CreateInterval(IntegerVariable start, IntegerVariable end, IntegerVariable size, IntegerValue fixed_size, LiteralIndex is_present)
Definition intervals.cc:53
IntegerValue MaxSize(IntervalVariable i) const
Definition intervals.h:103
IntegerValue MinSize(IntervalVariable i) const
Definition intervals.h:98
IntervalsRepository & operator=(const IntervalsRepository &)=delete
std::vector< IntervalVariable > AllIntervals() const
Definition intervals.h:108
bool IsPresent(IntervalVariable i) const
Definition intervals.h:77
LiteralIndex Index() const
Definition sat_base.h:92
std::function< int64_t(const Model &)> MaxSize(IntervalVariable v)
Definition intervals.h:248
std::function< IntervalVariable(Model *)> NewOptionalIntervalWithVariableSize(int64_t min_start, int64_t max_end, int64_t min_size, int64_t max_size, Literal is_present)
Definition intervals.h:333
std::function< Literal(const Model &)> IsPresentLiteral(IntervalVariable v)
Definition intervals.h:260
const LiteralIndex kNoLiteralIndex(-1)
std::function< IntervalVariable(Model *)> NewIntervalWithVariableSize(int64_t min_start, int64_t max_end, int64_t min_size, int64_t max_size)
Definition intervals.h:290
std::function< IntervalVariable(Model *)> NewInterval(int64_t min_start, int64_t max_end, int64_t size)
Definition intervals.h:267
std::function< void(Model *)> Implication(absl::Span< const Literal > enforcement_literals, IntegerLiteral i)
Definition integer.h:1889
std::function< IntervalVariable(Model *)> NewOptionalInterval(int64_t min_start, int64_t max_end, int64_t size, Literal is_present)
Definition intervals.h:302
void AppendVariablesFromCapacityAndDemands(const AffineExpression &capacity, SchedulingDemandHelper *demands_helper, Model *model, std::vector< IntegerVariable > *vars)
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
Definition integer.h:1771
std::function< int64_t(const Model &)> MinSize(IntervalVariable v)
Definition intervals.h:242
std::function< bool(const Model &)> IsOptional(IntervalVariable v)
Definition intervals.h:254
OR-Tools root namespace.
ClosedInterval::Iterator end(ClosedInterval interval)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)