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