22#include "absl/log/check.h"
23#include "absl/strings/str_cat.h"
24#include "absl/types/span.h"
43 std::vector<AffineExpression> starts, std::vector<AffineExpression> ends,
44 std::vector<AffineExpression> sizes,
45 std::vector<LiteralIndex> reason_for_presence,
Model* model)
47 sat_solver_(model->GetOrCreate<
SatSolver>()),
49 trail_(model->GetOrCreate<
Trail>()),
56 starts_(
std::move(starts)),
57 ends_(
std::move(ends)),
58 sizes_(
std::move(sizes)),
59 reason_for_presence_(
std::move(reason_for_presence)),
60 capacity_(starts_.size()),
61 cached_size_min_(new IntegerValue[capacity_]),
62 cached_start_min_(new IntegerValue[capacity_]),
63 cached_end_min_(new IntegerValue[capacity_]),
64 cached_negated_start_max_(new IntegerValue[capacity_]),
65 cached_negated_end_max_(new IntegerValue[capacity_]),
66 cached_shifted_start_min_(new IntegerValue[capacity_]),
67 cached_negated_shifted_end_max_(new IntegerValue[capacity_]) {
68 DCHECK_EQ(starts_.size(), ends_.size());
69 DCHECK_EQ(starts_.size(), sizes_.size());
70 DCHECK_EQ(starts_.size(), reason_for_presence_.size());
72 minus_starts_.clear();
73 minus_starts_.reserve(starts_.size());
75 minus_ends_.reserve(starts_.size());
76 for (
int i = 0;
i < starts_.size(); ++
i) {
77 minus_starts_.push_back(starts_[
i].Negated());
78 minus_ends_.push_back(ends_[
i].Negated());
90 sat_solver_(model->GetOrCreate<
SatSolver>()),
92 trail_(model->GetOrCreate<
Trail>()),
99 cached_size_min_(new IntegerValue[capacity_]),
100 cached_start_min_(new IntegerValue[capacity_]),
101 cached_end_min_(new IntegerValue[capacity_]),
102 cached_negated_start_max_(new IntegerValue[capacity_]),
103 cached_negated_end_max_(new IntegerValue[capacity_]),
104 cached_shifted_start_min_(new IntegerValue[capacity_]),
105 cached_negated_shifted_end_max_(new IntegerValue[capacity_]) {
106 starts_.resize(num_tasks);
111 return enforcement_helper_.Status(enforcement_id_) ==
117 recompute_all_cache_ =
true;
118 for (
const int id : propagator_ids_) watcher_->CallOnNextPropagate(
id);
123 const std::vector<int>& watch_indices) {
125 for (
const int t : watch_indices) recompute_cache_.Set(t);
126 for (
const int id : propagator_ids_) watcher_->CallOnNextPropagate(
id);
132 absl::Span<const Literal> enforcement_literals) {
133 const int id = watcher->
Register(
this);
134 const int num_tasks = starts_.size();
135 for (
int t = 0; t < num_tasks; ++t) {
152 enforcement_helper_.Register(enforcement_literals, watcher,
id);
155bool SchedulingConstraintHelper::UpdateCachedValues(
int t) {
158 IntegerValue smin = integer_trail_->
LowerBound(starts_[t]);
159 IntegerValue smax = integer_trail_->
UpperBound(starts_[t]);
160 IntegerValue emin = integer_trail_->
LowerBound(ends_[t]);
161 IntegerValue emax = integer_trail_->
UpperBound(ends_[t]);
169 std::max(IntegerValue(0), integer_trail_->
LowerBound(sizes_[t]));
170 IntegerValue dmax = integer_trail_->
UpperBound(sizes_[t]);
178 if (smin + dmin - emax > 0) {
185 if (smax + dmax - emin < 0) {
199 smin = std::max(smin, emin - dmax);
200 smax = std::min(smax, emax - dmin);
201 dmin = std::max(dmin, emin - smax);
202 emin = std::max(emin, smin + dmin);
203 emax = std::min(emax, smax + dmax);
205 if (emin != cached_end_min_[t]) {
206 recompute_energy_profile_ =
true;
211 recompute_by_start_max_ =
true;
212 recompute_by_end_min_ =
true;
214 cached_start_min_[t] = smin;
215 cached_end_min_[t] = emin;
216 cached_negated_start_max_[t] = -smax;
217 cached_negated_end_max_[t] = -emax;
218 cached_size_min_[t] = dmin;
221 const IntegerValue new_shifted_start_min = emin - dmin;
222 if (new_shifted_start_min != cached_shifted_start_min_[t]) {
223 recompute_energy_profile_ =
true;
224 recompute_shifted_start_min_ =
true;
225 cached_shifted_start_min_[t] = new_shifted_start_min;
227 const IntegerValue new_negated_shifted_end_max = -(smax + dmin);
228 if (new_negated_shifted_end_max != cached_negated_shifted_end_max_[t]) {
229 recompute_negated_shifted_end_max_ =
true;
230 cached_negated_shifted_end_max_[t] = new_negated_shifted_end_max;
237 current_time_direction_ = other.current_time_direction_;
239 const int num_tasks = tasks.size();
240 CHECK_LE(num_tasks, capacity_);
241 starts_.resize(num_tasks);
242 ends_.resize(num_tasks);
243 minus_ends_.resize(num_tasks);
244 minus_starts_.resize(num_tasks);
245 sizes_.resize(num_tasks);
246 reason_for_presence_.resize(num_tasks);
247 for (
int i = 0;
i < num_tasks; ++
i) {
248 const int t = tasks[
i];
249 starts_[
i] = other.starts_[t];
250 ends_[
i] = other.ends_[t];
251 minus_ends_[
i] = other.minus_ends_[t];
252 minus_starts_[
i] = other.minus_starts_[t];
253 sizes_[
i] = other.sizes_[t];
254 reason_for_presence_[
i] = other.reason_for_presence_[t];
261void SchedulingConstraintHelper::InitSortedVectors() {
262 const int num_tasks = starts_.size();
264 recompute_all_cache_ =
true;
265 recompute_cache_.
Resize(num_tasks);
266 non_fixed_intervals_.resize(num_tasks);
267 for (
int t = 0; t < num_tasks; ++t) {
268 non_fixed_intervals_[t] = t;
269 recompute_cache_.
Set(t);
273 CHECK_LE(num_tasks, capacity_);
275 task_by_increasing_start_min_.resize(num_tasks);
276 task_by_increasing_end_min_.resize(num_tasks);
277 task_by_increasing_negated_start_max_.resize(num_tasks);
278 task_by_decreasing_end_max_.resize(num_tasks);
279 task_by_increasing_shifted_start_min_.resize(num_tasks);
280 task_by_negated_shifted_end_max_.resize(num_tasks);
281 for (
int t = 0; t < num_tasks; ++t) {
282 task_by_increasing_start_min_[t].task_index = t;
283 task_by_increasing_end_min_[t].task_index = t;
284 task_by_increasing_negated_start_max_[t].task_index = t;
285 task_by_decreasing_end_max_[t].task_index = t;
287 task_by_increasing_shifted_start_min_[t].task_index = t;
288 task_by_increasing_shifted_start_min_[t].presence_lit =
289 reason_for_presence_[t];
290 task_by_negated_shifted_end_max_[t].task_index = t;
291 task_by_negated_shifted_end_max_[t].presence_lit = reason_for_presence_[t];
294 recompute_by_start_max_ =
true;
295 recompute_by_end_min_ =
true;
296 recompute_energy_profile_ =
true;
297 recompute_shifted_start_min_ =
true;
298 recompute_negated_shifted_end_max_ =
true;
302 if (current_time_direction_ != is_forward) {
303 current_time_direction_ = is_forward;
305 std::swap(starts_, minus_ends_);
306 std::swap(ends_, minus_starts_);
308 std::swap(task_by_increasing_start_min_, task_by_decreasing_end_max_);
309 std::swap(task_by_increasing_end_min_,
310 task_by_increasing_negated_start_max_);
311 std::swap(recompute_by_end_min_, recompute_by_start_max_);
312 std::swap(task_by_increasing_shifted_start_min_,
313 task_by_negated_shifted_end_max_);
315 recompute_energy_profile_ =
true;
316 std::swap(cached_start_min_, cached_negated_end_max_);
317 std::swap(cached_end_min_, cached_negated_start_max_);
318 std::swap(cached_shifted_start_min_, cached_negated_shifted_end_max_);
319 std::swap(recompute_shifted_start_min_, recompute_negated_shifted_end_max_);
329 if (sat_solver_->num_backtracks() != saved_num_backtracks_) {
330 recompute_all_cache_ =
true;
331 saved_num_backtracks_ = sat_solver_->num_backtracks();
334 if (recompute_all_cache_) {
335 for (
const int t : non_fixed_intervals_) {
336 if (!UpdateCachedValues(t))
return false;
341 if (sat_solver_->CurrentDecisionLevel() == 0) {
343 for (
const int t : non_fixed_intervals_) {
348 non_fixed_intervals_[new_size++] = t;
350 non_fixed_intervals_.resize(new_size);
353 for (
const int t : recompute_cache_) {
354 if (!UpdateCachedValues(t))
return false;
357 recompute_cache_.ClearAll();
358 recompute_all_cache_ =
false;
368 const IntegerValue expr_ub = linear2_bounds_->UpperBound(expr);
370 const IntegerValue ub_of_end_minus_start = expr_ub + needed_offset;
371 const IntegerValue distance = -ub_of_end_minus_start;
382 CHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
388 if (expr.coeffs[0] == 0 && expr.coeffs[1] == 0) {
390 sat_solver_->NotifyThatModelIsUnsat();
396 if (root_level_lin2_bounds_->AddUpperBound(expr, rhs)) {
402 {expr.coeffs[0].value(), expr.coeffs[1].value()},
403 rhs.value(), model_);
404 if (sat_solver_->ModelIsUnsat())
return false;
409absl::Span<const TaskTime>
411 for (
TaskTime& ref : task_by_increasing_start_min_) {
412 ref.time =
StartMin(ref.task_index);
415 task_by_increasing_start_min_.end());
416 return task_by_increasing_start_min_;
419absl::Span<const TaskTime>
421 if (!recompute_by_end_min_)
return task_by_increasing_end_min_;
422 for (
TaskTime& ref : task_by_increasing_end_min_) {
423 ref.time =
EndMin(ref.task_index);
426 task_by_increasing_end_min_.end());
427 recompute_by_end_min_ =
false;
428 return task_by_increasing_end_min_;
431absl::Span<const TaskTime>
433 if (!recompute_by_start_max_)
return task_by_increasing_negated_start_max_;
434 for (
TaskTime& ref : task_by_increasing_negated_start_max_) {
435 ref.time = cached_negated_start_max_[ref.task_index];
438 task_by_increasing_negated_start_max_.end());
439 recompute_by_start_max_ =
false;
440 return task_by_increasing_negated_start_max_;
443absl::Span<const TaskTime>
445 for (
TaskTime& ref : task_by_decreasing_end_max_) {
446 ref.time =
EndMax(ref.task_index);
449 task_by_decreasing_end_max_.end(), std::greater<TaskTime>());
450 return task_by_decreasing_end_max_;
453absl::Span<const CachedTaskBounds>
455 if (recompute_shifted_start_min_) {
456 recompute_shifted_start_min_ =
false;
457 bool is_sorted =
true;
461 is_sorted = is_sorted && ref.time >= previous;
464 if (is_sorted)
return task_by_increasing_shifted_start_min_;
466 task_by_increasing_shifted_start_min_.end());
468 return task_by_increasing_shifted_start_min_;
471absl::Span<const CachedTaskBounds>
473 if (recompute_negated_shifted_end_max_) {
474 recompute_negated_shifted_end_max_ =
false;
475 bool is_sorted =
true;
479 is_sorted = is_sorted && ref.time >= previous;
482 if (is_sorted)
return task_by_negated_shifted_end_max_;
484 task_by_negated_shifted_end_max_.end());
486 return task_by_negated_shifted_end_max_;
490const std::vector<SchedulingConstraintHelper::ProfileEvent>&
492 if (energy_profile_.empty()) {
494 for (
int t = 0; t < num_tasks; ++t) {
495 energy_profile_.push_back(
496 {cached_shifted_start_min_[t], t,
true});
497 energy_profile_.push_back({cached_end_min_[t], t,
false});
500 if (!recompute_energy_profile_)
return energy_profile_;
502 const int t = ref.task;
504 ref.time = cached_shifted_start_min_[t];
506 ref.time = cached_end_min_[t];
511 recompute_energy_profile_ =
false;
512 return energy_profile_;
517 const auto [expr, ub] =
519 return linear2_bounds_->UpperBound(expr) <= ub;
523 int before,
int after) {
524 FlagItemAsUsedInReason(before);
525 FlagItemAsUsedInReason(after);
531 starts_[before].var == ends_[before].var &&
532 starts_[after].var == ends_[after].var;
534 if (fixed_size && sizes_[after].constant == 0 &&
535 sizes_[before].constant == 0) {
537 const auto [expr, ub] =
539 DCHECK_LE(linear2_bounds_->UpperBound(expr), ub);
540 linear2_bounds_->AddReasonForUpperBoundLowerThan(expr, ub, &literal_reason_,
549 const auto [expr, ub] =
551 if (fixed_size || linear2_bounds_->UpperBound(expr) <= ub) {
552 linear2_bounds_->AddReasonForUpperBoundLowerThan(
553 expr, ub, &literal_reason_, &integer_reason_);
571 ends_[before], starts_[after],
SizeMin(before) +
SizeMin(after) - 1);
572 if (linear2_bounds_->UpperBound(expr) <= ub) {
575 linear2_bounds_->AddReasonForUpperBoundLowerThan(
576 expr, ub, &literal_reason_, &integer_reason_);
588 const auto [expr, ub] =
590 if (linear2_bounds_->UpperBound(expr) <= ub) {
591 linear2_bounds_->AddReasonForUpperBoundLowerThan(
592 expr, ub, &literal_reason_, &integer_reason_);
600 std::vector<IntegerVariable> vars;
601 std::vector<IntegerValue> coeffs;
604 const IntegerValue smax_before =
StartMax(before);
605 if (smax_before >= integer_trail_->UpperBound(starts_[before])) {
607 vars.push_back(
NegationOf(starts_[before].var));
608 coeffs.push_back(starts_[before].coeff);
612 vars.push_back(
NegationOf(ends_[before].var));
613 coeffs.push_back(ends_[before].coeff);
616 vars.push_back(sizes_[before].var);
617 coeffs.push_back(sizes_[before].coeff);
622 const IntegerValue emin_after =
EndMin(after);
623 if (emin_after <= integer_trail_->
LowerBound(ends_[after])) {
625 vars.push_back(ends_[after].var);
626 coeffs.push_back(ends_[after].coeff);
630 vars.push_back(starts_[after].var);
631 coeffs.push_back(starts_[after].coeff);
634 vars.push_back(sizes_[after].var);
635 coeffs.push_back(sizes_[after].coeff);
639 DCHECK_LT(smax_before, emin_after);
640 const IntegerValue slack = emin_after - smax_before - 1;
641 integer_trail_->AppendRelaxedLinearReason(slack, coeffs, vars,
646 CHECK(extra_explanation_callback_ ==
nullptr);
647 return integer_trail_->Enqueue(lit, literal_reason_, integer_reason_);
653 FlagItemAsUsedInReason(t);
656 return integer_trail_->ConditionalEnqueue(
659 return integer_trail_->Enqueue(lit, literal_reason_, integer_reason_);
664bool SchedulingConstraintHelper::PushIntervalBound(
int t,
IntegerLiteral lit) {
667 if (!UpdateCachedValues(t))
return false;
668 recompute_cache_.
Clear(t);
693 return PushIntervalBound(t, ends_[t].
LowerOrEqual(value));
697 return integer_trail_->EnqueueLiteral(l, literal_reason_, integer_reason_);
704 FlagItemAsUsedInReason(t);
707 literal_reason_.push_back(
Literal(reason_for_presence_[t]).Negated());
711 return integer_trail_->EnqueueLiteral(
712 Literal(reason_for_presence_[t]).Negated(), literal_reason_,
720 FlagItemAsUsedInReason(t);
723 literal_reason_.push_back(
Literal(reason_for_presence_[t]));
727 return integer_trail_->EnqueueLiteral(
Literal(reason_for_presence_[t]),
728 literal_reason_, integer_reason_);
733 CHECK_NE(t_before, t_after);
734 if (
IsAbsent(t_before))
return true;
738 const auto [expr, rhs] =
740 const auto status = linear2_bounds_->GetStatus(expr,
kMinIntegerValue, rhs);
747 linear2_bounds_->AddReasonForUpperBoundLowerThan(
748 negated_expr, -rhs - 1, &literal_reason_, &integer_reason_);
767 FlagItemAsUsedInReason(t_before);
768 FlagItemAsUsedInReason(t_after);
771 return linear2_bounds_->EnqueueLowerOrEqual(expr, rhs, literal_reason_,
778 return integer_trail_->ReportConflict(literal_reason_, integer_reason_);
785 propagator_ids_.push_back(
id);
788void SchedulingConstraintHelper::FlagItemAsUsedInReason(
int t) {
789 if (extra_explanation_callback_ ==
nullptr) {
792 used_items_for_reason_.
Set(t);
795void SchedulingConstraintHelper::RunCallbackIfSet() {
796 if (extra_explanation_callback_ ==
nullptr)
return;
798 &literal_reason_, &integer_reason_);
803 CHECK(other_helper.extra_explanation_callback_ ==
nullptr);
804 literal_reason_.insert(literal_reason_.end(),
805 other_helper.literal_reason_.begin(),
806 other_helper.literal_reason_.end());
807 integer_reason_.insert(integer_reason_.end(),
808 other_helper.integer_reason_.begin(),
809 other_helper.integer_reason_.end());
813 return absl::StrCat(
"t=", t,
" is_present=",
818 "]",
" start=[",
StartMin(t).value(),
",",
820 ",",
EndMax(t).value(),
"]");
825 IntegerValue
end)
const {
826 return std::min(std::min(
end - start,
SizeMin(t)),
831 IntegerValue start_min, IntegerValue start_max, IntegerValue end_min,
832 IntegerValue end_max, IntegerValue size_min, IntegerValue demand_min,
833 absl::Span<const LiteralValueValue> filtered_energy,
834 IntegerValue window_start, IntegerValue window_end) {
835 if (window_end <= window_start)
return IntegerValue(0);
838 if (end_min <= window_start)
return IntegerValue(0);
839 if (start_max >= window_end)
return IntegerValue(0);
840 const IntegerValue window_size = window_end - window_start;
841 const IntegerValue simple_energy_min =
842 demand_min * std::min({end_min - window_start, window_end - start_max,
843 size_min, window_size});
844 if (filtered_energy.empty())
return simple_energy_min;
847 for (
const auto [lit, fixed_size, fixed_demand] : filtered_energy) {
848 const IntegerValue alt_end_min = std::max(end_min, start_min + fixed_size);
849 const IntegerValue alt_start_max =
850 std::min(start_max, end_max - fixed_size);
851 const IntegerValue energy_min =
853 std::min({alt_end_min - window_start, window_end - alt_start_max,
854 fixed_size, window_size});
855 result = std::min(result, energy_min);
858 return std::max(simple_energy_min, result);
862 absl::Span<const AffineExpression> demands,
866 sat_solver_(model->GetOrCreate<
SatSolver>()),
868 demands_(demands.
begin(), demands.
end()),
870 const int num_tasks = helper->
NumTasks();
871 decomposed_energies_.resize(num_tasks);
874 energy_is_quadratic_.resize(num_tasks,
false);
883 const int num_tasks = helper_->NumTasks();
884 if (demands_.size() != num_tasks)
return;
885 for (
int t = 0; t < num_tasks; ++t) {
888 decomposed_energies_[t] = product_decomposer_->TryToDecompose(size, demand);
892IntegerValue SchedulingDemandHelper::SimpleEnergyMin(
int t)
const {
897IntegerValue SchedulingDemandHelper::DecomposedEnergyMin(
int t)
const {
900 for (
const auto [lit, fixed_size, fixed_demand] : decomposed_energies_[t]) {
902 return fixed_size * fixed_demand;
905 result = std::min(result, fixed_size * fixed_demand);
911IntegerValue SchedulingDemandHelper::SimpleEnergyMax(
int t)
const {
916IntegerValue SchedulingDemandHelper::DecomposedEnergyMax(
int t)
const {
919 for (
const auto [lit, fixed_size, fixed_demand] : decomposed_energies_[t]) {
920 if (assignment_.LiteralIsTrue(lit)) {
921 return fixed_size * fixed_demand;
923 if (assignment_.LiteralIsFalse(lit))
continue;
924 result = std::max(result, fixed_size * fixed_demand);
931 const int num_tasks = cached_energies_min_.size();
932 const bool is_at_level_zero = sat_solver_->CurrentDecisionLevel() == 0;
933 for (
int t = 0; t < num_tasks; ++t) {
935 if (is_at_level_zero) {
937 for (
int i = 0;
i < decomposed_energies_[t].size(); ++
i) {
938 if (assignment_.LiteralIsFalse(decomposed_energies_[t][
i].literal)) {
941 decomposed_energies_[t][new_size++] = decomposed_energies_[t][
i];
943 decomposed_energies_[t].resize(new_size);
946 cached_energies_min_[t] =
947 std::max(SimpleEnergyMin(t), DecomposedEnergyMin(t));
949 energy_is_quadratic_[t] =
950 decomposed_energies_[t].empty() && !demands_.empty() &&
951 !integer_trail_->IsFixed(demands_[t]) && !helper_->SizeIsFixed(t);
952 cached_energies_max_[t] =
953 std::min(SimpleEnergyMax(t), DecomposedEnergyMax(t));
961 DCHECK_LT(t, demands_.size());
962 return integer_trail_->LowerBound(demands_[t]);
966 DCHECK_LT(t, demands_.size());
967 return integer_trail_->UpperBound(demands_[t]);
971 return integer_trail_->IsFixed(demands_[t]);
975 if (helper_->IsAbsent(t))
return true;
976 if (value <
EnergyMin(t))
return helper_->PushTaskAbsence(t);
978 if (!decomposed_energies_[t].empty()) {
979 for (
const auto [lit, fixed_size, fixed_demand] : decomposed_energies_[t]) {
980 if (fixed_size * fixed_demand > value) {
983 if (assignment_.LiteralIsFalse(lit))
continue;
984 if (assignment_.LiteralIsTrue(lit)) {
986 if (helper_->PresenceLiteral(t) != lit) {
987 helper_->AddLiteralReason(lit.
Negated());
989 return helper_->PushTaskAbsence(t);
991 if (helper_->IsPresent(t)) {
993 DCHECK(!helper_->IsOptional(t) || helper_->PresenceLiteral(t) != lit);
994 helper_->AddPresenceReason(t);
995 if (!helper_->PushLiteral(lit.
Negated()))
return false;
1001 VLOG(3) <<
"Cumulative energy missed propagation";
1007 DCHECK_LT(t, demands_.size());
1009 helper_->AddIntegerReason(
1010 integer_trail_->LowerBoundAsLiteral(demands_[t].var));
1015 IntegerValue min_demand) {
1016 DCHECK_LT(t, demands_.size());
1018 helper_->AddIntegerReason(demands_[t].
GreaterOrEqual(min_demand));
1024 const IntegerValue value = cached_energies_min_[t];
1025 if (DecomposedEnergyMin(t) >= value) {
1026 for (
const auto [lit, fixed_size, fixed_demand] : decomposed_energies_[t]) {
1027 if (assignment_.LiteralIsTrue(lit)) {
1028 helper_->AddLiteralReason(lit.
Negated());
1032 for (
const auto [lit, fixed_size, fixed_demand] : decomposed_energies_[t]) {
1033 if (fixed_size * fixed_demand < value &&
1034 assignment_.LiteralIsFalse(lit)) {
1035 helper_->AddLiteralReason(lit);
1038 }
else if (SimpleEnergyMin(t) >= value) {
1040 helper_->AddSizeMinReason(t);
1046 if (helper_->IsPresent(t)) {
1047 if (!decomposed_energies_[t].empty()) {
1049 if (!builder->
AddLiteralTerm(entry.literal, entry.right_value)) {
1054 builder->
AddTerm(demands_[t], IntegerValue(1));
1056 }
else if (!helper_->IsAbsent(t)) {
1064 if (decomposed_energies_[index].empty())
return {};
1065 if (sat_solver_->CurrentDecisionLevel() == 0) {
1067 return decomposed_energies_[index];
1071 std::vector<LiteralValueValue> result;
1072 for (
const auto& e : decomposed_energies_[index]) {
1073 if (assignment_.LiteralIsFalse(e.literal))
continue;
1074 result.push_back(e);
1080 const std::vector<std::vector<LiteralValueValue>>& energies) {
1081 DCHECK_EQ(energies.size(), helper_->NumTasks());
1082 decomposed_energies_ = energies;
1086 int t, IntegerValue window_start, IntegerValue window_end) {
1088 helper_->StartMin(t), helper_->StartMax(t), helper_->EndMin(t),
1089 helper_->EndMax(t), helper_->SizeMin(t),
DemandMin(t),
1096 int t, IntegerValue window_start, IntegerValue window_end) {
1097 const IntegerValue actual_energy_min =
1099 if (actual_energy_min == 0)
return;
1103 const IntegerValue start_max = helper_->StartMax(t);
1104 const IntegerValue end_min = helper_->EndMin(t);
1105 const IntegerValue min_overlap =
1106 helper_->GetMinOverlap(t, window_start, window_end);
1107 const IntegerValue simple_energy_min =
DemandMin(t) * min_overlap;
1108 if (simple_energy_min == actual_energy_min) {
1110 helper_->AddSizeMinReason(t);
1111 helper_->AddStartMaxReason(t, start_max);
1112 helper_->AddEndMinReason(t, end_min);
1117 const IntegerValue start_min = helper_->StartMin(t);
1118 const IntegerValue end_max = helper_->EndMax(t);
1119 DCHECK(!decomposed_energies_[t].empty());
1120 helper_->AddStartMinReason(t, start_min);
1121 helper_->AddStartMaxReason(t, start_max);
1122 helper_->AddEndMinReason(t, end_min);
1123 helper_->AddEndMaxReason(t, end_max);
1125 DCHECK(!decomposed_energies_[t].empty());
1126 for (
const auto [lit, fixed_size, fixed_demand] : decomposed_energies_[t]) {
1128 if (assignment_.LiteralIsTrue(lit)) {
1129 helper_->AddLiteralReason(lit.
Negated());
1133 for (
const auto [lit, fixed_size, fixed_demand] : decomposed_energies_[t]) {
1134 if (assignment_.LiteralIsFalse(lit)) {
1135 const IntegerValue alt_em = std::max(end_min, start_min + fixed_size);
1136 const IntegerValue alt_sm = std::min(start_max, end_max - fixed_size);
1137 const IntegerValue energy_min =
1139 std::min({alt_em - window_start, window_end - alt_sm, fixed_size});
1140 if (energy_min >= actual_energy_min)
continue;
1141 helper_->AddLiteralReason(lit);
1148 linear2_bounds_->AddReasonForUpperBoundLowerThan(expr, ub, &literal_reason_,
1153 std::vector<IntegerLiteral>* integer_reason,
1154 std::vector<Literal>* literal_reason) {
1157 integer_reason->push_back(l);
1159 for (
const Literal l : literal_reason_) {
1160 literal_reason->push_back(l);
1167 std::vector<IntegerVariable>* vars,
1170 for (
int t = 0; t < helper->
NumTasks(); ++t) {
1173 vars->push_back(helper->
Starts()[t].var);
1177 vars->push_back(helper->
Sizes()[t].var);
1181 vars->push_back(helper->
Ends()[t].var);
1191 vars->push_back(view);
1198 Model* model, std::vector<IntegerVariable>* vars) {
1201 if (!integer_trail->IsFixed(demand_expr)) {
1202 vars->push_back(demand_expr.var);
1207 for (
const auto& lit_val_val : product) {
1212 vars->push_back(view);
1216 if (!integer_trail->IsFixed(capacity)) {
1217 vars->push_back(capacity.
var);
void Resize(IndexType size)
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
void Set(IntegerType index)
void WatchLiteral(Literal l, int id, int watch_index=-1)
void SetPropagatorPriority(int id, int priority)
void WatchIntegerVariable(IntegerVariable i, int id, int watch_index=-1)
int Register(PropagatorInterface *propagator)
ABSL_MUST_USE_RESULT bool LiteralOrNegationHasView(Literal lit, IntegerVariable *view=nullptr, bool *view_is_direct=nullptr) const
IntegerValue LowerBound(IntegerVariable i) const
IntegerValue UpperBound(IntegerVariable i) const
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff=IntegerValue(1))
void AddTerm(IntegerVariable var, IntegerValue coeff)
T Add(std::function< T(Model *)> f)
void AddPresenceReason(int t)
IntegerValue ShiftedEndMax(int t) const
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
absl::Span< const TaskTime > TaskByIncreasingStartMin()
ABSL_MUST_USE_RESULT bool PushTaskOrderWhenPresent(int t_before, int t_after)
void AddStartMaxReason(int t, IntegerValue upper_bound)
ABSL_MUST_USE_RESULT bool IncreaseEndMin(int t, IntegerValue value)
IntegerValue ShiftedStartMin(int t) const
void WatchAllTasks(int id)
bool TaskIsBeforeOrIsOverlapping(int before, int after)
absl::Span< const TaskTime > TaskByIncreasingNegatedStartMax()
bool SizeIsFixed(int t) const
IntegerValue GetCurrentMinDistanceBetweenTasks(int a, int b)
void AddSizeMaxReason(int t, IntegerValue upper_bound)
void AppendAndResetReason(std::vector< IntegerLiteral > *integer_reason, std::vector< Literal > *literal_reason)
IntegerValue StartMax(int t) const
ABSL_MUST_USE_RESULT bool DecreaseEndMax(int t, IntegerValue value)
ABSL_MUST_USE_RESULT bool ReportConflict()
absl::Span< const AffineExpression > Sizes() const
ABSL_MUST_USE_RESULT bool PushLiteral(Literal l)
const std::vector< ProfileEvent > & GetEnergyProfile()
ABSL_MUST_USE_RESULT bool PushTaskAbsence(int t)
bool IsPresent(int t) const
void SetTimeDirection(bool is_forward)
ABSL_MUST_USE_RESULT bool PushIntegerLiteralIfTaskPresent(int t, IntegerLiteral lit)
Literal PresenceLiteral(int index) const
void AddSizeMinReason(int t)
absl::Span< const AffineExpression > Starts() const
void AddEndMaxReason(int t, IntegerValue upper_bound)
absl::Span< const AffineExpression > Ends() const
bool NotifyLevelZeroPrecedence(int a, int b)
ABSL_MUST_USE_RESULT bool ResetFromSubset(const SchedulingConstraintHelper &other, absl::Span< const int > tasks)
ABSL_MUST_USE_RESULT bool IncreaseStartMin(int t, IntegerValue value)
IntegerValue SizeMax(int t) const
void AddEndMinReason(int t, IntegerValue lower_bound)
IntegerValue StartMin(int t) const
void AddReasonForUpperBoundLowerThan(LinearExpression2 expr, IntegerValue ub)
bool StartIsFixed(int t) const
SchedulingConstraintHelper(std::vector< AffineExpression > starts, std::vector< AffineExpression > ends, std::vector< AffineExpression > sizes, std::vector< LiteralIndex > reason_for_presence, Model *model)
IntegerValue SizeMin(int t) const
bool EndIsFixed(int t) const
absl::Span< const CachedTaskBounds > TaskByIncreasingShiftedStartMin()
absl::Span< const TaskTime > TaskByDecreasingEndMax()
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
ABSL_MUST_USE_RESULT bool PushTaskPresence(int t)
void AddReasonForBeingBeforeAssumingNoOverlap(int before, int after)
IntegerValue EndMax(int t) const
bool IsAbsent(int t) const
IntegerValue EndMin(int t) const
bool IsOptional(int t) const
std::string TaskDebugString(int t) const
ABSL_MUST_USE_RESULT bool PushIntegerLiteral(IntegerLiteral lit)
IntegerValue GetMinOverlap(int t, IntegerValue start, IntegerValue end) const
void RegisterWith(GenericLiteralWatcher *watcher, absl::Span< const Literal > enforcement_literals)
void AddStartMinReason(int t, IntegerValue lower_bound)
absl::Span< const CachedTaskBounds > TaskByIncreasingNegatedShiftedEndMax()
void ImportReasonsFromOther(const SchedulingConstraintHelper &other_helper)
absl::Span< const TaskTime > TaskByIncreasingEndMin()
void AddDemandMinReason(int t)
std::vector< LiteralValueValue > FilteredDecomposedEnergy(int index)
bool DemandIsFixed(int t) const
bool CacheAllEnergyValues()
IntegerValue EnergyMinInWindow(int t, IntegerValue window_start, IntegerValue window_end)
const std::vector< std::vector< LiteralValueValue > > & DecomposedEnergies() const
void AddEnergyMinReason(int t)
void InitDecomposedEnergies()
void AddEnergyMinInWindowReason(int t, IntegerValue window_start, IntegerValue window_end)
IntegerValue DemandMax(int t) const
SchedulingDemandHelper(absl::Span< const AffineExpression > demands, SchedulingConstraintHelper *helper, Model *model)
void OverrideDecomposedEnergies(const std::vector< std::vector< LiteralValueValue > > &energies)
const std::vector< AffineExpression > & Demands() const
ABSL_MUST_USE_RESULT bool DecreaseEnergyMax(int t, IntegerValue value)
IntegerValue DemandMin(int t) const
ABSL_MUST_USE_RESULT bool AddLinearizedDemand(int t, LinearConstraintBuilder *builder) const
IntegerValue EnergyMin(int t) const
bool LiteralIsFalse(Literal literal) const
bool LiteralIsTrue(Literal literal) const
std::function< IntegerVariable(Model *)> NewIntegerVariableFromLiteral(Literal lit)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
void AddWeightedSumLowerOrEqual(absl::Span< const Literal > enforcement_literals, absl::Span< const IntegerVariable > vars, absl::Span< const IntegerValue > coefficients, int64_t upper_bound, Model *model)
const LiteralIndex kNoLiteralIndex(-1)
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
std::function< void(Model *)> LowerOrEqual(IntegerVariable v, int64_t ub)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
void AddIntegerVariableFromIntervals(const SchedulingConstraintHelper *helper, Model *model, std::vector< IntegerVariable > *vars, int mask)
void AppendVariablesFromCapacityAndDemands(const AffineExpression &capacity, SchedulingDemandHelper *demands_helper, Model *model, std::vector< IntegerVariable > *vars)
std::function< void(Model *)> GreaterOrEqual(IntegerVariable v, int64_t lb)
std::pair< LinearExpression2, IntegerValue > EncodeDifferenceLowerThan(AffineExpression a, AffineExpression b, IntegerValue ub)
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
IntegerValue ComputeEnergyMinInWindow(IntegerValue start_min, IntegerValue start_max, IntegerValue end_min, IntegerValue end_max, IntegerValue size_min, IntegerValue demand_min, absl::Span< const LiteralValueValue > filtered_energy, IntegerValue window_start, IntegerValue window_end)
ClosedInterval::Iterator end(ClosedInterval interval)
void IncrementalSort(int max_comparisons, Iterator begin, Iterator end, Compare comp=Compare{}, bool is_stable=false)
ClosedInterval::Iterator begin(ClosedInterval interval)
IntegerLiteral Negated() const