OR-Tools  9.3
scheduling_constraints.cc
Go to the documentation of this file.
1// Copyright 2010-2021 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 <functional>
18#include <vector>
19
20#include "absl/types/span.h"
22#include "ortools/base/macros.h"
23#include "ortools/sat/integer.h"
27#include "ortools/sat/model.h"
32
33namespace operations_research {
34namespace sat {
35
37 public:
38 explicit SelectedMinPropagator(Literal enforcement_literal,
39 AffineExpression target,
40 const std::vector<AffineExpression>& exprs,
41 const std::vector<Literal>& selectors,
42 Model* model)
43 : enforcement_literal_(enforcement_literal),
44 target_(target),
45 exprs_(exprs),
46 selectors_(selectors),
47 trail_(model->GetOrCreate<Trail>()),
48 integer_trail_(model->GetOrCreate<IntegerTrail>()),
49 precedences_(model->GetOrCreate<PrecedencesPropagator>()),
50 true_literal_(model->GetOrCreate<IntegerEncoder>()->GetTrueLiteral()) {}
51 bool Propagate() final;
53
54 private:
55 const Literal enforcement_literal_;
56 const AffineExpression target_;
57 const std::vector<AffineExpression> exprs_;
58 const std::vector<Literal> selectors_;
59 Trail* trail_;
60 IntegerTrail* integer_trail_;
61 PrecedencesPropagator* precedences_;
62 const Literal true_literal_;
63
64 std::vector<Literal> literal_reason_;
65 std::vector<IntegerLiteral> integer_reason_;
66
67 DISALLOW_COPY_AND_ASSIGN(SelectedMinPropagator);
68};
69
71 const VariablesAssignment& assignment = trail_->Assignment();
72
73 // helpers.
74 const auto add_var_non_selection_to_reason = [&](int i) {
75 DCHECK(assignment.LiteralIsFalse(selectors_[i]));
76 literal_reason_.push_back(selectors_[i]);
77 };
78 const auto add_var_selection_to_reason = [&](int i) {
79 DCHECK(assignment.LiteralIsTrue(selectors_[i]));
80 literal_reason_.push_back(selectors_[i].Negated());
81 };
82
83 // Push the given integer literal if lit is true. Note that if lit is still
84 // not assigned, we may still be able to deduce something.
85 // TODO(user): Move this to integer_trail, and remove from here and
86 // from scheduling helper.
87 const auto push_bound = [&](Literal enforcement_lit, IntegerLiteral i_lit) {
88 if (assignment.LiteralIsFalse(enforcement_lit)) return true;
89 if (integer_trail_->OptionalLiteralIndex(i_lit.var) !=
90 enforcement_lit.Index()) {
91 if (assignment.LiteralIsTrue(enforcement_lit)) {
92 // We can still push, but we do need the presence reason.
93 literal_reason_.push_back(Literal(enforcement_lit).Negated());
94 } else {
95 // In this case we cannot push lit.var, but we may force the enforcement
96 // literal to be false.
97 if (i_lit.bound > integer_trail_->UpperBound(i_lit.var)) {
98 integer_reason_.push_back(
99 IntegerLiteral::LowerOrEqual(i_lit.var, i_lit.bound - 1));
100 DCHECK(!assignment.LiteralIsFalse(enforcement_lit));
101 integer_trail_->EnqueueLiteral(Literal(enforcement_lit).Negated(),
102 literal_reason_, integer_reason_);
103 }
104 return true;
105 }
106 }
107
108 if (!integer_trail_->Enqueue(i_lit, literal_reason_, integer_reason_)) {
109 return false;
110 }
111
112 return true;
113 };
114
115 // Propagation.
116 const int num_vars = exprs_.size();
117 const IntegerValue target_min = integer_trail_->LowerBound(target_);
118 const IntegerValue target_max = integer_trail_->UpperBound(target_);
119
120 // Loop through the variables, and fills the quantities below.
121 // In our naming scheme, a variable is either ignored, selected, or possible.
122 IntegerValue min_of_mins(kMaxIntegerValue);
123 IntegerValue min_of_selected_maxes(kMaxIntegerValue);
124 IntegerValue max_of_possible_maxes(kMinIntegerValue);
125 int num_possible_vars = 0;
126 int num_selected_vars = 0;
127 int min_of_selected_maxes_index = -1;
128 int first_selected = -1;
129 for (int i = 0; i < num_vars; ++i) {
130 if (assignment.LiteralIsFalse(selectors_[i])) continue;
131
132 const IntegerValue var_min = integer_trail_->LowerBound(exprs_[i]);
133 const IntegerValue var_max = integer_trail_->UpperBound(exprs_[i]);
134
135 min_of_mins = std::min(min_of_mins, var_min);
136
137 if (assignment.LiteralIsTrue(selectors_[i])) {
138 DCHECK(assignment.LiteralIsTrue(enforcement_literal_));
139 num_selected_vars++;
140 if (var_max < min_of_selected_maxes) {
141 min_of_selected_maxes = var_max;
142 min_of_selected_maxes_index = i;
143 }
144 if (first_selected == -1) {
145 first_selected = i;
146 }
147 } else {
148 DCHECK(!assignment.LiteralIsFalse(selectors_[i]));
149 num_possible_vars++;
150 max_of_possible_maxes = std::max(max_of_possible_maxes, var_max);
151 }
152 }
153
154 if (min_of_mins > target_min) {
155 literal_reason_.clear();
156 integer_reason_.clear();
157 for (int i = 0; i < num_vars; ++i) {
158 if (assignment.LiteralIsFalse(selectors_[i])) {
159 add_var_non_selection_to_reason(i);
160 } else if (exprs_[i].var != kNoIntegerVariable) {
161 integer_reason_.push_back(exprs_[i].GreaterOrEqual(min_of_mins));
162 }
163 }
164 if (!push_bound(enforcement_literal_,
165 target_.GreaterOrEqual(min_of_mins))) {
166 return false;
167 }
168 }
169
170 if (num_selected_vars > 0 && min_of_selected_maxes < target_max) {
171 DCHECK(assignment.LiteralIsTrue(enforcement_literal_));
172 DCHECK_NE(min_of_selected_maxes_index, -1);
173 DCHECK(assignment.LiteralIsTrue(selectors_[min_of_selected_maxes_index]));
174 literal_reason_.clear();
175 integer_reason_.clear();
176 add_var_selection_to_reason(min_of_selected_maxes_index);
177 if (exprs_[min_of_selected_maxes_index].var != kNoIntegerVariable) {
178 integer_reason_.push_back(
179 exprs_[min_of_selected_maxes_index].LowerOrEqual(
180 min_of_selected_maxes));
181 }
182 if (!integer_trail_->Enqueue(target_.LowerOrEqual(min_of_selected_maxes),
183 literal_reason_, integer_reason_)) {
184 return false;
185 }
186 }
187
188 // Propagates in case every vars are still optional.
189 if (num_possible_vars > 0 && num_selected_vars == 0) {
190 if (target_max > max_of_possible_maxes) {
191 literal_reason_.clear();
192 integer_reason_.clear();
193
194 for (int i = 0; i < num_vars; ++i) {
195 if (assignment.LiteralIsFalse(selectors_[i])) {
196 add_var_non_selection_to_reason(i);
197 } else if (exprs_[i].var != kNoIntegerVariable) {
198 integer_reason_.push_back(
199 exprs_[i].LowerOrEqual(max_of_possible_maxes));
200 }
201 }
202 if (!push_bound(enforcement_literal_,
203 target_.LowerOrEqual(max_of_possible_maxes))) {
204 return false;
205 }
206 }
207 }
208
209 // All propagations and checks belows rely of the presence of the target.
210 if (!assignment.LiteralIsTrue(enforcement_literal_)) return true;
211
212 DCHECK_GE(integer_trail_->LowerBound(target_), min_of_mins);
213
214 // Note that the case num_possible == 1, num_selected_vars == 0 shouldn't
215 // happen because we assume that the enforcement <=> at_least_one_present
216 // clause has already been propagated.
217 if (num_possible_vars > 0) {
218 DCHECK_GT(num_possible_vars + num_selected_vars, 1);
219 return true;
220 }
221 if (num_selected_vars != 1) return true;
222
223 DCHECK_NE(first_selected, -1);
224 DCHECK(assignment.LiteralIsTrue(selectors_[first_selected]));
225 const AffineExpression unique_selected_var = exprs_[first_selected];
226
227 // Propagate bound from target to the unique selected var.
228 if (target_min > integer_trail_->LowerBound(unique_selected_var)) {
229 literal_reason_.clear();
230 integer_reason_.clear();
231 for (int i = 0; i < num_vars; ++i) {
232 if (i != first_selected) {
233 add_var_non_selection_to_reason(i);
234 } else {
235 add_var_selection_to_reason(i);
236 }
237 }
238 if (target_.var != kNoIntegerVariable) {
239 integer_reason_.push_back(target_.GreaterOrEqual(target_min));
240 }
241 if (!integer_trail_->Enqueue(unique_selected_var.GreaterOrEqual(target_min),
242 literal_reason_, integer_reason_)) {
243 return false;
244 }
245 }
246
247 if (target_max < integer_trail_->UpperBound(unique_selected_var)) {
248 literal_reason_.clear();
249 integer_reason_.clear();
250 for (int i = 0; i < num_vars; ++i) {
251 if (i != first_selected) {
252 add_var_non_selection_to_reason(i);
253 } else {
254 add_var_selection_to_reason(i);
255 }
256 }
257 if (target_.var != kNoIntegerVariable) {
258 integer_reason_.push_back(target_.LowerOrEqual(target_max));
259 }
260 if (!integer_trail_->Enqueue(unique_selected_var.LowerOrEqual(target_max),
261 literal_reason_, integer_reason_)) {
262 return false;
263 }
264 }
265
266 return true;
267}
268
270 const int id = watcher->Register(this);
271 for (int t = 0; t < exprs_.size(); ++t) {
272 watcher->WatchAffineExpression(exprs_[t], id);
273 watcher->WatchLiteral(selectors_[t], id);
274 }
275 watcher->WatchAffineExpression(target_, id);
276 watcher->WatchLiteral(enforcement_literal_, id);
277 return id;
278}
279
280std::function<void(Model*)> EqualMinOfSelectedVariables(
281 Literal enforcement_literal, AffineExpression target,
282 const std::vector<AffineExpression>& exprs,
283 const std::vector<Literal>& selectors) {
284 CHECK_EQ(exprs.size(), selectors.size());
285 return [=](Model* model) {
286 // If both a variable is selected and the enforcement literal is true, then
287 // the var is always greater than the target.
288 for (int i = 0; i < exprs.size(); ++i) {
289 LinearConstraintBuilder builder(model, kMinIntegerValue, IntegerValue(0));
290 builder.AddTerm(target, IntegerValue(1));
291 builder.AddTerm(exprs[i], IntegerValue(-1));
292 LoadConditionalLinearConstraint({enforcement_literal, selectors[i]},
293 builder.Build(), model);
294 }
295
296 // Add the dedicated propagator.
298 enforcement_literal, target, exprs, selectors, model);
299 constraint->RegisterWith(model->GetOrCreate<GenericLiteralWatcher>());
300 model->TakeOwnership(constraint);
301 };
302}
303
304std::function<void(Model*)> EqualMaxOfSelectedVariables(
305 Literal enforcement_literal, AffineExpression target,
306 const std::vector<AffineExpression>& exprs,
307 const std::vector<Literal>& selectors) {
308 CHECK_EQ(exprs.size(), selectors.size());
309 return [=](Model* model) {
310 std::vector<AffineExpression> negations;
311 for (const AffineExpression expr : exprs) {
312 negations.push_back(expr.Negated());
313 }
315 enforcement_literal, target.Negated(), negations, selectors));
316 };
317}
318
319std::function<void(Model*)> SpanOfIntervals(
320 IntervalVariable span, const std::vector<IntervalVariable>& intervals) {
321 return [=](Model* model) {
322 auto* sat_solver = model->GetOrCreate<SatSolver>();
323 auto* repository = model->GetOrCreate<IntervalsRepository>();
324
325 // If the target is absent, then all tasks are absent.
326 if (repository->IsAbsent(span)) {
327 for (const IntervalVariable interval : intervals) {
328 if (repository->IsOptional(interval)) {
329 sat_solver->AddBinaryClause(
330 repository->PresenceLiteral(span).Negated(),
331 repository->PresenceLiteral(interval));
332 } else if (repository->IsPresent(interval)) {
333 sat_solver->NotifyThatModelIsUnsat();
334 return;
335 }
336 }
337 return;
338 }
339
340 // The target is present iif at least one interval is present. This is a
341 // strict equivalence.
342 std::vector<Literal> presence_literals;
343 std::vector<AffineExpression> starts;
344 std::vector<AffineExpression> ends;
345 std::vector<Literal> clause;
346 bool at_least_one_interval_is_present = false;
347 const Literal true_literal =
348 model->GetOrCreate<IntegerEncoder>()->GetTrueLiteral();
349
350 for (const IntervalVariable interval : intervals) {
351 if (repository->IsAbsent(interval)) continue;
352
353 if (repository->IsOptional(interval)) {
354 const Literal task_lit = repository->PresenceLiteral(interval);
355 presence_literals.push_back(task_lit);
356 clause.push_back(task_lit);
357
358 if (repository->IsOptional(span)) {
359 // task is present => target is present.
360 sat_solver->AddBinaryClause(task_lit.Negated(),
361 repository->PresenceLiteral(span));
362 }
363
364 } else {
365 presence_literals.push_back(true_literal);
366 at_least_one_interval_is_present = true;
367 }
368 starts.push_back(repository->Start(interval));
369 ends.push_back(repository->End(interval));
370 }
371
372 if (!at_least_one_interval_is_present) {
373 // enforcement_literal is true => one of the task is present.
374 if (repository->IsOptional(span)) {
375 clause.push_back(repository->PresenceLiteral(span).Negated());
376 }
377 sat_solver->AddProblemClause(clause);
378 }
379
380 // Link target start and end to the starts and ends of the tasks.
381 const Literal enforcement_literal =
382 repository->IsOptional(span)
383 ? repository->PresenceLiteral(span)
384 : model->GetOrCreate<IntegerEncoder>()->GetTrueLiteral();
385 model->Add(EqualMinOfSelectedVariables(enforcement_literal,
386 repository->Start(span), starts,
387 presence_literals));
389 enforcement_literal, repository->End(span), ends, presence_literals));
390 };
391}
392
393} // namespace sat
394} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:892
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:895
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:896
#define DCHECK(condition)
Definition: base/logging.h:890
void WatchLiteral(Literal l, int id, int watch_index=-1)
Definition: integer.h:1561
void WatchAffineExpression(AffineExpression e, int id)
Definition: integer.h:1278
int Register(PropagatorInterface *propagator)
Definition: integer.cc:2028
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:1048
LiteralIndex OptionalLiteralIndex(IntegerVariable i) const
Definition: integer.h:714
void EnqueueLiteral(Literal literal, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:1162
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1449
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1445
void AddTerm(IntegerVariable var, IntegerValue coeff)
LiteralIndex Index() const
Definition: sat_base.h:87
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
bool AddBinaryClause(Literal a, Literal b)
Definition: sat_solver.cc:189
int RegisterWith(GenericLiteralWatcher *watcher)
SelectedMinPropagator(Literal enforcement_literal, AffineExpression target, const std::vector< AffineExpression > &exprs, const std::vector< Literal > &selectors, Model *model)
const VariablesAssignment & Assignment() const
Definition: sat_base.h:383
bool LiteralIsTrue(Literal literal) const
Definition: sat_base.h:153
bool LiteralIsFalse(Literal literal) const
Definition: sat_base.h:150
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::function< void(Model *)> LowerOrEqual(IntegerVariable v, int64_t ub)
Definition: integer.h:1706
void LoadConditionalLinearConstraint(const absl::Span< const Literal > enforcement_literals, const LinearConstraint &cst, Model *model)
Definition: integer_expr.h:643
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
std::function< void(Model *)> EqualMaxOfSelectedVariables(Literal enforcement_literal, AffineExpression target, const std::vector< AffineExpression > &exprs, const std::vector< Literal > &selectors)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
Definition: integer.h:1669
std::function< void(Model *)> GreaterOrEqual(IntegerVariable v, int64_t lb)
Definition: integer.h:1691
std::function< void(Model *)> SpanOfIntervals(IntervalVariable span, const std::vector< IntervalVariable > &intervals)
std::function< void(Model *)> EqualMinOfSelectedVariables(Literal enforcement_literal, AffineExpression target, const std::vector< AffineExpression > &exprs, const std::vector< Literal > &selectors)
Collection of objects used to extend the Constraint Solver library.
STL namespace.
IntervalVar * interval
Definition: resource.cc:100
AffineExpression Negated() const
Definition: integer.h:258
IntegerLiteral GreaterOrEqual(IntegerValue bound) const
Definition: integer.h:1416
IntegerLiteral LowerOrEqual(IntegerValue bound) const
Definition: integer.h:1432
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1393