33 std::vector<IntegerValue> deltas,
34 std::vector<Literal> presences, int64_t min_level,
37 IntegerValue min_possible(0);
38 IntegerValue max_possible(0);
39 for (
const IntegerValue d : deltas) {
46 if (max_possible > max_level) {
48 times, deltas, presences, IntegerValue(max_level),
model));
50 if (min_possible < min_level) {
51 for (IntegerValue& ref : deltas) ref = -ref;
53 times, deltas, presences, IntegerValue(-min_level),
model));
58 const std::vector<AffineExpression>& times,
59 const std::vector<IntegerValue>& deltas,
63 presences_(presences),
68 const int id = watcher->
Register(
this);
69 const int num_events = times.size();
70 for (
int e = 0; e < num_events; e++) {
72 watcher->WatchUpperBound(times_[e].
var,
id);
73 watcher->WatchLiteral(presences_[e],
id);
76 watcher->WatchLowerBound(times_[e].
var,
id);
77 watcher->WatchLiteral(presences_[e].Negated(), id);
80 watcher->NotifyThatPropagatorMayNotReachFixedPointInOnePass(
id);
84 const int num_events = times_.size();
85 if (!BuildProfile())
return false;
86 for (
int e = 0; e < num_events; e++) {
90 if (deltas_[e] > 0 && !TryToIncreaseMin(e))
return false;
93 if (deltas_[e] < 0 && !TryToDecreaseMax(e))
return false;
102bool ReservoirTimeTabling::BuildProfile() {
105 const int num_events = times_.size();
107 for (
int e = 0; e < num_events; e++) {
108 if (deltas_[e] > 0) {
111 const IntegerValue ub = integer_trail_->
UpperBound(times_[e]);
112 profile_.push_back({ub, deltas_[e]});
113 }
else if (deltas_[e] < 0) {
116 profile_.push_back({integer_trail_->
LowerBound(times_[e]), deltas_[e]});
120 std::sort(profile_.begin(), profile_.end());
124 for (
const ProfileRectangle& rect : profile_) {
125 if (rect.start == profile_[last].start) {
126 profile_[last].height += rect.height;
129 profile_[last].start = rect.start;
130 profile_[last].height = rect.height + profile_[last - 1].height;
133 profile_.resize(last + 1);
136 for (
const ProfileRectangle& rect : profile_) {
137 if (rect.height <= capacity_)
continue;
138 FillReasonForProfileAtGivenTime(rect.start);
139 return integer_trail_->
ReportConflict(literal_reason_, integer_reason_);
151void ReservoirTimeTabling::FillReasonForProfileAtGivenTime(
152 IntegerValue t,
int event_to_ignore) {
153 integer_reason_.clear();
154 literal_reason_.clear();
155 const int num_events = times_.size();
156 for (
int e = 0; e < num_events; e++) {
157 if (e == event_to_ignore)
continue;
158 if (deltas_[e] > 0) {
160 if (integer_trail_->
UpperBound(times_[e]) > t)
continue;
162 literal_reason_.push_back(presences_[e].Negated());
163 }
else if (deltas_[e] < 0) {
165 literal_reason_.push_back(presences_[e]);
166 }
else if (integer_trail_->
LowerBound(times_[e]) > t) {
175bool ReservoirTimeTabling::TryToDecreaseMax(
int event) {
178 const IntegerValue
end = integer_trail_->
UpperBound(times_[event]);
185 DCHECK(std::is_sorted(profile_.begin(), profile_.end()));
188 [&](IntegerValue
value,
const ProfileRectangle& rect) {
189 return value < rect.start;
195 IntegerValue new_end =
end;
196 for (; profile_[rec_id].start <
end; ++rec_id) {
197 if (profile_[rec_id].height - deltas_[event] > capacity_) {
198 new_end = profile_[rec_id].start;
203 if (!push)
return true;
207 FillReasonForProfileAtGivenTime(new_end, event);
212 if (new_end <
start) {
213 integer_reason_.push_back(times_[event].
GreaterOrEqual(new_end + 1));
214 return integer_trail_->
ReportConflict(literal_reason_, integer_reason_);
222 integer_trail_->
EnqueueLiteral(presences_[event], literal_reason_,
228 literal_reason_, integer_reason_);
231bool ReservoirTimeTabling::TryToIncreaseMin(
int event) {
234 const IntegerValue
end = integer_trail_->
UpperBound(times_[event]);
244 DCHECK(std::is_sorted(profile_.begin(), profile_.end()));
247 [&](IntegerValue
value,
const ProfileRectangle& rect) {
248 return value < rect.start;
254 IntegerValue new_start =
start;
255 if (profile_[rec_id].height + deltas_[event] > capacity_) {
260 }
else if (profile_[rec_id].
start <
end) {
267 for (; profile_[rec_id].start >
start; --rec_id) {
268 if (profile_[rec_id - 1].height + deltas_[event] > capacity_) {
270 new_start = profile_[rec_id].start;
275 if (!push)
return true;
278 FillReasonForProfileAtGivenTime(new_start - 1, event);
281 &literal_reason_, &integer_reason_);
287 : num_tasks_(helper->NumTasks()),
290 integer_trail_(integer_trail),
295 profile_.reserve(2 * num_tasks_ + 4);
298 forward_num_tasks_to_sweep_ = num_tasks_;
299 forward_tasks_to_sweep_.resize(num_tasks_);
300 backward_num_tasks_to_sweep_ = num_tasks_;
301 backward_tasks_to_sweep_.resize(num_tasks_);
303 num_profile_tasks_ = 0;
304 profile_tasks_.resize(num_tasks_);
305 positions_in_profile_tasks_.resize(num_tasks_);
308 starting_profile_height_ = IntegerValue(0);
310 for (
int t = 0; t < num_tasks_; ++t) {
311 forward_tasks_to_sweep_[t] = t;
312 backward_tasks_to_sweep_[t] = t;
313 profile_tasks_[t] = t;
314 positions_in_profile_tasks_[t] = t;
319 const int id = watcher->
Register(
this);
322 for (
int t = 0; t < num_tasks_; t++) {
337 if (!BuildProfile())
return false;
340 if (!SweepAllTasks(
true))
return false;
347 if (!SweepAllTasks(
false))
return false;
352bool TimeTablingPerTask::BuildProfile() {
358 for (
int i = num_profile_tasks_; i < num_tasks_; ++i) {
359 const int t1 = profile_tasks_[i];
362 const int t2 = profile_tasks_[num_profile_tasks_];
363 profile_tasks_[i] = t2;
364 profile_tasks_[num_profile_tasks_] = t1;
365 positions_in_profile_tasks_[t1] = num_profile_tasks_;
366 positions_in_profile_tasks_[t2] = i;
367 num_profile_tasks_++;
387 IntegerValue current_height = starting_profile_height_;
391 int next_start = num_tasks_ - 1;
393 while (next_end < num_tasks_) {
394 const IntegerValue old_height = current_height;
396 IntegerValue t = by_end_min[next_end].time;
397 if (next_start >= 0) {
398 t =
std::min(t, by_decreasing_start_max[next_start].
time);
402 while (next_start >= 0 && by_decreasing_start_max[next_start].
time == t) {
403 const int task_index = by_decreasing_start_max[next_start].task_index;
404 if (IsInProfile(task_index)) current_height += DemandMin(task_index);
409 while (next_end < num_tasks_ && by_end_min[next_end].
time == t) {
410 const int task_index = by_end_min[next_end].task_index;
411 if (IsInProfile(task_index)) current_height -= DemandMin(task_index);
416 if (current_height != old_height) {
417 profile_.emplace_back(current_start, old_height);
418 if (current_height > profile_max_height_) {
419 profile_max_height_ = current_height;
420 max_height_start = t;
428 profile_.emplace_back(current_start, IntegerValue(0));
434 return IncreaseCapacity(max_height_start, profile_max_height_);
437void TimeTablingPerTask::ReverseProfile() {
439 for (
int i = 1; i + 1 < profile_.size(); ++i) {
440 profile_[i].start = -profile_[i + 1].start;
442 std::reverse(profile_.begin() + 1, profile_.end() - 1);
445bool TimeTablingPerTask::SweepAllTasks(
bool is_forward) {
447 const IntegerValue demand_threshold(
448 CapSub(CapacityMax().
value(), profile_max_height_.value()));
452 is_forward ? forward_num_tasks_to_sweep_ : backward_num_tasks_to_sweep_;
453 std::vector<int>& tasks =
454 is_forward ? forward_tasks_to_sweep_ : backward_tasks_to_sweep_;
459 for (
int i = num_tasks - 1; i >= 0; --i) {
460 const int t = tasks[i];
471 if (DemandMin(t) <= demand_threshold) {
472 if (DemandMax(t) == 0) {
483 if (helper_->
SizeMin(t) == 0) {
484 if (helper_->
SizeMax(t) == 0) {
490 if (!SweepTask(t))
return false;
496bool TimeTablingPerTask::SweepTask(
int task_id) {
498 const IntegerValue size_min = helper_->
SizeMin(task_id);
499 const IntegerValue initial_start_min = helper_->
StartMin(task_id);
500 const IntegerValue initial_end_min = helper_->
EndMin(task_id);
502 IntegerValue new_start_min = initial_start_min;
503 IntegerValue new_end_min = initial_end_min;
507 DCHECK(std::is_sorted(profile_.begin(), profile_.end()));
510 [&](IntegerValue
value,
const ProfileRectangle& rect) {
511 return value < rect.start;
518 const IntegerValue conflict_height = CapacityMax() - DemandMin(task_id);
521 bool conflict_found =
false;
534 for (; profile_[rec_id].start < limit; ++rec_id) {
536 if (profile_[rec_id].height <= conflict_height)
continue;
538 conflict_found =
true;
542 new_start_min = profile_[rec_id + 1].start;
544 if (IsInProfile(task_id)) {
554 new_end_min =
std::max(new_end_min, new_start_min + size_min);
557 if (profile_[rec_id].
start < initial_end_min) {
558 last_initial_conflict =
std::min(new_start_min, initial_end_min) - 1;
562 if (!conflict_found)
return true;
564 if (initial_start_min != new_start_min &&
565 !UpdateStartingTime(task_id, last_initial_conflict, new_start_min)) {
572bool TimeTablingPerTask::UpdateStartingTime(
int task_id, IntegerValue left,
573 IntegerValue right) {
576 AddProfileReason(left, right);
594void TimeTablingPerTask::AddProfileReason(IntegerValue left,
595 IntegerValue right) {
596 for (
int i = 0; i < num_profile_tasks_; ++i) {
597 const int t = profile_tasks_[i];
615bool TimeTablingPerTask::IncreaseCapacity(IntegerValue
time,
616 IntegerValue new_min) {
617 if (new_min <= CapacityMin())
return true;
#define CHECK_LT(val1, val2)
#define CHECK_GT(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK(condition)
An Assignment is a variable -> domains mapping, used to report solutions to the user.
void RegisterReversibleInt(int id, int *rev)
void WatchLowerBound(IntegerVariable var, int id, int watch_index=-1)
void WatchUpperBound(IntegerVariable var, int id, int watch_index=-1)
int Register(PropagatorInterface *propagator)
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
void EnqueueLiteral(Literal literal, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue LowerBound(IntegerVariable i) const
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
ABSL_MUST_USE_RESULT bool ConditionalEnqueue(Literal lit, IntegerLiteral i_lit, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason)
Class that owns everything related to a particular optimization model.
ReservoirTimeTabling(const std::vector< AffineExpression > ×, const std::vector< IntegerValue > &deltas, const std::vector< Literal > &presences, IntegerValue capacity, Model *model)
bool StartIsFixed(int t) const
IntegerValue EndMin(int t) const
ABSL_MUST_USE_RESULT bool PushIntegerLiteral(IntegerLiteral lit)
std::vector< IntegerLiteral > * MutableIntegerReason()
void AddSizeMinReason(int t)
void WatchAllTasks(int id, GenericLiteralWatcher *watcher, bool watch_start_max=true, bool watch_end_max=true) const
void AddPresenceReason(int t)
const std::vector< TaskTime > & TaskByIncreasingEndMin()
bool IsPresent(int t) const
void AddEndMinReason(int t, IntegerValue lower_bound)
ABSL_MUST_USE_RESULT bool IncreaseStartMin(int t, IntegerValue new_start_min)
bool IsAbsent(int t) const
ABSL_MUST_USE_RESULT bool ReportConflict()
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
IntegerValue StartMin(int t) const
const std::vector< TaskTime > & TaskByDecreasingStartMax()
IntegerValue StartMax(int t) const
void AddStartMaxReason(int t, IntegerValue upper_bound)
IntegerValue SizeMax(int t) const
IntegerValue SizeMin(int t) const
void RegisterWith(GenericLiteralWatcher *watcher)
TimeTablingPerTask(const std::vector< AffineExpression > &demands, AffineExpression capacity, IntegerTrail *integer_trail, SchedulingConstraintHelper *helper)
bool LiteralIsTrue(Literal literal) const
bool LiteralIsFalse(Literal literal) const
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
void AddReservoirConstraint(std::vector< AffineExpression > times, std::vector< IntegerValue > deltas, std::vector< Literal > presences, int64_t min_level, int64_t max_level, Model *model)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::function< void(Model *)> LowerOrEqual(IntegerVariable v, int64_t ub)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
std::function< void(Model *)> GreaterOrEqual(IntegerVariable v, int64_t lb)
Collection of objects used to extend the Constraint Solver library.
int64_t CapSub(int64_t x, int64_t y)
std::optional< int64_t > end
IntegerLiteral GreaterOrEqual(IntegerValue bound) const