20#include "absl/algorithm/container.h"
21#include "absl/log/check.h"
22#include "absl/types/span.h"
31#include "ortools/sat/sat_parameters.pb.h"
44 const auto& params = *model->
GetOrCreate<SatParameters>();
45 if (intervals.size() <=
46 params.max_size_to_create_precedence_literals_in_disjunctive() &&
47 params.use_strong_propagation_in_disjunctive()) {
51 bool is_all_different =
true;
53 for (
const IntervalVariable var : intervals) {
55 repository->
MaxSize(var) != 1) {
56 is_all_different =
false;
60 if (is_all_different) {
61 std::vector<AffineExpression> starts;
62 starts.reserve(intervals.size());
63 for (
const IntervalVariable interval : intervals) {
64 starts.push_back(repository->
Start(interval));
71 if (intervals.size() > 2 && params.use_combined_no_overlap()) {
83 std::vector<AffineExpression> demands(intervals.size(), one);
94 if (intervals.size() == 2) {
101 if (!params.use_strong_propagation_in_disjunctive()) {
106 const int id = simple_precedences->
RegisterWith(watcher);
107 watcher->SetPropagatorPriority(
id, 1);
115 watcher->SetPropagatorPriority(
id, 1);
118 for (
const bool time_direction : {
true,
false}) {
121 const int id = detectable_precedences->
RegisterWith(watcher);
122 watcher->SetPropagatorPriority(
id, 2);
125 for (
const bool time_direction : {
true,
false}) {
129 watcher->SetPropagatorPriority(
id, 3);
132 for (
const bool time_direction : {
true,
false}) {
136 watcher->SetPropagatorPriority(
id, 4);
144 if (params.use_precedences_in_disjunctive_constraint() &&
145 !params.use_combined_no_overlap()) {
146 for (
const bool time_direction : {
true,
false}) {
150 watcher->SetPropagatorPriority(
id, 5);
157 absl::Span<const IntervalVariable> intervals,
Model* model) {
159 for (
int i = 0;
i < intervals.size(); ++
i) {
160 for (
int j =
i + 1; j < intervals.size(); ++j) {
161 repo->CreateDisjunctivePrecedenceLiteral(intervals[
i], intervals[j]);
167 int j = sorted_tasks_.size();
168 sorted_tasks_.push_back(e);
169 while (j > 0 && sorted_tasks_[j - 1].start_min > e.
start_min) {
170 sorted_tasks_[j] = sorted_tasks_[j - 1];
173 sorted_tasks_[j] = e;
174 DCHECK(std::is_sorted(sorted_tasks_.begin(), sorted_tasks_.end()));
178 if (j <= optimized_restart_) optimized_restart_ = 0;
183 const IntegerValue dmin = helper.
SizeMin(t);
188 const int size = sorted_tasks_.size();
189 for (
int i = 0;; ++
i) {
190 if (
i == size)
return;
191 if (sorted_tasks_[
i].task == e.
task) {
192 for (
int j =
i; j + 1 < size; ++j) {
193 sorted_tasks_[j] = sorted_tasks_[j + 1];
195 sorted_tasks_.pop_back();
200 optimized_restart_ = sorted_tasks_.size();
201 sorted_tasks_.push_back(e);
202 DCHECK(std::is_sorted(sorted_tasks_.begin(), sorted_tasks_.end()));
206 DCHECK(std::is_sorted(sorted_tasks_.begin(), sorted_tasks_.end()));
207 const int size = sorted_tasks_.size();
209 for (
int i = optimized_restart_;
i < size; ++
i) {
210 const Entry& e = sorted_tasks_[
i];
212 optimized_restart_ =
i;
222 int* critical_index)
const {
224 DCHECK(std::is_sorted(sorted_tasks_.begin(), sorted_tasks_.end()));
225 bool ignored =
false;
226 const int size = sorted_tasks_.size();
231 if (optimized_restart_ + 1 == size &&
232 sorted_tasks_[optimized_restart_].task == task_to_ignore) {
233 optimized_restart_ = 0;
236 for (
int i = optimized_restart_;
i < size; ++
i) {
237 const Entry& e = sorted_tasks_[
i];
238 if (e.
task == task_to_ignore) {
244 if (!ignored) optimized_restart_ =
i;
254 DCHECK_EQ(helper_->NumTasks(), 2);
255 if (!helper_->SynchronizeAndSetTimeDirection(
true))
return false;
258 if (helper_->IsAbsent(0) || helper_->IsAbsent(1))
return true;
271 const bool task_0_before_task_1 = helper_->StartMax(0) < helper_->EndMin(1);
272 const bool task_1_before_task_0 = helper_->StartMax(1) < helper_->EndMin(0);
274 if (task_0_before_task_1 && task_1_before_task_0 &&
275 helper_->IsPresent(task_before) && helper_->IsPresent(task_after)) {
276 helper_->ClearReason();
277 helper_->AddPresenceReason(task_before);
278 helper_->AddPresenceReason(task_after);
279 helper_->AddReasonForBeingBefore(task_before, task_after);
280 helper_->AddReasonForBeingBefore(task_after, task_before);
281 return helper_->ReportConflict();
284 if (task_0_before_task_1) {
286 }
else if (task_1_before_task_0) {
288 std::swap(task_before, task_after);
293 if (helper_->IsPresent(task_before)) {
294 const IntegerValue end_min_before = helper_->EndMin(task_before);
295 if (helper_->StartMin(task_after) < end_min_before) {
297 helper_->ClearReason();
298 helper_->AddReasonForBeingBefore(task_before, task_after);
301 helper_->AddPresenceReason(task_before);
302 helper_->AddEndMinReason(task_before, end_min_before);
303 if (!helper_->IncreaseStartMin(task_after, end_min_before)) {
309 if (helper_->IsPresent(task_after)) {
310 const IntegerValue start_max_after = helper_->StartMax(task_after);
311 if (helper_->EndMax(task_before) > start_max_after) {
313 helper_->ClearReason();
314 helper_->AddReasonForBeingBefore(task_before, task_after);
317 helper_->AddPresenceReason(task_after);
318 helper_->AddStartMaxReason(task_after, start_max_after);
319 if (!helper_->DecreaseEndMax(task_before, start_max_after)) {
329 const int id = watcher->
Register(
this);
330 helper_->WatchAllTasks(
id);
335template <
bool time_direction>
339 task_to_disjunctives_.resize(helper_->NumTasks());
342 const int id = watcher->
Register(
this);
343 helper_->WatchAllTasks(
id);
344 watcher->NotifyThatPropagatorMayNotReachFixedPointInOnePass(
id);
347template <
bool time_direction>
349 absl::Span<const IntervalVariable> vars) {
350 const int index = task_sets_.size();
351 task_sets_.emplace_back(vars.size());
353 for (
const IntervalVariable var : vars) {
354 task_to_disjunctives_[var.value()].push_back(index);
358template <
bool time_direction>
360 if (!helper_->SynchronizeAndSetTimeDirection(time_direction))
return false;
361 const auto& task_by_increasing_end_min = helper_->TaskByIncreasingEndMin();
362 const auto& task_by_negated_start_max =
363 helper_->TaskByIncreasingNegatedStartMax();
365 for (
auto& task_set : task_sets_) task_set.Clear();
369 const int num_tasks = helper_->NumTasks();
370 task_is_added_.assign(num_tasks,
false);
371 int queue_index = num_tasks - 1;
372 for (
const auto task_time : task_by_increasing_end_min) {
373 const int t = task_time.task_index;
374 const IntegerValue end_min = task_time.time;
375 if (helper_->IsAbsent(t))
continue;
378 while (queue_index >= 0) {
379 const auto to_insert = task_by_negated_start_max[queue_index];
380 const int task_index = to_insert.task_index;
381 const IntegerValue start_max = -to_insert.time;
382 if (end_min <= start_max)
break;
383 if (helper_->IsPresent(task_index)) {
384 task_is_added_[task_index] =
true;
385 const IntegerValue shifted_smin = helper_->ShiftedStartMin(task_index);
386 const IntegerValue size_min = helper_->SizeMin(task_index);
387 for (
const int d_index : task_to_disjunctives_[task_index]) {
389 task_sets_[d_index].AddEntry({task_index, shifted_smin, size_min});
390 end_mins_[d_index] = task_sets_[d_index].ComputeEndMin();
391 max_of_end_min = std::max(max_of_end_min, end_mins_[d_index]);
399 IntegerValue new_start_min = helper_->StartMin(t);
400 if (new_start_min >= max_of_end_min)
continue;
401 int best_critical_index = 0;
402 int best_d_index = -1;
403 if (task_is_added_[t]) {
404 for (
const int d_index : task_to_disjunctives_[t]) {
405 if (new_start_min >= end_mins_[d_index])
continue;
406 int critical_index = 0;
407 const IntegerValue end_min_of_critical_tasks =
408 task_sets_[d_index].ComputeEndMin(t,
410 DCHECK_LE(end_min_of_critical_tasks, max_of_end_min);
411 if (end_min_of_critical_tasks > new_start_min) {
412 new_start_min = end_min_of_critical_tasks;
413 best_d_index = d_index;
414 best_critical_index = critical_index;
420 for (
const int d_index : task_to_disjunctives_[t]) {
421 if (end_mins_[d_index] > new_start_min) {
422 new_start_min = end_mins_[d_index];
423 best_d_index = d_index;
426 if (best_d_index != -1) {
427 const IntegerValue end_min_of_critical_tasks =
428 task_sets_[best_d_index].ComputeEndMin(t,
429 &best_critical_index);
430 CHECK_EQ(end_min_of_critical_tasks, new_start_min);
435 if (best_d_index == -1)
continue;
440 helper_->ClearReason();
441 const absl::Span<const TaskSet::Entry> sorted_tasks =
442 task_sets_[best_d_index].SortedTasks();
443 const IntegerValue window_start =
444 sorted_tasks[best_critical_index].start_min;
445 for (
int i = best_critical_index;
i < sorted_tasks.size(); ++
i) {
446 const int ct = sorted_tasks[
i].task;
447 if (ct == t)
continue;
448 helper_->AddPresenceReason(ct);
449 helper_->AddEnergyAfterReason(ct, sorted_tasks[
i].size_min, window_start);
450 helper_->AddStartMaxReason(ct, end_min - 1);
452 helper_->AddEndMinReason(t, end_min);
453 if (!helper_->IncreaseStartMin(t, new_start_min)) {
461 if (task_is_added_[t]) {
462 const IntegerValue shifted_smin = helper_->ShiftedStartMin(t);
463 const IntegerValue size_min = helper_->SizeMin(t);
464 for (
const int d_index : task_to_disjunctives_[t]) {
468 task_sets_[d_index].NotifyEntryIsNowLastIfPresent(
469 {t, shifted_smin, size_min});
470 end_mins_[d_index] = task_sets_[d_index].ComputeEndMin();
471 max_of_end_min = std::max(max_of_end_min, end_mins_[d_index]);
479 stats_.OnPropagate();
480 if (!helper_->SynchronizeAndSetTimeDirection(
true)) {
481 ++stats_.num_conflicts;
500 IntegerValue relevant_end;
502 int relevant_size = 0;
503 TaskTime*
const window = window_.get();
504 for (
const auto [task, presence_lit, start_min] :
505 helper_->TaskByIncreasingShiftedStartMin()) {
506 if (helper_->IsAbsent(presence_lit))
continue;
509 const IntegerValue size_min = helper_->SizeMin(task);
510 const IntegerValue end_min = start_min + size_min;
511 const IntegerValue start_max = helper_->StartMax(task);
512 if (start_max < task_with_max_end_min.
time &&
513 helper_->IsPresent(presence_lit) && size_min > 0) {
527 const int to_push = task_with_max_end_min.
task_index;
528 helper_->ClearReason();
529 helper_->AddPresenceReason(task);
530 helper_->AddReasonForBeingBefore(task, to_push);
531 helper_->AddEndMinReason(task, end_min);
533 if (!helper_->IncreaseStartMin(to_push, end_min)) {
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 if (start_min < window_end) {
555 window[window_size++] = {task, start_min};
556 if (window_end > start_max) {
557 window_end += size_min;
558 relevant_size = window_size;
559 relevant_end = window_end;
561 window_end += size_min;
569 if (relevant_size > 0 && !PropagateSubwindow(relevant_size, relevant_end)) {
570 ++stats_.num_conflicts;
576 window[window_size++] = {task, start_min};
577 window_end = start_min + size_min;
582 if (relevant_size > 0 && !PropagateSubwindow(relevant_size, relevant_end)) {
583 ++stats_.num_conflicts;
587 stats_.EndWithoutConflicts();
598bool DisjunctiveOverloadChecker::PropagateSubwindow(
599 int relevant_size, IntegerValue global_window_end) {
602 task_by_increasing_end_max_.clear();
603 for (
int i = 0;
i < relevant_size; ++
i) {
609 const IntegerValue end_max =
611 if (end_max < global_window_end) {
612 window_[num_events] = task_time;
613 task_to_event_[task] = num_events;
614 task_by_increasing_end_max_.push_back({task, end_max});
618 if (num_events <= 1)
return true;
619 theta_tree_.Reset(num_events);
623 absl::c_reverse(task_by_increasing_end_max_);
624 absl::c_stable_sort(task_by_increasing_end_max_);
625 for (
const auto task_time : task_by_increasing_end_max_) {
626 const int current_task = task_time.
task_index;
631 if (helper_->IsAbsent(current_task))
continue;
633 DCHECK_NE(task_to_event_[current_task], -1);
635 const int current_event = task_to_event_[current_task];
636 const IntegerValue energy_min = helper_->SizeMin(current_task);
637 if (helper_->IsPresent(current_task)) {
641 theta_tree_.AddOrUpdateEvent(current_event, window_[current_event].time,
642 energy_min, energy_min);
644 theta_tree_.AddOrUpdateOptionalEvent(
645 current_event, window_[current_event].time, energy_min);
649 const IntegerValue current_end = task_time.
time;
650 if (theta_tree_.GetEnvelope() > current_end) {
652 helper_->ClearReason();
653 const int critical_event =
654 theta_tree_.GetMaxEventWithEnvelopeGreaterThan(current_end);
655 const IntegerValue window_start = window_[critical_event].time;
656 const IntegerValue window_end =
657 theta_tree_.GetEnvelopeOf(critical_event) - 1;
658 for (
int event = critical_event;
event < num_events;
event++) {
659 const IntegerValue energy_min = theta_tree_.EnergyMin(event);
660 if (energy_min > 0) {
661 const int task = window_[event].task_index;
662 helper_->AddPresenceReason(task);
663 helper_->AddEnergyAfterReason(task, energy_min, window_start);
664 helper_->AddShiftedEndMaxReason(task, window_end);
667 return helper_->ReportConflict();
671 while (theta_tree_.GetOptionalEnvelope() > current_end) {
675 helper_->ClearReason();
678 IntegerValue available_energy;
679 theta_tree_.GetEventsWithOptionalEnvelopeGreaterThan(
680 current_end, &critical_event, &optional_event, &available_energy);
682 const int optional_task = window_[optional_event].task_index;
686 if (!helper_->IsAbsent(optional_task)) {
687 const IntegerValue optional_size_min = helper_->SizeMin(optional_task);
688 const IntegerValue window_start = window_[critical_event].time;
689 const IntegerValue window_end =
690 current_end + optional_size_min - available_energy - 1;
691 for (
int event = critical_event;
event < num_events;
event++) {
692 const IntegerValue energy_min = theta_tree_.EnergyMin(event);
693 if (energy_min > 0) {
694 const int task = window_[event].task_index;
695 helper_->AddPresenceReason(task);
696 helper_->AddEnergyAfterReason(task, energy_min, window_start);
697 helper_->AddShiftedEndMaxReason(task, window_end);
701 helper_->AddEnergyAfterReason(optional_task, optional_size_min,
703 helper_->AddShiftedEndMaxReason(optional_task, window_end);
705 ++stats_.num_propagations;
706 if (!helper_->PushTaskAbsence(optional_task))
return false;
709 theta_tree_.RemoveEvent(optional_event);
718 const int id = watcher->
Register(
this);
719 helper_->SetTimeDirection(
true);
721 helper_->WatchAllTasks(
id);
726 const int id = watcher->
Register(
this);
727 helper_->WatchAllTasks(
id);
733 stats_.OnPropagate();
735 const bool current_direction = helper_->CurrentTimeIsForward();
736 for (
const bool direction : {current_direction, !current_direction}) {
737 if (!helper_->SynchronizeAndSetTimeDirection(direction)) {
738 ++stats_.num_conflicts;
741 if (!PropagateOneDirection()) {
742 ++stats_.num_conflicts;
747 stats_.EndWithoutConflicts();
751bool DisjunctiveSimplePrecedences::Push(
TaskTime before,
int t) {
753 DCHECK_NE(t_before, t);
765bool DisjunctiveSimplePrecedences::PropagateOneDirection() {
768 absl::Span<const TaskTime> task_by_negated_start_max =
776 absl::Span<const TaskTime> task_by_increasing_end_min =
777 helper_->TaskByIncreasingEndMin();
779 for (; !task_by_increasing_end_min.empty();) {
781 if (helper_->IsAbsent(task_by_increasing_end_min.front().task_index)) {
782 task_by_increasing_end_min.remove_prefix(1);
787 int blocking_task = -1;
788 IntegerValue blocking_start_max;
789 IntegerValue current_end_min = task_by_increasing_end_min.front().time;
790 for (;
true; task_by_negated_start_max.remove_suffix(1)) {
791 if (task_by_negated_start_max.empty()) {
798 const auto [t, negated_start_max] = task_by_negated_start_max.back();
799 const IntegerValue start_max = -negated_start_max;
800 if (current_end_min <= start_max)
break;
801 if (!helper_->IsPresent(t))
continue;
809 const IntegerValue end_min = helper_->EndMin(t);
810 if (blocking_task == -1 && end_min >= current_end_min) {
811 DCHECK_LT(start_max, end_min) <<
" task should have mandatory part: "
812 << helper_->TaskDebugString(t);
814 blocking_start_max = start_max;
815 current_end_min = end_min;
816 }
else if (blocking_task != -1 && blocking_start_max < end_min) {
818 helper_->ClearReason();
819 helper_->AddPresenceReason(blocking_task);
820 helper_->AddPresenceReason(t);
821 helper_->AddReasonForBeingBefore(blocking_task, t);
822 helper_->AddReasonForBeingBefore(t, blocking_task);
823 return helper_->ReportConflict();
824 }
else if (end_min > best_task_before.time) {
825 best_task_before = {t, end_min};
830 if (blocking_task != -1) {
831 DCHECK(!helper_->IsAbsent(blocking_task));
832 if (best_task_before.time > helper_->StartMin(blocking_task)) {
833 if (!Push(best_task_before, blocking_task))
return false;
841 const IntegerValue end_min = helper_->EndMin(blocking_task);
842 best_task_before = {blocking_task, end_min};
846 for (; !task_by_increasing_end_min.empty();
847 task_by_increasing_end_min.remove_prefix(1)) {
848 const auto [t, end_min] = task_by_increasing_end_min.front();
849 if (end_min > current_end_min)
break;
850 if (t == blocking_task)
continue;
853 if (best_task_before.time > helper_->StartMin(t)) {
855 if (helper_->IsAbsent(t))
continue;
856 if (!Push(best_task_before, t))
return false;
864 stats_.OnPropagate();
865 if (!helper_->SynchronizeAndSetTimeDirection(time_direction_)) {
866 ++stats_.num_conflicts;
871 const auto by_shifted_smin = helper_->TaskByIncreasingShiftedStartMin();
874 int*
const ranks = ranks_.data();
875 for (
const auto [task, presence_lit, start_min] : by_shifted_smin) {
876 if (!helper_->IsPresent(presence_lit)) {
881 const IntegerValue size_min = helper_->SizeMin(task);
882 if (start_min < window_end) {
884 window_end += size_min;
886 ranks[task] = ++rank;
887 window_end = start_min + size_min;
891 if (!PropagateWithRanks()) {
892 ++stats_.num_conflicts;
895 stats_.EndWithoutConflicts();
908bool DisjunctiveDetectablePrecedences::Push(IntegerValue task_set_end_min,
911 const absl::Span<const TaskSet::Entry> sorted_tasks = task_set_.
SortedTasks();
917 const IntegerValue end_min_if_present =
919 const IntegerValue window_start = sorted_tasks[critical_index].start_min;
921 bool all_already_before =
true;
922 IntegerValue energy_of_task_before = 0;
923 for (
int i = critical_index;
i < sorted_tasks.size(); ++
i) {
924 const int ct = sorted_tasks[
i].task;
931 sorted_tasks[critical_index].task, ct,
943 energy_of_task_before += sorted_tasks[
i].size_min;
944 min_slack = std::min(min_slack, dist);
946 all_already_before =
false;
947 helper_->AddStartMaxReason(ct, end_min_if_present - 1);
953 IntegerValue new_start_min = task_set_end_min;
954 if (all_already_before) {
957 new_start_min += min_slack;
959 helper_->AddEndMinReason(t, end_min_if_present);
965 window_start + energy_of_task_before + min_slack > new_start_min) {
966 new_start_min = window_start + energy_of_task_before + min_slack;
970 if (helper_->CurrentDecisionLevel() == 0 && helper_->IsPresent(t)) {
971 for (
int i = critical_index;
i < sorted_tasks.size(); ++
i) {
972 if (!helper_->PropagatePrecedence(sorted_tasks[
i].task, t)) {
980 ++stats_.num_propagations;
981 if (!helper_->IncreaseStartMin(t, new_start_min)) {
988bool DisjunctiveDetectablePrecedences::PropagateWithRanks() {
990 absl::Span<const TaskTime> task_by_increasing_end_min =
991 helper_->TaskByIncreasingEndMin();
992 absl::Span<const TaskTime> task_by_negated_start_max =
993 helper_->TaskByIncreasingNegatedStartMax();
998 int highest_rank = 0;
999 bool some_propagation =
false;
1009 for (; !task_by_increasing_end_min.empty();) {
1011 int blocking_task = -1;
1012 IntegerValue blocking_start_max;
1013 IntegerValue current_end_min = task_by_increasing_end_min.front().time;
1015 for (;
true; task_by_negated_start_max.remove_suffix(1)) {
1016 if (task_by_negated_start_max.empty()) {
1023 const auto [t, negated_start_max] = task_by_negated_start_max.back();
1024 const IntegerValue start_max = -negated_start_max;
1025 if (current_end_min <= start_max)
break;
1028 const int rank = ranks_[t];
1029 if (rank < highest_rank)
continue;
1030 DCHECK(helper_->IsPresent(t));
1038 const IntegerValue end_min = helper_->EndMin(t);
1039 if (blocking_task == -1 && end_min >= current_end_min) {
1040 DCHECK_LT(start_max, end_min) <<
" task should have mandatory part: "
1041 << helper_->TaskDebugString(t);
1043 blocking_start_max = start_max;
1044 current_end_min = end_min;
1045 }
else if (blocking_task != -1 && blocking_start_max < end_min) {
1047 helper_->ClearReason();
1048 helper_->AddPresenceReason(blocking_task);
1049 helper_->AddPresenceReason(t);
1050 helper_->AddReasonForBeingBefore(blocking_task, t);
1051 helper_->AddReasonForBeingBefore(t, blocking_task);
1052 return helper_->ReportConflict();
1054 if (!some_propagation && rank > highest_rank) {
1057 highest_rank = rank;
1059 to_add_.push_back(t);
1064 if (blocking_task != -1) {
1065 DCHECK(!helper_->IsAbsent(blocking_task));
1067 if (!to_add_.empty()) {
1068 for (
const int t : to_add_) {
1069 task_set_.AddShiftedStartMinEntry(*helper_, t);
1072 task_set_end_min = task_set_.ComputeEndMin();
1075 if (task_set_end_min > helper_->StartMin(blocking_task)) {
1076 some_propagation =
true;
1077 if (!Push(task_set_end_min, blocking_task))
return false;
1082 if (helper_->InPropagationLoop())
return true;
1089 if (!some_propagation && ranks_[blocking_task] > highest_rank) {
1092 highest_rank = ranks_[blocking_task];
1094 to_add_.push_back(blocking_task);
1099 for (; !task_by_increasing_end_min.empty();
1100 task_by_increasing_end_min.remove_prefix(1)) {
1101 const auto [t, end_min] = task_by_increasing_end_min.front();
1102 if (end_min > current_end_min)
break;
1103 if (t == blocking_task)
continue;
1105 if (!to_add_.empty()) {
1106 for (
const int t : to_add_) {
1107 task_set_.AddShiftedStartMinEntry(*helper_, t);
1110 task_set_end_min = task_set_.ComputeEndMin();
1114 if (task_set_end_min > helper_->StartMin(t)) {
1116 if (helper_->IsAbsent(t))
continue;
1118 some_propagation =
true;
1119 if (!Push(task_set_end_min, t))
return false;
1129 const int id = watcher->
Register(
this);
1130 helper_->SetTimeDirection(time_direction_);
1131 helper_->WatchAllTasks(
id);
1137 stats_.OnPropagate();
1138 if (!helper_->SynchronizeAndSetTimeDirection(time_direction_)) {
1139 ++stats_.num_conflicts;
1147 for (
const auto [task, presence_lit, start_min] :
1148 helper_->TaskByIncreasingShiftedStartMin()) {
1149 if (!helper_->IsPresent(presence_lit))
continue;
1151 if (start_min < window_end) {
1152 window_.push_back({task, start_min});
1153 window_end += helper_->SizeMin(task);
1157 if (window_.size() > 1 && !PropagateSubwindow()) {
1158 ++stats_.num_conflicts;
1164 window_.push_back({task, start_min});
1165 window_end = start_min + helper_->SizeMin(task);
1167 if (window_.size() > 1 && !PropagateSubwindow()) {
1168 ++stats_.num_conflicts;
1172 stats_.EndWithoutConflicts();
1176bool DisjunctivePrecedences::PropagateSubwindow() {
1182 index_to_end_vars_.
clear();
1184 for (
const auto task_time : window_) {
1191 window_[new_size++] = task_time;
1194 window_.resize(new_size);
1200 const int size = before_.size();
1201 for (
int global_i = 0; global_i < size;) {
1202 const int global_start_i = global_i;
1203 const IntegerVariable var = before_[global_i].var;
1215 indices_before_.
clear();
1216 IntegerValue local_start;
1217 IntegerValue local_end;
1218 for (; global_i < size; ++global_i) {
1219 const PrecedenceRelations::PrecedenceData& data = before_[global_i];
1220 if (data.var != var)
break;
1221 const int index = data.index;
1222 const auto [t, start_of_t] = window_[index];
1223 if (global_i == global_start_i) {
1224 local_start = start_of_t;
1225 local_end = local_start + helper_->
SizeMin(t);
1227 if (start_of_t >= local_end)
break;
1228 local_end += helper_->
SizeMin(t);
1230 indices_before_.push_back(index);
1234 const int num_before = indices_before_.size();
1235 if (num_before < 2)
continue;
1236 skip_.assign(num_before,
false);
1241 IntegerValue end_min_when_all_present = local_end;
1249 int best_index = -1;
1250 const IntegerValue current_var_lb = integer_trail_->LowerBound(var);
1251 IntegerValue best_new_lb = current_var_lb;
1253 IntegerValue sum_of_duration = 0;
1254 for (
int i = num_before; --
i >= 0;) {
1255 const TaskTime task_time = window_[indices_before_[
i]];
1256 const AffineExpression& end_exp = helper_->Ends()[task_time.task_index];
1262 const IntegerValue inner_offset =
1263 precedence_relations_->GetConditionalOffset(end_exp.var, var);
1270 const IntegerValue offset = inner_offset - end_exp.constant;
1273 const IntegerValue task_size = helper_->SizeMin(task_time.task_index);
1274 if (end_min_when_all_present + offset <= best_new_lb) {
1282 end_min_when_all_present -= task_size;
1290 min_offset = std::min(min_offset, offset);
1291 sum_of_duration += task_size;
1292 const IntegerValue start = task_time.time;
1293 const IntegerValue new_lb = start + sum_of_duration + min_offset;
1294 if (new_lb > best_new_lb) {
1295 best_new_lb = new_lb;
1301 if (best_new_lb > current_var_lb) {
1302 DCHECK_NE(best_index, -1);
1303 helper_->ClearReason();
1304 const IntegerValue window_start =
1305 window_[indices_before_[best_index]].time;
1306 for (
int i = best_index;
i < num_before; ++
i) {
1307 if (skip_[
i])
continue;
1308 const int ct = window_[indices_before_[
i]].task_index;
1309 helper_->AddPresenceReason(ct);
1310 helper_->AddEnergyAfterReason(ct, helper_->SizeMin(ct), window_start);
1314 const AffineExpression& end_exp = helper_->Ends()[ct];
1315 for (
const Literal l :
1316 precedence_relations_->GetConditionalEnforcements(end_exp.var,
1318 helper_->MutableLiteralReason()->push_back(l.Negated());
1321 ++stats_.num_propagations;
1322 if (!helper_->PushIntegerLiteral(
1339 const int id = watcher->
Register(
this);
1340 helper_->SetTimeDirection(time_direction_);
1341 helper_->WatchAllTasks(
id);
1346 stats_.OnPropagate();
1347 if (!helper_->SynchronizeAndSetTimeDirection(time_direction_)) {
1348 ++stats_.num_conflicts;
1352 const auto task_by_negated_start_max =
1353 helper_->TaskByIncreasingNegatedStartMax();
1354 const auto task_by_increasing_shifted_start_min =
1355 helper_->TaskByIncreasingShiftedStartMin();
1368 int queue_index = task_by_negated_start_max.size() - 1;
1369 const int num_tasks = task_by_increasing_shifted_start_min.size();
1370 for (
int i = 0;
i < num_tasks;) {
1371 start_min_window_.clear();
1373 for (;
i < num_tasks; ++
i) {
1374 const auto [task, presence_lit, start_min] =
1375 task_by_increasing_shifted_start_min[
i];
1376 if (!helper_->IsPresent(presence_lit))
continue;
1378 if (start_min_window_.empty()) {
1379 start_min_window_.push_back({task, start_min});
1380 window_end = start_min + helper_->SizeMin(task);
1381 }
else if (start_min < window_end) {
1382 start_min_window_.push_back({task, start_min});
1383 window_end += helper_->SizeMin(task);
1391 start_max_window_.clear();
1392 for (; queue_index >= 0; queue_index--) {
1393 const auto [t, negated_start_max] =
1394 task_by_negated_start_max[queue_index];
1395 const IntegerValue start_max = -negated_start_max;
1398 if (start_max >= window_end)
break;
1399 if (helper_->IsAbsent(t))
continue;
1400 start_max_window_.push_back({t, start_max});
1406 if (start_min_window_.size() <= 1)
continue;
1409 if (!start_max_window_.empty() && !PropagateSubwindow()) {
1410 ++stats_.num_conflicts;
1415 stats_.EndWithoutConflicts();
1419bool DisjunctiveNotLast::PropagateSubwindow() {
1420 auto& task_by_increasing_end_max = start_max_window_;
1421 for (
TaskTime& entry : task_by_increasing_end_max) {
1422 entry.time = helper_->
EndMax(entry.task_index);
1425 task_by_increasing_end_max.end());
1427 const IntegerValue threshold = task_by_increasing_end_max.back().time;
1428 auto& task_by_increasing_start_max = start_min_window_;
1430 for (
const TaskTime entry : task_by_increasing_start_max) {
1431 const int task = entry.task_index;
1432 const IntegerValue start_max = helper_->
StartMax(task);
1434 if (start_max < threshold) {
1435 task_by_increasing_start_max[queue_size++] = {task, start_max};
1441 if (queue_size <= 1)
return true;
1443 task_by_increasing_start_max.resize(queue_size);
1444 std::sort(task_by_increasing_start_max.begin(),
1445 task_by_increasing_start_max.end());
1448 int queue_index = 0;
1449 for (
const auto task_time : task_by_increasing_end_max) {
1450 const int t = task_time.task_index;
1451 const IntegerValue end_max = task_time.time;
1455 if (helper_->IsAbsent(t))
continue;
1460 while (queue_index < queue_size) {
1461 const auto to_insert = task_by_increasing_start_max[queue_index];
1462 const IntegerValue start_max = to_insert.time;
1463 if (end_max <= start_max)
break;
1465 const int task_index = to_insert.task_index;
1466 DCHECK(helper_->IsPresent(task_index));
1467 task_set_.AddEntry({task_index, helper_->ShiftedStartMin(task_index),
1468 helper_->SizeMin(task_index)});
1482 int critical_index = 0;
1483 const IntegerValue end_min_of_critical_tasks =
1484 task_set_.ComputeEndMin(t, &critical_index);
1485 if (end_min_of_critical_tasks <= helper_->StartMax(t))
continue;
1490 const absl::Span<const TaskSet::Entry> sorted_tasks =
1491 task_set_.SortedTasks();
1492 const int sorted_tasks_size = sorted_tasks.size();
1493 for (
int i = critical_index;
i < sorted_tasks_size; ++
i) {
1494 const int ct = sorted_tasks[
i].task;
1495 if (t == ct)
continue;
1496 const IntegerValue start_max = helper_->StartMax(ct);
1499 if (start_max > largest_ct_start_max &&
1500 helper_->GetCurrentMinDistanceBetweenTasks(ct, t) < 0) {
1501 largest_ct_start_max = start_max;
1508 end_max > largest_ct_start_max);
1509 if (end_max > largest_ct_start_max) {
1510 helper_->ClearReason();
1512 const IntegerValue window_start = sorted_tasks[critical_index].start_min;
1513 for (
int i = critical_index;
i < sorted_tasks_size; ++
i) {
1514 const int ct = sorted_tasks[
i].task;
1515 if (ct == t)
continue;
1516 helper_->AddPresenceReason(ct);
1517 helper_->AddEnergyAfterReason(ct, sorted_tasks[
i].size_min,
1519 if (helper_->GetCurrentMinDistanceBetweenTasks(
1521 helper_->AddStartMaxReason(ct, largest_ct_start_max);
1526 helper_->AddStartMaxReason(t, end_min_of_critical_tasks - 1);
1533 return helper_->ReportConflict();
1538 ++stats_.num_propagations;
1539 if (!helper_->DecreaseEndMax(t, largest_ct_start_max))
return false;
1546 const int id = watcher->
Register(
this);
1547 helper_->WatchAllTasks(
id);
1553 stats_.OnPropagate();
1554 const int num_tasks = helper_->NumTasks();
1555 if (!helper_->SynchronizeAndSetTimeDirection(time_direction_)) {
1556 ++stats_.num_conflicts;
1559 is_gray_.resize(num_tasks,
false);
1560 non_gray_task_to_event_.resize(num_tasks);
1564 for (
const auto [task, presence_lit, shifted_smin] :
1565 helper_->TaskByIncreasingShiftedStartMin()) {
1566 if (helper_->IsAbsent(presence_lit))
continue;
1570 if (helper_->StartMin(task) < window_end) {
1571 window_.push_back({task, shifted_smin});
1572 window_end += helper_->SizeMin(task);
1578 if (window_.size() > 2 && !PropagateSubwindow(window_end)) {
1579 ++stats_.num_conflicts;
1585 if (helper_->IsAbsent(presence_lit)) {
1593 window_.push_back({task, shifted_smin});
1594 window_end = shifted_smin + helper_->SizeMin(task);
1596 if (window_.size() > 2 && !PropagateSubwindow(window_end)) {
1597 ++stats_.num_conflicts;
1601 stats_.EndWithoutConflicts();
1605bool DisjunctiveEdgeFinding::PropagateSubwindow(IntegerValue window_end_min) {
1607 task_by_increasing_end_max_.clear();
1608 for (
const auto task_time : window_) {
1609 const int task = task_time.task_index;
1620 const IntegerValue end_max = helper_->
EndMax(task);
1621 if (helper_->
IsPresent(task) && end_max < window_end_min) {
1622 is_gray_[task] =
false;
1623 task_by_increasing_end_max_.push_back({task, end_max});
1625 is_gray_[task] =
true;
1631 if (task_by_increasing_end_max_.size() < 2)
return true;
1632 std::sort(task_by_increasing_end_max_.begin(),
1633 task_by_increasing_end_max_.end());
1644 const int window_size = window_.size();
1645 event_size_.clear();
1646 theta_tree_.Reset(window_size);
1647 for (
int event = 0;
event < window_size; ++event) {
1648 const TaskTime task_time = window_[event];
1649 const int task = task_time.task_index;
1650 const IntegerValue energy_min = helper_->SizeMin(task);
1651 event_size_.push_back(energy_min);
1652 if (is_gray_[task]) {
1653 theta_tree_.AddOrUpdateOptionalEvent(event, task_time.time, energy_min);
1655 non_gray_task_to_event_[task] = event;
1656 theta_tree_.AddOrUpdateEvent(event, task_time.time, energy_min,
1664 DCHECK(!is_gray_[task_by_increasing_end_max_.back().task_index]);
1665 const IntegerValue non_gray_end_max =
1666 task_by_increasing_end_max_.back().time;
1669 const IntegerValue non_gray_end_min = theta_tree_.GetEnvelope();
1670 if (non_gray_end_min > non_gray_end_max) {
1671 helper_->ClearReason();
1674 const int critical_event =
1675 theta_tree_.GetMaxEventWithEnvelopeGreaterThan(non_gray_end_max);
1676 const IntegerValue window_start = window_[critical_event].time;
1677 const IntegerValue window_end =
1678 theta_tree_.GetEnvelopeOf(critical_event) - 1;
1679 for (
int event = critical_event;
event < window_size;
event++) {
1680 const int task = window_[event].task_index;
1681 if (is_gray_[task])
continue;
1682 helper_->AddPresenceReason(task);
1683 helper_->AddEnergyAfterReason(task, event_size_[event], window_start);
1684 helper_->AddEndMaxReason(task, window_end);
1686 return helper_->ReportConflict();
1698 while (theta_tree_.GetOptionalEnvelope() > non_gray_end_max) {
1699 const IntegerValue end_min_with_gray = theta_tree_.GetOptionalEnvelope();
1700 int critical_event_with_gray;
1702 IntegerValue available_energy;
1703 theta_tree_.GetEventsWithOptionalEnvelopeGreaterThan(
1704 non_gray_end_max, &critical_event_with_gray, &gray_event,
1706 const int gray_task = window_[gray_event].task_index;
1707 DCHECK(is_gray_[gray_task]);
1711 if (helper_->IsAbsent(gray_task)) {
1712 theta_tree_.RemoveEvent(gray_event);
1717 if (helper_->StartMin(gray_task) < non_gray_end_min) {
1720 const int critical_event =
1721 theta_tree_.GetMaxEventWithEnvelopeGreaterThan(non_gray_end_min -
1727 if (critical_event_with_gray > critical_event) {
1728 critical_event_with_gray = critical_event;
1731 const int first_event = critical_event_with_gray;
1732 const int second_event = critical_event;
1733 const IntegerValue first_start = window_[first_event].time;
1734 const IntegerValue second_start = window_[second_event].time;
1741 bool use_energy_reason =
true;
1742 IntegerValue window_end;
1743 if (helper_->EndMin(gray_task) > non_gray_end_max) {
1748 use_energy_reason =
false;
1749 window_end = helper_->EndMin(gray_task) - 1;
1751 window_end = end_min_with_gray - 1;
1753 CHECK_GE(window_end, non_gray_end_max);
1756 helper_->ClearReason();
1757 bool all_before =
true;
1758 for (
int event = first_event;
event < window_size;
event++) {
1759 const int task = window_[event].task_index;
1760 if (is_gray_[task])
continue;
1761 helper_->AddPresenceReason(task);
1762 helper_->AddEnergyAfterReason(
1763 task, event_size_[event],
1764 event >= second_event ? second_start : first_start);
1766 const IntegerValue dist = helper_->GetCurrentMinDistanceBetweenTasks(
1767 task, gray_task,
true);
1770 helper_->AddEndMaxReason(task, window_end);
1777 if (use_energy_reason) {
1778 helper_->AddEnergyAfterReason(gray_task, event_size_[gray_event],
1781 helper_->AddEndMinReason(gray_task, helper_->EndMin(gray_task));
1786 if (helper_->CurrentDecisionLevel() == 0 &&
1787 helper_->IsPresent(gray_task)) {
1788 for (
int i = first_event;
i < window_size; ++
i) {
1789 const int task = window_[
i].task_index;
1790 if (!is_gray_[task]) {
1791 if (!helper_->PropagatePrecedence(task, gray_task)) {
1803 ++stats_.num_propagations;
1804 if (!helper_->IncreaseStartMin(gray_task, non_gray_end_min)) {
1810 theta_tree_.RemoveEvent(gray_event);
1814 if (task_by_increasing_end_max_.size() <= 2)
break;
1817 if (task_by_increasing_end_max_[0].time >=
1818 theta_tree_.GetOptionalEnvelope()) {
1823 const int new_gray_task = task_by_increasing_end_max_.back().task_index;
1824 task_by_increasing_end_max_.pop_back();
1825 const int new_gray_event = non_gray_task_to_event_[new_gray_task];
1826 DCHECK(!is_gray_[new_gray_task]);
1827 is_gray_[new_gray_task] =
true;
1828 theta_tree_.AddOrUpdateOptionalEvent(new_gray_event,
1829 window_[new_gray_event].time,
1830 event_size_[new_gray_event]);
1837 const int id = watcher->
Register(
this);
1838 helper_->SetTimeDirection(time_direction_);
1839 helper_->WatchAllTasks(
id);
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 NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)
int Register(PropagatorInterface *propagator)
Registers a propagator and returns its unique ids.
SchedulingDemandHelper * GetOrCreateDemandHelper(SchedulingConstraintHelper *helper, absl::Span< const AffineExpression > demands)
AffineExpression Start(IntervalVariable i) const
bool IsOptional(IntervalVariable i) const
Returns whether or not a interval is optional and the associated literal.
IntegerValue MaxSize(IntervalVariable i) const
Return the maximum size of the given IntervalVariable.
IntegerValue MinSize(IntervalVariable i) const
Return the minimum size of the given IntervalVariable.
SchedulingConstraintHelper * GetOrCreateHelper(const std::vector< IntervalVariable > &variables, bool register_as_disjunctive_helper=false)
T Add(std::function< T(Model *)> f)
void CollectPrecedences(absl::Span< const IntegerVariable > vars, std::vector< PrecedenceData > *output)
void AddPresenceReason(int t)
void AddReasonForBeingBefore(int before, int after)
Produces a relaxed reason for StartMax(before) < EndMin(after).
void AddEnergyAfterReason(int t, IntegerValue energy_min, IntegerValue time)
IntegerValue ShiftedStartMin(int t) const
absl::Span< const TaskTime > TaskByIncreasingNegatedStartMax()
IntegerValue StartMax(int t) const
bool IsPresent(int t) const
void AddSizeMinReason(int t)
absl::Span< const AffineExpression > Ends() const
ABSL_MUST_USE_RESULT bool IncreaseStartMin(int t, IntegerValue value)
void AddEndMinReason(int t, IntegerValue lower_bound)
IntegerValue StartMin(int t) const
void ClearReason()
Functions to clear and then set the current reason.
IntegerValue SizeMin(int t) const
IntegerValue EndMax(int t) const
bool IsAbsent(int t) const
IntegerValue EndMin(int t) const
IntegerValue GetCurrentMinDistanceBetweenTasks(int a, int b, bool add_reason_if_after=false)
void NotifyEntryIsNowLastIfPresent(const Entry &e)
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)
void AddDisjunctive(const std::vector< IntervalVariable > &intervals, Model *model)
std::function< void(Model *)> AllDifferentOnBounds(const std::vector< AffineExpression > &expressions)
void AddDisjunctiveWithBooleanPrecedencesOnly(absl::Span< const IntervalVariable > intervals, Model *model)
In SWIG mode, we don't want anything besides these top-level includes.
void IncrementalSort(int max_comparisons, Iterator begin, Iterator end, Compare comp=Compare{}, bool is_stable=false)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)