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