@@ -391,6 +391,28 @@ std::string DiffnCtEvent::DebugString() const {
391
391
// The original cut is:
392
392
// sum(end_min_i * duration_min_i) >=
393
393
// (sum(duration_min_i^2) + sum(duration_min_i)^2) / 2
394
+ //
395
+ // Let's build a figure where each horizontal rectangle represent a task. It
396
+ // ends at the end of the task, and its height is the duration of the task.
397
+ // For a given order, we pack each rectangle to the left while not overlapping,
398
+ // that is one rectangle starts when the previous one ends.
399
+ //
400
+ // e1
401
+ // -----
402
+ // :\ | s1
403
+ // : \| e2
404
+ // -------------
405
+ // :\ |
406
+ // : \ | s2
407
+ // : \| e3
408
+ // ----------------
409
+ // : \| s3
410
+ // ----------------
411
+ //
412
+ // We can notice that the total area is independent of the order of tasks.
413
+ // The first term of the rhs is the area above the diagonal.
414
+ // The second term of the rhs is the area below the diagonal.
415
+ //
394
416
// We apply the following changes (see the code for cumulative constraints):
395
417
// - we strengthen this cuts by noticing that if all tasks starts after S,
396
418
// then replacing end_min_i by (end_min_i - S) is still valid.
@@ -461,12 +483,11 @@ void GenerateNoOvelap2dCompletionTimeCuts(absl::string_view cut_name,
461
483
// Best cut so far for this loop.
462
484
int best_end = -1 ;
463
485
double best_efficacy = 0.01 ;
464
- IntegerValue best_min_contrib = 0 ;
465
- IntegerValue best_capacity = 0 ;
466
- IntegerValue best_y_range = 0 ;
486
+ IntegerValue best_min_rhs = 0 ;
487
+ bool best_use_subset_sum = false ;
467
488
468
489
// Used in the first term of the rhs of the equation.
469
- IntegerValue sum_event_contributions = 0 ;
490
+ IntegerValue sum_event_areas = 0 ;
470
491
// Used in the second term of the rhs of the equation.
471
492
IntegerValue sum_energy = 0 ;
472
493
// For normalization.
@@ -486,8 +507,7 @@ void GenerateNoOvelap2dCompletionTimeCuts(absl::string_view cut_name,
486
507
DCHECK_GE (event.x_start_min , sequence_start_min);
487
508
// Make sure we do not overflow.
488
509
if (!AddTo (event.energy_min , &sum_energy)) break ;
489
- if (!AddProductTo (event.energy_min , event.x_size_min ,
490
- &sum_event_contributions)) {
510
+ if (!AddProductTo (event.energy_min , event.x_size_min , &sum_event_areas)) {
491
511
break ;
492
512
}
493
513
if (!AddSquareTo (event.energy_min , &sum_square_energy)) break ;
@@ -505,7 +525,8 @@ void GenerateNoOvelap2dCompletionTimeCuts(absl::string_view cut_name,
505
525
if (i == 0 ) {
506
526
dp.Reset ((y_max_of_subset - y_min_of_subset).value ());
507
527
} else {
508
- if (y_max_of_subset - y_min_of_subset != dp.Bound ()) {
528
+ // TODO(user): Can we increase the bound dynamically ?
529
+ if (y_max_of_subset - y_min_of_subset > dp.Bound ()) {
509
530
use_dp = false ;
510
531
}
511
532
}
@@ -522,23 +543,21 @@ void GenerateNoOvelap2dCompletionTimeCuts(absl::string_view cut_name,
522
543
if (sum_of_y_size_min <= reachable_capacity) continue ;
523
544
524
545
// Do we have a violated cut ?
525
- const IntegerValue large_rectangle_contrib =
526
- CapProdI (sum_energy, sum_energy);
527
- if (AtMinOrMaxInt64I (large_rectangle_contrib)) break ;
528
- const IntegerValue mean_large_rectangle_contrib =
529
- CeilRatio (large_rectangle_contrib, reachable_capacity);
546
+ const IntegerValue square_sum_energy = CapProdI (sum_energy, sum_energy);
547
+ if (AtMinOrMaxInt64I (square_sum_energy)) break ;
548
+ const IntegerValue rhs_second_term =
549
+ CeilRatio (square_sum_energy, reachable_capacity);
530
550
531
- IntegerValue min_contrib =
532
- CapAddI (sum_event_contributions, mean_large_rectangle_contrib);
533
- if (AtMinOrMaxInt64I (min_contrib)) break ;
534
- min_contrib = CeilRatio (min_contrib, 2 );
551
+ IntegerValue min_rhs = CapAddI (sum_event_areas, rhs_second_term);
552
+ if (AtMinOrMaxInt64I (min_rhs)) break ;
553
+ min_rhs = CeilRatio (min_rhs, 2 );
535
554
536
555
// shift contribution by current_start_min.
537
- if (!AddProductTo (sum_energy, current_start_min, &min_contrib )) break ;
556
+ if (!AddProductTo (sum_energy, current_start_min, &min_rhs )) break ;
538
557
539
558
// The efficacy of the cut is the normalized violation of the above
540
559
// equation. We will normalize by the sqrt of the sum of squared energies.
541
- const double efficacy = (ToDouble (min_contrib ) - lp_contrib) /
560
+ const double efficacy = (ToDouble (min_rhs ) - lp_contrib) /
542
561
std::sqrt (ToDouble (sum_square_energy));
543
562
544
563
// For a given start time, we only keep the best cut.
@@ -550,13 +569,13 @@ void GenerateNoOvelap2dCompletionTimeCuts(absl::string_view cut_name,
550
569
if (efficacy > best_efficacy) {
551
570
best_efficacy = efficacy;
552
571
best_end = i;
553
- best_min_contrib = min_contrib ;
554
- best_capacity = reachable_capacity;
555
- best_y_range = y_max_of_subset - y_min_of_subset;
572
+ best_min_rhs = min_rhs ;
573
+ best_use_subset_sum =
574
+ reachable_capacity < y_max_of_subset - y_min_of_subset;
556
575
}
557
576
}
558
577
if (best_end != -1 ) {
559
- LinearConstraintBuilder cut (model, best_min_contrib , kMaxIntegerValue );
578
+ LinearConstraintBuilder cut (model, best_min_rhs , kMaxIntegerValue );
560
579
bool is_lifted = false ;
561
580
bool add_energy_to_name = false ;
562
581
for (int i = 0 ; i <= best_end; ++i) {
@@ -568,9 +587,7 @@ void GenerateNoOvelap2dCompletionTimeCuts(absl::string_view cut_name,
568
587
std::string full_name (cut_name);
569
588
if (is_lifted) full_name.append (" _lifted" );
570
589
if (add_energy_to_name) full_name.append (" _energy" );
571
- if (best_capacity < best_y_range) {
572
- full_name.append (" _subsetsum" );
573
- }
590
+ if (best_use_subset_sum) full_name.append (" _subsetsum" );
574
591
top_n_cuts.AddCut (cut.Build (), full_name, manager->LpValues ());
575
592
}
576
593
}
0 commit comments