20#include "absl/algorithm/container.h"
21#include "absl/log/check.h"
22#include "absl/types/span.h"
30#include "ortools/sat/sat_parameters.pb.h"
43 const auto& params = *
model->GetOrCreate<SatParameters>();
44 if (intervals.size() <=
45 params.max_size_to_create_precedence_literals_in_disjunctive() &&
46 params.use_strong_propagation_in_disjunctive()) {
50 bool is_all_different =
true;
52 for (
const IntervalVariable
var : intervals) {
55 is_all_different =
false;
59 if (is_all_different) {
60 std::vector<AffineExpression> starts;
61 starts.reserve(intervals.size());
62 for (
const IntervalVariable
interval : intervals) {
70 if (intervals.size() > 2 && params.use_combined_no_overlap()) {
82 std::vector<AffineExpression> demands(intervals.size(), one);
89 model->TakeOwnership(timetable);
93 if (intervals.size() == 2) {
96 model->TakeOwnership(propagator);
100 if (!params.use_strong_propagation_in_disjunctive()) {
105 const int id = simple_precedences->
RegisterWith(watcher);
106 watcher->SetPropagatorPriority(
id, 1);
107 model->TakeOwnership(simple_precedences);
114 watcher->SetPropagatorPriority(
id, 1);
115 model->TakeOwnership(overload_checker);
117 for (
const bool time_direction : {
true,
false}) {
120 const int id = detectable_precedences->
RegisterWith(watcher);
121 watcher->SetPropagatorPriority(
id, 2);
122 model->TakeOwnership(detectable_precedences);
124 for (
const bool time_direction : {
true,
false}) {
128 watcher->SetPropagatorPriority(
id, 3);
129 model->TakeOwnership(not_last);
131 for (
const bool time_direction : {
true,
false}) {
135 watcher->SetPropagatorPriority(
id, 4);
136 model->TakeOwnership(edge_finding);
143 if (params.use_precedences_in_disjunctive_constraint() &&
144 !params.use_combined_no_overlap()) {
145 for (
const bool time_direction : {
true,
false}) {
149 watcher->SetPropagatorPriority(
id, 5);
150 model->TakeOwnership(precedences);
156 const std::vector<IntervalVariable>& intervals,
Model*
model) {
158 for (
int i = 0;
i < intervals.size(); ++
i) {
159 for (
int j =
i + 1; j < intervals.size(); ++j) {
160 repo->CreateDisjunctivePrecedenceLiteral(intervals[
i], intervals[j]);
166 int j = sorted_tasks_.size();
167 sorted_tasks_.push_back(e);
169 sorted_tasks_[j] = sorted_tasks_[j - 1];
172 sorted_tasks_[j] = e;
173 DCHECK(std::is_sorted(sorted_tasks_.begin(), sorted_tasks_.end()));
177 if (j <= optimized_restart_) optimized_restart_ = 0;
182 const IntegerValue dmin = helper.
SizeMin(t);
187 const int size = sorted_tasks_.size();
188 for (
int i = 0;; ++
i) {
189 if (
i ==
size)
return;
190 if (sorted_tasks_[
i].task == e.
task) {
191 for (
int j =
i; j + 1 <
size; ++j) {
192 sorted_tasks_[j] = sorted_tasks_[j + 1];
194 sorted_tasks_.pop_back();
199 optimized_restart_ = sorted_tasks_.size();
200 sorted_tasks_.push_back(e);
201 DCHECK(std::is_sorted(sorted_tasks_.begin(), sorted_tasks_.end()));
205 DCHECK(std::is_sorted(sorted_tasks_.begin(), sorted_tasks_.end()));
206 const int size = sorted_tasks_.size();
208 for (
int i = optimized_restart_;
i <
size; ++
i) {
209 const Entry& e = sorted_tasks_[
i];
211 optimized_restart_ =
i;
221 int* critical_index)
const {
223 DCHECK(std::is_sorted(sorted_tasks_.begin(), sorted_tasks_.end()));
224 bool ignored =
false;
225 const int size = sorted_tasks_.size();
230 if (optimized_restart_ + 1 ==
size &&
231 sorted_tasks_[optimized_restart_].task == task_to_ignore) {
232 optimized_restart_ = 0;
235 for (
int i = optimized_restart_;
i <
size; ++
i) {
236 const Entry& e = sorted_tasks_[
i];
237 if (e.
task == task_to_ignore) {
243 if (!ignored) optimized_restart_ =
i;
273 std::swap(task_before, task_after);
279 const IntegerValue end_min_before = helper_->
EndMin(task_before);
280 if (helper_->
StartMin(task_after) < end_min_before) {
295 const IntegerValue start_max_after = helper_->
StartMax(task_after);
296 if (helper_->
EndMax(task_before) > start_max_after) {
314 const int id = watcher->
Register(
this);
320template <
bool time_direction>
323 task_to_disjunctives_.resize(helper_->
NumTasks());
326 const int id = watcher->
Register(
this);
328 watcher->NotifyThatPropagatorMayNotReachFixedPointInOnePass(
id);
331template <
bool time_direction>
333 absl::Span<const IntervalVariable> vars) {
334 const int index = task_sets_.size();
335 task_sets_.emplace_back(vars.size());
337 for (
const IntervalVariable
var : vars) {
338 task_to_disjunctives_[
var.value()].push_back(
index);
342template <
bool time_direction>
344 if (!helper_->SynchronizeAndSetTimeDirection(time_direction))
return false;
345 const auto& task_by_increasing_end_min = helper_->TaskByIncreasingEndMin();
346 const auto& task_by_negated_start_max =
347 helper_->TaskByIncreasingNegatedStartMax();
349 for (
auto& task_set : task_sets_) task_set.Clear();
353 const int num_tasks = helper_->NumTasks();
354 task_is_added_.assign(num_tasks,
false);
355 int queue_index = num_tasks - 1;
356 for (
const auto task_time : task_by_increasing_end_min) {
357 const int t = task_time.task_index;
358 const IntegerValue
end_min = task_time.time;
359 if (helper_->IsAbsent(t))
continue;
362 while (queue_index >= 0) {
363 const auto to_insert = task_by_negated_start_max[queue_index];
364 const int task_index = to_insert.task_index;
365 const IntegerValue
start_max = -to_insert.time;
367 if (helper_->IsPresent(task_index)) {
368 task_is_added_[task_index] =
true;
369 const IntegerValue shifted_smin = helper_->ShiftedStartMin(task_index);
370 const IntegerValue size_min = helper_->SizeMin(task_index);
371 for (
const int d_index : task_to_disjunctives_[task_index]) {
373 task_sets_[d_index].AddEntry({task_index, shifted_smin, size_min});
374 end_mins_[d_index] = task_sets_[d_index].ComputeEndMin();
375 max_of_end_min = std::max(max_of_end_min, end_mins_[d_index]);
383 IntegerValue new_start_min = helper_->StartMin(t);
384 if (new_start_min >= max_of_end_min)
continue;
385 int best_critical_index = 0;
386 int best_d_index = -1;
387 if (task_is_added_[t]) {
388 for (
const int d_index : task_to_disjunctives_[t]) {
389 if (new_start_min >= end_mins_[d_index])
continue;
390 int critical_index = 0;
391 const IntegerValue end_min_of_critical_tasks =
392 task_sets_[d_index].ComputeEndMin(t,
394 DCHECK_LE(end_min_of_critical_tasks, max_of_end_min);
395 if (end_min_of_critical_tasks > new_start_min) {
396 new_start_min = end_min_of_critical_tasks;
397 best_d_index = d_index;
398 best_critical_index = critical_index;
404 for (
const int d_index : task_to_disjunctives_[t]) {
405 if (end_mins_[d_index] > new_start_min) {
406 new_start_min = end_mins_[d_index];
407 best_d_index = d_index;
410 if (best_d_index != -1) {
411 const IntegerValue end_min_of_critical_tasks =
412 task_sets_[best_d_index].ComputeEndMin(t,
413 &best_critical_index);
414 CHECK_EQ(end_min_of_critical_tasks, new_start_min);
419 if (best_d_index == -1)
continue;
424 helper_->ClearReason();
425 const absl::Span<const TaskSet::Entry> sorted_tasks =
426 task_sets_[best_d_index].SortedTasks();
427 const IntegerValue window_start =
428 sorted_tasks[best_critical_index].start_min;
429 for (
int i = best_critical_index;
i < sorted_tasks.size(); ++
i) {
430 const int ct = sorted_tasks[
i].task;
431 if (
ct == t)
continue;
432 helper_->AddPresenceReason(
ct);
433 helper_->AddEnergyAfterReason(
ct, sorted_tasks[
i].size_min, window_start);
434 helper_->AddStartMaxReason(
ct,
end_min - 1);
436 helper_->AddEndMinReason(t,
end_min);
437 if (!helper_->IncreaseStartMin(t, new_start_min)) {
445 if (task_is_added_[t]) {
446 const IntegerValue shifted_smin = helper_->ShiftedStartMin(t);
447 const IntegerValue size_min = helper_->SizeMin(t);
448 for (
const int d_index : task_to_disjunctives_[t]) {
452 task_sets_[d_index].NotifyEntryIsNowLastIfPresent(
453 {t, shifted_smin, size_min});
454 end_mins_[d_index] = task_sets_[d_index].ComputeEndMin();
455 max_of_end_min = std::max(max_of_end_min, end_mins_[d_index]);
484 IntegerValue relevant_end;
486 int relevant_size = 0;
487 TaskTime*
const window = window_.get();
488 for (
const auto [task, presence_lit,
start_min] :
490 if (helper_->
IsAbsent(presence_lit))
continue;
493 const IntegerValue size_min = helper_->
SizeMin(task);
497 helper_->
IsPresent(presence_lit) && size_min > 0) {
511 const int to_push = task_with_max_end_min.
task_index;
534 task_with_max_end_min = {task,
end_min};
539 window[window_size++] = {task,
start_min};
541 window_end += size_min;
542 relevant_size = window_size;
543 relevant_end = window_end;
545 window_end += size_min;
553 if (relevant_size > 0 && !PropagateSubwindow(relevant_size, relevant_end)) {
560 window[window_size++] = {task,
start_min};
566 if (relevant_size > 0 && !PropagateSubwindow(relevant_size, relevant_end)) {
582bool DisjunctiveOverloadChecker::PropagateSubwindow(
583 int relevant_size, IntegerValue global_window_end) {
586 task_by_increasing_end_max_.clear();
587 for (
int i = 0;
i < relevant_size; ++
i) {
595 if (
end_max < global_window_end) {
596 window_[num_events] = task_time;
597 task_to_event_[task] = num_events;
598 task_by_increasing_end_max_.push_back({task,
end_max});
602 if (num_events <= 1)
return true;
603 theta_tree_.
Reset(num_events);
607 absl::c_reverse(task_by_increasing_end_max_);
608 absl::c_stable_sort(task_by_increasing_end_max_);
609 for (
const auto task_time : task_by_increasing_end_max_) {
610 const int current_task = task_time.
task_index;
615 if (helper_->
IsAbsent(current_task))
continue;
617 DCHECK_NE(task_to_event_[current_task], -1);
619 const int current_event = task_to_event_[current_task];
620 const IntegerValue energy_min = helper_->
SizeMin(current_task);
626 energy_min, energy_min);
629 current_event, window_[current_event].
time, energy_min);
633 const IntegerValue current_end = task_time.
time;
637 const int critical_event =
639 const IntegerValue window_start = window_[critical_event].time;
640 const IntegerValue window_end =
642 for (
int event = critical_event;
event < num_events;
event++) {
643 const IntegerValue energy_min = theta_tree_.
EnergyMin(event);
644 if (energy_min > 0) {
645 const int task = window_[event].task_index;
662 IntegerValue available_energy;
664 current_end, &critical_event, &optional_event, &available_energy);
666 const int optional_task = window_[optional_event].task_index;
670 if (!helper_->
IsAbsent(optional_task)) {
671 const IntegerValue optional_size_min = helper_->
SizeMin(optional_task);
672 const IntegerValue window_start = window_[critical_event].time;
673 const IntegerValue window_end =
674 current_end + optional_size_min - available_energy - 1;
675 for (
int event = critical_event;
event < num_events;
event++) {
676 const IntegerValue energy_min = theta_tree_.
EnergyMin(event);
677 if (energy_min > 0) {
678 const int task = window_[event].task_index;
702 const int id = watcher->
Register(
this);
710 const int id = watcher->
Register(
this);
720 for (
const bool direction : {current_direction, !current_direction}) {
725 if (!PropagateOneDirection()) {
735bool DisjunctiveSimplePrecedences::Push(
TaskTime before,
int t) {
737 DCHECK_NE(t_before, t);
749bool DisjunctiveSimplePrecedences::PropagateOneDirection() {
752 absl::Span<const TaskTime> task_by_negated_start_max =
760 absl::Span<const TaskTime> task_by_increasing_end_min =
763 for (; !task_by_increasing_end_min.empty();) {
765 if (helper_->
IsAbsent(task_by_increasing_end_min.front().task_index)) {
766 task_by_increasing_end_min.remove_prefix(1);
771 int blocking_task = -1;
772 IntegerValue blocking_start_max;
773 IntegerValue current_end_min = task_by_increasing_end_min.front().time;
774 for (;
true; task_by_negated_start_max.remove_suffix(1)) {
775 if (task_by_negated_start_max.empty()) {
782 const auto [t, negated_start_max] = task_by_negated_start_max.back();
783 const IntegerValue
start_max = -negated_start_max;
794 if (blocking_task == -1 &&
end_min >= current_end_min) {
800 }
else if (blocking_task != -1 && blocking_start_max <
end_min) {
808 }
else if (
end_min > best_task_before.time) {
809 best_task_before = {t,
end_min};
814 if (blocking_task != -1) {
815 DCHECK(!helper_->
IsAbsent(blocking_task));
816 if (best_task_before.time > helper_->
StartMin(blocking_task)) {
817 if (!Push(best_task_before, blocking_task))
return false;
826 best_task_before = {blocking_task,
end_min};
830 for (; !task_by_increasing_end_min.empty();
831 task_by_increasing_end_min.remove_prefix(1)) {
832 const auto [t,
end_min] = task_by_increasing_end_min.front();
833 if (
end_min > current_end_min)
break;
834 if (t == blocking_task)
continue;
837 if (best_task_before.time > helper_->
StartMin(t)) {
840 if (!Push(best_task_before, t))
return false;
858 for (
const auto [task, presence_lit,
start_min] : by_shifted_smin) {
864 const IntegerValue size_min = helper_->
SizeMin(task);
867 window_end += size_min;
869 ranks_[task] = ++rank;
874 if (!PropagateWithRanks()) {
891bool DisjunctiveDetectablePrecedences::Push(IntegerValue task_set_end_min,
894 const absl::Span<const TaskSet::Entry> sorted_tasks = task_set_.
SortedTasks();
900 const IntegerValue end_min_if_present =
902 const IntegerValue window_start = sorted_tasks[critical_index].start_min;
904 bool all_already_before =
true;
905 IntegerValue energy_of_task_before = 0;
906 for (
int i = critical_index;
i < sorted_tasks.size(); ++
i) {
907 const int ct = sorted_tasks[
i].task;
914 sorted_tasks[critical_index].task,
ct,
926 energy_of_task_before += sorted_tasks[
i].size_min;
927 min_slack = std::min(min_slack, dist);
929 all_already_before =
false;
936 IntegerValue new_start_min = task_set_end_min;
937 if (all_already_before) {
940 new_start_min += min_slack;
948 window_start + energy_of_task_before + min_slack > new_start_min) {
949 new_start_min = window_start + energy_of_task_before + min_slack;
954 for (
int i = critical_index;
i < sorted_tasks.size(); ++
i) {
971bool DisjunctiveDetectablePrecedences::PropagateWithRanks() {
973 absl::Span<const TaskTime> task_by_increasing_end_min =
975 absl::Span<const TaskTime> task_by_negated_start_max =
981 int highest_rank = 0;
982 bool some_propagation =
false;
992 for (; !task_by_increasing_end_min.empty();) {
994 int blocking_task = -1;
995 IntegerValue blocking_start_max;
996 IntegerValue current_end_min = task_by_increasing_end_min.front().time;
998 for (;
true; task_by_negated_start_max.remove_suffix(1)) {
999 if (task_by_negated_start_max.empty()) {
1006 const auto [t, negated_start_max] = task_by_negated_start_max.back();
1007 const IntegerValue
start_max = -negated_start_max;
1008 if (current_end_min <=
start_max)
break;
1011 const int rank = ranks_[t];
1012 if (rank < highest_rank)
continue;
1022 if (blocking_task == -1 &&
end_min >= current_end_min) {
1028 }
else if (blocking_task != -1 && blocking_start_max <
end_min) {
1037 if (!some_propagation && rank > highest_rank) {
1040 highest_rank = rank;
1047 if (blocking_task != -1) {
1048 DCHECK(!helper_->
IsAbsent(blocking_task));
1050 if (!to_add_.
empty()) {
1051 for (
const int t : to_add_) {
1058 if (task_set_end_min > helper_->
StartMin(blocking_task)) {
1059 some_propagation =
true;
1060 if (!Push(task_set_end_min, blocking_task))
return false;
1072 if (!some_propagation && ranks_[blocking_task] > highest_rank) {
1075 highest_rank = ranks_[blocking_task];
1077 to_add_.push_back(blocking_task);
1082 for (; !task_by_increasing_end_min.empty();
1083 task_by_increasing_end_min.remove_prefix(1)) {
1084 const auto [t,
end_min] = task_by_increasing_end_min.front();
1085 if (
end_min > current_end_min)
break;
1086 if (t == blocking_task)
continue;
1088 if (!to_add_.empty()) {
1089 for (
const int t : to_add_) {
1097 if (task_set_end_min > helper_->
StartMin(t)) {
1099 if (helper_->
IsAbsent(t))
continue;
1101 some_propagation =
true;
1102 if (!Push(task_set_end_min, t))
return false;
1112 const int id = watcher->
Register(
this);
1130 for (
const auto [task, presence_lit,
start_min] :
1132 if (!helper_->
IsPresent(presence_lit))
continue;
1136 window_end += helper_->
SizeMin(task);
1140 if (window_.size() > 1 && !PropagateSubwindow()) {
1150 if (window_.size() > 1 && !PropagateSubwindow()) {
1159bool DisjunctivePrecedences::PropagateSubwindow() {
1165 index_to_end_vars_.
clear();
1167 for (
const auto task_time : window_) {
1174 window_[new_size++] = task_time;
1177 window_.resize(new_size);
1183 const int size = before_.size();
1184 for (
int global_i = 0; global_i <
size;) {
1185 const int global_start_i = global_i;
1186 const IntegerVariable
var = before_[global_i].var;
1198 indices_before_.
clear();
1199 IntegerValue local_start;
1200 IntegerValue local_end;
1201 for (; global_i <
size; ++global_i) {
1202 const PrecedenceRelations::PrecedenceData& data = before_[global_i];
1203 if (data.var !=
var)
break;
1204 const int index = data.index;
1205 const auto [t, start_of_t] = window_[
index];
1206 if (global_i == global_start_i) {
1207 local_start = start_of_t;
1208 local_end = local_start + helper_->
SizeMin(t);
1210 if (start_of_t >= local_end)
break;
1211 local_end += helper_->
SizeMin(t);
1217 const int num_before = indices_before_.
size();
1218 if (num_before < 2)
continue;
1219 skip_.assign(num_before,
false);
1224 IntegerValue end_min_when_all_present = local_end;
1232 int best_index = -1;
1233 const IntegerValue current_var_lb = integer_trail_->
LowerBound(
var);
1234 IntegerValue best_new_lb = current_var_lb;
1236 IntegerValue sum_of_duration = 0;
1237 for (
int i = num_before; --
i >= 0;) {
1238 const TaskTime task_time = window_[indices_before_[
i]];
1239 const AffineExpression& end_exp = helper_->
Ends()[task_time.task_index];
1245 const IntegerValue inner_offset =
1253 const IntegerValue offset = inner_offset - end_exp.constant;
1256 const IntegerValue task_size = helper_->
SizeMin(task_time.task_index);
1257 if (end_min_when_all_present + offset <= best_new_lb) {
1265 end_min_when_all_present -= task_size;
1273 min_offset = std::min(min_offset, offset);
1274 sum_of_duration += task_size;
1275 const IntegerValue
start = task_time.time;
1276 const IntegerValue new_lb =
start + sum_of_duration + min_offset;
1277 if (new_lb > best_new_lb) {
1278 best_new_lb = new_lb;
1284 if (best_new_lb > current_var_lb) {
1285 DCHECK_NE(best_index, -1);
1287 const IntegerValue window_start =
1288 window_[indices_before_[best_index]].time;
1289 for (
int i = best_index;
i < num_before; ++
i) {
1290 if (skip_[
i])
continue;
1291 const int ct = window_[indices_before_[
i]].task_index;
1297 const AffineExpression& end_exp = helper_->
Ends()[
ct];
1298 for (
const Literal l :
1316 const int id = watcher->
Register(
this);
1329 const auto task_by_negated_start_max =
1331 const auto task_by_increasing_shifted_start_min =
1345 int queue_index = task_by_negated_start_max.size() - 1;
1346 const int num_tasks = task_by_increasing_shifted_start_min.size();
1347 for (
int i = 0;
i < num_tasks;) {
1348 start_min_window_.clear();
1350 for (;
i < num_tasks; ++
i) {
1351 const auto [task, presence_lit,
start_min] =
1352 task_by_increasing_shifted_start_min[
i];
1353 if (!helper_->
IsPresent(presence_lit))
continue;
1355 if (start_min_window_.empty()) {
1356 start_min_window_.push_back({task,
start_min});
1359 start_min_window_.push_back({task,
start_min});
1360 window_end += helper_->
SizeMin(task);
1368 start_max_window_.clear();
1369 for (; queue_index >= 0; queue_index--) {
1370 const auto [t, negated_start_max] =
1371 task_by_negated_start_max[queue_index];
1372 const IntegerValue
start_max = -negated_start_max;
1376 if (helper_->
IsAbsent(t))
continue;
1377 start_max_window_.push_back({t,
start_max});
1383 if (start_min_window_.size() <= 1)
continue;
1386 if (!start_max_window_.empty() && !PropagateSubwindow()) {
1396bool DisjunctiveNotLast::PropagateSubwindow() {
1397 auto& task_by_increasing_end_max = start_max_window_;
1398 for (
TaskTime& entry : task_by_increasing_end_max) {
1399 entry.time = helper_->
EndMax(entry.task_index);
1402 task_by_increasing_end_max.end());
1404 const IntegerValue threshold = task_by_increasing_end_max.back().time;
1405 auto& task_by_increasing_start_max = start_min_window_;
1407 for (
const TaskTime entry : task_by_increasing_start_max) {
1408 const int task = entry.task_index;
1412 task_by_increasing_start_max[queue_size++] = {task,
start_max};
1418 if (queue_size <= 1)
return true;
1420 task_by_increasing_start_max.resize(queue_size);
1421 std::sort(task_by_increasing_start_max.begin(),
1422 task_by_increasing_start_max.end());
1425 int queue_index = 0;
1426 for (
const auto task_time : task_by_increasing_end_max) {
1427 const int t = task_time.task_index;
1428 const IntegerValue
end_max = task_time.time;
1432 if (helper_->
IsAbsent(t))
continue;
1437 while (queue_index < queue_size) {
1438 const auto to_insert = task_by_increasing_start_max[queue_index];
1439 const IntegerValue
start_max = to_insert.time;
1442 const int task_index = to_insert.task_index;
1445 helper_->
SizeMin(task_index)});
1459 int critical_index = 0;
1460 const IntegerValue end_min_of_critical_tasks =
1462 if (end_min_of_critical_tasks <= helper_->StartMax(t))
continue;
1467 const absl::Span<const TaskSet::Entry> sorted_tasks =
1469 const int sorted_tasks_size = sorted_tasks.size();
1470 for (
int i = critical_index;
i < sorted_tasks_size; ++
i) {
1471 const int ct = sorted_tasks[
i].task;
1472 if (t ==
ct)
continue;
1485 end_max > largest_ct_start_max);
1486 if (
end_max > largest_ct_start_max) {
1489 const IntegerValue window_start = sorted_tasks[critical_index].start_min;
1490 for (
int i = critical_index;
i < sorted_tasks_size; ++
i) {
1491 const int ct = sorted_tasks[
i].task;
1492 if (
ct == t)
continue;
1516 if (!helper_->
DecreaseEndMax(t, largest_ct_start_max))
return false;
1523 const int id = watcher->
Register(
this);
1531 const int num_tasks = helper_->
NumTasks();
1536 is_gray_.resize(num_tasks,
false);
1537 non_gray_task_to_event_.resize(num_tasks);
1541 for (
const auto [task, presence_lit, shifted_smin] :
1543 if (helper_->
IsAbsent(presence_lit))
continue;
1547 if (helper_->
StartMin(task) < window_end) {
1548 window_.push_back({task, shifted_smin});
1549 window_end += helper_->
SizeMin(task);
1555 if (window_.size() > 2 && !PropagateSubwindow(window_end)) {
1562 window_.push_back({task, shifted_smin});
1563 window_end = shifted_smin + helper_->
SizeMin(task);
1565 if (window_.size() > 2 && !PropagateSubwindow(window_end)) {
1574bool DisjunctiveEdgeFinding::PropagateSubwindow(IntegerValue window_end_min) {
1576 task_by_increasing_end_max_.clear();
1577 for (
const auto task_time : window_) {
1578 const int task = task_time.task_index;
1591 is_gray_[task] =
false;
1592 task_by_increasing_end_max_.push_back({task,
end_max});
1594 is_gray_[task] =
true;
1600 if (task_by_increasing_end_max_.size() < 2)
return true;
1601 std::sort(task_by_increasing_end_max_.begin(),
1602 task_by_increasing_end_max_.end());
1613 const int window_size = window_.size();
1614 event_size_.
clear();
1615 theta_tree_.
Reset(window_size);
1616 for (
int event = 0;
event < window_size; ++event) {
1617 const TaskTime task_time = window_[event];
1618 const int task = task_time.task_index;
1619 const IntegerValue energy_min = helper_->
SizeMin(task);
1621 if (is_gray_[task]) {
1624 non_gray_task_to_event_[task] = event;
1633 DCHECK(!is_gray_[task_by_increasing_end_max_.back().task_index]);
1634 const IntegerValue non_gray_end_max =
1635 task_by_increasing_end_max_.back().time;
1638 const IntegerValue non_gray_end_min = theta_tree_.
GetEnvelope();
1639 if (non_gray_end_min > non_gray_end_max) {
1643 const int critical_event =
1645 const IntegerValue window_start = window_[critical_event].time;
1646 const IntegerValue window_end =
1648 for (
int event = critical_event;
event < window_size;
event++) {
1649 const int task = window_[event].task_index;
1650 if (is_gray_[task])
continue;
1669 int critical_event_with_gray;
1671 IntegerValue available_energy;
1673 non_gray_end_max, &critical_event_with_gray, &gray_event,
1675 const int gray_task = window_[gray_event].task_index;
1676 DCHECK(is_gray_[gray_task]);
1680 if (helper_->
IsAbsent(gray_task)) {
1686 if (helper_->
StartMin(gray_task) < non_gray_end_min) {
1689 const int critical_event =
1696 if (critical_event_with_gray > critical_event) {
1697 critical_event_with_gray = critical_event;
1700 const int first_event = critical_event_with_gray;
1701 const int second_event = critical_event;
1702 const IntegerValue first_start = window_[first_event].time;
1703 const IntegerValue second_start = window_[second_event].time;
1710 bool use_energy_reason =
true;
1711 IntegerValue window_end;
1712 if (helper_->
EndMin(gray_task) > non_gray_end_max) {
1717 use_energy_reason =
false;
1718 window_end = helper_->
EndMin(gray_task) - 1;
1720 window_end = end_min_with_gray - 1;
1722 CHECK_GE(window_end, non_gray_end_max);
1726 bool all_before =
true;
1727 for (
int event = first_event;
event < window_size;
event++) {
1728 const int task = window_[event].task_index;
1729 if (is_gray_[task])
continue;
1732 task, event_size_[event],
1733 event >= second_event ? second_start : first_start);
1736 task, gray_task,
true);
1746 if (use_energy_reason) {
1757 for (
int i = first_event;
i < window_size; ++
i) {
1758 const int task = window_[
i].task_index;
1759 if (!is_gray_[task]) {
1783 if (task_by_increasing_end_max_.size() <= 2)
break;
1786 if (task_by_increasing_end_max_[0].
time >=
1792 const int new_gray_task = task_by_increasing_end_max_.back().task_index;
1793 task_by_increasing_end_max_.pop_back();
1794 const int new_gray_event = non_gray_task_to_event_[new_gray_task];
1795 DCHECK(!is_gray_[new_gray_task]);
1796 is_gray_[new_gray_task] =
true;
1798 window_[new_gray_event].
time,
1799 event_size_[new_gray_event]);
1806 const int id = watcher->
Register(
this);
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.
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
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)
void CollectPrecedences(absl::Span< const IntegerVariable > vars, std::vector< PrecedenceData > *output)
IntegerValue GetConditionalOffset(IntegerVariable a, IntegerVariable b) const
absl::Span< const Literal > GetConditionalEnforcements(IntegerVariable a, IntegerVariable b) const
void AddPresenceReason(int t)
void WatchAllTasks(int id, bool watch_max_side=true)
void AddReasonForBeingBefore(int before, int after)
Produces a relaxed reason for StartMax(before) < EndMin(after).
void AddEnergyAfterReason(int t, IntegerValue energy_min, IntegerValue time)
void AddStartMaxReason(int t, IntegerValue upper_bound)
IntegerValue ShiftedStartMin(int t) const
absl::Span< const TaskTime > TaskByIncreasingNegatedStartMax()
IntegerValue StartMax(int t) const
bool PropagatePrecedence(int a, int b)
ABSL_MUST_USE_RESULT bool DecreaseEndMax(int t, IntegerValue value)
int NumTasks() const
Returns the number of task.
ABSL_MUST_USE_RESULT bool ReportConflict()
ABSL_MUST_USE_RESULT bool PushTaskAbsence(int t)
bool IsPresent(int t) const
void SetTimeDirection(bool is_forward)
void AddSizeMinReason(int t)
void AddEndMaxReason(int t, IntegerValue upper_bound)
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.
bool InPropagationLoop() const
IntegerValue SizeMin(int t) const
absl::Span< const CachedTaskBounds > TaskByIncreasingShiftedStartMin()
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
IntegerValue EndMax(int t) const
bool IsAbsent(int t) const
IntegerValue EndMin(int t) const
std::string TaskDebugString(int t) const
Returns a string with the current task bounds.
ABSL_MUST_USE_RESULT bool PushIntegerLiteral(IntegerLiteral lit)
void AddShiftedEndMaxReason(int t, IntegerValue upper_bound)
IntegerValue GetCurrentMinDistanceBetweenTasks(int a, int b, bool add_reason_if_after=false)
std::vector< Literal > * MutableLiteralReason()
absl::Span< const TaskTime > TaskByIncreasingEndMin()
int CurrentDecisionLevel() const
bool CurrentTimeIsForward() const
void NotifyEntryIsNowLastIfPresent(const Entry &e)
IntegerValue ComputeEndMin(int task_to_ignore, int *critical_index) const
int GetCriticalIndex() const
void AddEntry(const Entry &e)
IntegerValue ComputeEndMin() const
absl::Span< const Entry > SortedTasks() const
void AddShiftedStartMinEntry(const SchedulingConstraintHelper &helper, int t)
void Clear()
Insertion and modification. These leave sorted_tasks_ sorted.
void Reset(int num_events)
IntegerType EnergyMin(int event) const
Getters.
IntegerType GetOptionalEnvelope() const
IntegerType GetEnvelope() const
void RemoveEvent(int event)
Makes event absent, compute the new envelope in O(log n).
void AddOrUpdateEvent(int event, IntegerType initial_envelope, IntegerType energy_min, IntegerType energy_max)
void GetEventsWithOptionalEnvelopeGreaterThan(IntegerType target_envelope, int *critical_event, int *optional_event, IntegerType *available_energy) const
int GetMaxEventWithEnvelopeGreaterThan(IntegerType target_envelope) const
IntegerType GetEnvelopeOf(int event) const
void AddOrUpdateOptionalEvent(int event, IntegerType initial_envelope_opt, IntegerType energy_max)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
void AddDisjunctiveWithBooleanPrecedencesOnly(const std::vector< IntervalVariable > &intervals, Model *model)
void AddDisjunctive(const std::vector< IntervalVariable > &intervals, Model *model)
std::function< void(Model *)> AllDifferentOnBounds(const std::vector< AffineExpression > &expressions)
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)
void EndWithoutConflicts()