26#include "absl/strings/str_cat.h"
47const double kMinCutViolation = 1e-4;
50double GetLiteralLpValue(
53 const IntegerEncoder* encoder) {
54 const IntegerVariable direct_view = encoder->GetLiteralView(lit);
56 return lp_values[direct_view];
58 const IntegerVariable opposite_view = encoder->GetLiteralView(lit.Negated());
60 return 1.0 - lp_values[opposite_view];
63void AddIntegerVariableFromIntervals(SchedulingConstraintHelper* helper,
65 std::vector<IntegerVariable>* vars) {
66 IntegerEncoder* encoder =
model->GetOrCreate<IntegerEncoder>();
67 for (
int t = 0; t < helper->NumTasks(); ++t) {
69 vars->push_back(helper->Starts()[t].var);
72 vars->push_back(helper->Sizes()[t].var);
75 vars->push_back(helper->Ends()[t].var);
77 if (helper->IsOptional(t) && !helper->IsAbsent(t) &&
78 !helper->IsPresent(t)) {
79 const Literal l = helper->PresenceLiteral(t);
84 const IntegerVariable direct_view = encoder->GetLiteralView(l);
86 vars->push_back(direct_view);
88 vars->push_back(encoder->GetLiteralView(l.Negated()));
103 IntegerValue
y_min = IntegerValue(0);
104 IntegerValue
y_max = IntegerValue(0);
142 ", energy = ",
energy ?
energy.value().DebugString() :
"{}",
152 const std::string& cut_name,
159 std::sort(events.begin(), events.end(),
161 return std::tie(a.x_start_min, a.y_spread, a.x_end_max) <
162 std::tie(b.x_start_min, b.y_spread, b.x_end_max);
165 const double capacity_lp =
capacity.LpValue(lp_values);
168 double sum_of_all_energies = 0.0;
169 for (
const auto& e : events) {
170 sum_of_all_energies += e.energy_lp;
174 for (
int i1 = 0; i1 + 1 < events.size(); ++i1) {
177 if (events[i1].x_start_min == processed_start) {
179 }
else if (!no_overlap_2d) {
183 processed_start = events[i1].x_start_min;
188 int max_violation_end_index = -1;
189 double max_relative_violation = 1.01;
190 IntegerValue max_violation_window_start(0);
191 IntegerValue max_violation_window_end(0);
192 IntegerValue max_violation_y_min(0);
193 IntegerValue max_violation_y_max(0);
194 std::vector<EnergyEvent> max_violation_lifted_events;
197 double energy_lp = 0.0;
205 std::vector<EnergyEvent> residual_events(events.begin() + i1, events.end());
206 std::sort(residual_events.begin(), residual_events.end(),
208 return std::tie(a.x_end_max, a.y_spread) <
209 std::tie(b.x_end_max, b.y_spread);
213 for (
int i2 = 0; i2 < residual_events.size(); ++i2) {
224 if (i2 + 1 < residual_events.size() &&
225 residual_events[i2 + 1].x_start_min >= window_min &&
226 residual_events[i2 + 1].x_end_max <= window_max &&
227 residual_events[i2 + 1].y_min >= y_min &&
228 residual_events[i2 + 1].y_max <= y_max) {
233 DCHECK(capacity_lp == 0.0 || y_max == y_min);
234 const double window_lp =
ToDouble(window_max - window_min);
235 const double area = window_lp * (capacity_lp +
ToDouble(y_max - y_min));
236 if (area >= sum_of_all_energies) {
245 std::vector<EnergyEvent> lifted_events;
246 double lifted_contrib_lp = 0.0;
248 const auto check_lifted_cuts = [&lifted_events, window_min, window_max,
249 y_min, y_max, &lifted_contrib_lp,
257 if (e.
y_min < y_min || e.
y_max > y_max)
return;
265 const IntegerValue min_overlap = e.
MinOverlap(window_min, window_max);
267 if (min_overlap <= 0)
return;
269 lifted_events.push_back(e);
270 double contrib_lp = 0.0;
278 lifted_contrib_lp +=
std::max(contrib_lp, 0.0);
284 if (!no_overlap_2d) {
285 for (
int i3 = 0; i3 < i1; ++i3) {
286 check_lifted_cuts(events[i3]);
288 for (
int i3 = i2 + 1; i3 < residual_events.size(); ++i3) {
289 check_lifted_cuts(residual_events[i3]);
294 const double relative_violation = (energy_lp + lifted_contrib_lp) / area;
295 if (relative_violation > max_relative_violation) {
296 max_violation_end_index = i2;
297 max_relative_violation = relative_violation;
298 max_violation_window_start = window_min;
299 max_violation_window_end = window_max;
300 max_violation_y_min = y_min;
301 max_violation_y_max = y_max;
302 max_violation_lifted_events = lifted_events;
306 if (max_violation_end_index == -1)
continue;
309 bool cut_generated =
true;
310 bool has_opt_cuts =
false;
311 bool use_lifted_events =
false;
312 bool has_quadratic_cuts =
false;
313 bool use_energy =
false;
315 DCHECK(capacity_lp == 0.0 || max_violation_y_max == max_violation_y_min);
318 (max_violation_window_end - max_violation_window_start) *
319 (max_violation_y_max - max_violation_y_min));
323 max_violation_window_start - max_violation_window_end);
324 for (
int i2 = 0; i2 <= max_violation_end_index; ++i2) {
333 has_quadratic_cuts =
true;
337 const IntegerValue min_energy =
346 cut_generated =
false;
352 for (
const EnergyEvent& e : max_violation_lifted_events) {
353 const IntegerValue min_overlap = e.MinOverlap(window_min, window_max);
355 use_lifted_events =
true;
358 cut.
AddTerm(e.y_size, min_overlap);
362 min_overlap * e.y_size_min)) {
363 cut_generated =
false;
370 std::string full_name = cut_name;
371 if (has_opt_cuts) full_name.append(
"_opt");
372 if (has_quadratic_cuts) full_name.append(
"_quad");
373 if (use_lifted_events) full_name.append(
"_lifted");
374 if (use_energy) full_name.append(
"_energy");
375 top_n_cuts.AddCut(cut.
Build(), full_name, lp_values);
378 top_n_cuts.TransferToManager(lp_values, manager);
383 const std::vector<AffineExpression>& demands,
IntegerTrail* integer_trail,
386 if (!integer_trail->
IsFixed(demand_expr)) {
387 result->
vars.push_back(demand_expr.var);
396 const std::vector<IntervalVariable>& intervals,
398 const std::vector<AffineExpression>& demands,
399 const std::vector<LinearExpression>& energies,
Model*
model) {
404 model->TakeOwnership(helper);
410 AddIntegerVariableFromIntervals(helper,
model, &result.
vars);
418 [
capacity, demands, energies, trail, integer_trail, helper,
model,
423 std::vector<EnergyEvent> events;
424 for (
int i = 0; i < helper->
NumTasks(); ++i) {
426 if (integer_trail->LevelZeroUpperBound(demands[i]) == 0 ||
445 e.
y_size_min = integer_trail->LevelZeroLowerBound(demands[i]);
447 double energy_lp = 0.0;
450 energy_lp = e.
energy.value().LpValue(lp_values);
461 const IntegerValue min_energy =
473 false,
model, manager);
481 const std::vector<IntervalVariable>& intervals,
Model*
model) {
488 model->TakeOwnership(helper);
490 AddIntegerVariableFromIntervals(helper,
model, &result.
vars);
494 std::vector<LinearExpression> sizes;
495 sizes.reserve(intervals.size());
496 for (
int i = 0; i < intervals.size(); ++i) {
498 builder.
AddTerm(helper->Sizes()[i], IntegerValue(1));
503 [sizes, trail, helper,
model, encoder](
508 std::vector<EnergyEvent> events;
509 for (
int i = 0; i < helper->NumTasks(); ++i) {
510 if (helper->IsAbsent(i))
continue;
511 if (helper->SizeMin(i) == 0) {
520 e.
x_size = helper->Sizes()[i];
521 e.
y_size = IntegerValue(1);
525 double energy_lp = 0.0;
526 if (helper->IsPresent(i)) {
527 energy_lp = e.
energy->LpValue(lp_values);
540 IntegerValue(1),
false,
model, manager);
547 const std::vector<LinearExpression>& energies, absl::Span<int> rectangles,
548 const std::string& cut_name,
553 std::vector<EnergyEvent> events;
554 for (
const int rect : rectangles) {
555 if (y_helper->
SizeMax(rect) == 0 || x_helper->
SizeMax(rect) == 0) {
559 const LiteralIndex literal_index =
576 e.
energy = energies[rect];
582 double energy_lp = 0.0;
585 energy_lp = e.
energy.value().LpValue(lp_values);
595 const IntegerValue min_energy =
606 if (events.empty())
return;
609 double average_d = 0.0;
610 for (
const auto& e : events) {
611 average_d +=
ToDouble(e.y_min + e.y_max);
613 const int64_t average =
614 static_cast<int64_t
>(std::round(average_d / 2 / events.size()));
615 for (
auto& e : events) {
616 e.y_spread = IntegerValue(std::abs(e.y_max.value() - average) +
617 std::abs(average - e.y_min.value()));
624 const std::vector<IntervalVariable>& x_intervals,
625 const std::vector<IntervalVariable>& y_intervals,
Model*
model) {
629 const int num_rectangles = x_intervals.size();
631 std::vector<LinearExpression> energies;
632 std::vector<AffineExpression> x_sizes;
633 std::vector<AffineExpression> y_sizes;
634 for (
int i = 0; i < num_rectangles; ++i) {
635 x_sizes.push_back(intervals_repository->
Size(x_intervals[i]));
636 y_sizes.push_back(intervals_repository->
Size(y_intervals[i]));
642 model->TakeOwnership(x_helper);
646 model->TakeOwnership(y_helper);
648 AddIntegerVariableFromIntervals(x_helper,
model, &result.
vars);
649 AddIntegerVariableFromIntervals(y_helper,
model, &result.
vars);
657 [integer_trail, trail, encoder, x_helper, y_helper,
model, energies](
665 const int num_rectangles = x_helper->
NumTasks();
666 std::vector<int> active_rectangles;
667 std::vector<Rectangle> cached_rectangles(num_rectangles);
668 for (
int rect = 0; rect < num_rectangles; ++rect) {
682 Rectangle& rectangle = cached_rectangles[rect];
688 active_rectangles.push_back(rect);
691 if (active_rectangles.size() <= 1)
return true;
693 std::vector<absl::Span<int>> components =
695 cached_rectangles, absl::MakeSpan(active_rectangles));
698 for (absl::Span<int> rectangles : components) {
699 if (rectangles.size() <= 1)
continue;
702 energies, rectangles,
"NoOverlap2dXEnergy", lp_values,
model,
703 integer_trail, encoder, manager, x_helper, y_helper);
705 energies, rectangles,
"NoOverlap2dYEnergy", lp_values,
model,
706 integer_trail, encoder, manager, y_helper, x_helper);
715 const std::vector<IntervalVariable>& intervals,
717 const std::vector<AffineExpression>& demands,
Model*
model) {
722 model->TakeOwnership(helper);
727 AddIntegerVariableFromIntervals(helper,
model, &result.
vars);
730 struct TimeTableEvent {
745 std::vector<TimeTableEvent> events;
748 for (
int i = 0; i < helper->
NumTasks(); ++i) {
757 e1.interval_index = i;
759 e1.demand = demands[i];
762 TimeTableEvent e2 = e1;
765 events.push_back(e1);
766 events.push_back(e2);
772 std::sort(events.begin(), events.end(),
773 [](
const TimeTableEvent& i,
const TimeTableEvent& j) {
774 if (i.time == j.time) {
775 if (i.positive == j.positive) {
776 return i.interval_index < j.interval_index;
780 return i.time < j.time;
783 std::vector<TimeTableEvent> cut_events;
784 bool added_positive_event =
false;
785 for (
const TimeTableEvent& e : events) {
787 added_positive_event =
true;
788 cut_events.push_back(e);
791 if (added_positive_event && cut_events.size() > 1) {
793 bool cut_generated =
true;
797 for (
const TimeTableEvent& cut_event : cut_events) {
798 if (helper->
IsPresent(cut_event.interval_index)) {
799 cut.
AddTerm(cut_event.demand, IntegerValue(1));
804 if (!cut_generated)
break;
810 manager->AddCut(cut.
Build(),
"CumulativeTimeTable", lp_values);
815 for (
int i = 0; i < cut_events.size(); ++i) {
816 if (cut_events[i].interval_index == e.interval_index) {
819 cut_events[new_size] = cut_events[i];
822 cut_events.resize(new_size);
823 added_positive_event =
false;
842 const std::string& cut_name,
844 std::vector<PrecedenceEvent> events, IntegerValue capacity_max,
846 const int num_events = events.size();
847 if (num_events <= 1)
return;
849 std::sort(events.begin(), events.end(),
851 return e1.start_min < e2.start_min ||
852 (e1.start_min == e2.start_min && e1.end_max < e2.end_max);
855 const double tolerance = 1e-4;
857 for (
int i = 0; i + 1 < num_events; ++i) {
859 for (
int j = i + 1; j < num_events; ++j) {
869 if (interval_1_can_precede_2 && !interval_2_can_precede_1 &&
876 }
else if (interval_2_can_precede_1 && !interval_1_can_precede_2 &&
890 const std::vector<IntervalVariable>& intervals,
892 const std::vector<AffineExpression>& demands,
Model*
model) {
897 model->TakeOwnership(helper);
902 AddIntegerVariableFromIntervals(helper,
model, &result.
vars);
914 std::vector<PrecedenceEvent> events;
915 for (
int t = 0; t < helper->
NumTasks(); ++t) {
919 event.start_max = helper->
StartMax(t);
920 event.start = helper->
Starts()[t];
921 event.end_min = helper->
EndMin(t);
922 event.end_max = helper->
EndMax(t);
923 event.end = helper->
Ends()[t];
924 event.demand_min = integer_trail->
LowerBound(demands[t]);
925 events.push_back(event);
928 std::move(events), capacity_max,
model, manager);
935 const std::vector<IntervalVariable>& intervals,
Model*
model) {
940 model->TakeOwnership(helper);
942 AddIntegerVariableFromIntervals(helper,
model, &result.
vars);
948 [trail, helper,
model](
953 std::vector<PrecedenceEvent> events;
954 for (
int t = 0; t < helper->
NumTasks(); ++t) {
958 event.start_max = helper->
StartMax(t);
959 event.start = helper->
Starts()[t];
960 event.end_min = helper->
EndMin(t);
961 event.end_max = helper->
EndMax(t);
962 event.end = helper->
Ends()[t];
963 event.demand_min = IntegerValue(1);
964 events.push_back(event);
967 std::move(events), IntegerValue(1),
model,
1005 bool use_energy =
false;
1009 bool lifted =
false;
1012 return absl::StrCat(
"CtEvent(x_end = ", x_end.
DebugString(),
1013 ", x_start_min = ", x_start_min.value(),
1014 ", x_size_min = ", x_size_min.value(),
1015 ", x_lp_end = ", x_lp_end,
1016 ", y_start_min = ", y_start_min.value(),
1017 ", y_end_max = ", y_end_max.value(),
1018 ", energy_min = ", energy_min.value(),
1019 ", use_energy = ", use_energy,
", lifted = ", lifted);
1036 const std::string& cut_name,
1038 std::vector<CtEvent> events,
bool use_lifting,
Model*
model,
1043 std::sort(events.begin(), events.end(),
1045 return e1.x_start_min < e2.x_start_min;
1050 events[
start].x_start_min == events[
start - 1].x_start_min) {
1054 const IntegerValue sequence_start_min = events[
start].x_start_min;
1055 std::vector<CtEvent> residual_tasks(events.begin() +
start, events.end());
1063 for (
int before = 0; before <
start; ++before) {
1064 if (events[before].x_start_min + events[before].x_size_min >
1065 sequence_start_min) {
1067 CtEvent event = events[before];
1069 const IntegerValue old_size_min =
event.x_size_min;
1071 event.x_size_min +
event.x_start_min - sequence_start_min;
1072 event.x_start_min = sequence_start_min;
1083 event.energy_min =
event.energy_min *
event.x_size_min / old_size_min;
1084 residual_tasks.push_back(event);
1089 std::sort(residual_tasks.begin(), residual_tasks.end(),
1091 return e1.x_lp_end < e2.x_lp_end;
1095 double best_efficacy = 0.01;
1096 IntegerValue best_min_contrib(0);
1097 IntegerValue sum_duration(0);
1098 IntegerValue sum_square_duration(0);
1099 IntegerValue best_size_divisor(0);
1100 double unscaled_lp_contrib = 0;
1105 for (
int i = 0; i < residual_tasks.size(); ++i) {
1106 const CtEvent&
event = residual_tasks[i];
1107 DCHECK_GE(event.x_start_min, sequence_start_min);
1108 const IntegerValue
energy =
event.energy_min;
1112 current_start_min =
std::min(current_start_min, event.x_start_min);
1113 y_start_min =
std::min(y_start_min, event.y_start_min);
1114 y_end_max =
std::max(y_end_max, event.y_end_max);
1116 const IntegerValue size_divisor = y_end_max - y_start_min;
1122 const IntegerValue min_contrib =
1123 (sum_duration * sum_duration + sum_square_duration) / 2 +
1124 current_start_min * sum_duration * size_divisor;
1125 const double efficacy = (
ToDouble(min_contrib) -
1126 unscaled_lp_contrib *
ToDouble(size_divisor)) /
1127 std::sqrt(
ToDouble(sum_square_duration));
1129 if (efficacy > best_efficacy) {
1130 best_efficacy = efficacy;
1132 best_min_contrib = min_contrib;
1133 best_size_divisor = size_divisor;
1136 if (best_end != -1) {
1138 bool is_lifted =
false;
1139 bool use_energy =
false;
1140 for (
int i = 0; i <= best_end; ++i) {
1141 const CtEvent&
event = residual_tasks[i];
1142 is_lifted |=
event.
lifted;
1143 use_energy |=
event.use_energy;
1144 cut.
AddTerm(event.x_end, event.energy_min * best_size_divisor);
1146 std::string full_name = cut_name;
1147 if (is_lifted) full_name.append(
"_lifted");
1148 if (use_energy) full_name.append(
"_energy");
1149 top_n_cuts.
AddCut(cut.
Build(), full_name, lp_values);
1156 const std::vector<IntervalVariable>& intervals,
Model*
model) {
1161 model->TakeOwnership(helper);
1163 AddIntegerVariableFromIntervals(helper,
model, &result.
vars);
1169 [trail, helper,
model](
1174 auto generate_cuts = [&lp_values,
model, manager,
1175 helper](
const std::string& cut_name) {
1176 std::vector<CtEvent> events;
1184 event.x_size_min = size_min;
1185 event.x_end = end_expr;
1186 event.x_lp_end = end_expr.
LpValue(lp_values);
1187 event.y_start_min = IntegerValue(0);
1188 event.y_end_max = IntegerValue(1);
1189 event.energy_min = size_min;
1190 events.push_back(event);
1194 false,
model, manager);
1197 generate_cuts(
"NoOverlapCompletionTime");
1199 generate_cuts(
"NoOverlapCompletionTimeMirror");
1206 const std::vector<IntervalVariable>& intervals,
1208 const std::vector<AffineExpression>& demands,
1209 const std::vector<LinearExpression>& energies,
Model*
model) {
1214 model->TakeOwnership(helper);
1219 AddIntegerVariableFromIntervals(helper,
model, &result.
vars);
1225 [trail, integer_trail, helper, demands, energies,
capacity,
model](
1231 auto generate_cuts = [&lp_values,
model, manager, helper, capacity_max,
1232 integer_trail, &demands,
1233 &energies](
const std::string& cut_name) {
1234 std::vector<CtEvent> events;
1236 if (!helper->IsPresent(
index))
continue;
1237 if (helper->SizeMin(
index) > 0 &&
1238 integer_trail->LowerBound(demands[
index]) > 0) {
1240 const IntegerValue size_min = helper->SizeMin(
index);
1241 const IntegerValue demand_min =
1242 integer_trail->LowerBound(demands[
index]);
1246 event.x_size_min = size_min;
1247 event.x_end = end_expr;
1248 event.x_lp_end = end_expr.
LpValue(lp_values);
1249 event.y_start_min = IntegerValue(0);
1250 event.y_end_max = IntegerValue(capacity_max);
1251 event.energy_min = size_min * demand_min;
1255 const IntegerValue linearized_energy =
1258 event.energy_min = linearized_energy;
1259 event.use_energy =
true;
1262 events.push_back(event);
1266 true,
model, manager);
1268 if (!helper->SynchronizeAndSetTimeDirection(
true))
return false;
1269 generate_cuts(
"CumulativeCompletionTime");
1270 if (!helper->SynchronizeAndSetTimeDirection(
false))
return false;
1271 generate_cuts(
"CumulativeCompletionTimeMirror");
1278 const std::vector<IntervalVariable>& x_intervals,
1279 const std::vector<IntervalVariable>& y_intervals,
Model*
model) {
1284 model->TakeOwnership(x_helper);
1288 model->TakeOwnership(y_helper);
1290 AddIntegerVariableFromIntervals(x_helper,
model, &result.
vars);
1291 AddIntegerVariableFromIntervals(y_helper,
model, &result.
vars);
1297 [trail, x_helper, y_helper,
model](
1305 const int num_rectangles = x_helper->
NumTasks();
1306 std::vector<int> active_rectangles;
1307 std::vector<IntegerValue> cached_areas(num_rectangles);
1308 std::vector<Rectangle> cached_rectangles(num_rectangles);
1309 for (
int rect = 0; rect < num_rectangles; ++rect) {
1313 cached_areas[rect] =
1315 if (cached_areas[rect] == 0)
continue;
1321 Rectangle& rectangle = cached_rectangles[rect];
1327 active_rectangles.push_back(rect);
1330 if (active_rectangles.size() <= 1)
return true;
1332 std::vector<absl::Span<int>> components =
1334 cached_rectangles, absl::MakeSpan(active_rectangles));
1335 for (absl::Span<int> rectangles : components) {
1336 if (rectangles.size() <= 1)
continue;
1338 auto generate_cuts = [&lp_values,
model, manager, &rectangles,
1340 const std::string& cut_name,
1343 std::vector<CtEvent> events;
1345 for (
const int rect : rectangles) {
1348 event.
x_start_min = x_helper->ShiftedStartMin(rect);
1349 event.x_size_min = x_helper->SizeMin(rect);
1350 event.x_end = x_end_expr;
1351 event.x_lp_end = x_end_expr.
LpValue(lp_values);
1355 x_helper->SizeMin(rect) * y_helper->
SizeMin(rect);
1356 events.push_back(event);
1360 true,
model, manager);
1363 if (!x_helper->SynchronizeAndSetTimeDirection(
true))
return false;
1365 generate_cuts(
"NoOverlap2dXCompletionTime", x_helper, y_helper);
1366 generate_cuts(
"NoOverlap2dYCompletionTime", y_helper, x_helper);
1367 if (!x_helper->SynchronizeAndSetTimeDirection(
false))
return false;
1369 generate_cuts(
"NoOverlap2dXCompletionTimeMirror", x_helper, y_helper);
1370 generate_cuts(
"NoOverlap2dYCompletionTimeMirror", y_helper, x_helper);
#define DCHECK_NE(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK_GT(val1, val2)
#define DCHECK(condition)
bool IsFixed(IntegerVariable i) const
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue LowerBound(IntegerVariable i) const
AffineExpression Size(IntervalVariable i) const
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff)
void AddLinearExpression(const LinearExpression &expr)
void AddQuadraticLowerBound(AffineExpression left, AffineExpression right, IntegerTrail *integer_trail)
LinearExpression BuildExpression()
void AddTerm(IntegerVariable var, IntegerValue coeff)
bool AddCut(LinearConstraint ct, std::string type_name, const absl::StrongVector< IntegerVariable, double > &lp_solution, std::string extra_info="")
LiteralIndex Index() const
Class that owns everything related to a particular optimization model.
IntegerValue ShiftedStartMin(int t) const
IntegerValue EndMin(int t) const
const std::vector< AffineExpression > & Starts() const
bool IsPresent(int t) const
IntegerValue ShiftedEndMax(int t) const
const std::vector< AffineExpression > & Sizes() const
bool IsAbsent(int t) const
IntegerValue EndMax(int t) const
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
IntegerValue StartMin(int t) const
Literal PresenceLiteral(int index) const
IntegerValue StartMax(int t) const
const std::vector< AffineExpression > & Ends() const
IntegerValue SizeMax(int t) const
IntegerValue SizeMin(int t) const
void AddCut(LinearConstraint ct, const std::string &name, const absl::StrongVector< IntegerVariable, double > &lp_solution)
void TransferToManager(const absl::StrongVector< IntegerVariable, double > &lp_solution, LinearConstraintManager *manager)
int CurrentDecisionLevel() const
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
static double ToDouble(double f)
CutGenerator CreateCumulativeEnergyCutGenerator(const std::vector< IntervalVariable > &intervals, const AffineExpression &capacity, const std::vector< AffineExpression > &demands, const std::vector< LinearExpression > &energies, Model *model)
std::function< IntegerVariable(Model *)> NewIntegerVariableFromLiteral(Literal lit)
CutGenerator CreateNoOverlap2dEnergyCutGenerator(const std::vector< IntervalVariable > &x_intervals, const std::vector< IntervalVariable > &y_intervals, Model *model)
void GeneratePrecedenceCuts(const std::string &cut_name, const absl::StrongVector< IntegerVariable, double > &lp_values, std::vector< PrecedenceEvent > events, IntegerValue capacity_max, Model *model, LinearConstraintManager *manager)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntegerValue LinExprLowerBound(const LinearExpression &expr, const IntegerTrail &integer_trail)
CutGenerator CreateNoOverlapCompletionTimeCutGenerator(const std::vector< IntervalVariable > &intervals, Model *model)
CutGenerator CreateNoOverlapPrecedenceCutGenerator(const std::vector< IntervalVariable > &intervals, Model *model)
const LiteralIndex kNoLiteralIndex(-1)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
std::vector< absl::Span< int > > GetOverlappingRectangleComponents(const std::vector< Rectangle > &rectangles, absl::Span< int > active_rectangles)
void LinearizeInnerProduct(const std::vector< AffineExpression > &left, const std::vector< AffineExpression > &right, Model *model, std::vector< LinearExpression > *energies)
void GenerateNoOverlap2dEnergyCut(const std::vector< LinearExpression > &energies, absl::Span< int > rectangles, const std::string &cut_name, const absl::StrongVector< IntegerVariable, double > &lp_values, Model *model, IntegerTrail *integer_trail, IntegerEncoder *encoder, LinearConstraintManager *manager, SchedulingConstraintHelper *x_helper, SchedulingConstraintHelper *y_helper)
void GenerateEnergeticCuts(const std::string &cut_name, const absl::StrongVector< IntegerVariable, double > &lp_values, std::vector< EnergyEvent > events, const AffineExpression capacity, bool no_overlap_2d, Model *model, LinearConstraintManager *manager)
const IntegerVariable kNoIntegerVariable(-1)
void AppendVariablesToCumulativeCut(const AffineExpression &capacity, const std::vector< AffineExpression > &demands, IntegerTrail *integer_trail, CutGenerator *result)
CutGenerator CreateCumulativeTimeTableCutGenerator(const std::vector< IntervalVariable > &intervals, const AffineExpression &capacity, const std::vector< AffineExpression > &demands, Model *model)
void GenerateCompletionTimeCuts(const std::string &cut_name, const absl::StrongVector< IntegerVariable, double > &lp_values, std::vector< CtEvent > events, bool use_lifting, Model *model, LinearConstraintManager *manager)
CutGenerator CreateNoOverlap2dCompletionTimeCutGenerator(const std::vector< IntervalVariable > &x_intervals, const std::vector< IntervalVariable > &y_intervals, Model *model)
bool ProductIsLinearized(const LinearExpression &expr)
CutGenerator CreateCumulativeCompletionTimeCutGenerator(const std::vector< IntervalVariable > &intervals, const AffineExpression &capacity, const std::vector< AffineExpression > &demands, const std::vector< LinearExpression > &energies, Model *model)
CutGenerator CreateNoOverlapEnergyCutGenerator(const std::vector< IntervalVariable > &intervals, Model *model)
CutGenerator CreateCumulativePrecedenceCutGenerator(const std::vector< IntervalVariable > &intervals, const AffineExpression &capacity, const std::vector< AffineExpression > &demands, Model *model)
double ToDouble(IntegerValue value)
Collection of objects used to extend the Constraint Solver library.
std::optional< int64_t > end
double LpValue(const absl::StrongVector< IntegerVariable, double > &lp_values) const
const std::string DebugString() const
std::string DebugString() const
std::vector< IntegerVariable > vars
std::function< bool(const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraintManager *manager)> generate_cuts
LiteralIndex presence_literal_index
std::optional< LinearExpression > energy
std::string DebugString() const
IntegerValue MinOverlap(IntegerValue start, IntegerValue end) const