29 std::vector<IntegerValue> deltas,
30 std::vector<Literal> presences, int64_t min_level,
33 IntegerValue min_possible(0);
34 IntegerValue max_possible(0);
35 for (
const IntegerValue d : deltas) {
42 if (max_possible > max_level) {
44 times, deltas, presences, IntegerValue(max_level),
model));
46 if (min_possible < min_level) {
47 for (IntegerValue& ref : deltas) ref = -ref;
49 times, deltas, presences, IntegerValue(-min_level),
model));
54 const std::vector<AffineExpression>& times,
55 const std::vector<IntegerValue>& deltas,
59 presences_(presences),
64 const int id = watcher->
Register(
this);
65 const int num_events = times.size();
66 for (
int e = 0; e < num_events; e++) {
68 watcher->WatchUpperBound(times_[e].
var,
id);
69 watcher->WatchLiteral(presences_[e],
id);
72 watcher->WatchLowerBound(times_[e].
var,
id);
73 watcher->WatchLiteral(presences_[e].Negated(), id);
76 watcher->NotifyThatPropagatorMayNotReachFixedPointInOnePass(
id);
80 const int num_events = times_.size();
81 if (!BuildProfile())
return false;
82 for (
int e = 0; e < num_events; e++) {
86 if (deltas_[e] > 0 && !TryToIncreaseMin(e))
return false;
89 if (deltas_[e] < 0 && !TryToDecreaseMax(e))
return false;
98bool ReservoirTimeTabling::BuildProfile() {
101 const int num_events = times_.size();
103 for (
int e = 0; e < num_events; e++) {
104 if (deltas_[e] > 0) {
107 const IntegerValue ub = integer_trail_->
UpperBound(times_[e]);
108 profile_.push_back({ub, deltas_[e]});
109 }
else if (deltas_[e] < 0) {
112 profile_.push_back({integer_trail_->
LowerBound(times_[e]), deltas_[e]});
116 std::sort(profile_.begin(), profile_.end());
120 for (
const ProfileRectangle& rect : profile_) {
121 if (rect.start == profile_[last].start) {
122 profile_[last].height += rect.height;
125 profile_[last].start = rect.start;
126 profile_[last].height = rect.height + profile_[last - 1].height;
129 profile_.resize(last + 1);
132 for (
const ProfileRectangle& rect : profile_) {
133 if (rect.height <= capacity_)
continue;
134 FillReasonForProfileAtGivenTime(rect.start);
135 return integer_trail_->
ReportConflict(literal_reason_, integer_reason_);
147void ReservoirTimeTabling::FillReasonForProfileAtGivenTime(
148 IntegerValue t,
int event_to_ignore) {
149 integer_reason_.clear();
150 literal_reason_.clear();
151 const int num_events = times_.size();
152 for (
int e = 0; e < num_events; e++) {
153 if (e == event_to_ignore)
continue;
154 if (deltas_[e] > 0) {
156 if (integer_trail_->
UpperBound(times_[e]) > t)
continue;
158 literal_reason_.push_back(presences_[e].Negated());
159 }
else if (deltas_[e] < 0) {
161 literal_reason_.push_back(presences_[e]);
162 }
else if (integer_trail_->
LowerBound(times_[e]) > t) {
171bool ReservoirTimeTabling::TryToDecreaseMax(
int event) {
174 const IntegerValue
end = integer_trail_->
UpperBound(times_[event]);
181 DCHECK(std::is_sorted(profile_.begin(), profile_.end()));
184 [&](IntegerValue
value,
const ProfileRectangle& rect) {
185 return value < rect.start;
191 IntegerValue new_end =
end;
192 for (; profile_[rec_id].start <
end; ++rec_id) {
193 if (profile_[rec_id].height - deltas_[event] > capacity_) {
194 new_end = profile_[rec_id].start;
199 if (!push)
return true;
203 FillReasonForProfileAtGivenTime(new_end, event);
208 if (new_end <
start) {
209 integer_reason_.push_back(times_[event].
GreaterOrEqual(new_end + 1));
210 return integer_trail_->
ReportConflict(literal_reason_, integer_reason_);
218 integer_trail_->
EnqueueLiteral(presences_[event], literal_reason_,
224 literal_reason_, integer_reason_);
227bool ReservoirTimeTabling::TryToIncreaseMin(
int event) {
230 const IntegerValue
end = integer_trail_->
UpperBound(times_[event]);
240 DCHECK(std::is_sorted(profile_.begin(), profile_.end()));
243 [&](IntegerValue
value,
const ProfileRectangle& rect) {
244 return value < rect.start;
250 IntegerValue new_start =
start;
251 if (profile_[rec_id].height + deltas_[event] > capacity_) {
256 }
else if (profile_[rec_id].
start <
end) {
263 for (; profile_[rec_id].start >
start; --rec_id) {
264 if (profile_[rec_id - 1].height + deltas_[event] > capacity_) {
266 new_start = profile_[rec_id].start;
271 if (!push)
return true;
274 FillReasonForProfileAtGivenTime(new_start - 1, event);
277 &literal_reason_, &integer_reason_);
283 : num_tasks_(helper->NumTasks()),
286 integer_trail_(integer_trail),
291 profile_.reserve(2 * num_tasks_ + 4);
294 forward_num_tasks_to_sweep_ = num_tasks_;
295 forward_tasks_to_sweep_.resize(num_tasks_);
296 backward_num_tasks_to_sweep_ = num_tasks_;
297 backward_tasks_to_sweep_.resize(num_tasks_);
299 num_profile_tasks_ = 0;
300 profile_tasks_.resize(num_tasks_);
301 positions_in_profile_tasks_.resize(num_tasks_);
304 starting_profile_height_ = IntegerValue(0);
306 for (
int t = 0; t < num_tasks_; ++t) {
307 forward_tasks_to_sweep_[t] = t;
308 backward_tasks_to_sweep_[t] = t;
309 profile_tasks_[t] = t;
310 positions_in_profile_tasks_[t] = t;
315 const int id = watcher->
Register(
this);
318 for (
int t = 0; t < num_tasks_; t++) {
333 if (!BuildProfile())
return false;
336 if (!SweepAllTasks(
true))
return false;
343 if (!SweepAllTasks(
false))
return false;
348bool TimeTablingPerTask::BuildProfile() {
354 for (
int i = num_profile_tasks_; i < num_tasks_; ++i) {
355 const int t1 = profile_tasks_[i];
358 const int t2 = profile_tasks_[num_profile_tasks_];
359 profile_tasks_[i] = t2;
360 profile_tasks_[num_profile_tasks_] = t1;
361 positions_in_profile_tasks_[t1] = num_profile_tasks_;
362 positions_in_profile_tasks_[t2] = i;
363 num_profile_tasks_++;
383 IntegerValue current_height = starting_profile_height_;
387 int next_start = num_tasks_ - 1;
389 while (next_end < num_tasks_) {
390 const IntegerValue old_height = current_height;
392 IntegerValue t = by_end_min[next_end].time;
393 if (next_start >= 0) {
394 t =
std::min(t, by_decreasing_start_max[next_start].
time);
398 while (next_start >= 0 && by_decreasing_start_max[next_start].
time == t) {
399 const int task_index = by_decreasing_start_max[next_start].task_index;
400 if (IsInProfile(task_index)) current_height += DemandMin(task_index);
405 while (next_end < num_tasks_ && by_end_min[next_end].
time == t) {
406 const int task_index = by_end_min[next_end].task_index;
407 if (IsInProfile(task_index)) current_height -= DemandMin(task_index);
412 if (current_height != old_height) {
413 profile_.emplace_back(current_start, old_height);
414 if (current_height > profile_max_height_) {
415 profile_max_height_ = current_height;
416 max_height_start = t;
424 profile_.emplace_back(current_start, IntegerValue(0));
430 return IncreaseCapacity(max_height_start, profile_max_height_);
433void TimeTablingPerTask::ReverseProfile() {
435 for (
int i = 1; i + 1 < profile_.size(); ++i) {
436 profile_[i].start = -profile_[i + 1].start;
438 std::reverse(profile_.begin() + 1, profile_.end() - 1);
441bool TimeTablingPerTask::SweepAllTasks(
bool is_forward) {
443 const IntegerValue demand_threshold(
444 CapSub(CapacityMax().
value(), profile_max_height_.value()));
448 is_forward ? forward_num_tasks_to_sweep_ : backward_num_tasks_to_sweep_;
449 std::vector<int>& tasks =
450 is_forward ? forward_tasks_to_sweep_ : backward_tasks_to_sweep_;
455 for (
int i = num_tasks - 1; i >= 0; --i) {
456 const int t = tasks[i];
467 if (DemandMin(t) <= demand_threshold) {
468 if (DemandMax(t) == 0) {
479 if (helper_->
SizeMin(t) == 0) {
480 if (helper_->
SizeMax(t) == 0) {
486 if (!SweepTask(t))
return false;
492bool TimeTablingPerTask::SweepTask(
int task_id) {
494 const IntegerValue size_min = helper_->
SizeMin(task_id);
495 const IntegerValue initial_start_min = helper_->
StartMin(task_id);
496 const IntegerValue initial_end_min = helper_->
EndMin(task_id);
498 IntegerValue new_start_min = initial_start_min;
499 IntegerValue new_end_min = initial_end_min;
503 DCHECK(std::is_sorted(profile_.begin(), profile_.end()));
506 [&](IntegerValue
value,
const ProfileRectangle& rect) {
507 return value < rect.start;
514 const IntegerValue conflict_height = CapacityMax() - DemandMin(task_id);
517 bool conflict_found =
false;
530 for (; profile_[rec_id].start < limit; ++rec_id) {
532 if (profile_[rec_id].height <= conflict_height)
continue;
534 conflict_found =
true;
538 new_start_min = profile_[rec_id + 1].start;
540 if (IsInProfile(task_id)) {
550 new_end_min =
std::max(new_end_min, new_start_min + size_min);
553 if (profile_[rec_id].
start < initial_end_min) {
554 last_initial_conflict =
std::min(new_start_min, initial_end_min) - 1;
558 if (!conflict_found)
return true;
560 if (initial_start_min != new_start_min &&
561 !UpdateStartingTime(task_id, last_initial_conflict, new_start_min)) {
568bool TimeTablingPerTask::UpdateStartingTime(
int task_id, IntegerValue left,
569 IntegerValue right) {
572 AddProfileReason(left, right);
590void TimeTablingPerTask::AddProfileReason(IntegerValue left,
591 IntegerValue right) {
592 for (
int i = 0; i < num_profile_tasks_; ++i) {
593 const int t = profile_tasks_[i];
611bool TimeTablingPerTask::IncreaseCapacity(IntegerValue
time,
612 IntegerValue new_min) {
613 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)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
std::function< void(Model *)> LowerOrEqual(IntegerVariable v, int64_t ub)
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