28#include "absl/algorithm/container.h"
29#include "absl/base/attributes.h"
30#include "absl/container/btree_set.h"
31#include "absl/container/flat_hash_map.h"
32#include "absl/log/check.h"
33#include "absl/strings/str_cat.h"
34#include "absl/strings/str_join.h"
35#include "absl/strings/string_view.h"
36#include "absl/types/span.h"
62const double kMinCutViolation = 1e-4;
71 const std::vector<LiteralValueValue>& decomposed_energy =
72 demands_helper->DecomposedEnergies()[t];
73 if (decomposed_energy.empty())
return true;
76 int num_false_literals = 0;
77 int num_true_literals = 0;
78 for (
const auto [lit, fixed_size, fixed_demand] : decomposed_energy) {
79 if (assignment.LiteralIsFalse(lit)) ++num_false_literals;
80 if (assignment.LiteralIsTrue(lit)) ++num_true_literals;
82 if (num_true_literals > 1)
return false;
83 if (num_true_literals == 1 &&
84 num_false_literals < decomposed_energy.size() - 1) {
87 if (num_false_literals == decomposed_energy.size())
return false;
88 if (decomposed_energy.size() == 1 && num_true_literals != 1)
return false;
95 for (
const auto [lit, fixed_size, fixed_demand] : decomposed_energy) {
96 if (assignment.LiteralIsFalse(lit))
continue;
98 if (assignment.LiteralIsTrue(lit)) {
99 if (fixed_size != helper->SizeMin(t) ||
100 fixed_size != helper->SizeMax(t) ||
101 fixed_demand != demands_helper->DemandMin(t) ||
102 fixed_demand != demands_helper->DemandMax(t)) {
108 if (fixed_size < helper->SizeMin(t) || fixed_size > helper->SizeMax(t) ||
109 fixed_demand < demands_helper->DemandMin(t) ||
110 fixed_demand > demands_helper->DemandMax(t)) {
113 propagated_size_min = std::min(propagated_size_min, fixed_size);
114 propagated_size_max = std::max(propagated_size_max, fixed_size);
115 propagated_demand_min = std::min(propagated_demand_min, fixed_demand);
116 propagated_demand_max = std::max(propagated_demand_max, fixed_demand);
119 if (propagated_size_min != helper->SizeMin(t) ||
120 propagated_size_max != helper->SizeMax(t) ||
121 propagated_demand_min != demands_helper->DemandMin(t) ||
122 propagated_demand_max != demands_helper->DemandMax(t)) {
229ABSL_MUST_USE_RESULT
bool AddOneEvent(
230 const EnergyEvent& event, IntegerValue window_start,
231 IntegerValue window_end, LinearConstraintBuilder& cut,
232 bool* add_energy_to_name =
nullptr,
bool* add_quadratic_to_name =
nullptr,
233 bool* add_opt_to_name =
nullptr,
bool* add_lifted_to_name =
nullptr) {
234 if (event.end_min <= window_start || event.start_max >= window_end) {
238 if (event.start_min >= window_start && event.end_max <= window_end) {
240 cut.AddLinearExpression(event.linearized_energy);
242 if (event.energy_is_quadratic && add_quadratic_to_name !=
nullptr) {
243 *add_quadratic_to_name =
true;
245 if (add_energy_to_name !=
nullptr &&
246 event.energy_min > event.size_min * event.demand_min) {
247 *add_energy_to_name =
true;
249 if (!event.IsPresent() && add_opt_to_name !=
nullptr) {
250 *add_opt_to_name =
true;
253 const IntegerValue min_overlap =
254 event.GetMinOverlap(window_start, window_end);
255 if (min_overlap <= 0)
return true;
256 if (add_lifted_to_name !=
nullptr) *add_lifted_to_name =
true;
258 if (event.IsPresent()) {
259 const std::vector<LiteralValueValue>& energy =
event.decomposed_energy;
260 if (energy.empty()) {
261 cut.AddTerm(event.demand, min_overlap);
263 const IntegerValue window_size = window_end - window_start;
264 for (
const auto [lit, fixed_size, fixed_demand] : energy) {
265 const IntegerValue alt_end_min =
266 std::max(event.end_min, event.start_min + fixed_size);
267 const IntegerValue alt_start_max =
268 std::min(event.start_max, event.end_max - fixed_size);
269 const IntegerValue energy_min =
271 std::min({alt_end_min - window_start, window_end - alt_start_max,
272 fixed_size, window_size});
273 if (energy_min == 0)
continue;
274 if (!cut.AddLiteralTerm(lit, energy_min))
return false;
276 if (add_energy_to_name !=
nullptr) *add_energy_to_name =
true;
279 if (add_opt_to_name !=
nullptr) *add_opt_to_name =
true;
281 event.start_min, event.start_max, event.end_min, event.end_max,
282 event.size_min, event.demand_min, event.decomposed_energy,
283 window_start, window_end);
284 if (min_energy > event.size_min * event.demand_min &&
285 add_energy_to_name !=
nullptr) {
286 *add_energy_to_name =
true;
288 if (!cut.AddLiteralTerm(
Literal(event.presence_literal_index),
299std::vector<int64_t> FindPossibleDemands(
const EnergyEvent& event,
302 std::vector<int64_t> possible_demands;
303 if (event.decomposed_energy.empty()) {
304 if (integer_trail->IsFixed(event.demand)) {
305 possible_demands.push_back(
306 integer_trail->FixedValue(event.demand).value());
308 if (integer_trail->InitialVariableDomain(event.demand.var).Size() >
312 for (
const int64_t var_value :
313 integer_trail->InitialVariableDomain(event.demand.var).Values()) {
314 possible_demands.push_back(event.demand.ValueAt(var_value).value());
318 for (
const auto [lit, fixed_size, fixed_demand] : event.decomposed_energy) {
319 if (assignment.LiteralIsFalse(lit))
continue;
320 possible_demands.push_back(fixed_demand.value());
323 return possible_demands;
341 absl::string_view cut_name,
343 absl::Span<std::unique_ptr<EnergyEvent>> events, IntegerValue capacity,
348 DCHECK(integer_trail->
IsFixed(capacity));
357 std::vector<IntegerValue> time_points;
358 std::vector<std::vector<int64_t>> possible_demands(events.size());
359 const IntegerValue makespan_min = integer_trail->
LowerBound(makespan);
362 for (
int i = 0;
i < events.size(); ++
i) {
364 if (event.start_min < makespan_min) {
365 time_points.push_back(event.start_min);
367 if (event.start_max < makespan_min) {
368 time_points.push_back(event.start_max);
370 if (event.end_min < makespan_min) {
371 time_points.push_back(event.end_min);
373 if (event.end_max < makespan_min) {
374 time_points.push_back(event.end_max);
376 max_end_min = std::max(max_end_min, event.end_min);
377 max_end_max = std::max(max_end_max, event.end_max);
378 possible_demands[
i] = FindPossibleDemands(event, assignment, integer_trail);
380 time_points.push_back(makespan_min);
381 time_points.push_back(max_end_max);
384 const int num_time_points = time_points.size();
385 absl::flat_hash_map<IntegerValue, IntegerValue> reachable_capacity_ending_at;
388 for (
int i = 1;
i < num_time_points; ++
i) {
389 const IntegerValue window_start = time_points[
i - 1];
390 const IntegerValue window_end = time_points[
i];
391 reachable_capacity_subset_sum.
Reset(capacity.value());
392 for (
int i = 0;
i < events.size(); ++
i) {
394 if (event.start_min >= window_end || event.end_max <= window_start) {
397 if (possible_demands[
i].empty()) {
399 reachable_capacity_subset_sum.
Add(capacity.value());
401 reachable_capacity_subset_sum.
AddChoices(possible_demands[
i]);
403 if (reachable_capacity_subset_sum.
CurrentMax() == capacity.value())
break;
405 reachable_capacity_ending_at[window_end] =
409 const double capacity_lp =
ToDouble(capacity);
410 const double makespan_lp = makespan.
LpValue(lp_values);
411 const double makespan_min_lp =
ToDouble(makespan_min);
413 for (
int i = 0;
i + 1 < num_time_points; ++
i) {
415 if (events.size() > 50 && time_limit->
LimitReached())
return;
417 const IntegerValue window_start = time_points[
i];
419 if (window_start >= max_end_min)
break;
421 IntegerValue cumulated_max_energy = 0;
422 IntegerValue cumulated_max_energy_before_makespan_min = 0;
423 bool use_subset_sum =
false;
424 bool use_subset_sum_before_makespan_min =
false;
426 for (
int j =
i + 1; j < num_time_points; ++j) {
427 const IntegerValue strip_start = time_points[j - 1];
428 const IntegerValue window_end = time_points[j];
429 const IntegerValue max_reachable_capacity_in_current_strip =
430 reachable_capacity_ending_at[window_end];
431 DCHECK_LE(max_reachable_capacity_in_current_strip, capacity);
434 if (max_reachable_capacity_in_current_strip < capacity) {
435 use_subset_sum =
true;
436 if (window_end <= makespan_min) {
437 use_subset_sum_before_makespan_min =
true;
441 const IntegerValue energy_in_strip =
442 (window_end - strip_start) * max_reachable_capacity_in_current_strip;
443 cumulated_max_energy += energy_in_strip;
444 if (window_end <= makespan_min) {
445 cumulated_max_energy_before_makespan_min += energy_in_strip;
448 if (window_start >= makespan_min) {
449 DCHECK_EQ(cumulated_max_energy_before_makespan_min, 0);
451 DCHECK_LE(cumulated_max_energy, capacity * (window_end - window_start));
452 const double max_energy_up_to_makespan_lp =
453 strip_start >= makespan_min
454 ?
ToDouble(cumulated_max_energy_before_makespan_min) +
455 (makespan_lp - makespan_min_lp) * capacity_lp
456 : std::numeric_limits<double>::infinity();
463 bool cut_generated =
true;
464 bool add_opt_to_name =
false;
465 bool add_lifted_to_name =
false;
466 bool add_quadratic_to_name =
false;
467 bool add_energy_to_name =
false;
469 temp_builder.
Clear();
470 const bool use_makespan =
471 max_energy_up_to_makespan_lp <=
472 ToDouble(cumulated_max_energy) + kMinCutViolation;
473 const bool use_subset_sum_in_cut =
474 use_makespan ? use_subset_sum_before_makespan_min : use_subset_sum;
478 temp_builder.
AddTerm(makespan, -capacity);
482 for (
const std::unique_ptr<EnergyEvent>& event : events) {
483 if (!AddOneEvent(*event, window_start, window_end, temp_builder,
484 &add_energy_to_name, &add_quadratic_to_name,
485 &add_opt_to_name, &add_lifted_to_name)) {
486 cut_generated =
false;
493 if (!cut_generated)
break;
496 const IntegerValue energy_rhs =
497 use_makespan ? cumulated_max_energy_before_makespan_min
498 : cumulated_max_energy;
503 std::string full_name(cut_name);
504 if (add_energy_to_name) full_name.append(
"_energy");
505 if (add_lifted_to_name) full_name.append(
"_lifted");
506 if (use_makespan) full_name.append(
"_makespan");
507 if (add_opt_to_name) full_name.append(
"_optional");
508 if (add_quadratic_to_name) full_name.append(
"_quadratic");
509 if (use_subset_sum_in_cut) full_name.append(
"_subsetsum");
510 top_n_cuts.
AddCut(std::move(potential_cut), full_name, lp_values);
517 absl::string_view cut_name,
519 absl::Span<std::unique_ptr<EnergyEvent>> events,
522 double max_possible_energy_lp = 0.0;
523 for (
const std::unique_ptr<EnergyEvent>& event : events) {
524 max_possible_energy_lp +=
event->linearized_energy_lp_value;
534 const double capacity_lp = capacity.
LpValue(lp_values);
538 absl::btree_set<IntegerValue> time_points_set;
540 for (
const std::unique_ptr<EnergyEvent>& event : events) {
541 time_points_set.insert(event->start_min);
542 time_points_set.insert(event->start_max);
543 time_points_set.insert(event->end_min);
544 time_points_set.insert(event->end_max);
545 max_end_min = std::max(max_end_min, event->end_min);
547 const std::vector<IntegerValue> time_points(time_points_set.begin(),
548 time_points_set.end());
549 const int num_time_points = time_points.size();
552 for (
int i = 0;
i + 1 < num_time_points; ++
i) {
554 if (events.size() > 50 && time_limit->
LimitReached())
return;
556 const IntegerValue window_start = time_points[
i];
558 if (window_start >= max_end_min)
break;
560 for (
int j =
i + 1; j < num_time_points; ++j) {
561 const IntegerValue window_end = time_points[j];
562 const double available_energy_lp =
563 ToDouble(window_end - window_start) * capacity_lp;
564 if (available_energy_lp >= max_possible_energy_lp)
break;
566 bool cut_generated =
true;
567 bool add_opt_to_name =
false;
568 bool add_lifted_to_name =
false;
569 bool add_quadratic_to_name =
false;
570 bool add_energy_to_name =
false;
571 temp_builder.
Clear();
574 temp_builder.
AddTerm(capacity, window_start - window_end);
577 for (
const std::unique_ptr<EnergyEvent>& event : events) {
578 if (!AddOneEvent(*event, window_start, window_end, temp_builder,
579 &add_energy_to_name, &add_quadratic_to_name,
580 &add_opt_to_name, &add_lifted_to_name)) {
581 cut_generated =
false;
588 if (!cut_generated)
break;
595 std::string full_name(cut_name);
596 if (add_energy_to_name) full_name.append(
"_energy");
597 if (add_lifted_to_name) full_name.append(
"_lifted");
598 if (add_opt_to_name) full_name.append(
"_optional");
599 if (add_quadratic_to_name) full_name.append(
"_quadratic");
600 top_n_cuts.
AddCut(std::move(potential_cut), full_name, lp_values);
609 const std::optional<AffineExpression>& makespan,
Model* model) {
615 helper, model, &result.
vars,
617 if (makespan.has_value() && !makespan.value().IsConstant()) {
618 result.
vars.push_back(makespan.value().var);
625 result.
generate_cuts = [makespan, capacity, demands_helper, helper,
626 integer_trail, time_limit, sat_solver,
631 const auto& lp_values = manager->LpValues();
634 std::vector<std::unique_ptr<EnergyEvent>> events;
641 if (!DecomposedEnergyIsPropagated(assignment,
i, helper,
646 auto e = std::make_unique<EnergyEvent>(
i, helper);
647 e->demand = demands_helper->
Demands()[
i];
650 if (e->decomposed_energy.size() == 1) {
652 e->decomposed_energy.clear();
660 if (!e->FillEnergyLp(helper->
Sizes()[
i], lp_values, model))
continue;
661 events.push_back(std::move(e));
665 std::vector<absl::Span<std::unique_ptr<EnergyEvent>>> disjoint_events =
668 for (
const absl::Span<std::unique_ptr<EnergyEvent>> cluster :
670 if (makespan.has_value() && integer_trail->
IsFixed(capacity)) {
672 "CumulativeEnergyM", lp_values, cluster,
673 integer_trail->
FixedValue(capacity), makespan.value(), time_limit,
678 capacity, time_limit, model,
691 const std::optional<AffineExpression>& makespan,
Model* model) {
695 helper, model, &result.
vars,
697 if (makespan.has_value() && !makespan.value().IsConstant()) {
698 result.
vars.push_back(makespan.value().var);
707 const auto& lp_values = manager->LpValues();
708 std::vector<std::unique_ptr<EnergyEvent>> events;
711 if (helper->
SizeMin(
i) == 0)
continue;
713 auto e = std::make_unique<EnergyEvent>(
i, helper);
714 e->demand = IntegerValue(1);
715 e->demand_min = IntegerValue(1);
716 e->energy_min = e->size_min;
721 if (!e->FillEnergyLp(helper->
Sizes()[
i], lp_values, model))
continue;
722 events.push_back(std::move(e));
726 std::vector<absl::Span<std::unique_ptr<EnergyEvent>>> disjoint_events =
728 for (
const absl::Span<std::unique_ptr<EnergyEvent>> cluster :
730 if (makespan.has_value()) {
732 "NoOverlapEnergyM", lp_values, cluster,
733 IntegerValue(1), makespan.value(), time_limit, model,
738 time_limit, model, top_n_cuts);
758 struct TimeTableEvent {
762 double demand_lp = 0.0;
763 bool is_positive =
false;
764 bool use_decomposed_energy_min =
false;
765 bool is_optional =
false;
774 std::vector<std::unique_ptr<TimeTableEvent>> events;
775 const auto& lp_values = manager->LpValues();
776 if (lp_values.empty())
return true;
777 const double capacity_lp = capacity.
LpValue(lp_values);
784 const IntegerValue start_max = helper->
StartMax(
i);
785 const IntegerValue end_min = helper->
EndMin(
i);
787 if (start_max >= end_min)
continue;
789 auto e1 = std::make_unique<TimeTableEvent>();
790 e1->interval_index =
i;
791 e1->time = start_max;
798 e1->demand_lp = e1->demand.LpValue(lp_values);
799 e1->is_positive =
true;
800 e1->use_decomposed_energy_min =
804 auto e2 = std::make_unique<TimeTableEvent>(*e1);
806 e2->is_positive =
false;
808 events.push_back(std::move(e1));
809 events.push_back(std::move(e2));
815 absl::c_stable_sort(events, [](
const std::unique_ptr<TimeTableEvent>&
i,
816 const std::unique_ptr<TimeTableEvent>& j) {
817 return std::tie(
i->time,
i->is_positive) <
818 std::tie(j->time, j->is_positive);
821 double sum_of_demand_lp = 0.0;
822 bool positive_event_added_since_last_check =
false;
823 for (
int i = 0;
i < events.size(); ++
i) {
824 const TimeTableEvent* e = events[
i].get();
825 if (e->is_positive) {
826 positive_event_added_since_last_check =
true;
827 sum_of_demand_lp += e->demand_lp;
831 if (positive_event_added_since_last_check) {
834 positive_event_added_since_last_check =
false;
836 if (sum_of_demand_lp >= capacity_lp + kMinCutViolation) {
838 bool use_decomposed_energy_min =
false;
839 bool use_optional =
false;
841 cut.
AddTerm(capacity, IntegerValue(-1));
844 DCHECK(!events[
i]->is_positive);
845 const IntegerValue time_point = events[
i - 1]->time;
847 for (
int j = 0; j <
i; ++j) {
848 const TimeTableEvent* cut_event = events[j].get();
849 const int t = cut_event->interval_index;
850 DCHECK_LE(helper->
StartMax(t), time_point);
851 if (!cut_event->is_positive || helper->
EndMin(t) <= time_point) {
856 use_decomposed_energy_min |= cut_event->use_decomposed_energy_min;
857 use_optional |= cut_event->is_optional;
860 std::string cut_name =
"CumulativeTimeTable";
861 if (use_optional) cut_name +=
"_optional";
862 if (use_decomposed_energy_min) cut_name +=
"_energy";
863 top_n_cuts.
AddCut(cut.
Build(), cut_name, lp_values);
869 sum_of_demand_lp -= e->demand_lp;
885 start(helper->Starts()[t]),
888 end(helper->Ends()[t]),
903 absl::string_view cut_name,
bool ignore_zero_size_intervals,
905 absl::Span<std::unique_ptr<CachedIntervalData>> events,
906 IntegerValue capacity_max,
Model* model,
TopNCuts& top_n_cuts) {
907 const int num_events = events.size();
908 if (num_events <= 1)
return;
910 absl::c_stable_sort(events,
911 [](
const std::unique_ptr<CachedIntervalData>& e1,
912 const std::unique_ptr<CachedIntervalData>& e2) {
913 return std::tie(e1->start_min, e1->end_max) <
914 std::tie(e2->start_min, e2->end_max);
926 const auto add_balas_disjunctive_cut =
927 [&](absl::string_view local_cut_name, IntegerValue start_min_1,
929 IntegerValue start_min_2, IntegerValue duration_min_2,
932 if (start_min_2 >= start_min_1 + duration_min_1 ||
933 start_min_1 >= start_min_2 + duration_min_2) {
936 const IntegerValue coeff_1 = duration_min_1 + start_min_1 - start_min_2;
937 const IntegerValue coeff_2 = duration_min_2 + start_min_2 - start_min_1;
938 const IntegerValue rhs = duration_min_1 * duration_min_2 +
939 duration_min_1 * start_min_2 +
940 duration_min_2 * start_min_1;
942 if (
ToDouble(coeff_1) * start_1.LpValue(lp_values) +
943 ToDouble(coeff_2) * start_2.LpValue(lp_values) <=
948 top_n_cuts.
AddCut(cut.
Build(), local_cut_name, lp_values);
952 for (
int i = 0;
i + 1 < num_events; ++
i) {
954 for (
int j =
i + 1; j < num_events; ++j) {
961 if (ignore_zero_size_intervals &&
969 if (interval_1_can_precede_2 && !interval_2_can_precede_1 &&
977 absl::StrCat(cut_name,
"DetectedPrecedence"),
979 }
else if (interval_2_can_precede_1 && !interval_1_can_precede_2 &&
987 absl::StrCat(cut_name,
"DetectedPrecedence"),
990 add_balas_disjunctive_cut(absl::StrCat(cut_name,
"DisjunctionOnStart"),
993 add_balas_disjunctive_cut(absl::StrCat(cut_name,
"DisjunctionOnEnd"),
1010 helper, model, &result.
vars,
1015 result.
generate_cuts = [integer_trail, helper, demands_helper, capacity,
1019 std::vector<std::unique_ptr<CachedIntervalData>> events;
1020 for (
int t = 0; t < helper->
NumTasks(); ++t) {
1022 auto event = std::make_unique<CachedIntervalData>(t, helper);
1023 event->demand_min = demands_helper->
DemandMin(t);
1024 events.push_back(std::move(event));
1027 const IntegerValue capacity_max = integer_trail->
UpperBound(capacity);
1030 std::vector<absl::Span<std::unique_ptr<CachedIntervalData>>>
1032 for (absl::Span<std::unique_ptr<CachedIntervalData>> cluster :
1036 manager->LpValues(), cluster, capacity_max, model, top_n_cuts);
1049 helper, model, &result.
vars,
1056 std::vector<std::unique_ptr<CachedIntervalData>> events;
1057 for (
int t = 0; t < helper->
NumTasks(); ++t) {
1059 auto event = std::make_unique<CachedIntervalData>(t, helper);
1060 event->demand_min = IntegerValue(1);
1061 events.push_back(std::move(event));
1065 std::vector<absl::Span<std::unique_ptr<CachedIntervalData>>>
1067 for (absl::Span<std::unique_ptr<CachedIntervalData>> cluster :
1071 manager->LpValues(), cluster, IntegerValue(1), model, top_n_cuts);
1089 start(helper->Starts()[t]),
1090 end(helper->Ends()[t]) {
1091 if (demands_helper ==
nullptr) {
1112 return absl::StrCat(
1113 "CompletionTimeEvent(task_index = ",
task_index,
1119 ", lifted = ",
lifted,
", decomposed_energy = [",
1128 absl::Span<std::unique_ptr<CompletionTimeEvent>> events,
Model* model) {
1129 max_task_index_ = 0;
1130 if (events.empty())
return;
1134 for (
const auto& event : events) {
1135 max_task_index_ = std::max(max_task_index_, event->task_index);
1137 BuildPredecessors(events, model);
1138 VLOG(2) <<
"num_tasks:" << max_task_index_ + 1
1139 <<
" num_precedences:" << predecessors_.num_entries()
1140 <<
" predecessors size:" << predecessors_.size();
1143void CtExhaustiveHelper::BuildPredecessors(
1144 absl::Span<std::unique_ptr<CompletionTimeEvent>> events,
Model* model) {
1145 predecessors_.
clear();
1146 if (events.size() > 100)
return;
1151 std::vector<const CompletionTimeEvent*> sorted_events;
1152 sorted_events.reserve(events.size());
1153 for (
const auto& event : events) {
1154 sorted_events.push_back(event.get());
1156 std::sort(sorted_events.begin(), sorted_events.end(),
1157 [](
const CompletionTimeEvent* a,
const CompletionTimeEvent*
b) {
1158 return a->task_index < b->task_index;
1161 predecessors_.
reserve(max_task_index_ + 1);
1162 for (
const auto* e1 : sorted_events) {
1163 for (
const auto* e2 : sorted_events) {
1164 if (e2->task_index == e1->task_index)
continue;
1168 while (predecessors_.
size() <= e1->task_index) predecessors_.
Add({});
1176 absl::Span<std::unique_ptr<CompletionTimeEvent>> events,
1177 absl::Span<const int> permutation) {
1178 if (predecessors_.num_entries() == 0)
return true;
1179 visited_.assign(max_task_index_ + 1,
false);
1180 for (
int i = permutation.size() - 1;
i >= 0; --
i) {
1182 if (event->task_index >= predecessors_.size())
continue;
1183 for (
const int predecessor : predecessors_[event->task_index]) {
1184 if (visited_[predecessor])
return false;
1186 visited_[
event->task_index] =
true;
1193bool ComputeWeightedSumOfEndMinsOfOnePermutationForNoOverlap(
1194 absl::Span<CompletionTimeEvent*> events, absl::Span<const int> permutation,
1195 IntegerValue& sum_of_ends, IntegerValue& sum_of_weighted_ends) {
1198 sum_of_weighted_ends = 0;
1202 for (
const int index : permutation) {
1204 const IntegerValue task_start_min =
1205 std::max(event->start_min, end_min_of_previous_task);
1206 if (event->start_max < task_start_min)
return false;
1208 end_min_of_previous_task = task_start_min +
event->size_min;
1209 sum_of_ends += end_min_of_previous_task;
1210 sum_of_weighted_ends +=
event->energy_min * end_min_of_previous_task;
1223bool ComputeWeightedSumOfEndMinsOfOnePermutation(
1224 absl::Span<CompletionTimeEvent*> events, absl::Span<const int> permutation,
1225 IntegerValue capacity_max, CtExhaustiveHelper& helper,
1226 IntegerValue& sum_of_ends, IntegerValue& sum_of_weighted_ends,
1227 bool& cut_use_precedences) {
1228 DCHECK_EQ(permutation.size(), events.size());
1230 if (capacity_max == 1) {
1231 return ComputeWeightedSumOfEndMinsOfOnePermutationForNoOverlap(
1232 events, permutation, sum_of_ends, sum_of_weighted_ends);
1237 sum_of_weighted_ends = 0;
1242 IntegerValue demand_min_of_previous_task = 0;
1245 for (
const int index : permutation) {
1247 const IntegerValue threshold = std::max(
1249 (event->demand_min + demand_min_of_previous_task > capacity_max)
1250 ? end_min_of_previous_task
1251 : start_min_of_previous_task);
1252 if (event->start_max < threshold) {
1256 start_min_of_previous_task = threshold;
1257 end_min_of_previous_task = threshold +
event->size_min;
1258 demand_min_of_previous_task =
event->demand_min;
1263 helper.profile_.clear();
1268 helper.assigned_ends_.assign(helper.max_task_index() + 1,
kMinIntegerValue);
1270 for (
const int index : permutation) {
1272 const IntegerValue start_min =
1273 std::max(event->start_min, start_of_previous_task);
1277 auto profile_it = helper.profile_.begin();
1278 while ((profile_it + 1)->first <= start_min ||
1279 profile_it->second < event->demand_min) {
1283 IntegerValue actual_start = std::max(start_min, profile_it->first);
1284 const IntegerValue initial_start_min = actual_start;
1289 if (event->task_index < helper.predecessors().size()) {
1290 for (
const int predecessor : helper.predecessors()[event->task_index]) {
1293 std::max(actual_start, helper.assigned_ends_[predecessor]);
1297 if (actual_start > initial_start_min) {
1298 cut_use_precedences =
true;
1300 while ((profile_it + 1)->first <= actual_start) ++profile_it;
1301 VLOG(3) <<
"push from " << initial_start_min <<
" to " << actual_start;
1305 if (actual_start > event->start_max) {
1309 const IntegerValue actual_end = actual_start +
event->size_min;
1312 helper.assigned_ends_[
event->task_index] = actual_end;
1313 sum_of_ends += actual_end;
1314 sum_of_weighted_ends +=
event->energy_min * actual_end;
1315 start_of_previous_task = actual_start;
1318 if (event->task_index == events[permutation.back()]->task_index)
break;
1321 helper.new_profile_.clear();
1322 const IntegerValue demand_min =
event->demand_min;
1325 helper.new_profile_.push_back(
1326 {actual_start, profile_it->second - demand_min});
1330 while (profile_it->first < actual_end) {
1331 helper.new_profile_.push_back(
1332 {profile_it->first, profile_it->second - demand_min});
1337 if (profile_it->first > actual_end) {
1338 helper.new_profile_.push_back({actual_end, (profile_it - 1)->second});
1342 helper.new_profile_.insert(helper.new_profile_.end(), profile_it,
1343 helper.profile_.end());
1345 helper.profile_.swap(helper.new_profile_);
1350const int kCtExhaustiveTargetSize = 6;
1354const int kExplorationLimit = 873;
1359 absl::Span<CompletionTimeEvent*> events, IntegerValue capacity_max,
1360 double unweighted_threshold,
double weighted_threshold,
1362 double& min_sum_of_weighted_ends,
bool& cut_use_precedences,
1363 int& exploration_credit) {
1365 min_sum_of_ends = std::numeric_limits<double>::max();
1366 min_sum_of_weighted_ends = std::numeric_limits<double>::max();
1368 for (
int i = 0;
i < events.size(); ++
i) {
1373 for (
int i = 0;
i < events.size(); ++
i) {
1374 const int task_i = events[
i]->task_index;
1375 if (task_i >= predecessors.size())
continue;
1376 for (
const int task_j : predecessors[task_i]) {
1384 bool is_dag =
false;
1385 int num_valid_permutations = 0;
1388 if (--exploration_credit < 0)
break;
1390 IntegerValue sum_of_ends = 0;
1391 IntegerValue sum_of_weighted_ends = 0;
1393 if (ComputeWeightedSumOfEndMinsOfOnePermutation(
1394 events, permutation, capacity_max, helper, sum_of_ends,
1395 sum_of_weighted_ends, cut_use_precedences)) {
1396 min_sum_of_ends = std::min(
ToDouble(sum_of_ends), min_sum_of_ends);
1397 min_sum_of_weighted_ends =
1398 std::min(
ToDouble(sum_of_weighted_ends), min_sum_of_weighted_ends);
1399 ++num_valid_permutations;
1401 if (min_sum_of_ends <= unweighted_threshold &&
1402 min_sum_of_weighted_ends <= weighted_threshold) {
1412 : num_valid_permutations > 0
1415 VLOG(2) <<
"DP: size:" << events.size()
1416 <<
", num_valid_permutations:" << num_valid_permutations
1417 <<
", min_sum_of_end_mins:" << min_sum_of_ends
1418 <<
", min_sum_of_weighted_end_mins:" << min_sum_of_weighted_ends
1419 <<
", unweighted_threshold:" << unweighted_threshold
1420 <<
", weighted_threshold:" << weighted_threshold
1421 <<
", exploration_credit:" << exploration_credit
1422 <<
", status:" << status;
1430 absl::string_view cut_name,
1432 absl::Span<std::unique_ptr<CompletionTimeEvent>> events,
1435 std::vector<CompletionTimeEvent>& residual_event_storage) {
1437 std::sort(events.begin(), events.end(),
1438 [](
const std::unique_ptr<CompletionTimeEvent>& e1,
1439 const std::unique_ptr<CompletionTimeEvent>& e2) {
1440 return std::tie(e1->start_min, e1->energy_min, e1->lp_end,
1442 std::tie(e2->start_min, e2->energy_min, e2->lp_end,
1445 for (
int start = 0; start + 1 < events.size(); ++start) {
1447 if (start > 0 && events[start]->start_min == events[start - 1]->start_min) {
1451 bool cut_use_precedences =
false;
1452 const IntegerValue sequence_start_min = events[start]->start_min;
1456 residual_event_storage.clear();
1458 for (
int before = 0; before < start; ++before) {
1459 if (events[before]->start_min + events[before]->size_min >
1460 sequence_start_min) {
1461 residual_event_storage.push_back(*events[before]);
1462 residual_event_storage.back().lifted =
true;
1465 for (
int i = 0;
i < residual_event_storage.size(); ++
i) {
1470 for (
int i = start;
i < events.size(); ++
i) {
1476 return std::tie(e1->lp_end, e1->task_index) <
1477 std::tie(e2->lp_end, e2->task_index);
1480 double sum_of_ends_lp = 0.0;
1481 double sum_of_weighted_ends_lp = 0.0;
1482 IntegerValue sum_of_demands = 0;
1483 double sum_of_square_energies = 0;
1484 double min_sum_of_ends = std::numeric_limits<double>::max();
1485 double min_sum_of_weighted_ends = std::numeric_limits<double>::max();
1486 int exploration_limit = kExplorationLimit;
1489 for (
int i = 0;
i < kMaxSize; ++
i) {
1491 const double energy =
ToDouble(event->energy_min);
1492 sum_of_ends_lp +=
event->lp_end;
1493 sum_of_weighted_ends_lp +=
event->lp_end * energy;
1494 sum_of_demands +=
event->demand_min;
1495 sum_of_square_energies += energy * energy;
1501 if (
i <= 1 || sum_of_demands <= capacity_max)
continue;
1503 absl::Span<CompletionTimeEvent*> tasks_to_explore =
1507 tasks_to_explore, capacity_max,
1508 sum_of_ends_lp + kMinCutViolation,
1509 sum_of_weighted_ends_lp +
1511 helper, min_sum_of_ends, min_sum_of_weighted_ends,
1512 cut_use_precedences, exploration_limit);
1520 const double unweigthed_violation =
1521 (min_sum_of_ends - sum_of_ends_lp) / std::sqrt(
ToDouble(
i + 1));
1522 const double weighted_violation =
1523 (min_sum_of_weighted_ends - sum_of_weighted_ends_lp) /
1524 std::sqrt(sum_of_square_energies);
1527 if (unweigthed_violation > weighted_violation &&
1528 unweigthed_violation > kMinCutViolation) {
1530 bool is_lifted =
false;
1531 for (
int j = 0; j <=
i; ++j) {
1533 is_lifted |=
event->lifted;
1534 cut.
AddTerm(event->end, IntegerValue(1));
1536 std::string full_name(cut_name);
1537 if (cut_use_precedences) full_name.append(
"_prec");
1538 if (is_lifted) full_name.append(
"_lifted");
1539 top_n_cuts.
AddCut(cut.
Build(), full_name, lp_values);
1543 if (weighted_violation >= unweigthed_violation &&
1544 weighted_violation > kMinCutViolation) {
1547 bool is_lifted =
false;
1548 for (
int j = 0; j <=
i; ++j) {
1550 is_lifted |=
event->lifted;
1551 cut.
AddTerm(event->end, event->energy_min);
1553 std::string full_name(cut_name);
1554 if (is_lifted) full_name.append(
"_lifted");
1555 if (cut_use_precedences) full_name.append(
"_prec");
1556 full_name.append(
"_weighted");
1557 top_n_cuts.
AddCut(cut.
Build(), full_name, lp_values);
1567void CopyAndTrimEventAfterAndAppendIfPositiveEnergy(
1568 const CompletionTimeEvent* old_event, IntegerValue time,
1569 std::vector<CompletionTimeEvent>& events) {
1570 CHECK_GT(time, old_event->start_min);
1571 CHECK_GT(old_event->start_min + old_event->size_min, time);
1572 CompletionTimeEvent event(*old_event);
1573 event.lifted =
true;
1576 const IntegerValue shift = time -
event.start_min;
1577 CHECK_GT(shift, IntegerValue(0));
1578 event.size_min -= shift;
1579 event.energy_min =
event.size_min *
event.demand_min;
1580 if (!event.decomposed_energy.empty()) {
1583 for (
auto& [literal, fixed_size, fixed_demand] : event.decomposed_energy) {
1584 CHECK_GT(fixed_size, shift);
1585 fixed_size -= shift;
1586 propagated_energy_min =
1587 std::min(propagated_energy_min, fixed_demand * fixed_size);
1590 DCHECK_GT(propagated_energy_min, 0);
1591 if (propagated_energy_min > event.energy_min) {
1592 event.use_decomposed_energy_min =
true;
1593 event.energy_min = propagated_energy_min;
1595 event.use_decomposed_energy_min =
false;
1598 event.start_min = time;
1599 if (event.energy_min > 0) {
1600 events.push_back(std::move(event));
1606void AddEventDemandsToCapacitySubsetSum(
1608 IntegerValue capacity_max, std::vector<int64_t>& tmp_possible_demands,
1610 if (dp.CurrentMax() != capacity_max) {
1611 if (event->demand_is_fixed) {
1612 dp.Add(event->demand_min.value());
1613 }
else if (!event->decomposed_energy.empty()) {
1614 tmp_possible_demands.clear();
1615 for (
const auto& [literal, size, demand] : event->decomposed_energy) {
1616 if (assignment.LiteralIsFalse(literal))
continue;
1617 if (assignment.LiteralIsTrue(literal)) {
1618 tmp_possible_demands.assign({demand.value()});
1621 tmp_possible_demands.push_back(demand.value());
1623 dp.AddChoices(tmp_possible_demands);
1627 dp.Add(capacity_max.value());
1679 absl::string_view cut_name,
1681 absl::Span<std::unique_ptr<CompletionTimeEvent>> events,
1682 IntegerValue capacity_max,
Model* model,
TopNCuts& top_n_cuts,
1683 std::vector<CompletionTimeEvent>& residual_event_storage) {
1686 std::vector<int64_t> tmp_possible_demands;
1689 absl::c_stable_sort(
1690 events, [](
const std::unique_ptr<CompletionTimeEvent>& e1,
1691 const std::unique_ptr<CompletionTimeEvent>& e2) {
1692 return std::tie(e1->start_min, e1->demand_min, e1->lp_end) <
1693 std::tie(e2->start_min, e2->demand_min, e2->lp_end);
1698 std::vector<CompletionTimeEvent*> residual_tasks;
1699 for (
int start = 0; start + 1 < events.size(); ++start) {
1701 if (start > 0 && events[start]->start_min == events[start - 1]->start_min) {
1705 const IntegerValue sequence_start_min = events[start]->start_min;
1706 residual_tasks.clear();
1707 for (
int i = start;
i < events.size(); ++
i) {
1708 residual_tasks.push_back(events[
i].get());
1716 residual_event_storage.clear();
1717 for (
int before = 0; before < start; ++before) {
1718 if (events[before]->start_min + events[before]->size_min >
1719 sequence_start_min) {
1720 CopyAndTrimEventAfterAndAppendIfPositiveEnergy(
1721 events[before].get(), sequence_start_min, residual_event_storage);
1724 for (
int i = 0;
i < residual_event_storage.size(); ++
i) {
1725 residual_tasks.push_back(&residual_event_storage[
i]);
1730 if (residual_tasks.size() <= kCtExhaustiveTargetSize)
continue;
1734 double best_efficacy = 0.01;
1735 IntegerValue best_min_contrib = 0;
1736 bool best_uses_subset_sum =
false;
1739 IntegerValue sum_event_contributions = 0;
1741 IntegerValue sum_energy = 0;
1743 IntegerValue sum_square_energy = 0;
1745 double lp_contrib = 0.0;
1748 dp.Reset(capacity_max.value());
1758 for (
int i = 0;
i < residual_tasks.size(); ++
i) {
1760 DCHECK_GE(event->start_min, sequence_start_min);
1763 if (!
AddTo(event->energy_min, &sum_energy))
break;
1772 &sum_event_contributions)) {
1775 if (!
AddSquareTo(event->energy_min, &sum_square_energy))
break;
1777 lp_contrib +=
event->lp_end *
ToDouble(event->energy_min);
1778 current_start_min = std::min(current_start_min, event->start_min);
1781 AddEventDemandsToCapacitySubsetSum(event, assignment, capacity_max,
1782 tmp_possible_demands, dp);
1785 if (
i < kCtExhaustiveTargetSize)
continue;
1787 const IntegerValue reachable_capacity = dp.CurrentMax();
1790 const IntegerValue large_rectangle_contrib =
1793 const IntegerValue mean_large_rectangle_contrib =
1794 CeilRatio(large_rectangle_contrib, reachable_capacity);
1796 IntegerValue min_contrib =
1797 CapAddI(sum_event_contributions, mean_large_rectangle_contrib);
1799 min_contrib =
CeilRatio(min_contrib, 2);
1802 if (!
AddProductTo(sum_energy, current_start_min, &min_contrib))
break;
1806 const double efficacy = (
ToDouble(min_contrib) - lp_contrib) /
1807 std::sqrt(
ToDouble(sum_square_energy));
1815 if (efficacy > best_efficacy) {
1816 best_efficacy = efficacy;
1818 best_min_contrib = min_contrib;
1819 best_uses_subset_sum = reachable_capacity < capacity_max;
1825 if (best_end != -1) {
1827 bool is_lifted =
false;
1828 bool add_energy_to_name =
false;
1829 for (
int i = 0;
i <= best_end; ++
i) {
1831 is_lifted |=
event->lifted;
1832 add_energy_to_name |=
event->use_decomposed_energy_min;
1833 cut.
AddTerm(event->end, event->energy_min);
1835 std::string full_name(cut_name);
1836 if (add_energy_to_name) full_name.append(
"_energy");
1837 if (is_lifted) full_name.append(
"_lifted");
1838 if (best_uses_subset_sum) full_name.append(
"_subsetsum");
1839 top_n_cuts.
AddCut(cut.
Build(), full_name, lp_values);
1855 auto generate_cuts = [model, manager,
1856 helper](
bool time_is_forward) ->
bool {
1860 std::vector<std::unique_ptr<CompletionTimeEvent>> events;
1861 const auto& lp_values = manager->LpValues();
1862 for (
int index = 0; index < helper->
NumTasks(); ++index) {
1863 if (!helper->
IsPresent(index))
continue;
1864 const IntegerValue size_min = helper->
SizeMin(index);
1867 std::make_unique<CompletionTimeEvent>(index, helper,
nullptr);
1868 event->lp_end =
event->end.LpValue(lp_values);
1869 events.push_back(std::move(event));
1874 helper.
Init(absl::MakeSpan(events), model);
1877 std::vector<CompletionTimeEvent> residual_event_storage;
1878 std::vector<absl::Span<std::unique_ptr<CompletionTimeEvent>>>
1880 for (
const absl::Span<std::unique_ptr<CompletionTimeEvent>>& cluster :
1883 "NoOverlapCompletionTimeExhaustive", lp_values, cluster,
1884 IntegerValue(1), helper, model, top_n_cuts,
1885 residual_event_storage)) {
1890 "NoOverlapCompletionTimeQueyrane", lp_values, cluster,
1891 IntegerValue(1), model, top_n_cuts,
1892 residual_event_storage);
1897 if (!generate_cuts(
true))
return false;
1898 if (!generate_cuts(
false))
return false;
1917 result.
generate_cuts = [integer_trail, helper, demands_helper, capacity,
1920 auto generate_cuts = [integer_trail, sat_solver, model, manager, helper,
1922 capacity](
bool time_is_forward) ->
bool {
1923 DCHECK_EQ(sat_solver->CurrentDecisionLevel(), 0);
1924 if (!helper->SynchronizeAndSetTimeDirection(time_is_forward)) {
1927 if (!demands_helper->CacheAllEnergyValues())
return true;
1929 std::vector<std::unique_ptr<CompletionTimeEvent>> events;
1930 const auto& lp_values = manager->LpValues();
1932 for (
int index = 0; index < helper->NumTasks(); ++index) {
1933 if (!helper->IsPresent(index))
continue;
1934 if (!DecomposedEnergyIsPropagated(assignment, index, helper,
1938 if (helper->SizeMin(index) > 0 &&
1939 demands_helper->DemandMin(index) > 0) {
1940 auto event = std::make_unique<CompletionTimeEvent>(index, helper,
1942 event->lp_end =
event->end.LpValue(lp_values);
1943 events.push_back(std::move(event));
1948 helper.
Init(absl::MakeSpan(events), model);
1950 const IntegerValue capacity_max = integer_trail->
UpperBound(capacity);
1952 std::vector<absl::Span<std::unique_ptr<CompletionTimeEvent>>>
1954 std::vector<CompletionTimeEvent> residual_event_storage;
1955 for (absl::Span<std::unique_ptr<CompletionTimeEvent>> cluster :
1958 "CumulativeCompletionTimeExhaustive", lp_values, cluster,
1959 capacity_max, helper, model, top_n_cuts,
1960 residual_event_storage)) {
1965 "CumulativeCompletionTimeQueyrane", lp_values, cluster,
1966 capacity_max, model, top_n_cuts, residual_event_storage);
1972 if (!generate_cuts(
true))
return false;
1973 if (!generate_cuts(
false))
return false;
void AppendToLastVector(const V &value)
int Add(absl::Span< const V > values)
const CompactVectorVector< int > & predecessors() const
int max_task_index() const
void Init(absl::Span< std::unique_ptr< CompletionTimeEvent > > events, Model *model)
bool PermutationIsCompatibleWithPrecedences(absl::Span< std::unique_ptr< CompletionTimeEvent > > events, absl::Span< const int > permutation)
std::vector< CompletionTimeEvent * > residual_events_
DagTopologicalSortIterator valid_permutation_iterator_
std::vector< int > task_to_index_
void AddArc(int from, int to)
IntegerValue LowerBound(IntegerVariable i) const
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue FixedValue(IntegerVariable i) const
bool IsFixed(IntegerVariable i) const
void AddQuadraticLowerBound(AffineExpression left, AffineExpression right, IntegerTrail *integer_trail, bool *is_quadratic=nullptr)
LinearExpression BuildExpression()
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff=IntegerValue(1))
void AddTerm(IntegerVariable var, IntegerValue coeff)
void AddLinearExpression(const LinearExpression &expr)
void AddConstant(IntegerValue value)
LinearConstraint BuildConstraint(IntegerValue lb, IntegerValue ub)
ABSL_MUST_USE_RESULT bool AddDecomposedProduct(absl::Span< const LiteralValueValue > product)
LiteralIndex Index() const
void AddChoices(absl::Span< const int64_t > choices)
int64_t CurrentMax() const
void Reset(int64_t bound)
RelationStatus GetLevelZeroStatus(LinearExpression2 expr, IntegerValue lb, IntegerValue ub) const
IntegerValue StartMax(int t) const
absl::Span< const AffineExpression > Sizes() const
bool IsPresent(int t) const
Literal PresenceLiteral(int index) const
IntegerValue SizeMin(int t) const
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
bool IsAbsent(int t) const
IntegerValue EndMin(int t) const
bool DemandIsFixed(int t) const
bool CacheAllEnergyValues()
bool EnergyIsQuadratic(int t) const
const std::vector< std::vector< LiteralValueValue > > & DecomposedEnergies() const
IntegerValue DemandMax(int t) const
const std::vector< AffineExpression > & Demands() const
IntegerValue DemandMin(int t) const
ABSL_MUST_USE_RESULT bool AddLinearizedDemand(int t, LinearConstraintBuilder *builder) const
IntegerValue EnergyMin(int t) const
void TransferToManager(LinearConstraintManager *manager)
void AddCut(LinearConstraint ct, absl::string_view name, const util_intops::StrongVector< IntegerVariable, double > &lp_solution)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
bool AddSquareTo(IntegerValue a, IntegerValue *result)
bool AddProductTo(IntegerValue a, IntegerValue b, IntegerValue *result)
CutGenerator CreateCumulativeCompletionTimeCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, Model *model)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
CompletionTimeExplorationStatus ComputeMinSumOfWeightedEndMins(absl::Span< CompletionTimeEvent * > events, IntegerValue capacity_max, double unweighted_threshold, double weighted_threshold, CtExhaustiveHelper &helper, double &min_sum_of_ends, double &min_sum_of_weighted_ends, bool &cut_use_precedences, int &exploration_credit)
bool AddTo(IntegerValue a, IntegerValue *result)
void GenerateCutsBetweenPairOfNonOverlappingTasks(absl::string_view cut_name, bool ignore_zero_size_intervals, const util_intops::StrongVector< IntegerVariable, double > &lp_values, absl::Span< std::unique_ptr< CachedIntervalData > > events, IntegerValue capacity_max, Model *model, TopNCuts &top_n_cuts)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
const LiteralIndex kNoLiteralIndex(-1)
ABSL_MUST_USE_RESULT bool GenerateShortCompletionTimeCutsWithExactBound(absl::string_view cut_name, const util_intops::StrongVector< IntegerVariable, double > &lp_values, absl::Span< std::unique_ptr< CompletionTimeEvent > > events, IntegerValue capacity_max, CtExhaustiveHelper &helper, Model *model, TopNCuts &top_n_cuts, std::vector< CompletionTimeEvent > &residual_event_storage)
CutGenerator CreateCumulativePrecedenceCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, Model *model)
void GenerateCompletionTimeCutsWithEnergy(absl::string_view cut_name, const util_intops::StrongVector< IntegerVariable, double > &lp_values, absl::Span< std::unique_ptr< CompletionTimeEvent > > events, IntegerValue capacity_max, Model *model, TopNCuts &top_n_cuts, std::vector< CompletionTimeEvent > &residual_event_storage)
std::vector< absl::Span< std::unique_ptr< E > > > SplitEventsInIndendentSets(std::vector< std::unique_ptr< E > > &events)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
CutGenerator CreateCumulativeEnergyCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, const std::optional< AffineExpression > &makespan, Model *model)
void AddIntegerVariableFromIntervals(const SchedulingConstraintHelper *helper, Model *model, std::vector< IntegerVariable > *vars, int mask)
CutGenerator CreateCumulativeTimeTableCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, Model *model)
void AppendVariablesFromCapacityAndDemands(const AffineExpression &capacity, SchedulingDemandHelper *demands_helper, Model *model, std::vector< IntegerVariable > *vars)
CutGenerator CreateNoOverlapCompletionTimeCutGenerator(SchedulingConstraintHelper *helper, Model *model)
IntegerValue CapAddI(IntegerValue a, IntegerValue b)
CutGenerator CreateNoOverlapEnergyCutGenerator(SchedulingConstraintHelper *helper, const std::optional< AffineExpression > &makespan, Model *model)
std::pair< LinearExpression2, IntegerValue > EncodeDifferenceLowerThan(AffineExpression a, AffineExpression b, IntegerValue ub)
CutGenerator CreateNoOverlapPrecedenceCutGenerator(SchedulingConstraintHelper *helper, Model *model)
CompletionTimeExplorationStatus
void GenerateCumulativeEnergeticCuts(absl::string_view cut_name, const util_intops::StrongVector< IntegerVariable, double > &lp_values, absl::Span< std::unique_ptr< EnergyEvent > > events, const AffineExpression &capacity, TimeLimit *time_limit, Model *model, TopNCuts &top_n_cuts)
bool AtMinOrMaxInt64I(IntegerValue t)
void GenerateCumulativeEnergeticCutsWithMakespanAndFixedCapacity(absl::string_view cut_name, const util_intops::StrongVector< IntegerVariable, double > &lp_values, absl::Span< std::unique_ptr< EnergyEvent > > events, IntegerValue capacity, AffineExpression makespan, TimeLimit *time_limit, Model *model, TopNCuts &top_n_cuts)
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
double ToDouble(IntegerValue value)
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)
AffineExpression Negated() const
double LpValue(const util_intops::StrongVector< IntegerVariable, double > &lp_values) const
CachedIntervalData(int t, SchedulingConstraintHelper *helper)
std::vector< LiteralValueValue > decomposed_energy
CompletionTimeEvent(int t, SchedulingConstraintHelper *x_helper, SchedulingDemandHelper *demands_helper)
std::string DebugString() const
bool use_decomposed_energy_min
absl::AnyInvocable< bool(LinearConstraintManager *manager)> generate_cuts
std::vector< IntegerVariable > vars
bool only_run_at_level_zero
ABSL_MUST_USE_RESULT bool FillEnergyLp(AffineExpression size, const util_intops::StrongVector< IntegerVariable, double > &lp_values, Model *model)
LiteralIndex presence_literal_index
EnergyEvent(int t, SchedulingConstraintHelper *x_helper)
bool use_decomposed_energy
double linearized_energy_lp_value
IntegerValue GetMinOverlap(IntegerValue start, IntegerValue end) const
std::vector< LiteralValueValue > decomposed_energy
LinearExpression linearized_energy
std::string DebugString() const
double NormalizedViolation(const util_intops::StrongVector< IntegerVariable, double > &lp_values) const