OR-Tools  9.6
scheduling_constraints.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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"
21 #include "ortools/base/logging.h"
22 #include "ortools/base/macros.h"
23 #include "ortools/sat/integer.h"
25 #include "ortools/sat/intervals.h"
27 #include "ortools/sat/model.h"
29 #include "ortools/sat/sat_base.h"
30 #include "ortools/sat/sat_solver.h"
32 
33 namespace operations_research {
34 namespace 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;
52  int RegisterWith(GenericLiteralWatcher* watcher);
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 on the presence of the target.
210  if (!assignment.LiteralIsTrue(enforcement_literal_)) return true;
211 
212  // Note that the case num_possible == 1, num_selected_vars == 0 shouldn't
213  // happen because we assume that the enforcement <=> at_least_one_present
214  // clause has already been propagated.
215  if (num_possible_vars > 0) {
216  DCHECK_GT(num_possible_vars + num_selected_vars, 1);
217  return true;
218  }
219  if (num_selected_vars != 1) return true;
220 
221  DCHECK_NE(first_selected, -1);
222  DCHECK(assignment.LiteralIsTrue(selectors_[first_selected]));
223  const AffineExpression unique_selected_var = exprs_[first_selected];
224 
225  // Propagate bound from target to the unique selected var.
226  if (target_min > integer_trail_->LowerBound(unique_selected_var)) {
227  literal_reason_.clear();
228  integer_reason_.clear();
229  for (int i = 0; i < num_vars; ++i) {
230  if (i != first_selected) {
231  add_var_non_selection_to_reason(i);
232  } else {
233  add_var_selection_to_reason(i);
234  }
235  }
236  if (target_.var != kNoIntegerVariable) {
237  integer_reason_.push_back(target_.GreaterOrEqual(target_min));
238  }
239  if (!integer_trail_->Enqueue(unique_selected_var.GreaterOrEqual(target_min),
240  literal_reason_, integer_reason_)) {
241  return false;
242  }
243  }
244 
245  if (target_max < integer_trail_->UpperBound(unique_selected_var)) {
246  literal_reason_.clear();
247  integer_reason_.clear();
248  for (int i = 0; i < num_vars; ++i) {
249  if (i != first_selected) {
250  add_var_non_selection_to_reason(i);
251  } else {
252  add_var_selection_to_reason(i);
253  }
254  }
255  if (target_.var != kNoIntegerVariable) {
256  integer_reason_.push_back(target_.LowerOrEqual(target_max));
257  }
258  if (!integer_trail_->Enqueue(unique_selected_var.LowerOrEqual(target_max),
259  literal_reason_, integer_reason_)) {
260  return false;
261  }
262  }
263 
264  return true;
265 }
266 
268  const int id = watcher->Register(this);
269  for (int t = 0; t < exprs_.size(); ++t) {
270  watcher->WatchAffineExpression(exprs_[t], id);
271  watcher->WatchLiteral(selectors_[t], id);
272  }
273  watcher->WatchAffineExpression(target_, id);
274  watcher->WatchLiteral(enforcement_literal_, id);
275  return id;
276 }
277 
278 // TODO(user): Change API to not use the enforcement literal.
279 std::function<void(Model*)> EqualMinOfSelectedVariables(
280  Literal enforcement_literal, AffineExpression target,
281  const std::vector<AffineExpression>& exprs,
282  const std::vector<Literal>& selectors) {
283  CHECK_EQ(exprs.size(), selectors.size());
284  return [=](Model* model) {
285  // If both a variable is selected and the enforcement literal is true, then
286  // the var is always greater than the target.
287  for (int i = 0; i < exprs.size(); ++i) {
288  LinearConstraintBuilder builder(model, kMinIntegerValue, IntegerValue(0));
289  builder.AddTerm(target, IntegerValue(1));
290  builder.AddTerm(exprs[i], IntegerValue(-1));
291  LoadConditionalLinearConstraint({enforcement_literal, selectors[i]},
292  builder.Build(), model);
293  }
294 
295  // Add the dedicated propagator.
297  enforcement_literal, target, exprs, selectors, model);
298  constraint->RegisterWith(model->GetOrCreate<GenericLiteralWatcher>());
299  model->TakeOwnership(constraint);
300  };
301 }
302 
303 std::function<void(Model*)> EqualMaxOfSelectedVariables(
304  Literal enforcement_literal, AffineExpression target,
305  const std::vector<AffineExpression>& exprs,
306  const std::vector<Literal>& selectors) {
307  CHECK_EQ(exprs.size(), selectors.size());
308  return [=](Model* model) {
309  std::vector<AffineExpression> negations;
310  for (const AffineExpression expr : exprs) {
311  negations.push_back(expr.Negated());
312  }
314  enforcement_literal, target.Negated(), negations, selectors));
315  };
316 }
317 
318 std::function<void(Model*)> SpanOfIntervals(
319  IntervalVariable span, const std::vector<IntervalVariable>& intervals) {
320  return [=](Model* model) {
321  auto* sat_solver = model->GetOrCreate<SatSolver>();
322  auto* repository = model->GetOrCreate<IntervalsRepository>();
323 
324  // If the target is absent, then all tasks are absent.
325  if (repository->IsAbsent(span)) {
326  for (const IntervalVariable interval : intervals) {
327  if (repository->IsOptional(interval)) {
328  sat_solver->AddBinaryClause(
329  repository->PresenceLiteral(span).Negated(),
330  repository->PresenceLiteral(interval));
331  } else if (repository->IsPresent(interval)) {
332  sat_solver->NotifyThatModelIsUnsat();
333  return;
334  }
335  }
336  return;
337  }
338 
339  // The target is present iff at least one interval is present. This is a
340  // strict equivalence.
341  std::vector<Literal> presence_literals;
342  std::vector<AffineExpression> starts;
343  std::vector<AffineExpression> ends;
344  std::vector<Literal> clause;
345  bool at_least_one_interval_is_present = false;
346  const Literal true_literal =
347  model->GetOrCreate<IntegerEncoder>()->GetTrueLiteral();
348 
349  for (const IntervalVariable interval : intervals) {
350  if (repository->IsAbsent(interval)) continue;
351 
352  if (repository->IsOptional(interval)) {
353  const Literal task_lit = repository->PresenceLiteral(interval);
354  presence_literals.push_back(task_lit);
355  clause.push_back(task_lit);
356 
357  if (repository->IsOptional(span)) {
358  // task is present => target is present.
359  sat_solver->AddBinaryClause(task_lit.Negated(),
360  repository->PresenceLiteral(span));
361  }
362 
363  } else {
364  presence_literals.push_back(true_literal);
365  at_least_one_interval_is_present = true;
366  }
367  starts.push_back(repository->Start(interval));
368  ends.push_back(repository->End(interval));
369  }
370 
371  if (!at_least_one_interval_is_present) {
372  // enforcement_literal is true => one of the task is present.
373  if (repository->IsOptional(span)) {
374  clause.push_back(repository->PresenceLiteral(span).Negated());
375  }
376  sat_solver->AddProblemClause(clause, /*is_safe=*/false);
377  }
378 
379  // Link target start and end to the starts and ends of the tasks.
380  const Literal enforcement_literal =
381  repository->IsOptional(span)
382  ? repository->PresenceLiteral(span)
383  : model->GetOrCreate<IntegerEncoder>()->GetTrueLiteral();
384  model->Add(EqualMinOfSelectedVariables(enforcement_literal,
385  repository->Start(span), starts,
386  presence_literals));
388  enforcement_literal, repository->End(span), ends, presence_literals));
389  };
390 }
391 
392 } // namespace sat
393 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
void WatchLiteral(Literal l, int id, int watch_index=-1)
Definition: integer.h:1673
void WatchAffineExpression(AffineExpression e, int id)
Definition: integer.h:1376
int Register(PropagatorInterface *propagator)
Definition: integer.cc:2286
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:1228
LiteralIndex OptionalLiteralIndex(IntegerVariable i) const
Definition: integer.h:784
void EnqueueLiteral(Literal literal, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:1387
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1561
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1557
void AddTerm(IntegerVariable var, IntegerValue coeff)
LiteralIndex Index() const
Definition: sat_base.h:90
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:190
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:402
bool LiteralIsTrue(Literal literal) const
Definition: sat_base.h:164
bool LiteralIsFalse(Literal literal) const
Definition: sat_base.h:161
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
std::function< void(Model *)> GreaterOrEqual(IntegerVariable v, int64_t lb)
Definition: integer.h:1803
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
Definition: integer.h:1781
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
void LoadConditionalLinearConstraint(const absl::Span< const Literal > enforcement_literals, const LinearConstraint &cst, Model *model)
Definition: integer_expr.h:606
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< void(Model *)> LowerOrEqual(IntegerVariable v, int64_t ub)
Definition: integer.h:1818
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.
IntervalVar * interval
Definition: resource.cc:101
AffineExpression Negated() const
Definition: integer.h:276
IntegerLiteral GreaterOrEqual(IntegerValue bound) const
Definition: integer.h:1528
IntegerLiteral LowerOrEqual(IntegerValue bound) const
Definition: integer.h:1544
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1505