21#include "absl/algorithm/container.h"
22#include "absl/base/log_severity.h"
23#include "absl/cleanup/cleanup.h"
24#include "absl/log/check.h"
25#include "absl/log/log.h"
26#include "absl/types/span.h"
47 const std::vector<IntervalVariable>& intervals,
52 if (intervals.size() <=
53 params.max_size_to_create_precedence_literals_in_disjunctive() &&
54 params.use_strong_propagation_in_disjunctive() &&
55 enforcement_literals.empty()) {
59 bool is_all_different =
true;
61 for (
const IntervalVariable var : intervals) {
63 repository->
MaxSize(var) != 1) {
64 is_all_different =
false;
68 if (is_all_different) {
69 std::vector<AffineExpression> starts;
70 starts.reserve(intervals.size());
71 for (
const IntervalVariable interval : intervals) {
72 starts.push_back(repository->
Start(interval));
79 if (intervals.size() > 2 && params.use_combined_no_overlap() &&
80 enforcement_literals.empty()) {
87 enforcement_literals, intervals,
true);
90 if ( (
false && enforcement_literals.empty())) {
92 std::vector<AffineExpression> demands(intervals.size(), one);
103 if (intervals.size() == 2) {
110 if (!params.use_strong_propagation_in_disjunctive()) {
115 const int id = simple_precedences->
RegisterWith(watcher);
116 watcher->SetPropagatorPriority(
id, 1);
124 watcher->SetPropagatorPriority(
id, 1);
127 for (
const bool time_direction : {
true,
false}) {
130 const int id = detectable_precedences->
RegisterWith(watcher);
131 watcher->SetPropagatorPriority(
id, 2);
134 for (
const bool time_direction : {
true,
false}) {
138 watcher->SetPropagatorPriority(
id, 3);
141 for (
const bool time_direction : {
true,
false}) {
145 watcher->SetPropagatorPriority(
id, 4);
153 if (params.use_precedences_in_disjunctive_constraint() &&
154 !params.use_combined_no_overlap()) {
158 for (
const bool time_direction : {
true,
false}) {
162 watcher->SetPropagatorPriority(
id, 5);
169 absl::Span<const IntervalVariable> intervals,
Model* model) {
171 for (
int i = 0;
i < intervals.size(); ++
i) {
172 for (
int j =
i + 1; j < intervals.size(); ++j) {
173 repo->CreateDisjunctivePrecedenceLiteral(intervals[
i], intervals[j]);
179 int j = sorted_tasks_.size();
180 sorted_tasks_.push_back(e);
181 while (j > 0 && sorted_tasks_[j - 1].start_min > e.
start_min) {
182 sorted_tasks_[j] = sorted_tasks_[j - 1];
185 sorted_tasks_[j] = e;
186 DCHECK(std::is_sorted(sorted_tasks_.begin(), sorted_tasks_.end()));
190 if (j <= optimized_restart_) optimized_restart_ = 0;
195 const IntegerValue dmin = helper.
SizeMin(t);
200 const int size = sorted_tasks_.size();
201 for (
int i = 0;; ++
i) {
202 if (
i == size)
return;
203 if (sorted_tasks_[
i].task == e.
task) {
204 for (
int j =
i; j + 1 < size; ++j) {
205 sorted_tasks_[j] = sorted_tasks_[j + 1];
207 sorted_tasks_.pop_back();
212 optimized_restart_ = sorted_tasks_.size();
213 sorted_tasks_.push_back(e);
214 DCHECK(std::is_sorted(sorted_tasks_.begin(), sorted_tasks_.end()));
218 DCHECK(std::is_sorted(sorted_tasks_.begin(), sorted_tasks_.end()));
219 const int size = sorted_tasks_.size();
221 for (
int i = optimized_restart_;
i < size; ++
i) {
222 const Entry& e = sorted_tasks_[
i];
224 optimized_restart_ =
i;
234 int* critical_index)
const {
236 DCHECK(std::is_sorted(sorted_tasks_.begin(), sorted_tasks_.end()));
237 bool ignored =
false;
238 const int size = sorted_tasks_.size();
243 if (optimized_restart_ + 1 == size &&
244 sorted_tasks_[optimized_restart_].task == task_to_ignore) {
245 optimized_restart_ = 0;
248 for (
int i = optimized_restart_;
i < size; ++
i) {
249 const Entry& e = sorted_tasks_[
i];
250 if (e.
task == task_to_ignore) {
256 if (!ignored) optimized_restart_ =
i;
266 DCHECK_EQ(helper_->NumTasks(), 2);
267 if (!helper_->IsEnforced())
return true;
268 if (!helper_->SynchronizeAndSetTimeDirection(
true))
return false;
271 if (helper_->IsAbsent(0) || helper_->IsAbsent(1))
return true;
274 if (!helper_->IsPresent(0) && !helper_->IsPresent(0))
return true;
287 const bool task_0_before_task_1 = helper_->TaskIsBeforeOrIsOverlapping(0, 1);
288 const bool task_1_before_task_0 = helper_->TaskIsBeforeOrIsOverlapping(1, 0);
290 if (task_0_before_task_1 && task_1_before_task_0 &&
291 helper_->IsPresent(task_before) && helper_->IsPresent(task_after)) {
292 helper_->ResetReason();
293 helper_->AddPresenceReason(task_before);
294 helper_->AddPresenceReason(task_after);
295 helper_->AddReasonForBeingBeforeAssumingNoOverlap(task_before, task_after);
296 helper_->AddReasonForBeingBeforeAssumingNoOverlap(task_after, task_before);
297 return helper_->ReportConflict();
300 if (task_0_before_task_1) {
302 }
else if (task_1_before_task_0) {
304 std::swap(task_before, task_after);
309 helper_->ResetReason();
310 helper_->AddReasonForBeingBeforeAssumingNoOverlap(task_before, task_after);
311 if (!helper_->PushTaskOrderWhenPresent(task_before, task_after)) {
318 const int id = watcher->
Register(
this);
319 helper_->WatchAllTasks(
id);
324template <
bool time_direction>
329 task_to_disjunctives_.resize(helper_->NumTasks());
332 const int id = watcher->
Register(
this);
333 helper_->WatchAllTasks(
id);
334 watcher->NotifyThatPropagatorMayNotReachFixedPointInOnePass(
id);
337template <
bool time_direction>
339 absl::Span<const IntervalVariable> vars) {
340 const int index = task_sets_.size();
341 task_sets_.emplace_back(task_set_storage_.emplace_back());
343 for (
const IntervalVariable var : vars) {
344 task_to_disjunctives_[var.value()].push_back(index);
348template <
bool time_direction>
350 if (!helper_->SynchronizeAndSetTimeDirection(time_direction))
return false;
351 const auto& task_by_increasing_end_min = helper_->TaskByIncreasingEndMin();
352 const auto& task_by_negated_start_max =
353 helper_->TaskByIncreasingNegatedStartMax();
355 for (
auto& task_set : task_sets_) task_set.Clear();
359 const int num_tasks = helper_->NumTasks();
360 task_is_added_.assign(num_tasks,
false);
361 int queue_index = num_tasks - 1;
362 for (
const auto task_time : task_by_increasing_end_min) {
363 const int t = task_time.task_index;
364 const IntegerValue end_min = task_time.time;
365 if (helper_->IsAbsent(t))
continue;
368 while (queue_index >= 0) {
369 const auto to_insert = task_by_negated_start_max[queue_index];
370 const int task_index = to_insert.task_index;
371 const IntegerValue start_max = -to_insert.time;
372 if (end_min <= start_max)
break;
373 if (helper_->IsPresent(task_index)) {
374 task_is_added_[task_index] =
true;
375 const IntegerValue shifted_smin = helper_->ShiftedStartMin(task_index);
376 const IntegerValue size_min = helper_->SizeMin(task_index);
377 for (
const int d_index : task_to_disjunctives_[task_index]) {
379 task_sets_[d_index].AddEntry({task_index, shifted_smin, size_min});
380 end_mins_[d_index] = task_sets_[d_index].ComputeEndMin();
381 max_of_end_min = std::max(max_of_end_min, end_mins_[d_index]);
389 IntegerValue new_start_min = helper_->StartMin(t);
390 if (new_start_min >= max_of_end_min)
continue;
391 int best_critical_index = 0;
392 int best_d_index = -1;
393 if (task_is_added_[t]) {
394 for (
const int d_index : task_to_disjunctives_[t]) {
395 if (new_start_min >= end_mins_[d_index])
continue;
396 int critical_index = 0;
397 const IntegerValue end_min_of_critical_tasks =
398 task_sets_[d_index].ComputeEndMin(t,
400 DCHECK_LE(end_min_of_critical_tasks, max_of_end_min);
401 if (end_min_of_critical_tasks > new_start_min) {
402 new_start_min = end_min_of_critical_tasks;
403 best_d_index = d_index;
404 best_critical_index = critical_index;
410 for (
const int d_index : task_to_disjunctives_[t]) {
411 if (end_mins_[d_index] > new_start_min) {
412 new_start_min = end_mins_[d_index];
413 best_d_index = d_index;
416 if (best_d_index != -1) {
417 const IntegerValue end_min_of_critical_tasks =
418 task_sets_[best_d_index].ComputeEndMin(t,
419 &best_critical_index);
420 CHECK_EQ(end_min_of_critical_tasks, new_start_min);
425 if (best_d_index == -1)
continue;
430 helper_->ResetReason();
431 const absl::Span<const TaskSet::Entry> sorted_tasks =
432 task_sets_[best_d_index].SortedTasks();
433 const IntegerValue window_start =
434 sorted_tasks[best_critical_index].start_min;
435 for (
int i = best_critical_index;
i < sorted_tasks.size(); ++
i) {
436 const int ct = sorted_tasks[
i].task;
437 if (ct == t)
continue;
438 helper_->AddPresenceReason(ct);
439 helper_->AddEnergyAfterReason(ct, sorted_tasks[
i].size_min, window_start);
440 helper_->AddStartMaxReason(ct, end_min - 1);
442 helper_->AddEndMinReason(t, end_min);
443 if (!helper_->IncreaseStartMin(t, new_start_min)) {
451 if (task_is_added_[t]) {
452 const IntegerValue shifted_smin = helper_->ShiftedStartMin(t);
453 const IntegerValue size_min = helper_->SizeMin(t);
454 for (
const int d_index : task_to_disjunctives_[t]) {
458 task_sets_[d_index].NotifyEntryIsNowLastIfPresent(
459 {t, shifted_smin, size_min});
460 end_mins_[d_index] = task_sets_[d_index].ComputeEndMin();
461 max_of_end_min = std::max(max_of_end_min, end_mins_[d_index]);
469 if (!helper_->IsEnforced())
return true;
470 stats_.OnPropagate();
471 if (!helper_->SynchronizeAndSetTimeDirection(
true)) {
472 ++stats_.num_conflicts;
476 absl::Span<TaskTime> window_span =
477 absl::MakeSpan(helper_->GetScratchTaskTimeVector1());
484 absl::Span<const CachedTaskBounds>
485 task_by_increasing_negated_shifted_end_max =
486 helper_->TaskByIncreasingNegatedShiftedEndMax();
505 IntegerValue relevant_end;
507 int relevant_size = 0;
508 TaskTime*
const window = window_span.data();
509 for (
const auto [task, presence_lit, start_min] :
510 helper_->TaskByIncreasingShiftedStartMin()) {
511 if (helper_->IsAbsent(presence_lit))
continue;
514 const IntegerValue size_min = helper_->SizeMin(task);
515 const IntegerValue end_min = start_min + size_min;
516 const IntegerValue start_max = helper_->StartMax(task);
517 if (start_max < task_with_max_end_min.
time &&
518 helper_->IsPresent(presence_lit) && size_min > 0) {
530 const int to_push = task_with_max_end_min.
task_index;
531 helper_->ResetReason();
532 helper_->AddReasonForBeingBeforeAssumingNoOverlap(task, to_push);
533 if (!helper_->PushTaskOrderWhenPresent(task, to_push)) {
534 ++stats_.num_conflicts;
542 ++stats_.num_propagations;
544 stats_.EndWithoutConflicts();
549 if (end_min > task_with_max_end_min.
time) {
550 task_with_max_end_min = {task, end_min};
554 const IntegerValue old_value = end_min_with_one_optional;
555 if (start_min < end_min_with_one_optional) {
556 window[window_size++] = {task, start_min};
558 if (helper_->IsPresent(presence_lit)) {
560 std::max(start_min + size_min, end_min_of_present + size_min);
561 end_min_with_one_optional += size_min;
565 end_min_with_one_optional =
566 std::max(end_min_with_one_optional, start_min + size_min);
568 end_min_with_one_optional =
569 std::max(end_min_with_one_optional,
570 std::max(end_min_of_present, start_min) + size_min);
574 if (old_value > start_max) {
575 relevant_size = window_size;
576 relevant_end = end_min_with_one_optional;
584 if (relevant_size > 0 &&
585 !PropagateSubwindow(window_span.subspan(0, relevant_size), relevant_end,
586 &task_by_increasing_negated_shifted_end_max)) {
587 ++stats_.num_conflicts;
593 if (helper_->IsAbsent(task_with_max_end_min.
task_index)) {
599 window[window_size++] = {task, start_min};
601 if (helper_->IsPresent(presence_lit)) {
602 end_min_of_present = start_min + size_min;
603 end_min_with_one_optional = end_min_of_present;
606 end_min_with_one_optional = start_min + size_min;
613 if (relevant_size > 0 &&
614 !PropagateSubwindow(window_span.subspan(0, relevant_size), relevant_end,
615 &task_by_increasing_negated_shifted_end_max)) {
616 ++stats_.num_conflicts;
631 task_by_increasing_negated_shifted_end_max =
632 helper_->TaskByIncreasingNegatedShiftedEndMax();
633 for (
const auto [task, presence_lit, start_min] :
634 helper_->TaskByIncreasingShiftedStartMin()) {
635 if (helper_->IsAbsent(presence_lit))
continue;
636 window[window_size++] = {task, start_min};
638 const int64_t saved = helper_->PropagationTimestamp();
639 CHECK(PropagateSubwindow(window_span.subspan(0, window_size),
641 &task_by_increasing_negated_shifted_end_max));
642 CHECK_EQ(saved, helper_->PropagationTimestamp());
645 stats_.EndWithoutConflicts();
656bool DisjunctiveOverloadChecker::PropagateSubwindow(
657 absl::Span<TaskTime> sub_window, IntegerValue global_window_end,
658 absl::Span<const CachedTaskBounds>*
659 task_by_increasing_negated_shifted_end_max) {
661 TaskTime*
const window = sub_window.data();
663 for (
int i = 0;
i < sub_window.size(); ++
i) {
670 if (end_max < global_window_end) {
671 window[num_events] = task_time;
672 task_to_event_[task] = num_events;
676 if (num_events <= 1)
return true;
677 theta_tree_.
Reset(num_events);
680 while (!task_by_increasing_negated_shifted_end_max->empty()) {
681 const auto [current_task, presence_lit, minus_shifted_end_max] =
682 task_by_increasing_negated_shifted_end_max->back();
683 const IntegerValue current_end = -minus_shifted_end_max;
684 DCHECK_EQ(current_end, helper_->
ShiftedEndMax(current_task));
685 if (current_end >= global_window_end)
return true;
686 task_by_increasing_negated_shifted_end_max->remove_suffix(1);
693 const int current_event = task_to_event_[current_task];
694 if (current_event < 0 || current_event >= num_events)
continue;
695 if (window[current_event].task_index != current_task)
continue;
700 if (helper_->
IsAbsent(presence_lit))
continue;
702 DCHECK_NE(task_to_event_[current_task], -1);
704 const IntegerValue energy_min = helper_->
SizeMin(current_task);
710 energy_min, energy_min);
712 theta_tree_.AddOrUpdateOptionalEvent(
713 current_event, window[current_event].time, energy_min);
717 if (theta_tree_.GetEnvelope() > current_end) {
719 helper_->ResetReason();
720 const int critical_event =
721 theta_tree_.GetMaxEventWithEnvelopeGreaterThan(current_end);
722 const IntegerValue window_start = window[critical_event].
time;
723 const IntegerValue window_end =
724 theta_tree_.GetEnvelopeOf(critical_event) - 1;
725 for (
int event = critical_event;
event < num_events;
event++) {
726 const IntegerValue energy_min = theta_tree_.EnergyMin(event);
727 if (energy_min > 0) {
729 helper_->AddPresenceReason(task);
730 helper_->AddEnergyAfterReason(task, energy_min, window_start);
731 helper_->AddShiftedEndMaxReason(task, window_end);
734 return helper_->ReportConflict();
738 while (theta_tree_.GetOptionalEnvelope() > current_end) {
742 helper_->ResetReason();
745 IntegerValue available_energy;
746 theta_tree_.GetEventsWithOptionalEnvelopeGreaterThan(
747 current_end, &critical_event, &optional_event, &available_energy);
749 const int optional_task = window[optional_event].
task_index;
753 if (!helper_->IsAbsent(optional_task)) {
754 const IntegerValue optional_size_min = helper_->SizeMin(optional_task);
755 const IntegerValue window_start = window[critical_event].
time;
756 const IntegerValue window_end =
757 current_end + optional_size_min - available_energy - 1;
758 for (
int event = critical_event;
event < num_events;
event++) {
759 const IntegerValue energy_min = theta_tree_.EnergyMin(event);
760 if (energy_min > 0) {
762 helper_->AddPresenceReason(task);
763 helper_->AddEnergyAfterReason(task, energy_min, window_start);
764 helper_->AddShiftedEndMaxReason(task, window_end);
768 helper_->AddEnergyAfterReason(optional_task, optional_size_min,
770 helper_->AddShiftedEndMaxReason(optional_task, window_end);
772 ++stats_.num_propagations;
773 if (!helper_->PushTaskAbsence(optional_task))
return false;
776 theta_tree_.RemoveEvent(optional_event);
787 const int id = watcher->
Register(
this);
788 helper_->SetTimeDirection(
true);
790 helper_->WatchAllTasks(
id);
795 const int id = watcher->
Register(
this);
796 helper_->WatchAllTasks(
id);
802 if (!helper_->IsEnforced())
return true;
803 stats_.OnPropagate();
805 const bool current_direction = helper_->CurrentTimeIsForward();
806 for (
const bool direction : {current_direction, !current_direction}) {
807 if (!helper_->SynchronizeAndSetTimeDirection(direction)) {
808 ++stats_.num_conflicts;
811 if (!PropagateOneDirection()) {
812 ++stats_.num_conflicts;
817 stats_.EndWithoutConflicts();
821bool DisjunctiveSimplePrecedences::Push(
TaskTime before,
int t) {
824 DCHECK_NE(t_before, t);
832bool DisjunctiveSimplePrecedences::PropagateOneDirection() {
835 absl::Span<const TaskTime> task_by_negated_start_max =
843 absl::Span<const TaskTime> task_by_increasing_end_min =
846 for (; !task_by_increasing_end_min.empty();) {
848 if (helper_->
IsAbsent(task_by_increasing_end_min.front().task_index)) {
849 task_by_increasing_end_min.remove_prefix(1);
854 int blocking_task = -1;
855 IntegerValue blocking_start_max;
856 IntegerValue current_end_min = task_by_increasing_end_min.front().time;
857 for (;
true; task_by_negated_start_max.remove_suffix(1)) {
858 if (task_by_negated_start_max.empty()) {
865 const auto [t, negated_start_max] = task_by_negated_start_max.back();
866 const IntegerValue start_max = -negated_start_max;
867 if (current_end_min <= start_max)
break;
868 if (!helper_->IsPresent(t))
continue;
876 const IntegerValue end_min = helper_->EndMin(t);
877 if (blocking_task == -1 && end_min >= current_end_min) {
878 DCHECK_LT(start_max, end_min) <<
" task should have mandatory part: "
879 << helper_->TaskDebugString(t);
881 blocking_start_max = start_max;
882 current_end_min = end_min;
883 }
else if (blocking_task != -1 && blocking_start_max < end_min) {
885 helper_->ResetReason();
886 helper_->AddPresenceReason(blocking_task);
887 helper_->AddPresenceReason(t);
888 helper_->AddReasonForBeingBeforeAssumingNoOverlap(blocking_task, t);
889 helper_->AddReasonForBeingBeforeAssumingNoOverlap(t, blocking_task);
890 return helper_->ReportConflict();
891 }
else if (end_min > best_task_before.time) {
892 best_task_before = {t, end_min};
897 if (blocking_task != -1) {
898 DCHECK(!helper_->IsAbsent(blocking_task));
899 if (best_task_before.time > helper_->StartMin(blocking_task)) {
900 if (!Push(best_task_before, blocking_task))
return false;
908 const IntegerValue end_min = helper_->EndMin(blocking_task);
909 best_task_before = {blocking_task, end_min};
913 for (; !task_by_increasing_end_min.empty();
914 task_by_increasing_end_min.remove_prefix(1)) {
915 const auto [t, end_min] = task_by_increasing_end_min.front();
916 if (end_min > current_end_min)
break;
917 if (t == blocking_task)
continue;
920 if (best_task_before.time > helper_->StartMin(t)) {
922 if (helper_->IsAbsent(t))
continue;
923 if (!Push(best_task_before, t))
return false;
931 if (!helper_->IsEnforced())
return true;
932 stats_.OnPropagate();
933 if (!helper_->SynchronizeAndSetTimeDirection(time_direction_)) {
934 ++stats_.num_conflicts;
939 const auto by_shifted_smin = helper_->TaskByIncreasingShiftedStartMin();
942 int*
const ranks = ranks_.data();
943 for (
const auto [task, presence_lit, start_min] : by_shifted_smin) {
944 if (!helper_->IsPresent(presence_lit)) {
949 const IntegerValue size_min = helper_->SizeMin(task);
950 if (start_min < window_end) {
952 window_end += size_min;
954 ranks[task] = ++rank;
955 window_end = start_min + size_min;
959 if (!PropagateWithRanks()) {
960 ++stats_.num_conflicts;
963 stats_.EndWithoutConflicts();
976bool DisjunctiveDetectablePrecedences::Push(IntegerValue task_set_end_min,
979 const absl::Span<const TaskSet::Entry> sorted_tasks = task_set.
SortedTasks();
985 const IntegerValue end_min_if_present =
987 const IntegerValue window_start = sorted_tasks[critical_index].start_min;
989 bool all_already_before =
true;
990 IntegerValue energy_of_task_before = 0;
991 for (
int i = critical_index;
i < sorted_tasks.size(); ++
i) {
992 const int ct = sorted_tasks[
i].task;
1002 sorted_tasks[critical_index].task, ct) >= 0) {
1004 sorted_tasks[critical_index].task, ct);
1015 energy_of_task_before += sorted_tasks[
i].size_min;
1016 min_slack = std::min(min_slack, dist);
1023 all_already_before =
false;
1024 helper_->AddStartMaxReason(ct, end_min_if_present - 1);
1030 IntegerValue new_start_min = task_set_end_min;
1031 if (all_already_before) {
1034 new_start_min += min_slack;
1036 helper_->AddEndMinReason(t, end_min_if_present);
1042 window_start + energy_of_task_before + min_slack > new_start_min) {
1043 new_start_min = window_start + energy_of_task_before + min_slack;
1047 if (helper_->CurrentDecisionLevel() == 0 && helper_->IsPresent(t)) {
1048 for (
int i = critical_index;
i < sorted_tasks.size(); ++
i) {
1049 if (!helper_->NotifyLevelZeroPrecedence(sorted_tasks[
i].task, t)) {
1057 ++stats_.num_propagations;
1058 if (!helper_->IncreaseStartMin(t, new_start_min)) {
1065bool DisjunctiveDetectablePrecedences::PropagateWithRanks() {
1067 absl::Span<const TaskTime> task_by_increasing_end_min =
1068 helper_->TaskByIncreasingEndMin();
1069 absl::Span<const TaskTime> task_by_negated_start_max =
1070 helper_->TaskByIncreasingNegatedStartMax();
1075 int highest_rank = 0;
1076 bool some_propagation =
false;
1083 TaskSet task_set(helper_->GetScratchTaskSet());
1086 for (; !task_by_increasing_end_min.empty();) {
1088 int blocking_task = -1;
1089 IntegerValue blocking_start_max;
1090 IntegerValue current_end_min = task_by_increasing_end_min.front().time;
1092 for (;
true; task_by_negated_start_max.remove_suffix(1)) {
1093 if (task_by_negated_start_max.empty()) {
1100 const auto [t, negated_start_max] = task_by_negated_start_max.back();
1101 const IntegerValue start_max = -negated_start_max;
1102 if (current_end_min <= start_max)
break;
1105 const int rank = ranks_[t];
1106 if (rank < highest_rank)
continue;
1107 DCHECK(helper_->IsPresent(t));
1115 const IntegerValue end_min = helper_->EndMin(t);
1116 if (blocking_task == -1 && end_min >= current_end_min) {
1117 DCHECK_LT(start_max, end_min) <<
" task should have mandatory part: "
1118 << helper_->TaskDebugString(t);
1120 blocking_start_max = start_max;
1121 current_end_min = end_min;
1122 }
else if (blocking_task != -1 && blocking_start_max < end_min) {
1124 helper_->ResetReason();
1125 helper_->AddPresenceReason(blocking_task);
1126 helper_->AddPresenceReason(t);
1127 helper_->AddReasonForBeingBeforeAssumingNoOverlap(blocking_task, t);
1128 helper_->AddReasonForBeingBeforeAssumingNoOverlap(t, blocking_task);
1129 return helper_->ReportConflict();
1131 if (!some_propagation && rank > highest_rank) {
1134 highest_rank = rank;
1136 to_add_.push_back(t);
1141 if (blocking_task != -1) {
1142 DCHECK(!helper_->IsAbsent(blocking_task));
1144 if (!to_add_.empty()) {
1145 for (
const int t : to_add_) {
1152 if (task_set_end_min > helper_->StartMin(blocking_task)) {
1153 some_propagation =
true;
1154 if (!Push(task_set_end_min, blocking_task, task_set))
return false;
1159 if (helper_->InPropagationLoop())
return true;
1166 if (!some_propagation && ranks_[blocking_task] > highest_rank) {
1169 highest_rank = ranks_[blocking_task];
1171 to_add_.push_back(blocking_task);
1176 for (; !task_by_increasing_end_min.empty();
1177 task_by_increasing_end_min.remove_prefix(1)) {
1178 const auto [t, end_min] = task_by_increasing_end_min.front();
1179 if (end_min > current_end_min)
break;
1180 if (t == blocking_task)
continue;
1182 if (!to_add_.empty()) {
1183 for (
const int t : to_add_) {
1191 if (task_set_end_min > helper_->StartMin(t)) {
1193 if (helper_->IsAbsent(t))
continue;
1195 some_propagation =
true;
1196 if (!Push(task_set_end_min, t, task_set))
return false;
1206 const int id = watcher->
Register(
this);
1207 helper_->SetTimeDirection(time_direction_);
1208 helper_->WatchAllTasks(
id);
1214 if (!helper_->IsEnforced())
return true;
1215 stats_.OnPropagate();
1216 if (!helper_->SynchronizeAndSetTimeDirection(time_direction_)) {
1217 ++stats_.num_conflicts;
1225 for (
const auto [task, presence_lit, start_min] :
1226 helper_->TaskByIncreasingShiftedStartMin()) {
1227 if (!helper_->IsPresent(presence_lit))
continue;
1229 if (start_min < window_end) {
1231 window_end += helper_->SizeMin(task);
1235 if (window.
size() > 1 && !PropagateSubwindow(absl::MakeSpan(window))) {
1236 ++stats_.num_conflicts;
1243 window_end = start_min + helper_->SizeMin(task);
1245 if (window.
size() > 1 && !PropagateSubwindow(absl::MakeSpan(window))) {
1246 ++stats_.num_conflicts;
1250 stats_.EndWithoutConflicts();
1254bool DisjunctivePrecedences::PropagateSubwindow(absl::Span<TaskTime> window) {
1256 int64_t num_hash_lookup = 0;
1257 auto cleanup = ::absl::MakeCleanup([&num_hash_lookup,
this]() {
1267 index_to_end_vars_.
clear();
1269 for (
const auto task_time : window) {
1271 const AffineExpression& end_exp = helper_->
Ends()[task];
1276 window[new_size++] = task_time;
1277 index_to_end_vars_.
push_back(end_exp.var);
1279 window = window.subspan(0, new_size);
1285 const int size = before_.size();
1286 for (
int global_i = 0; global_i < size;) {
1287 const int global_start_i = global_i;
1288 const IntegerVariable var = before_[global_i].var;
1298 indices_before_.
clear();
1299 IntegerValue local_start;
1300 IntegerValue local_end;
1301 for (; global_i < size; ++global_i) {
1302 const EnforcedLinear2Bounds::PrecedenceData& data = before_[global_i];
1303 if (data.var != var)
break;
1304 const int index = data.var_index;
1305 const auto [t, start_of_t] = window[index];
1306 if (global_i == global_start_i) {
1307 local_start = start_of_t;
1308 local_end = local_start + helper_->
SizeMin(t);
1310 if (start_of_t >= local_end)
break;
1311 local_end += helper_->SizeMin(t);
1313 indices_before_.push_back({index, data.lin2_index});
1317 const int num_before = indices_before_.size();
1318 if (num_before < 2)
continue;
1319 skip_.assign(num_before,
false);
1324 IntegerValue end_min_when_all_present = local_end;
1332 int best_index = -1;
1333 const IntegerValue current_var_lb = integer_trail_.LowerBound(var);
1334 IntegerValue best_new_lb = current_var_lb;
1337 IntegerValue sum_of_duration = 0;
1338 for (
int i = num_before; --
i >= 0;) {
1339 const auto [task_index, lin2_index] = indices_before_[
i];
1340 const TaskTime task_time = window[task_index];
1341 const AffineExpression& end_exp = helper_->Ends()[task_time.task_index];
1346 const IntegerValue inner_offset =
1347 -linear2_bounds_->NonTrivialUpperBound(lin2_index);
1354 const IntegerValue offset = inner_offset - end_exp.constant;
1357 const IntegerValue task_size = helper_->SizeMin(task_time.task_index);
1358 if (end_min_when_all_present + offset <= best_new_lb) {
1366 end_min_when_all_present -= task_size;
1374 min_offset = std::min(min_offset, offset);
1375 sum_of_duration += task_size;
1376 const IntegerValue start = task_time.time;
1377 const IntegerValue new_lb = start + sum_of_duration + min_offset;
1378 if (new_lb > best_new_lb) {
1379 min_offset_at_best = min_offset;
1380 best_new_lb = new_lb;
1386 if (best_new_lb > current_var_lb) {
1387 DCHECK_NE(best_index, -1);
1388 helper_->ResetReason();
1389 const IntegerValue window_start =
1390 window[indices_before_[best_index].first].time;
1391 for (
int i = best_index;
i < num_before; ++
i) {
1392 if (skip_[
i])
continue;
1393 const int ct = window[indices_before_[
i].first].task_index;
1394 helper_->AddPresenceReason(ct);
1395 helper_->AddEnergyAfterReason(ct, helper_->SizeMin(ct), window_start);
1400 helper_->Ends()[ct], var, -min_offset_at_best);
1401 helper_->AddReasonForUpperBoundLowerThan(expr, ub);
1403 ++stats_.num_propagations;
1404 if (!helper_->PushIntegerLiteral(
1421 const int id = watcher->
Register(
this);
1422 helper_->SetTimeDirection(time_direction_);
1423 helper_->WatchAllTasks(
id);
1428 if (!helper_->IsEnforced())
return true;
1429 stats_.OnPropagate();
1430 if (!helper_->SynchronizeAndSetTimeDirection(time_direction_)) {
1431 ++stats_.num_conflicts;
1435 const auto task_by_negated_start_max =
1436 helper_->TaskByIncreasingNegatedStartMax();
1437 const auto task_by_increasing_shifted_start_min =
1438 helper_->TaskByIncreasingShiftedStartMin();
1440 TaskSet task_set(helper_->GetScratchTaskSet());
1442 helper_->GetScratchTaskTimeVector1();
1444 helper_->GetScratchTaskTimeVector2();
1457 int queue_index = task_by_negated_start_max.
size() - 1;
1458 const int num_tasks = task_by_increasing_shifted_start_min.size();
1459 for (
int i = 0;
i < num_tasks;) {
1460 start_min_window.
clear();
1462 for (;
i < num_tasks; ++
i) {
1463 const auto [task, presence_lit, start_min] =
1464 task_by_increasing_shifted_start_min[
i];
1465 if (!helper_->IsPresent(presence_lit))
continue;
1467 if (start_min_window.
empty()) {
1468 start_min_window.
push_back({task, start_min});
1469 window_end = start_min + helper_->SizeMin(task);
1470 }
else if (start_min < window_end) {
1471 start_min_window.
push_back({task, start_min});
1472 window_end += helper_->SizeMin(task);
1480 start_max_window.
clear();
1481 for (; queue_index >= 0; queue_index--) {
1482 const auto [t, negated_start_max] =
1483 task_by_negated_start_max[queue_index];
1484 const IntegerValue start_max = -negated_start_max;
1487 if (start_max >= window_end)
break;
1488 if (helper_->IsAbsent(t))
continue;
1489 start_max_window.
push_back({t, start_max});
1495 if (start_min_window.
size() <= 1)
continue;
1498 if (!start_max_window.
empty() &&
1499 !PropagateSubwindow(task_set, absl::MakeSpan(start_min_window),
1500 absl::MakeSpan(start_max_window))) {
1501 ++stats_.num_conflicts;
1506 stats_.EndWithoutConflicts();
1510bool DisjunctiveNotLast::PropagateSubwindow(
1511 TaskSet& task_set, absl::Span<TaskTime> task_by_increasing_start_max,
1512 absl::Span<TaskTime> task_by_increasing_end_max) {
1513 for (
TaskTime& entry : task_by_increasing_end_max) {
1514 entry.time = helper_->
EndMax(entry.task_index);
1517 task_by_increasing_end_max.end());
1519 const IntegerValue threshold = task_by_increasing_end_max.back().time;
1521 for (
const TaskTime entry : task_by_increasing_start_max) {
1522 const int task = entry.task_index;
1523 const IntegerValue start_max = helper_->
StartMax(task);
1525 if (start_max < threshold) {
1526 task_by_increasing_start_max[queue_size++] = {task, start_max};
1532 if (queue_size <= 1)
return true;
1534 task_by_increasing_start_max =
1535 task_by_increasing_start_max.subspan(0, queue_size);
1536 std::sort(task_by_increasing_start_max.begin(),
1537 task_by_increasing_start_max.end());
1540 int queue_index = 0;
1541 for (
const auto task_time : task_by_increasing_end_max) {
1542 const int t = task_time.task_index;
1543 const IntegerValue end_max = task_time.time;
1547 if (helper_->IsAbsent(t))
continue;
1552 while (queue_index < queue_size) {
1553 const auto to_insert = task_by_increasing_start_max[queue_index];
1554 const IntegerValue start_max = to_insert.time;
1555 if (end_max <= start_max)
break;
1557 const int task_index = to_insert.task_index;
1558 DCHECK(helper_->IsPresent(task_index));
1559 task_set.
AddEntry({task_index, helper_->ShiftedStartMin(task_index),
1560 helper_->SizeMin(task_index)});
1574 int critical_index = 0;
1575 const IntegerValue end_min_of_critical_tasks =
1577 if (end_min_of_critical_tasks <= helper_->StartMax(t))
continue;
1582 const absl::Span<const TaskSet::Entry> sorted_tasks =
1584 const int sorted_tasks_size = sorted_tasks.size();
1585 for (
int i = critical_index;
i < sorted_tasks_size; ++
i) {
1586 const int ct = sorted_tasks[
i].task;
1587 if (t == ct)
continue;
1588 const IntegerValue start_max = helper_->StartMax(ct);
1591 if (start_max > largest_ct_start_max &&
1592 helper_->GetCurrentMinDistanceBetweenTasks(ct, t) < 0) {
1593 largest_ct_start_max = start_max;
1600 end_max > largest_ct_start_max);
1601 if (end_max > largest_ct_start_max) {
1602 helper_->ResetReason();
1604 const IntegerValue window_start = sorted_tasks[critical_index].start_min;
1605 for (
int i = critical_index;
i < sorted_tasks_size; ++
i) {
1606 const int ct = sorted_tasks[
i].task;
1607 if (ct == t)
continue;
1608 helper_->AddPresenceReason(ct);
1609 helper_->AddEnergyAfterReason(ct, sorted_tasks[
i].size_min,
1611 if (helper_->GetCurrentMinDistanceBetweenTasks(ct, t) >= 0) {
1612 helper_->AddReasonForBeingBeforeAssumingNoOverlap(ct, t);
1614 helper_->AddStartMaxReason(ct, largest_ct_start_max);
1619 helper_->AddStartMaxReason(t, end_min_of_critical_tasks - 1);
1626 return helper_->ReportConflict();
1631 ++stats_.num_propagations;
1632 if (!helper_->DecreaseEndMax(t, largest_ct_start_max))
return false;
1639 const int id = watcher->
Register(
this);
1640 helper_->WatchAllTasks(
id);
1646 if (!helper_->IsEnforced())
return true;
1647 stats_.OnPropagate();
1648 const int num_tasks = helper_->NumTasks();
1649 if (!helper_->SynchronizeAndSetTimeDirection(time_direction_)) {
1650 ++stats_.num_conflicts;
1653 is_gray_.resize(num_tasks,
false);
1654 non_gray_task_to_event_.resize(num_tasks);
1660 helper_->GetScratchTaskTimeVector2();
1663 for (
const auto [task, presence_lit, shifted_smin] :
1664 helper_->TaskByIncreasingShiftedStartMin()) {
1665 if (helper_->IsAbsent(presence_lit))
continue;
1669 if (helper_->StartMin(task) < window_end) {
1671 window_end += helper_->SizeMin(task);
1677 if (window.
size() > 2 &&
1678 !PropagateSubwindow(window_end, absl::MakeSpan(window),
1679 task_by_increasing_end_max)) {
1680 ++stats_.num_conflicts;
1686 if (helper_->IsAbsent(presence_lit)) {
1695 window_end = shifted_smin + helper_->SizeMin(task);
1697 if (window.
size() > 2 &&
1698 !PropagateSubwindow(window_end, absl::MakeSpan(window),
1699 task_by_increasing_end_max)) {
1700 ++stats_.num_conflicts;
1704 stats_.EndWithoutConflicts();
1708bool DisjunctiveEdgeFinding::PropagateSubwindow(
1709 IntegerValue window_end_min, absl::Span<const TaskTime> window,
1712 task_by_increasing_end_max.
clear();
1713 for (
const auto task_time : window) {
1714 const int task = task_time.task_index;
1725 const IntegerValue end_max = helper_->
EndMax(task);
1726 if (helper_->
IsPresent(task) && end_max < window_end_min) {
1727 is_gray_[task] =
false;
1728 task_by_increasing_end_max.
push_back({task, end_max});
1730 is_gray_[task] =
true;
1736 if (task_by_increasing_end_max.
size() < 2)
return true;
1737 std::sort(task_by_increasing_end_max.
begin(),
1738 task_by_increasing_end_max.
end());
1749 const int window_size = window.size();
1750 event_size_.clear();
1751 theta_tree_.Reset(window_size);
1752 for (
int event = 0;
event < window_size; ++event) {
1753 const TaskTime task_time = window[event];
1754 const int task = task_time.task_index;
1755 const IntegerValue energy_min = helper_->SizeMin(task);
1756 event_size_.push_back(energy_min);
1757 if (is_gray_[task]) {
1758 theta_tree_.AddOrUpdateOptionalEvent(event, task_time.time, energy_min);
1760 non_gray_task_to_event_[task] = event;
1761 theta_tree_.AddOrUpdateEvent(event, task_time.time, energy_min,
1769 DCHECK(!is_gray_[task_by_increasing_end_max.
back().task_index]);
1770 const IntegerValue non_gray_end_max =
1771 task_by_increasing_end_max.
back().time;
1774 const IntegerValue non_gray_end_min = theta_tree_.GetEnvelope();
1775 if (non_gray_end_min > non_gray_end_max) {
1776 helper_->ResetReason();
1779 const int critical_event =
1780 theta_tree_.GetMaxEventWithEnvelopeGreaterThan(non_gray_end_max);
1781 const IntegerValue window_start = window[critical_event].time;
1782 const IntegerValue window_end =
1783 theta_tree_.GetEnvelopeOf(critical_event) - 1;
1784 for (
int event = critical_event;
event < window_size;
event++) {
1785 const int task = window[event].task_index;
1786 if (is_gray_[task])
continue;
1787 helper_->AddPresenceReason(task);
1788 helper_->AddEnergyAfterReason(task, event_size_[event], window_start);
1789 helper_->AddEndMaxReason(task, window_end);
1791 return helper_->ReportConflict();
1803 while (theta_tree_.GetOptionalEnvelope() > non_gray_end_max) {
1804 const IntegerValue end_min_with_gray = theta_tree_.GetOptionalEnvelope();
1805 int critical_event_with_gray;
1807 IntegerValue available_energy;
1808 theta_tree_.GetEventsWithOptionalEnvelopeGreaterThan(
1809 non_gray_end_max, &critical_event_with_gray, &gray_event,
1811 const int gray_task = window[gray_event].task_index;
1812 DCHECK(is_gray_[gray_task]);
1816 if (helper_->IsAbsent(gray_task)) {
1817 theta_tree_.RemoveEvent(gray_event);
1822 if (helper_->StartMin(gray_task) < non_gray_end_min) {
1825 const int critical_event =
1826 theta_tree_.GetMaxEventWithEnvelopeGreaterThan(non_gray_end_min -
1832 if (critical_event_with_gray > critical_event) {
1833 critical_event_with_gray = critical_event;
1836 const int first_event = critical_event_with_gray;
1837 const int second_event = critical_event;
1838 const IntegerValue first_start = window[first_event].time;
1839 const IntegerValue second_start = window[second_event].time;
1846 bool use_energy_reason =
true;
1847 IntegerValue window_end;
1848 if (helper_->EndMin(gray_task) > non_gray_end_max) {
1853 use_energy_reason =
false;
1854 window_end = helper_->EndMin(gray_task) - 1;
1856 window_end = end_min_with_gray - 1;
1858 CHECK_GE(window_end, non_gray_end_max);
1861 helper_->ResetReason();
1862 bool all_before =
true;
1863 for (
int event = first_event;
event < window_size;
event++) {
1864 const int task = window[event].task_index;
1865 if (is_gray_[task])
continue;
1866 helper_->AddPresenceReason(task);
1867 helper_->AddEnergyAfterReason(
1868 task, event_size_[event],
1869 event >= second_event ? second_start : first_start);
1871 const IntegerValue dist =
1872 helper_->GetCurrentMinDistanceBetweenTasks(task, gray_task);
1874 helper_->AddReasonForBeingBeforeAssumingNoOverlap(task, gray_task);
1877 helper_->AddEndMaxReason(task, window_end);
1884 if (use_energy_reason) {
1885 helper_->AddEnergyAfterReason(gray_task, event_size_[gray_event],
1888 helper_->AddEndMinReason(gray_task, helper_->EndMin(gray_task));
1893 if (helper_->CurrentDecisionLevel() == 0 &&
1894 helper_->IsPresent(gray_task)) {
1895 for (
int i = first_event;
i < window_size; ++
i) {
1896 const int task = window[
i].task_index;
1897 if (!is_gray_[task]) {
1898 if (!helper_->NotifyLevelZeroPrecedence(task, gray_task)) {
1910 ++stats_.num_propagations;
1911 if (!helper_->IncreaseStartMin(gray_task, non_gray_end_min)) {
1917 theta_tree_.RemoveEvent(gray_event);
1921 if (task_by_increasing_end_max.
size() <= 2)
break;
1924 if (task_by_increasing_end_max[0].time >=
1925 theta_tree_.GetOptionalEnvelope()) {
1930 const int new_gray_task = task_by_increasing_end_max.
back().task_index;
1931 task_by_increasing_end_max.
pop_back();
1932 const int new_gray_event = non_gray_task_to_event_[new_gray_task];
1933 DCHECK(!is_gray_[new_gray_task]);
1934 is_gray_[new_gray_task] =
true;
1935 theta_tree_.AddOrUpdateOptionalEvent(new_gray_event,
1936 window[new_gray_event].time,
1937 event_size_[new_gray_event]);
1944 const int id = watcher->
Register(
this);
1945 helper_->SetTimeDirection(time_direction_);
1946 helper_->WatchAllTasks(
id);
void AddOrUpdateEvent(int event, IntegerType initial_envelope, IntegerType energy_min, IntegerType energy_max)
void Reset(int num_events)
void AdvanceDeterministicTime(double deterministic_duration)
void AddNoOverlap(absl::Span< const IntervalVariable > var)
CombinedDisjunctive(Model *model)
int RegisterWith(GenericLiteralWatcher *watcher)
int RegisterWith(GenericLiteralWatcher *watcher)
int RegisterWith(GenericLiteralWatcher *watcher)
int RegisterWith(GenericLiteralWatcher *watcher)
int RegisterWith(GenericLiteralWatcher *watcher)
int RegisterWith(GenericLiteralWatcher *watcher)
int RegisterWith(GenericLiteralWatcher *watcher)
void CollectPrecedences(absl::Span< const IntegerVariable > vars, std::vector< PrecedenceData > *output)
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)
int Register(PropagatorInterface *propagator)
SchedulingConstraintHelper * GetOrCreateHelper(std::vector< Literal > enforcement_literals, const std::vector< IntervalVariable > &variables, bool register_as_disjunctive_helper=false)
SchedulingDemandHelper * GetOrCreateDemandHelper(SchedulingConstraintHelper *helper, absl::Span< const AffineExpression > demands)
AffineExpression Start(IntervalVariable i) const
bool IsOptional(IntervalVariable i) const
IntegerValue MaxSize(IntervalVariable i) const
IntegerValue MinSize(IntervalVariable i) const
std::vector< IntervalVariable > AllIntervals() const
T Add(std::function< T(Model *)> f)
void AddPresenceReason(int t)
IntegerValue ShiftedEndMax(int t) const
void AddEnergyAfterReason(int t, IntegerValue energy_min, IntegerValue time)
ABSL_MUST_USE_RESULT bool PushTaskOrderWhenPresent(int t_before, int t_after)
IntegerValue ShiftedStartMin(int t) const
absl::Span< const TaskTime > TaskByIncreasingNegatedStartMax()
IntegerValue GetCurrentMinDistanceBetweenTasks(int a, int b)
IntegerValue StartMax(int t) const
bool IsPresent(int t) const
void AddSizeMinReason(int t)
absl::Span< const AffineExpression > Ends() const
IntegerValue StartMin(int t) const
IntegerValue SizeMin(int t) const
void AddReasonForBeingBeforeAssumingNoOverlap(int before, int after)
IntegerValue EndMax(int t) const
bool IsAbsent(int t) const
IntegerValue EndMin(int t) const
absl::Span< const TaskTime > TaskByIncreasingEndMin()
void NotifyEntryIsNowLastIfPresent(const Entry &e)
IntegerValue ComputeEndMin(int task_to_ignore, int *critical_index) const
SchedulingConstraintHelper::TaskInfo Entry
int GetCriticalIndex() const
void AddEntry(const Entry &e)
IntegerValue ComputeEndMin() const
absl::Span< const Entry > SortedTasks() const
void AddShiftedStartMinEntry(const SchedulingConstraintHelper &helper, int t)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
std::function< void(Model *)> AllDifferentOnBounds(absl::Span< const Literal > enforcement_literals, absl::Span< const AffineExpression > expressions)
std::pair< LinearExpression2, IntegerValue > EncodeDifferenceLowerThan(AffineExpression a, AffineExpression b, IntegerValue ub)
void AddDisjunctive(const std::vector< Literal > &enforcement_literals, const std::vector< IntervalVariable > &intervals, Model *model)
void AddDisjunctiveWithBooleanPrecedencesOnly(absl::Span< const IntervalVariable > intervals, Model *model)
void IncrementalSort(int max_comparisons, Iterator begin, Iterator end, Compare comp=Compare{}, bool is_stable=false)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)