140 absl::Span<
const std::vector<LiteralValueValue>> energies,
141 absl::Span<const int> rectangles, absl::string_view cut_name,
Model* model,
145 std::vector<DiffnEnergyEvent> events;
146 const auto& lp_values = manager->
LpValues();
147 for (
const int rect : rectangles) {
148 if (y_helper->
SizeMax(rect) == 0 || x_helper->
SizeMax(rect) == 0) {
172 if (events.empty())
return;
175 double average_d = 0.0;
176 for (
const auto& e : events) {
177 average_d +=
ToDouble(e.y_min + e.y_max);
179 const double average = average_d / 2.0 /
static_cast<double>(events.size());
180 for (
auto& e : events) {
181 e.y_spread = std::abs(
ToDouble(e.y_max) - average) +
182 std::abs(average -
ToDouble(e.y_min));
187 std::sort(events.begin(), events.end(),
189 return std::tie(a.x_start_min, a.y_spread, a.x_end_max) <
190 std::tie(b.x_start_min, b.y_spread, b.x_end_max);
194 double sum_of_all_energies = 0.0;
195 for (
const auto& e : events) {
196 sum_of_all_energies += e.linearized_energy_lp_value;
200 for (
int i1 = 0; i1 + 1 < events.size(); ++i1) {
203 int max_violation_end_index = -1;
204 double max_relative_violation = 1.0 + kMinCutViolation;
205 IntegerValue max_violation_window_start(0);
206 IntegerValue max_violation_window_end(0);
207 IntegerValue max_violation_y_min(0);
208 IntegerValue max_violation_y_max(0);
209 IntegerValue max_violation_area(0);
210 bool max_violation_use_precise_area =
false;
213 double energy_lp = 0.0;
218 capacity_profile.
Clear();
222 std::vector<DiffnEnergyEvent> residual_events(events.begin() + i1,
224 std::sort(residual_events.begin(), residual_events.end(),
226 return std::tie(a.x_end_max, a.y_spread) <
227 std::tie(b.x_end_max, b.y_spread);
231 for (
int i2 = 0; i2 < residual_events.size(); ++i2) {
235 window_max = std::max(window_max, e.
x_end_max);
236 y_min = std::min(y_min, e.
y_min);
237 y_max = std::max(y_max, e.
y_max);
244 if (i2 + 1 < residual_events.size() &&
245 residual_events[i2 + 1].x_start_min >= window_min &&
246 residual_events[i2 + 1].x_end_max <= window_max &&
247 residual_events[i2 + 1].y_min >= y_min &&
248 residual_events[i2 + 1].y_max <= y_max) {
256 bool use_precise_area =
false;
257 IntegerValue precise_area(0);
258 double area_lp = 0.0;
259 const IntegerValue bbox_area =
260 (window_max - window_min) * (y_max - y_min);
262 use_precise_area = precise_area < bbox_area;
263 area_lp =
ToDouble(std::min(precise_area, bbox_area));
265 if (area_lp >= sum_of_all_energies) {
270 const double relative_violation = energy_lp / area_lp;
271 if (relative_violation > max_relative_violation) {
272 max_violation_end_index = i2;
273 max_relative_violation = relative_violation;
274 max_violation_window_start = window_min;
275 max_violation_window_end = window_max;
276 max_violation_y_min = y_min;
277 max_violation_y_max = y_max;
278 max_violation_area = std::min(precise_area, bbox_area);
279 max_violation_use_precise_area = use_precise_area;
283 if (max_violation_end_index == -1)
continue;
287 bool add_opt_to_name =
false;
288 bool add_quadratic_to_name =
false;
289 bool add_energy_to_name =
false;
291 for (
int i2 = 0; i2 <= max_violation_end_index; ++i2) {
294 if (!event.IsPresent()) add_opt_to_name =
true;
295 if (event.energy_is_quadratic) add_quadratic_to_name =
true;
296 if (event.energy_min > event.x_size_min * event.y_size_min) {
297 add_energy_to_name =
true;
300 std::string full_name(cut_name);
301 if (add_opt_to_name) full_name.append(
"_optional");
302 if (add_quadratic_to_name) full_name.append(
"_quadratic");
303 if (add_energy_to_name) full_name.append(
"_energy");
304 if (max_violation_use_precise_area) full_name.append(
"_precise");
305 top_n_cuts.
AddCut(cut.
Build(), full_name, lp_values);
431 std::sort(events.begin(), events.end(),
433 return std::tie(e1.x_start_min, e1.y_size_min, e1.x_lp_end) <
434 std::tie(e2.x_start_min, e2.y_size_min, e2.x_lp_end);
436 for (
int start = 0; start + 1 < events.size(); ++start) {
439 events[start].x_start_min == events[start - 1].x_start_min) {
443 const IntegerValue sequence_start_min = events[start].x_start_min;
444 std::vector<DiffnCtEvent> residual_tasks(events.begin() + start,
453 for (
int before = 0; before < start; ++before) {
454 if (events[before].x_start_min + events[before].x_size_min >
455 sequence_start_min) {
457 DiffnCtEvent
event = events[before];
460 event.x_start_min, event.x_start_max, event.x_end_min,
461 event.x_end_max, event.x_size_min, event.y_size_min,
462 event.decomposed_energy, sequence_start_min, event.x_end_max);
464 event.x_size_min +
event.x_start_min - sequence_start_min;
465 event.x_start_min = sequence_start_min;
466 if (event.energy_min > event.x_size_min * event.y_size_min) {
467 event.use_energy =
true;
469 DCHECK_GE(event.energy_min, event.x_size_min * event.y_size_min);
470 if (event.energy_min <= 0)
continue;
471 residual_tasks.push_back(event);
476 std::sort(residual_tasks.begin(), residual_tasks.end(),
478 return e1.x_lp_end < e2.x_lp_end;
483 double best_efficacy = 0.01;
484 IntegerValue best_min_total_area = 0;
485 bool best_use_subset_sum =
false;
488 IntegerValue sum_event_areas = 0;
490 IntegerValue sum_energy = 0;
492 IntegerValue sum_square_energy = 0;
494 double lp_contrib = 0.0;
498 IntegerValue sum_of_y_size_min = 0;
503 for (
int i = 0;
i < residual_tasks.size(); ++
i) {
505 DCHECK_GE(event.x_start_min, sequence_start_min);
507 if (!
AddTo(event.energy_min, &sum_energy))
break;
508 if (!
AddProductTo(event.energy_min, event.x_size_min, &sum_event_areas)) {
511 if (!
AddSquareTo(event.energy_min, &sum_square_energy))
break;
512 if (!
AddTo(event.y_size_min, &sum_of_y_size_min))
break;
514 lp_contrib +=
event.x_lp_end *
ToDouble(event.energy_min);
515 current_start_min = std::min(current_start_min, event.x_start_min);
519 y_min_of_subset = std::min(y_min_of_subset, event.y_min);
520 y_max_of_subset = std::max(y_max_of_subset, event.y_max);
521 if (!event.y_size_is_fixed) use_dp =
false;
524 dp.Reset((y_max_of_subset - y_min_of_subset).value());
527 if (y_max_of_subset - y_min_of_subset > dp.Bound()) {
533 dp.Add(event.y_size_min.value());
536 const IntegerValue reachable_capacity =
537 use_dp ? IntegerValue(dp.CurrentMax())
538 : y_max_of_subset - y_min_of_subset;
541 if (sum_of_y_size_min <= reachable_capacity)
continue;
544 const IntegerValue square_sum_energy =
CapProdI(sum_energy, sum_energy);
546 const IntegerValue rhs_second_term =
547 CeilRatio(square_sum_energy, reachable_capacity);
549 IntegerValue min_total_area =
CapAddI(sum_event_areas, rhs_second_term);
551 min_total_area =
CeilRatio(min_total_area, 2);
554 if (!
AddProductTo(sum_energy, current_start_min, &min_total_area))
break;
558 const double efficacy = (
ToDouble(min_total_area) - lp_contrib) /
559 std::sqrt(
ToDouble(sum_square_energy));
567 if (efficacy > best_efficacy) {
568 best_efficacy = efficacy;
570 best_min_total_area = min_total_area;
571 best_use_subset_sum =
572 reachable_capacity < y_max_of_subset - y_min_of_subset;
575 if (best_end != -1) {
577 bool is_lifted =
false;
578 bool add_energy_to_name =
false;
579 for (
int i = 0;
i <= best_end; ++
i) {
581 is_lifted |=
event.
lifted;
582 add_energy_to_name |=
event.use_energy;
583 cut.AddTerm(event.x_end, event.energy_min);
585 std::string full_name(cut_name);
586 if (is_lifted) full_name.append(
"_lifted");
587 if (add_energy_to_name) full_name.append(
"_energy");
588 if (best_use_subset_sum) full_name.append(
"_subsetsum");
589 top_n_cuts.AddCut(cut.Build(), full_name, manager->
LpValues());
592 top_n_cuts.TransferToManager(manager);
600 &helper->x_helper(), model, &result.vars,
603 &helper->y_helper(), model, &result.vars,
608 result.generate_cuts = [helper, product_decomposer,
610 if (!helper->SynchronizeAndSetDirection()) {
614 const int num_rectangles = helper->NumBoxes();
615 std::vector<int> rectangles;
616 rectangles.reserve(num_rectangles);
617 const SchedulingConstraintHelper* x_helper = &helper->x_helper();
618 const SchedulingConstraintHelper* y_helper = &helper->y_helper();
619 for (
const auto& component :
620 helper->connected_components().AsVectorOfSpan()) {
622 if (component.size() <= 1)
continue;
623 for (
int rect : component) {
624 if (!helper->IsPresent(rect))
continue;
625 if (x_helper->SizeMin(rect) == 0 || y_helper->SizeMin(rect) == 0) {
628 rectangles.push_back(rect);
631 auto generate_cuts = [product_decomposer, manager, model, helper,
632 &rectangles](absl::string_view cut_name) {
633 std::vector<DiffnCtEvent> events;
637 const auto& lp_values = manager->LpValues();
638 for (
const int rect : rectangles) {
640 event.x_end = x_helper->Ends()[rect];
641 event.x_lp_end =
event.x_end.LpValue(lp_values);
642 event.y_min = y_helper->StartMin(rect);
643 event.y_max = y_helper->EndMax(rect);
644 event.y_size_min = y_helper->SizeMin(rect);
647 event.energy_min =
event.x_size_min *
event.y_size_min;
648 event.decomposed_energy = product_decomposer->TryToDecompose(
649 x_helper->Sizes()[rect], y_helper->Sizes()[rect]);
650 events.push_back(event);
658 if (!helper->SynchronizeAndSetDirection(
true,
true,
false)) {
661 generate_cuts(
"NoOverlap2dXCompletionTime");
662 if (!helper->SynchronizeAndSetDirection(
true,
true,
true)) {
665 generate_cuts(
"NoOverlap2dYCompletionTime");
666 if (!helper->SynchronizeAndSetDirection(
false,
false,
false)) {
669 generate_cuts(
"NoOverlap2dXCompletionTime");
670 if (!helper->SynchronizeAndSetDirection(
false,
false,
true)) {
673 generate_cuts(
"NoOverlap2dYCompletionTime");