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_->IsEnforced())
return true;
62 if (!helper_->SynchronizeAndSetTimeDirection(
true))
return false;
63 if (!TimeTableEdgeFindingPass())
return false;
64 if (!helper_->SynchronizeAndSetTimeDirection(
false))
return false;
65 if (!TimeTableEdgeFindingPass())
return false;
69void TimeTableEdgeFinding::BuildTimeTable() {
77 const IntegerValue start_max = -negated_smax;
78 if (start_max < helper_->EndMin(t)) {
79 scp_.push_back({t, start_max});
84 for (
const auto task_time : helper_->TaskByIncreasingEndMin()) {
85 const int t = task_time.task_index;
86 if (!helper_->IsPresent(t))
continue;
87 if (helper_->StartMax(t) < task_time.time) {
88 ecp_.push_back(task_time);
92 DCHECK_EQ(scp_.size(), ecp_.size());
94 const auto by_decreasing_end_max = helper_->TaskByDecreasingEndMax();
95 const auto by_start_min = helper_->TaskByIncreasingStartMin();
97 IntegerValue height = IntegerValue(0);
98 IntegerValue energy = IntegerValue(0);
102 IntegerValue previous_time = IntegerValue(0);
107 int index_emax = num_tasks_ - 1;
109 while (index_emax >= 0) {
111 IntegerValue time = by_decreasing_end_max[index_emax].time;
112 if (index_smin < num_tasks_) {
113 time = std::min(time, by_start_min[index_smin].time);
115 if (index_scp < scp_.size()) {
116 time = std::min(time, scp_[index_scp].time);
118 if (index_ecp < ecp_.size()) {
119 time = std::min(time, ecp_[index_ecp].time);
123 energy += (time - previous_time) * height;
124 previous_time = time;
127 while (index_smin < num_tasks_ && by_start_min[index_smin].time == time) {
128 mandatory_energy_before_start_min_[by_start_min[index_smin].task_index] =
134 while (index_emax >= 0 && by_decreasing_end_max[index_emax].time == time) {
135 mandatory_energy_before_end_max_[by_decreasing_end_max[index_emax]
136 .task_index] = energy;
141 while (index_scp < scp_.size() && scp_[index_scp].time == time) {
142 height += demands_->DemandMin(scp_[index_scp].task_index);
147 while (index_ecp < ecp_.size() && ecp_[index_ecp].time == time) {
148 height -= demands_->DemandMin(ecp_[index_ecp].task_index);
154bool TimeTableEdgeFinding::TimeTableEdgeFindingPass() {
155 if (!demands_->CacheAllEnergyValues())
return true;
157 IntegerValue earliest_start_min = std::numeric_limits<IntegerValue>::max();
158 IntegerValue latest_end_max = std::numeric_limits<IntegerValue>::min();
159 IntegerValue maximum_demand_min = IntegerValue(0);
162 for (
int t = 0; t < num_tasks_; ++t) {
164 const IntegerValue start_max = helper_->StartMax(t);
165 const IntegerValue end_min = helper_->EndMin(t);
166 const IntegerValue demand_min = demands_->DemandMin(t);
167 IntegerValue mandatory_energy(0);
169 earliest_start_min = std::min(earliest_start_min, helper_->StartMin(t));
170 latest_end_max = std::max(latest_end_max, helper_->EndMax(t));
171 maximum_demand_min = std::max(maximum_demand_min, demand_min);
173 if (start_max >= end_min) {
174 size_free_[t] = helper_->SizeMin(t);
176 const IntegerValue mandatory_size = end_min - start_max;
177 size_free_[t] = helper_->SizeMin(t) - mandatory_size;
178 mandatory_energy = mandatory_size * demand_min;
181 const IntegerValue min_energy = demands_->EnergyMin(t);
182 energy_free_[t] = min_energy - mandatory_energy;
183 DCHECK_GE(energy_free_[t], 0);
187 maximum_demand_min))) {
196 const auto& by_start_min = helper_->TaskByIncreasingStartMin();
203 for (
const TaskTime end_task_time : helper_->TaskByDecreasingEndMax()) {
204 const int end_task = end_task_time.task_index;
207 if (!helper_->IsPresent(end_task))
continue;
208 if (energy_free_[end_task] == 0)
continue;
211 if (end_task_time.time == previous_end)
continue;
212 previous_end = end_task_time.time;
216 IntegerValue energy_free_parts = IntegerValue(0);
217 reason_tasks_fully_included_in_window_.clear();
218 reason_tasks_partially_included_in_window_.clear();
224 IntegerValue free_energy_of_max_task_in_window(0);
228 const IntegerValue window_max = end_task_time.time;
230 const int begin_task = begin_task_time.task_index;
234 const IntegerValue window_min = begin_task_time.time;
237 if (window_max <= window_min)
continue;
240 if (!helper_->IsPresent(begin_task))
continue;
241 if (energy_free_[begin_task] == 0)
continue;
259 const IntegerValue end_max = helper_->EndMax(begin_task);
260 if (end_max <= window_max) {
262 reason_tasks_fully_included_in_window_.push_back(begin_task);
263 energy_free_parts += energy_free_[begin_task];
265 const IntegerValue demand_min = demands_->DemandMin(begin_task);
266 const IntegerValue extra_energy =
267 std::min(size_free_[begin_task], (window_max - window_min)) *
272 const IntegerValue free_energy_in_window =
273 std::max(IntegerValue(0),
274 size_free_[begin_task] - (end_max - window_max)) *
279 if (extra_energy > extra_energy_required_by_max_task) {
280 if (max_task != -1 && free_energy_of_max_task_in_window > 0) {
281 reason_tasks_partially_included_in_window_.push_back(max_task);
284 max_task = begin_task;
285 extra_energy_required_by_max_task = extra_energy;
289 energy_free_parts += free_energy_of_max_task_in_window;
290 free_energy_of_max_task_in_window = free_energy_in_window;
291 }
else if (free_energy_in_window > 0) {
292 reason_tasks_partially_included_in_window_.push_back(begin_task);
293 energy_free_parts += free_energy_in_window;
306 if (max_task == -1 || demands_->DemandMin(max_task) == 0)
continue;
309 const IntegerValue window_energy =
310 CapacityMax() * (window_max - window_min);
311 const IntegerValue energy_mandatory =
312 mandatory_energy_before_end_max_[end_task] -
313 mandatory_energy_before_start_min_[begin_task];
314 const IntegerValue available_energy =
315 window_energy - energy_free_parts - energy_mandatory;
321 if (extra_energy_required_by_max_task <= available_energy) {
328 if (energy_free_[max_task] > available_energy &&
329 helper_->EndMin(max_task) <= window_max) {
330 FillEnergyInWindowReason(window_min, window_max, max_task);
331 demands_->AddEnergyMinReason(max_task);
332 helper_->AddStartMinReason(max_task, window_min);
333 if (!helper_->IncreaseEndMin(max_task, window_max + 1))
return false;
344 const IntegerValue mandatory_size_in_window =
345 std::max(IntegerValue(0),
346 std::min(window_max, helper_->EndMin(max_task)) -
347 std::max(window_min, helper_->StartMax(max_task)));
350 const IntegerValue max_free_size_that_fit =
351 available_energy / demands_->DemandMin(max_task);
352 const IntegerValue new_start =
353 window_max - mandatory_size_in_window - max_free_size_that_fit;
356 if (helper_->StartMin(max_task) < new_start) {
357 FillEnergyInWindowReason(window_min, window_max, max_task);
361 helper_->AddStartMinReason(max_task, window_min);
362 demands_->AddDemandMinReason(max_task);
364 if (!helper_->IncreaseStartMin(max_task, new_start))
return false;
372void TimeTableEdgeFinding::FillEnergyInWindowReason(IntegerValue window_min,
373 IntegerValue window_max,
375 helper_->ResetReason();
379 helper_->AddIntegerReason(
380 integer_trail_->UpperBoundAsLiteral(capacity_.var));
384 for (
int t = 0; t < num_tasks_; ++t) {
385 if (t == task_index)
continue;
386 if (!helper_->IsPresent(t))
continue;
387 const IntegerValue smax = helper_->StartMax(t);
388 const IntegerValue emin = helper_->EndMin(t);
389 if (smax >= emin)
continue;
390 if (emin <= window_min)
continue;
391 if (smax >= window_max)
continue;
392 helper_->AddStartMaxReason(t, std::max(smax, window_min));
393 helper_->AddEndMinReason(t, std::min(emin, window_max));
394 helper_->AddPresenceReason(t);
395 demands_->AddDemandMinReason(t);
402 for (
const int t : reason_tasks_fully_included_in_window_) {
403 DCHECK_NE(t, task_index);
404 DCHECK(helper_->IsPresent(t));
405 DCHECK_GT(helper_->EndMax(t), window_min);
406 DCHECK_LT(helper_->StartMin(t), window_max);
407 DCHECK_GE(helper_->StartMin(t), window_min);
409 helper_->AddStartMinReason(t, helper_->StartMin(t));
410 helper_->AddEndMaxReason(t, std::max(window_max, helper_->EndMax(t)));
411 helper_->AddPresenceReason(t);
412 demands_->AddEnergyMinReason(t);
414 for (
const int t : reason_tasks_partially_included_in_window_) {
415 DCHECK_NE(t, task_index);
416 DCHECK(helper_->IsPresent(t));
417 DCHECK_GT(helper_->EndMax(t), window_min);
418 DCHECK_LT(helper_->StartMin(t), window_max);
419 DCHECK_GE(helper_->StartMin(t), window_min);
421 helper_->AddStartMinReason(t, helper_->StartMin(t));
422 helper_->AddEndMaxReason(t, std::max(window_max, helper_->EndMax(t)));
423 helper_->AddPresenceReason(t);
425 helper_->AddSizeMinReason(t);
426 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)
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)