OR-Tools  8.0
cumulative_energy.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 <memory>
17 #include <utility>
18 
19 #include "ortools/base/int_type.h"
21 #include "ortools/base/logging.h"
22 #include "ortools/sat/sat_base.h"
23 
24 namespace operations_research {
25 namespace sat {
26 
27 void AddCumulativeEnergyConstraint(std::vector<AffineExpression> energies,
30  Model* model) {
31  auto* watcher = model->GetOrCreate<GenericLiteralWatcher>();
32  auto* integer_trail = model->GetOrCreate<IntegerTrail>();
33 
35  std::move(energies), capacity, integer_trail, helper);
36  constraint->RegisterWith(watcher);
37  model->TakeOwnership(constraint);
38 }
39 
40 void AddCumulativeOverloadChecker(const std::vector<AffineExpression>& demands,
43  Model* model) {
44  auto* watcher = model->GetOrCreate<GenericLiteralWatcher>();
45  auto* integer_trail = model->GetOrCreate<IntegerTrail>();
46 
47  std::vector<AffineExpression> energies;
48  const int num_tasks = helper->NumTasks();
49  CHECK_EQ(demands.size(), num_tasks);
50  for (int t = 0; t < num_tasks; ++t) {
51  // TODO(user): Remove once helper->Durations()[t] is an expression.
52  const AffineExpression duration =
53  helper->DurationVars()[t] == kNoIntegerVariable ||
54  integer_trail->IsFixed(helper->DurationVars()[t])
55  ? AffineExpression(helper->DurationMin(t))
56  : AffineExpression(helper->DurationVars()[t]);
57  const AffineExpression demand = demands[t];
58 
59  if (demand.var == kNoIntegerVariable &&
60  duration.var == kNoIntegerVariable) {
61  CHECK_GE(demand.constant, 0);
62  CHECK_GE(duration.constant, 0);
63  energies.emplace_back(demand.constant * duration.constant);
64  } else if (demand.var == kNoIntegerVariable) {
65  CHECK_GE(demand.constant, 0);
66  energies.push_back(duration);
67  energies.back().coeff *= demand.constant;
68  energies.back().constant *= demand.constant;
69  } else if (duration.var == kNoIntegerVariable) {
70  CHECK_GE(duration.constant, 0);
71  energies.push_back(demand);
72  energies.back().coeff *= duration.constant;
73  energies.back().constant *= duration.constant;
74  } else {
75  // The case where both demand and duration are variable should be rare.
76  //
77  // TODO(user): Handle when needed by creating an intermediate product
78  // variable equal to demand * duration. Note that because of the affine
79  // expression, we do need some custom code for this.
80  LOG(INFO)
81  << "Overload checker with variable demand and varialbe duration "
82  "is currently not implemented. Skipping.";
83  return;
84  }
85  }
86 
87  CumulativeEnergyConstraint* constraint =
88  new CumulativeEnergyConstraint(energies, capacity, integer_trail, helper);
89  constraint->RegisterWith(watcher);
90  model->TakeOwnership(constraint);
91 }
92 
94  std::vector<AffineExpression> energies, AffineExpression capacity,
95  IntegerTrail* integer_trail, SchedulingConstraintHelper* helper)
96  : energies_(std::move(energies)),
97  capacity_(capacity),
98  integer_trail_(integer_trail),
99  helper_(helper),
100  theta_tree_() {
101  const int num_tasks = helper_->NumTasks();
102  CHECK_EQ(energies_.size(), num_tasks);
103  task_to_start_event_.resize(num_tasks);
104 }
105 
107  const int id = watcher->Register(this);
108  helper_->WatchAllTasks(id, watcher);
110 }
111 
113  // This only uses one time direction, but the helper might be used elsewhere.
114  // TODO(user): just keep the current direction?
115  helper_->SetTimeDirection(true);
116 
117  const IntegerValue capacity_max = integer_trail_->UpperBound(capacity_);
118  // TODO(user): force capacity_max >= 0, fail/remove optionals when 0.
119  if (capacity_max <= 0) return true;
120 
121  // Set up theta tree.
122  start_event_task_time_.clear();
123  int num_events = 0;
124  for (const auto task_time : helper_->TaskByIncreasingStartMin()) {
125  const int task = task_time.task_index;
126  if (helper_->IsAbsent(task) ||
127  integer_trail_->UpperBound(energies_[task]) == 0) {
128  task_to_start_event_[task] = -1;
129  continue;
130  }
131  start_event_task_time_.emplace_back(task_time);
132  task_to_start_event_[task] = num_events;
133  num_events++;
134  }
135  start_event_is_present_.assign(num_events, false);
136  theta_tree_.Reset(num_events);
137 
138  bool tree_has_mandatory_intervals = false;
139 
140  // Main loop: insert tasks by increasing end_max, check for overloads.
141  for (const auto task_time :
143  const int current_task = task_time.task_index;
144  const IntegerValue current_end = task_time.time;
145  if (task_to_start_event_[current_task] == -1) continue;
146 
147  // Add the current task to the tree.
148  {
149  const int current_event = task_to_start_event_[current_task];
150  const IntegerValue start_min = start_event_task_time_[current_event].time;
151  const bool is_present = helper_->IsPresent(current_task);
152  start_event_is_present_[current_event] = is_present;
153  if (is_present) {
154  tree_has_mandatory_intervals = true;
155  theta_tree_.AddOrUpdateEvent(
156  current_event, start_min * capacity_max,
157  integer_trail_->LowerBound(energies_[current_task]),
158  integer_trail_->UpperBound(energies_[current_task]));
159  } else {
160  theta_tree_.AddOrUpdateOptionalEvent(
161  current_event, start_min * capacity_max,
162  integer_trail_->UpperBound(energies_[current_task]));
163  }
164  }
165 
166  if (tree_has_mandatory_intervals) {
167  // Find the critical interval.
168  const IntegerValue envelope = theta_tree_.GetEnvelope();
169  const int critical_event =
170  theta_tree_.GetMaxEventWithEnvelopeGreaterThan(envelope - 1);
171  const IntegerValue window_start =
172  start_event_task_time_[critical_event].time;
173  const IntegerValue window_end = current_end;
174  const IntegerValue window_size = window_end - window_start;
175  if (window_size == 0) continue;
176  const IntegerValue new_capacity_min =
177  CeilRatio(envelope - window_start * capacity_max, window_size);
178 
179  // Push the new capacity min, note that this can fail if it go above the
180  // maximum capacity.
181  //
182  // TODO(user): We do not need the capacity max in the reason, but by using
183  // a lower one, we could maybe have propagated more the minimum capacity.
184  // investigate.
185  if (new_capacity_min > integer_trail_->LowerBound(capacity_)) {
186  helper_->ClearReason();
187  for (int event = critical_event; event < num_events; event++) {
188  if (start_event_is_present_[event]) {
189  const int task = start_event_task_time_[event].task_index;
190  helper_->AddPresenceReason(task);
191  if (energies_[task].var != kNoIntegerVariable) {
192  helper_->MutableIntegerReason()->push_back(
193  integer_trail_->LowerBoundAsLiteral(energies_[task].var));
194  }
195  helper_->AddStartMinReason(task, window_start);
196  helper_->AddEndMaxReason(task, window_end);
197  }
198  }
199  if (capacity_.var == kNoIntegerVariable) {
200  return helper_->ReportConflict();
201  } else {
202  if (!helper_->PushIntegerLiteral(
203  capacity_.GreaterOrEqual(new_capacity_min))) {
204  return false;
205  }
206  }
207  }
208  }
209 
210  // Reduce energy of all tasks whose max energy would exceed an interval
211  // ending at current_end.
212  while (theta_tree_.GetOptionalEnvelope() > current_end * capacity_max) {
213  // Some task's max energy is too high, reduce its maximal energy.
214  // Explain with tasks present in the critical interval.
215  // If it is optional, it might get excluded, in that case,
216  // remove it from the tree.
217  // TODO(user): This could be done lazily.
218  // TODO(user): the same required task can have its energy pruned
219  // several times, making this algorithm O(n^2 log n). Is there a way
220  // to get the best pruning in one go? This looks like edge-finding not
221  // being able to converge in one pass, so it might not be easy.
222  helper_->ClearReason();
223  int critical_event;
224  int event_with_new_energy_max;
225  IntegerValue new_energy_max;
227  current_end * capacity_max, &critical_event,
228  &event_with_new_energy_max, &new_energy_max);
229 
230  const IntegerValue window_start =
231  start_event_task_time_[critical_event].time;
232 
233  // TODO(user): Improve window_end using envelope of critical event.
234  const IntegerValue window_end = current_end;
235  for (int event = critical_event; event < num_events; event++) {
236  if (start_event_is_present_[event]) {
237  if (event == event_with_new_energy_max) continue;
238  const int task = start_event_task_time_[event].task_index;
239  helper_->AddPresenceReason(task);
240  helper_->AddStartMinReason(task, window_start);
241  helper_->AddEndMaxReason(task, window_end);
242  if (energies_[task].var != kNoIntegerVariable) {
243  helper_->MutableIntegerReason()->push_back(
244  integer_trail_->LowerBoundAsLiteral(energies_[task].var));
245  }
246  }
247  }
248  if (capacity_.var != kNoIntegerVariable) {
249  helper_->MutableIntegerReason()->push_back(
250  integer_trail_->UpperBoundAsLiteral(capacity_.var));
251  }
252 
253  const int task_with_new_energy_max =
254  start_event_task_time_[event_with_new_energy_max].task_index;
255  helper_->AddStartMinReason(task_with_new_energy_max, window_start);
256  helper_->AddEndMaxReason(task_with_new_energy_max, window_end);
257 
258  if (new_energy_max <
259  integer_trail_->LowerBound(energies_[task_with_new_energy_max])) {
260  if (helper_->IsOptional(task_with_new_energy_max)) {
261  return helper_->PushTaskAbsence(task_with_new_energy_max);
262  } else {
263  return helper_->ReportConflict();
264  }
265  } else {
266  const IntegerLiteral deduction =
267  energies_[task_with_new_energy_max].LowerOrEqual(new_energy_max);
268  if (!helper_->PushIntegerLiteralIfTaskPresent(task_with_new_energy_max,
269  deduction)) {
270  return false;
271  }
272  }
273 
274  if (helper_->IsPresent(task_with_new_energy_max)) {
275  theta_tree_.AddOrUpdateEvent(
276  task_to_start_event_[task_with_new_energy_max],
277  start_event_task_time_[event_with_new_energy_max].time *
278  capacity_max,
279  integer_trail_->LowerBound(energies_[task_with_new_energy_max]),
280  new_energy_max);
281  } else {
282  theta_tree_.RemoveEvent(event_with_new_energy_max);
283  }
284  }
285  }
286  return true;
287 }
288 
289 } // namespace sat
290 } // namespace operations_research
operations_research::sat::CumulativeEnergyConstraint::CumulativeEnergyConstraint
CumulativeEnergyConstraint(std::vector< AffineExpression > energies, AffineExpression capacity, IntegerTrail *integer_trail, SchedulingConstraintHelper *helper)
Definition: cumulative_energy.cc:93
operations_research::sat::CumulativeEnergyConstraint::RegisterWith
void RegisterWith(GenericLiteralWatcher *watcher)
Definition: cumulative_energy.cc:106
var
IntVar * var
Definition: expr_array.cc:1858
operations_research::sat::IntegerLiteral
Definition: integer.h:164
cumulative_energy.h
operations_research::sat::kNoIntegerVariable
const IntegerVariable kNoIntegerVariable(-1)
operations_research::sat::SchedulingConstraintHelper::IsAbsent
bool IsAbsent(int t) const
Definition: intervals.h:397
operations_research::sat::AddCumulativeOverloadChecker
void AddCumulativeOverloadChecker(const std::vector< AffineExpression > &demands, AffineExpression capacity, SchedulingConstraintHelper *helper, Model *model)
Definition: cumulative_energy.cc:40
operations_research::sat::SchedulingConstraintHelper::AddEndMaxReason
void AddEndMaxReason(int t, IntegerValue upper_bound)
Definition: intervals.h:473
operations_research::sat::CeilRatio
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:92
operations_research::sat::IntegerTrail::UpperBound
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1221
operations_research::sat::SchedulingConstraintHelper::DurationVars
const std::vector< IntegerVariable > & DurationVars() const
Definition: intervals.h:257
start_min
Rev< int64 > start_min
Definition: sched_constraints.cc:241
logging.h
operations_research::sat::SchedulingConstraintHelper::SetTimeDirection
void SetTimeDirection(bool is_forward)
Definition: intervals.cc:142
operations_research::sat::SchedulingConstraintHelper
Definition: intervals.h:137
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::SchedulingConstraintHelper::ClearReason
void ClearReason()
Definition: intervals.h:402
operations_research::sat::GenericLiteralWatcher::Register
int Register(PropagatorInterface *propagator)
Definition: integer.cc:1798
operations_research::sat::IntegerTrail
Definition: integer.h:534
operations_research::sat::GenericLiteralWatcher::NotifyThatPropagatorMayNotReachFixedPointInOnePass
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)
Definition: integer.cc:1828
operations_research::sat::SchedulingConstraintHelper::TaskByIncreasingStartMin
const std::vector< TaskTime > & TaskByIncreasingStartMin()
Definition: intervals.cc:156
operations_research::sat::Model
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
operations_research::sat::ThetaLambdaTree::AddOrUpdateEvent
void AddOrUpdateEvent(int event, IntegerType initial_envelope, IntegerType energy_min, IntegerType energy_max)
Definition: theta_tree.cc:111
operations_research::sat::ThetaLambdaTree::Reset
void Reset(int num_events)
Definition: theta_tree.cc:40
operations_research::sat::CumulativeEnergyConstraint
Definition: cumulative_energy.h:50
sat_base.h
operations_research::sat::SchedulingConstraintHelper::NumTasks
int NumTasks() const
Definition: intervals.h:157
demand
int64 demand
Definition: resource.cc:123
operations_research::sat::AddCumulativeEnergyConstraint
void AddCumulativeEnergyConstraint(std::vector< AffineExpression > energies, AffineExpression capacity, SchedulingConstraintHelper *helper, Model *model)
Definition: cumulative_energy.cc:27
operations_research::sat::ThetaLambdaTree::GetOptionalEnvelope
IntegerType GetOptionalEnvelope() const
Definition: theta_tree.cc:173
operations_research::sat::SchedulingConstraintHelper::PushIntegerLiteralIfTaskPresent
ABSL_MUST_USE_RESULT bool PushIntegerLiteralIfTaskPresent(int t, IntegerLiteral bound)
Definition: intervals.cc:271
operations_research::sat::SchedulingConstraintHelper::TaskByDecreasingEndMax
const std::vector< TaskTime > & TaskByDecreasingEndMax()
Definition: intervals.cc:193
operations_research::sat::GenericLiteralWatcher
Definition: integer.h:1056
operations_research::sat::ThetaLambdaTree::GetEventsWithOptionalEnvelopeGreaterThan
void GetEventsWithOptionalEnvelopeGreaterThan(IntegerType target_envelope, int *critical_event, int *optional_event, IntegerType *available_energy) const
Definition: theta_tree.cc:189
operations_research::sat::SchedulingConstraintHelper::IsOptional
bool IsOptional(int t) const
Definition: intervals.h:388
int_type.h
operations_research::sat::SchedulingConstraintHelper::WatchAllTasks
void WatchAllTasks(int id, GenericLiteralWatcher *watcher, bool watch_start_max=true, bool watch_end_max=true) const
Definition: intervals.cc:343
operations_research::sat::AffineExpression
Definition: integer.h:214
operations_research::sat::IntegerTrail::UpperBoundAsLiteral
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
Definition: integer.h:1252
operations_research::sat::SchedulingConstraintHelper::PushTaskAbsence
ABSL_MUST_USE_RESULT bool PushTaskAbsence(int t)
Definition: intervals.cc:322
operations_research::sat::SchedulingConstraintHelper::IsPresent
bool IsPresent(int t) const
Definition: intervals.h:392
operations_research::sat::SchedulingConstraintHelper::AddStartMinReason
void AddStartMinReason(int t, IntegerValue lower_bound)
Definition: intervals.h:437
operations_research::sat::IntegerTrail::LowerBoundAsLiteral
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
Definition: integer.h:1247
operations_research::sat::ThetaLambdaTree::GetEnvelope
IntegerType GetEnvelope() const
Definition: theta_tree.cc:168
operations_research::sat::CumulativeEnergyConstraint::Propagate
bool Propagate() final
Definition: cumulative_energy.cc:112
operations_research::sat::SchedulingConstraintHelper::PushIntegerLiteral
ABSL_MUST_USE_RESULT bool PushIntegerLiteral(IntegerLiteral bound)
Definition: intervals.cc:266
model
GRBmodel * model
Definition: gurobi_interface.cc:195
operations_research::sat::SchedulingConstraintHelper::MutableIntegerReason
std::vector< IntegerLiteral > * MutableIntegerReason()
Definition: intervals.h:233
operations_research::sat::IntegerTrail::LowerBound
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1217
gtl::reversed_view
ReverseView< Container > reversed_view(const Container &c)
Definition: iterator_adaptors.h:33
iterator_adaptors.h
operations_research::sat::AffineExpression::var
IntegerVariable var
Definition: integer.h:236
operations_research::sat::AffineExpression::constant
IntegerValue constant
Definition: integer.h:238
operations_research::sat::AffineExpression::GreaterOrEqual
IntegerLiteral GreaterOrEqual(IntegerValue bound) const
Definition: integer.cc:28
operations_research::sat::ThetaLambdaTree::GetMaxEventWithEnvelopeGreaterThan
int GetMaxEventWithEnvelopeGreaterThan(IntegerType target_envelope) const
Definition: theta_tree.cc:179
operations_research::sat::SchedulingConstraintHelper::ReportConflict
ABSL_MUST_USE_RESULT bool ReportConflict()
Definition: intervals.cc:338
capacity
int64 capacity
Definition: routing_flow.cc:129
operations_research::sat::SchedulingConstraintHelper::DurationMin
IntegerValue DurationMin(int t) const
Definition: intervals.h:347
operations_research::sat::ThetaLambdaTree::AddOrUpdateOptionalEvent
void AddOrUpdateOptionalEvent(int event, IntegerType initial_envelope_opt, IntegerType energy_max)
Definition: theta_tree.cc:124
operations_research::sat::ThetaLambdaTree::RemoveEvent
void RemoveEvent(int event)
Definition: theta_tree.cc:147
operations_research::sat::SchedulingConstraintHelper::AddPresenceReason
void AddPresenceReason(int t)
Definition: intervals.h:411
time
int64 time
Definition: resource.cc:1683