87 if (!helper_->IsEnforced())
return true;
90 if (!helper_->SynchronizeAndSetTimeDirection(
true))
return false;
91 if (!demands_->CacheAllEnergyValues())
return true;
93 const IntegerValue capacity_max = integer_trail_->UpperBound(capacity_);
95 if (capacity_max <= 0)
return true;
98 start_event_task_time_.clear();
100 for (
const auto task_time : helper_->TaskByIncreasingStartMin()) {
101 const int task = task_time.task_index;
102 if (helper_->IsAbsent(task) || demands_->EnergyMax(task) == 0) {
103 task_to_start_event_[task] = -1;
106 start_event_task_time_.emplace_back(task_time);
107 task_to_start_event_[task] = num_events;
110 start_event_is_present_.assign(num_events,
false);
111 theta_tree_.Reset(num_events);
113 bool tree_has_mandatory_intervals =
false;
115 const IntegerValue start_end_magnitude =
117 helper_->TaskByDecreasingEndMax().front().task_index)),
118 IntTypeAbs(helper_->TaskByIncreasingStartMin().front().time));
124 const auto by_decreasing_end_max = helper_->TaskByDecreasingEndMax();
125 for (
const auto [current_task, current_end] :
127 if (task_to_start_event_[current_task] == -1)
continue;
131 const int current_event = task_to_start_event_[current_task];
132 const IntegerValue start_min = start_event_task_time_[current_event].time;
133 const bool is_present = helper_->IsPresent(current_task);
134 start_event_is_present_[current_event] = is_present;
136 tree_has_mandatory_intervals =
true;
137 theta_tree_.AddOrUpdateEvent(current_event, start_min * capacity_max,
138 demands_->EnergyMin(current_task),
139 demands_->EnergyMax(current_task));
141 theta_tree_.AddOrUpdateOptionalEvent(current_event,
142 start_min * capacity_max,
143 demands_->EnergyMax(current_task));
147 if (tree_has_mandatory_intervals) {
149 const IntegerValue envelope = theta_tree_.GetEnvelope();
150 const int critical_event =
151 theta_tree_.GetMaxEventWithEnvelopeGreaterThan(envelope - 1);
152 const IntegerValue window_start =
153 start_event_task_time_[critical_event].time;
154 const IntegerValue window_end = current_end;
155 const IntegerValue window_size = window_end - window_start;
156 if (window_size == 0)
continue;
157 const IntegerValue new_capacity_min =
158 CeilRatio(envelope - window_start * capacity_max, window_size);
166 if (new_capacity_min > integer_trail_->LowerBound(capacity_)) {
167 helper_->ResetReason();
168 for (
int event = critical_event;
event < num_events;
event++) {
169 if (start_event_is_present_[event]) {
170 const int task = start_event_task_time_[event].task_index;
171 helper_->AddPresenceReason(task);
172 demands_->AddEnergyMinReason(task);
173 helper_->AddStartMinReason(task, window_start);
174 helper_->AddEndMaxReason(task, window_end);
178 return helper_->ReportConflict();
180 if (!helper_->PushIntegerLiteral(
181 capacity_.GreaterOrEqual(new_capacity_min))) {
190 while (theta_tree_.GetOptionalEnvelope() > current_end * capacity_max) {
200 helper_->ResetReason();
202 int event_with_new_energy_max;
203 IntegerValue new_energy_max;
204 theta_tree_.GetEventsWithOptionalEnvelopeGreaterThan(
205 current_end * capacity_max, &critical_event,
206 &event_with_new_energy_max, &new_energy_max);
208 const IntegerValue window_start =
209 start_event_task_time_[critical_event].time;
212 const IntegerValue window_end = current_end;
213 for (
int event = critical_event;
event < num_events;
event++) {
214 if (start_event_is_present_[event]) {
215 if (event == event_with_new_energy_max)
continue;
216 const int task = start_event_task_time_[event].task_index;
217 helper_->AddPresenceReason(task);
218 helper_->AddStartMinReason(task, window_start);
219 helper_->AddEndMaxReason(task, window_end);
220 demands_->AddEnergyMinReason(task);
224 helper_->AddIntegerReason(
225 integer_trail_->UpperBoundAsLiteral(capacity_.var));
228 const int task_with_new_energy_max =
229 start_event_task_time_[event_with_new_energy_max].task_index;
230 helper_->AddStartMinReason(task_with_new_energy_max, window_start);
231 helper_->AddEndMaxReason(task_with_new_energy_max, window_end);
232 if (!demands_->DecreaseEnergyMax(task_with_new_energy_max,
237 if (helper_->IsPresent(task_with_new_energy_max)) {
238 theta_tree_.AddOrUpdateEvent(
239 task_to_start_event_[task_with_new_energy_max],
240 start_event_task_time_[event_with_new_energy_max].time *
242 demands_->EnergyMin(task_with_new_energy_max), new_energy_max);
244 theta_tree_.RemoveEvent(event_with_new_energy_max);
271 if (!helper_->IsEnforced())
return true;
272 if (!helper_->SynchronizeAndSetTimeDirection(
true))
return false;
278 IntegerValue energy_after_time(0);
279 IntegerValue profile_height(0);
284 const IntegerValue capacity_max = integer_trail_->UpperBound(capacity_);
285 dp_.Reset(capacity_max.value());
289 const auto& profile = helper_->GetEnergyProfile();
291 for (
int i = profile.size() - 1;
i >= 0;) {
294 const int t = profile[
i].task;
295 if (!helper_->IsPresent(t) || !is_in_subtasks_[t]) {
301 const IntegerValue time = profile[
i].time;
302 if (profile_height > 0) {
303 energy_after_time += profile_height * (previous_time - time);
305 previous_time = time;
309 const IntegerValue saved_capa_max = dp_.CurrentMax();
310 const IntegerValue saved_min_offset = min_offset;
312 for (;
i >= 0 && profile[
i].time == time; --
i) {
314 const int t = profile[
i].task;
315 if (!helper_->IsPresent(t) || !is_in_subtasks_[t])
continue;
317 min_offset = std::min(min_offset, task_offsets_[t]);
318 const IntegerValue demand_min = demands_->DemandMin(t);
319 if (profile[
i].is_first) {
320 profile_height -= demand_min;
322 profile_height += demand_min;
323 if (demands_->Demands()[t].IsConstant()) {
324 dp_.Add(demand_min.value());
326 dp_.Add(capacity_max.value());
338 if (energy_after_time == 0)
continue;
339 DCHECK_GT(saved_capa_max, 0);
341 const IntegerValue end_min_with_offset =
342 time +
CeilRatio(energy_after_time, saved_capa_max) + saved_min_offset;
343 if (end_min_with_offset > best_bound) {
345 best_bound = end_min_with_offset;
348 DCHECK_EQ(profile_height, 0);
351 if (best_bound > integer_trail_->LowerBound(var_to_push_)) {
354 helper_->ResetReason();
355 for (
int t = 0; t < helper_->NumTasks(); ++t) {
356 if (!is_in_subtasks_[t])
continue;
357 if (!helper_->IsPresent(t))
continue;
359 const IntegerValue size_min = helper_->SizeMin(t);
360 if (size_min == 0)
continue;
362 const IntegerValue demand_min = demands_->DemandMin(t);
363 if (demand_min == 0)
continue;
365 const IntegerValue end_min = helper_->EndMin(t);
366 if (end_min <= best_time)
continue;
368 helper_->AddEndMinReason(t, std::min(best_time + size_min, end_min));
369 helper_->AddSizeMinReason(t);
370 helper_->AddPresenceReason(t);
371 demands_->AddDemandMinReason(t);
374 helper_->AddIntegerReason(
375 integer_trail_->UpperBoundAsLiteral(capacity_.var));
379 if (!helper_->PushIntegerLiteral(
520 if (!helper_->IsEnforced())
return true;
521 if (!helper_->SynchronizeAndSetTimeDirection(
true))
return false;
522 if (!demands_->CacheAllEnergyValues())
return true;
524 const IntegerValue capacity_max = integer_trail_->UpperBound(capacity_);
525 if (capacity_max <= 0)
return true;
528 start_event_task_time_.clear();
530 for (
const auto task_time : helper_->TaskByIncreasingStartMin()) {
531 const int task = task_time.task_index;
532 if (!helper_->IsPresent(task) || demands_->DemandMin(task) == 0) {
533 task_to_start_event_[task] = -1;
536 start_event_task_time_.emplace_back(task_time);
537 task_to_start_event_[task] = num_events;
541 if (num_events == 0)
return true;
544 const IntegerValue start_end_magnitude =
546 helper_->TaskByDecreasingEndMax().front().task_index)),
547 IntTypeAbs(helper_->TaskByIncreasingStartMin().front().time));
548 if (start_end_magnitude == 0)
return true;
550 const IntegerValue max_energy =
556 const IntegerValue max_for_fixpoint_inverse =
557 std::numeric_limits<IntegerValue>::max() / max_energy;
559 theta_tree_.Reset(num_events);
585 std::vector<std::pair<IntegerValue, IntegerValue>> candidates_for_conflict;
586 const auto by_decreasing_end_max = helper_->TaskByDecreasingEndMax();
587 for (
const auto [current_task, current_end] :
589 if (task_to_start_event_[current_task] == -1)
continue;
590 if (!helper_->IsPresent(current_task))
continue;
591 if (helper_->SizeMin(current_task) == 0)
continue;
592 if (demands_->DemandMin(current_task) == 0)
continue;
594 if (demands_->DemandMin(current_task) > capacity_max) {
597 demands_->AddDemandMinReason(current_task);
600 helper_->AddIntegerReason(
601 integer_trail_->UpperBoundAsLiteral(capacity_));
609 return helper_->ReportConflict();
614 const IntegerValue current_pseudo_energy =
615 helper_->SizeMin(current_task) *
616 (max_for_fixpoint_inverse /
617 (capacity_max / demands_->DemandMin(current_task)));
618 const int current_event = task_to_start_event_[current_task];
619 const IntegerValue start_min = start_event_task_time_[current_event].time;
620 theta_tree_.AddOrUpdateEvent(
621 current_event, start_min * max_for_fixpoint_inverse,
622 current_pseudo_energy, current_pseudo_energy);
627 const IntegerValue envelope = theta_tree_.GetEnvelope();
628 const int critical_event =
629 theta_tree_.GetMaxEventWithEnvelopeGreaterThan(envelope - 1);
630 const IntegerValue window_start =
631 start_event_task_time_[critical_event].time;
632 const IntegerValue window_end = current_end;
633 const IntegerValue window_size = window_end - window_start;
634 if (window_size == 0)
continue;
636 if (envelope > window_end * max_for_fixpoint_inverse) {
637 candidates_for_conflict.push_back({window_start, window_end});
641 VLOG_EVERY_N_SEC(2, 3) <<
"Found " << candidates_for_conflict.size()
642 <<
" intervals with potential energy conflict using a "
643 "DFF on a problem of size "
644 << num_events <<
".";
646 if (candidates_for_conflict.empty()) {
647 ++num_no_potential_window_;
656 absl::InlinedVector<std::pair<IntegerValue, IntegerValue>, 3>
658 std::sample(candidates_for_conflict.begin(), candidates_for_conflict.end(),
659 std::back_inserter(sampled_candidates), 3, *random_);
660 for (
const auto& [window_start, window_end] : sampled_candidates) {
661 if (!FindAndPropagateConflict(window_start, window_end)) {