24#include "absl/base/attributes.h"
25#include "absl/log/check.h"
26#include "absl/strings/str_cat.h"
27#include "absl/strings/string_view.h"
28#include "absl/types/span.h"
50const double kMinCutViolation = 1e-4;
55 : x_start_min(x_helper->StartMin(t)),
56 x_start_max(x_helper->StartMax(t)),
57 x_end_min(x_helper->EndMin(t)),
58 x_end_max(x_helper->EndMax(t)),
59 x_size_min(x_helper->SizeMin(t)) {}
125 "DiffnEnergyEvent(x_start_min = ",
x_start_min.value(),
139 absl::Span<
const std::vector<LiteralValueValue>> energies,
140 absl::Span<int> rectangles, absl::string_view cut_name,
Model*
model,
144 std::vector<DiffnEnergyEvent> events;
145 const auto& lp_values = manager->
LpValues();
146 for (
const int rect : rectangles) {
147 if (y_helper->
SizeMax(rect) == 0 || x_helper->
SizeMax(rect) == 0) {
171 if (events.empty())
return;
174 double average_d = 0.0;
175 for (
const auto& e : events) {
176 average_d +=
ToDouble(e.y_min + e.y_max);
178 const double average = average_d / 2.0 /
static_cast<double>(events.size());
179 for (
auto& e : events) {
186 std::sort(events.begin(), events.end(),
188 return std::tie(a.x_start_min, a.y_spread, a.x_end_max) <
189 std::tie(b.x_start_min, b.y_spread, b.x_end_max);
193 double sum_of_all_energies = 0.0;
194 for (
const auto& e : events) {
195 sum_of_all_energies += e.linearized_energy_lp_value;
199 for (
int i1 = 0; i1 + 1 < events.size(); ++i1) {
202 int max_violation_end_index = -1;
203 double max_relative_violation = 1.0 + kMinCutViolation;
204 IntegerValue max_violation_window_start(0);
205 IntegerValue max_violation_window_end(0);
206 IntegerValue max_violation_y_min(0);
207 IntegerValue max_violation_y_max(0);
208 IntegerValue max_violation_area(0);
209 bool max_violation_use_precise_area =
false;
212 double energy_lp = 0.0;
217 capacity_profile.
Clear();
221 std::vector<DiffnEnergyEvent> residual_events(events.begin() + i1,
223 std::sort(residual_events.begin(), residual_events.end(),
225 return std::tie(a.x_end_max, a.y_spread) <
226 std::tie(b.x_end_max, b.y_spread);
230 for (
int i2 = 0; i2 < residual_events.size(); ++i2) {
234 window_max = std::max(window_max, e.
x_end_max);
235 y_min = std::min(y_min, e.
y_min);
236 y_max = std::max(y_max, e.
y_max);
243 if (i2 + 1 < residual_events.size() &&
244 residual_events[i2 + 1].x_start_min >= window_min &&
245 residual_events[i2 + 1].x_end_max <= window_max &&
246 residual_events[i2 + 1].y_min >= y_min &&
247 residual_events[i2 + 1].y_max <= y_max) {
255 bool use_precise_area =
false;
256 IntegerValue precise_area(0);
257 double area_lp = 0.0;
258 const IntegerValue bbox_area =
259 (window_max - window_min) * (y_max - y_min);
261 use_precise_area = precise_area < bbox_area;
262 area_lp =
ToDouble(std::min(precise_area, bbox_area));
264 if (area_lp >= sum_of_all_energies) {
269 const double relative_violation = energy_lp / area_lp;
270 if (relative_violation > max_relative_violation) {
271 max_violation_end_index = i2;
272 max_relative_violation = relative_violation;
273 max_violation_window_start = window_min;
274 max_violation_window_end = window_max;
275 max_violation_y_min = y_min;
276 max_violation_y_max = y_max;
277 max_violation_area = std::min(precise_area, bbox_area);
278 max_violation_use_precise_area = use_precise_area;
282 if (max_violation_end_index == -1)
continue;
286 bool add_opt_to_name =
false;
287 bool add_quadratic_to_name =
false;
288 bool add_energy_to_name =
false;
290 for (
int i2 = 0; i2 <= max_violation_end_index; ++i2) {
293 if (!event.IsPresent()) add_opt_to_name =
true;
294 if (event.energy_is_quadratic) add_quadratic_to_name =
true;
295 if (event.energy_min > event.x_size_min * event.y_size_min) {
296 add_energy_to_name =
true;
299 std::string full_name(cut_name);
300 if (add_opt_to_name) full_name.append(
"_optional");
301 if (add_quadratic_to_name) full_name.append(
"_quadratic");
302 if (add_energy_to_name) full_name.append(
"_energy");
303 if (max_violation_use_precise_area) full_name.append(
"_precise");
304 top_n_cuts.
AddCut(cut.
Build(), full_name, lp_values);
313 const std::vector<std::vector<LiteralValueValue>>& energies,
Model*
model) {
320 result.
generate_cuts = [x_helper, y_helper, x_demands_helper,
321 y_demands_helper,
model,
328 const int num_rectangles = x_helper->
NumTasks();
329 std::vector<int> active_rectangles;
330 std::vector<Rectangle> cached_rectangles(num_rectangles);
331 for (
int rect = 0; rect < num_rectangles; ++rect) {
344 Rectangle& rectangle = cached_rectangles[rect];
345 rectangle.x_min = x_helper->
StartMin(rect);
346 rectangle.x_max = x_helper->
EndMax(rect);
347 rectangle.y_min = y_helper->
StartMin(rect);
348 rectangle.y_max = y_helper->
EndMax(rect);
350 active_rectangles.push_back(rect);
353 if (active_rectangles.size() <= 1)
return true;
356 cached_rectangles, absl::MakeSpan(active_rectangles));
359 for (absl::Span<int> rectangles : components) {
360 if (rectangles.size() <= 1)
continue;
363 model, manager, x_helper, y_helper,
366 model, manager, y_helper, x_helper,
384 ", y_max = ",
y_max.value(),
405 absl::string_view cut_name, std::vector<DiffnCtEvent> events,
406 bool use_lifting,
bool skip_low_sizes,
Model*
model,
411 std::sort(events.begin(), events.end(),
413 return std::tie(e1.x_start_min, e1.y_size_min, e1.x_lp_end) <
414 std::tie(e2.x_start_min, e2.y_size_min, e2.x_lp_end);
419 events[
start].x_start_min == events[
start - 1].x_start_min) {
423 const IntegerValue sequence_start_min = events[
start].x_start_min;
424 std::vector<DiffnCtEvent> residual_tasks(events.begin() +
start,
433 for (
int before = 0; before <
start; ++before) {
434 if (events[before].x_start_min + events[before].x_size_min >
435 sequence_start_min) {
440 event.x_start_min, event.x_start_max, event.x_end_min,
441 event.x_end_max, event.x_size_min, event.y_size_min,
442 event.decomposed_energy, sequence_start_min, event.x_end_max);
444 event.x_size_min +
event.x_start_min - sequence_start_min;
445 event.x_start_min = sequence_start_min;
446 if (event.energy_min > event.x_size_min * event.y_size_min) {
447 event.use_energy =
true;
449 DCHECK_GE(event.energy_min, event.x_size_min * event.y_size_min);
450 if (event.energy_min <= 0)
continue;
451 residual_tasks.push_back(event);
456 std::sort(residual_tasks.begin(), residual_tasks.end(),
458 return e1.x_lp_end < e2.x_lp_end;
462 double best_efficacy = 0.01;
463 IntegerValue best_min_contrib(0);
464 IntegerValue sum_duration(0);
465 IntegerValue sum_square_duration(0);
466 IntegerValue best_capacity(0);
467 double unscaled_lp_contrib = 0.0;
474 for (
int i = 0;
i < residual_tasks.size(); ++
i) {
476 DCHECK_GE(event.x_start_min, sequence_start_min);
477 const IntegerValue
energy =
event.energy_min;
482 current_start_min = std::min(current_start_min, event.x_start_min);
486 if (skip_low_sizes &&
i < 7)
continue;
494 y_min = std::min(y_min, event.y_min);
495 y_max = std::max(y_max, event.y_max);
496 if (!event.y_size_is_fixed) use_dp =
false;
501 if (y_max - y_min != dp.
Bound()) {
507 dp.
Add(event.y_size_min.value());
510 const IntegerValue capacity =
511 use_dp ? IntegerValue(dp.
CurrentMax()) : y_max - y_min;
517 IntegerValue min_contrib = 0;
518 if (!
AddProductTo(sum_duration, sum_duration, &min_contrib))
break;
519 if (!
AddTo(sum_square_duration, &min_contrib))
break;
520 min_contrib = min_contrib / 2;
522 const IntegerValue intermediate =
CapProdI(sum_duration, capacity);
524 const IntegerValue offset =
CapProdI(current_start_min, intermediate);
526 if (!
AddTo(offset, &min_contrib))
break;
530 const double efficacy =
532 std::sqrt(
ToDouble(sum_square_duration));
535 if (efficacy > best_efficacy) {
536 best_efficacy = efficacy;
538 best_min_contrib = min_contrib;
539 best_capacity = capacity;
542 if (best_end != -1) {
544 bool is_lifted =
false;
545 bool add_energy_to_name =
false;
546 for (
int i = 0;
i <= best_end; ++
i) {
548 is_lifted |=
event.lifted;
549 add_energy_to_name |=
event.use_energy;
550 cut.
AddTerm(event.x_end, event.energy_min * best_capacity);
552 std::string full_name(cut_name);
553 if (is_lifted) full_name.append(
"_lifted");
554 if (add_energy_to_name) full_name.append(
"_energy");
572 result.
generate_cuts = [x_helper, y_helper, product_decomposer,
577 const int num_rectangles = x_helper->
NumTasks();
578 std::vector<int> active_rectangles;
579 std::vector<IntegerValue> cached_areas(num_rectangles);
580 std::vector<Rectangle> cached_rectangles(num_rectangles);
581 for (
int rect = 0; rect < num_rectangles; ++rect) {
584 cached_areas[rect] = x_helper->
SizeMin(rect) * y_helper->
SizeMin(rect);
585 if (cached_areas[rect] == 0)
continue;
591 Rectangle& rectangle = cached_rectangles[rect];
592 rectangle.x_min = x_helper->
StartMin(rect);
593 rectangle.x_max = x_helper->
EndMax(rect);
594 rectangle.y_min = y_helper->
StartMin(rect);
595 rectangle.y_max = y_helper->
EndMax(rect);
597 active_rectangles.push_back(rect);
600 if (active_rectangles.size() <= 1)
return true;
603 cached_rectangles, absl::MakeSpan(active_rectangles));
604 for (absl::Span<int> rectangles : components) {
605 if (rectangles.size() <= 1)
continue;
607 auto generate_cuts = [product_decomposer, manager,
model, &rectangles](
608 absl::string_view cut_name,
611 std::vector<DiffnCtEvent> events;
613 const auto& lp_values = manager->LpValues();
614 for (
const int rect : rectangles) {
616 event.x_end = x_helper->Ends()[rect];
617 event.x_lp_end =
event.x_end.LpValue(lp_values);
618 event.y_min = y_helper->
StartMin(rect);
619 event.y_max = y_helper->
EndMax(rect);
620 event.y_size_min = y_helper->
SizeMin(rect);
623 event.energy_min =
event.x_size_min *
event.y_size_min;
624 event.decomposed_energy = product_decomposer->TryToDecompose(
625 x_helper->Sizes()[rect], y_helper->
Sizes()[rect]);
626 events.push_back(event);
630 cut_name, std::move(events),
632 false,
model, manager);
635 if (!x_helper->SynchronizeAndSetTimeDirection(
true))
return false;
637 generate_cuts(
"NoOverlap2dXCompletionTime", x_helper, y_helper);
638 generate_cuts(
"NoOverlap2dYCompletionTime", y_helper, x_helper);
639 if (!x_helper->SynchronizeAndSetTimeDirection(
false))
return false;
641 generate_cuts(
"NoOverlap2dXCompletionTimeMirror", x_helper, y_helper);
642 generate_cuts(
"NoOverlap2dYCompletionTimeMirror", y_helper, x_helper);
void AddRectangle(IntegerValue x_min, IntegerValue x_max, IntegerValue y_min, IntegerValue y_max)
Adds a rectangle to the current shape.
IntegerValue GetBoundingArea()
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)
ABSL_MUST_USE_RESULT bool AddDecomposedProduct(absl::Span< const LiteralValueValue > product)
const util_intops::StrongVector< IntegerVariable, double > & LpValues()
To simplify CutGenerator api.
LiteralIndex Index() const
int64_t CurrentMax() const
void Add(int64_t value)
Add a value to the base set for which subset sums will be taken.
void Reset(int64_t bound)
Helper class to express a product as a linear constraint.
int NumTasks() const
Returns the number of task.
absl::Span< const AffineExpression > Sizes() const
bool IsPresent(int t) const
Literal PresenceLiteral(int index) const
IntegerValue SizeMax(int t) const
IntegerValue StartMin(int t) const
IntegerValue SizeMin(int t) const
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
IntegerValue EndMax(int t) const
bool IsAbsent(int t) const
bool EnergyIsQuadratic(int t) const
void CacheAllEnergyValues()
IntegerValue EnergyMin(int t) const
void TransferToManager(LinearConstraintManager *manager)
Empty the local pool and add all its content to the manager.
void AddCut(LinearConstraint ct, absl::string_view name, const util_intops::StrongVector< IntegerVariable, double > &lp_solution)
Adds a cut to the local pool.
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
bool AddProductTo(IntegerValue a, IntegerValue b, IntegerValue *result)
Computes result += a * b, and return false iff there is an overflow.
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
bool AddTo(IntegerValue a, IntegerValue *result)
const LiteralIndex kNoLiteralIndex(-1)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
CutGenerator CreateNoOverlap2dCompletionTimeCutGenerator(SchedulingConstraintHelper *x_helper, SchedulingConstraintHelper *y_helper, Model *model)
std::vector< absl::Span< int > > GetOverlappingRectangleComponents(absl::Span< const Rectangle > rectangles, absl::Span< int > active_rectangles)
void AddIntegerVariableFromIntervals(SchedulingConstraintHelper *helper, Model *model, std::vector< IntegerVariable > *vars)
Cuts helpers.
void GenerateNoOverlap2dEnergyCut(absl::Span< const std::vector< LiteralValueValue > > energies, absl::Span< int > rectangles, absl::string_view cut_name, Model *model, LinearConstraintManager *manager, SchedulingConstraintHelper *x_helper, SchedulingConstraintHelper *y_helper, SchedulingDemandHelper *y_demands_helper)
CutGenerator CreateNoOverlap2dEnergyCutGenerator(SchedulingConstraintHelper *x_helper, SchedulingConstraintHelper *y_helper, SchedulingDemandHelper *x_demands_helper, SchedulingDemandHelper *y_demands_helper, const std::vector< std::vector< LiteralValueValue > > &energies, Model *model)
bool AtMinOrMaxInt64I(IntegerValue t)
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
Overflows and saturated arithmetic.
double ToDouble(IntegerValue value)
void GenerateNoOvelap2dCompletionTimeCutsWithEnergy(absl::string_view cut_name, std::vector< DiffnCtEvent > events, bool use_lifting, bool skip_low_sizes, Model *model, LinearConstraintManager *manager)
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)
In SWIG mode, we don't want anything besides these top-level includes.
double average
The average absolute value of the finite values.
std::optional< int64_t > end
std::string DebugString() const
std::vector< IntegerVariable > vars
bool only_run_at_level_zero
std::function< bool(LinearConstraintManager *manager)> generate_cuts
Internal methods and data structures, useful for testing.
std::vector< LiteralValueValue > decomposed_energy
IntegerValue x_start_min
Cache of the intervals bound on the x direction.
IntegerValue y_min
Useful for no_overlap_2d or cumulative.
DiffnBaseEvent(int t, SchedulingConstraintHelper *x_helper)
IntegerValue energy_min
The energy min of this event.
std::string DebugString() const
DiffnCtEvent(int t, SchedulingConstraintHelper *x_helper)
AffineExpression x_end
The lp value of the end of the x interval.
std::string DebugString() const
AffineExpression y_size
We need this for linearizing the energy in some cases.
bool energy_is_quadratic
True if linearized_energy is not exact and a McCormick relaxation.
LiteralIndex presence_literal_index
If set, this event is optional and its presence is controlled by this.
LinearExpression linearized_energy
double y_spread
Used to minimize the increase on the y axis for rectangles.
DiffnEnergyEvent(int t, SchedulingConstraintHelper *x_helper)
IntegerValue GetMinOverlap(IntegerValue start, IntegerValue end) const
ABSL_MUST_USE_RESULT bool FillEnergyLp(AffineExpression x_size, const util_intops::StrongVector< IntegerVariable, double > &lp_values, Model *model)
double linearized_energy_lp_value
double LpValue(const util_intops::StrongVector< IntegerVariable, double > &lp_values) const
Return[s] the evaluation of the linear expression.