20#include "absl/log/check.h"
35 : num_tasks_(helper->NumTasks()),
41 mandatory_energy_before_end_max_.resize(num_tasks_);
42 mandatory_energy_before_start_min_.resize(num_tasks_);
45 size_free_.resize(num_tasks_);
46 energy_free_.resize(num_tasks_);
50 const int id = watcher->
Register(
this);
52 helper_->WatchAllTasks(
id);
53 for (
int t = 0; t < num_tasks_; t++) {
61 if (!helper_->SynchronizeAndSetTimeDirection(
true))
return false;
62 if (!TimeTableEdgeFindingPass())
return false;
63 if (!helper_->SynchronizeAndSetTimeDirection(
false))
return false;
64 if (!TimeTableEdgeFindingPass())
return false;
68void TimeTableEdgeFinding::BuildTimeTable() {
76 const IntegerValue start_max = -negated_smax;
77 if (start_max < helper_->EndMin(t)) {
78 scp_.push_back({t, start_max});
83 for (
const auto task_time : helper_->TaskByIncreasingEndMin()) {
84 const int t = task_time.task_index;
85 if (!helper_->IsPresent(t))
continue;
86 if (helper_->StartMax(t) < task_time.time) {
87 ecp_.push_back(task_time);
91 DCHECK_EQ(scp_.size(), ecp_.size());
93 const auto by_decreasing_end_max = helper_->TaskByDecreasingEndMax();
94 const auto by_start_min = helper_->TaskByIncreasingStartMin();
96 IntegerValue height = IntegerValue(0);
97 IntegerValue energy = IntegerValue(0);
101 IntegerValue previous_time = IntegerValue(0);
106 int index_emax = num_tasks_ - 1;
108 while (index_emax >= 0) {
110 IntegerValue time = by_decreasing_end_max[index_emax].time;
111 if (index_smin < num_tasks_) {
112 time = std::min(time, by_start_min[index_smin].time);
114 if (index_scp < scp_.size()) {
115 time = std::min(time, scp_[index_scp].time);
117 if (index_ecp < ecp_.size()) {
118 time = std::min(time, ecp_[index_ecp].time);
122 energy += (time - previous_time) * height;
123 previous_time = time;
126 while (index_smin < num_tasks_ && by_start_min[index_smin].time == time) {
127 mandatory_energy_before_start_min_[by_start_min[index_smin].task_index] =
133 while (index_emax >= 0 && by_decreasing_end_max[index_emax].time == time) {
134 mandatory_energy_before_end_max_[by_decreasing_end_max[index_emax]
135 .task_index] = energy;
140 while (index_scp < scp_.size() && scp_[index_scp].time == time) {
141 height += demands_->DemandMin(scp_[index_scp].task_index);
146 while (index_ecp < ecp_.size() && ecp_[index_ecp].time == time) {
147 height -= demands_->DemandMin(ecp_[index_ecp].task_index);
153bool TimeTableEdgeFinding::TimeTableEdgeFindingPass() {
154 if (!demands_->CacheAllEnergyValues())
return true;
156 IntegerValue earliest_start_min = std::numeric_limits<IntegerValue>::max();
157 IntegerValue latest_end_max = std::numeric_limits<IntegerValue>::min();
158 IntegerValue maximum_demand_min = IntegerValue(0);
161 for (
int t = 0; t < num_tasks_; ++t) {
163 const IntegerValue start_max = helper_->StartMax(t);
164 const IntegerValue end_min = helper_->EndMin(t);
165 const IntegerValue demand_min = demands_->DemandMin(t);
166 IntegerValue mandatory_energy(0);
168 earliest_start_min = std::min(earliest_start_min, helper_->StartMin(t));
169 latest_end_max = std::max(latest_end_max, helper_->EndMax(t));
170 maximum_demand_min = std::max(maximum_demand_min, demand_min);
172 if (start_max >= end_min) {
173 size_free_[t] = helper_->SizeMin(t);
175 const IntegerValue mandatory_size = end_min - start_max;
176 size_free_[t] = helper_->SizeMin(t) - mandatory_size;
177 mandatory_energy = mandatory_size * demand_min;
180 const IntegerValue min_energy = demands_->EnergyMin(t);
181 energy_free_[t] = min_energy - mandatory_energy;
182 DCHECK_GE(energy_free_[t], 0);
186 maximum_demand_min))) {
195 const auto& by_start_min = helper_->TaskByIncreasingStartMin();
202 for (
const TaskTime end_task_time : helper_->TaskByDecreasingEndMax()) {
203 const int end_task = end_task_time.task_index;
206 if (!helper_->IsPresent(end_task))
continue;
207 if (energy_free_[end_task] == 0)
continue;
210 if (end_task_time.time == previous_end)
continue;
211 previous_end = end_task_time.time;
215 IntegerValue energy_free_parts = IntegerValue(0);
216 reason_tasks_fully_included_in_window_.clear();
217 reason_tasks_partially_included_in_window_.clear();
223 IntegerValue free_energy_of_max_task_in_window(0);
227 const IntegerValue window_max = end_task_time.time;
229 const int begin_task = begin_task_time.task_index;
233 const IntegerValue window_min = begin_task_time.time;
236 if (window_max <= window_min)
continue;
239 if (!helper_->IsPresent(begin_task))
continue;
240 if (energy_free_[begin_task] == 0)
continue;
258 const IntegerValue end_max = helper_->EndMax(begin_task);
259 if (end_max <= window_max) {
261 reason_tasks_fully_included_in_window_.push_back(begin_task);
262 energy_free_parts += energy_free_[begin_task];
264 const IntegerValue demand_min = demands_->DemandMin(begin_task);
265 const IntegerValue extra_energy =
266 std::min(size_free_[begin_task], (window_max - window_min)) *
271 const IntegerValue free_energy_in_window =
272 std::max(IntegerValue(0),
273 size_free_[begin_task] - (end_max - window_max)) *
278 if (extra_energy > extra_energy_required_by_max_task) {
279 if (max_task != -1 && free_energy_of_max_task_in_window > 0) {
280 reason_tasks_partially_included_in_window_.push_back(max_task);
283 max_task = begin_task;
284 extra_energy_required_by_max_task = extra_energy;
288 energy_free_parts += free_energy_of_max_task_in_window;
289 free_energy_of_max_task_in_window = free_energy_in_window;
290 }
else if (free_energy_in_window > 0) {
291 reason_tasks_partially_included_in_window_.push_back(begin_task);
292 energy_free_parts += free_energy_in_window;
305 if (max_task == -1 || demands_->DemandMin(max_task) == 0)
continue;
308 const IntegerValue window_energy =
309 CapacityMax() * (window_max - window_min);
310 const IntegerValue energy_mandatory =
311 mandatory_energy_before_end_max_[end_task] -
312 mandatory_energy_before_start_min_[begin_task];
313 const IntegerValue available_energy =
314 window_energy - energy_free_parts - energy_mandatory;
320 if (extra_energy_required_by_max_task <= available_energy) {
327 if (energy_free_[max_task] > available_energy &&
328 helper_->EndMin(max_task) <= window_max) {
329 FillEnergyInWindowReason(window_min, window_max, max_task);
330 demands_->AddEnergyMinReason(max_task);
331 helper_->AddStartMinReason(max_task, window_min);
332 if (!helper_->IncreaseEndMin(max_task, window_max + 1))
return false;
343 const IntegerValue mandatory_size_in_window =
344 std::max(IntegerValue(0),
345 std::min(window_max, helper_->EndMin(max_task)) -
346 std::max(window_min, helper_->StartMax(max_task)));
349 const IntegerValue max_free_size_that_fit =
350 available_energy / demands_->DemandMin(max_task);
351 const IntegerValue new_start =
352 window_max - mandatory_size_in_window - max_free_size_that_fit;
355 if (helper_->StartMin(max_task) < new_start) {
356 FillEnergyInWindowReason(window_min, window_max, max_task);
360 helper_->AddStartMinReason(max_task, window_min);
361 demands_->AddDemandMinReason(max_task);
363 if (!helper_->IncreaseStartMin(max_task, new_start))
return false;
371void TimeTableEdgeFinding::FillEnergyInWindowReason(IntegerValue window_min,
372 IntegerValue window_max,
374 helper_->ClearReason();
378 helper_->MutableIntegerReason()->push_back(
379 integer_trail_->UpperBoundAsLiteral(capacity_.var));
383 for (
int t = 0; t < num_tasks_; ++t) {
384 if (t == task_index)
continue;
385 if (!helper_->IsPresent(t))
continue;
386 const IntegerValue smax = helper_->StartMax(t);
387 const IntegerValue emin = helper_->EndMin(t);
388 if (smax >= emin)
continue;
389 if (emin <= window_min)
continue;
390 if (smax >= window_max)
continue;
391 helper_->AddStartMaxReason(t, std::max(smax, window_min));
392 helper_->AddEndMinReason(t, std::min(emin, window_max));
393 helper_->AddPresenceReason(t);
394 demands_->AddDemandMinReason(t);
401 for (
const int t : reason_tasks_fully_included_in_window_) {
402 DCHECK_NE(t, task_index);
403 DCHECK(helper_->IsPresent(t));
404 DCHECK_GT(helper_->EndMax(t), window_min);
405 DCHECK_LT(helper_->StartMin(t), window_max);
406 DCHECK_GE(helper_->StartMin(t), window_min);
408 helper_->AddStartMinReason(t, helper_->StartMin(t));
409 helper_->AddEndMaxReason(t, std::max(window_max, helper_->EndMax(t)));
410 helper_->AddPresenceReason(t);
411 demands_->AddEnergyMinReason(t);
413 for (
const int t : reason_tasks_partially_included_in_window_) {
414 DCHECK_NE(t, task_index);
415 DCHECK(helper_->IsPresent(t));
416 DCHECK_GT(helper_->EndMax(t), window_min);
417 DCHECK_LT(helper_->StartMin(t), window_max);
418 DCHECK_GE(helper_->StartMin(t), window_min);
420 helper_->AddStartMinReason(t, helper_->StartMin(t));
421 helper_->AddEndMaxReason(t, std::max(window_max, helper_->EndMax(t)));
422 helper_->AddPresenceReason(t);
424 helper_->AddSizeMinReason(t);
425 demands_->AddDemandMinReason(t);
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)
void WatchUpperBound(IntegerVariable var, int id, int watch_index=-1)
void WatchLowerBound(IntegerVariable var, int id, int watch_index=-1)
void SetPropagatorPriority(int id, int priority)
int Register(PropagatorInterface *propagator)
Registers a propagator and returns its unique ids.
absl::Span< const TaskTime > TaskByIncreasingNegatedStartMax()
bool IsPresent(int t) const
ReverseView< Container > reversed_view(const Container &c)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
IntegerValue CapSubI(IntegerValue a, IntegerValue b)
bool AtMinOrMaxInt64I(IntegerValue t)
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
Overflows and saturated arithmetic.
In SWIG mode, we don't want anything besides these top-level includes.