forked from google/or-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
scheduling_cuts.h
183 lines (156 loc) · 7.24 KB
/
scheduling_cuts.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// Copyright 2010-2022 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef OR_TOOLS_SAT_SCHEDULING_CUTS_H_
#define OR_TOOLS_SAT_SCHEDULING_CUTS_H_
#include <optional>
#include <string>
#include <vector>
#include "ortools/sat/cuts.h"
#include "ortools/sat/integer.h"
#include "ortools/sat/intervals.h"
#include "ortools/sat/model.h"
namespace operations_research {
namespace sat {
// For a given set of intervals and demands, we compute the energy of
// each task and make sure their sum fits in the span of the intervals * its
// capacity.
//
// If an interval is optional, it contributes
// min_demand * min_size * presence_literal
// amount of total energy.
//
// If an interval is performed, we use the linear energy formulation (if
// defined, that is if different from a constant -1), or the McCormick
// relaxation of the product size * demand if not defined.
//
// The maximum energy is capacity * span of intervals at level 0.
CutGenerator CreateCumulativeEnergyCutGenerator(
SchedulingConstraintHelper* helper, SchedulingDemandHelper* demands_helper,
const AffineExpression& capacity,
const std::optional<AffineExpression>& makespan, Model* model);
// For a given set of intervals and demands, we first compute the mandatory part
// of the interval as [start_max , end_min]. We use this to calculate mandatory
// demands for each start_max time points for eligible intervals.
// Since the sum of these mandatory demands must be smaller or equal to the
// capacity, we create a cut representing that.
//
// If an interval is optional, it contributes min_demand * presence_literal
// amount of demand to the mandatory demands sum. So the final cut is generated
// as follows:
// sum(demands of always present intervals)
// + sum(presence_literal * min_of_demand) <= capacity.
CutGenerator CreateCumulativeTimeTableCutGenerator(
SchedulingConstraintHelper* helper, SchedulingDemandHelper* demands_helper,
const AffineExpression& capacity, Model* model);
// Completion time cuts for the cumulative constraint. It is a simple relaxation
// where we replace a cumulative task with demand k and duration d by a
// no_overlap task with duration d * k / capacity_max.
CutGenerator CreateCumulativeCompletionTimeCutGenerator(
SchedulingConstraintHelper* helper, SchedulingDemandHelper* demands_helper,
const AffineExpression& capacity, Model* model);
// For a given set of intervals in a cumulative constraint, we detect violated
// mandatory precedences and create a cut for these.
CutGenerator CreateCumulativePrecedenceCutGenerator(
SchedulingConstraintHelper* helper, SchedulingDemandHelper* demands_helper,
const AffineExpression& capacity, Model* model);
// For a given set of intervals, we first compute the min and max of all
// intervals. Then we create a cut that indicates that all intervals must fit
// in that span.
//
// If an interval is optional, it contributes min_size * presence_literal
// amount of demand to the mandatory demands sum. So the final cut is generated
// as follows:
// sum(sizes of always present intervals)
// + sum(presence_literal * min_of_size) <= span of all intervals.
CutGenerator CreateNoOverlapEnergyCutGenerator(
SchedulingConstraintHelper* helper,
const std::optional<AffineExpression>& makespan, Model* model);
// For a given set of intervals in a no_overlap constraint, we detect violated
// mandatory precedences and create a cut for these.
CutGenerator CreateNoOverlapPrecedenceCutGenerator(
SchedulingConstraintHelper* helper, Model* model);
// For a given set of intervals in a no_overlap constraint, we detect violated
// area based cuts from Queyranne 93 [see note in the code] and create a cut for
// these.
CutGenerator CreateNoOverlapCompletionTimeCutGenerator(
SchedulingConstraintHelper* helper, Model* model);
// Internal methods and data structures, useful for testing.
// Base event type for scheduling cuts.
struct BaseEvent {
BaseEvent(int t, SchedulingConstraintHelper* x_helper);
// Cache of the intervals bound on the x direction.
IntegerValue x_start_min;
IntegerValue x_start_max;
IntegerValue x_end_min;
IntegerValue x_end_max;
IntegerValue x_size_min;
// Cache of the bounds on the y direction.
IntegerValue y_size_min;
// The energy min of this event.
IntegerValue energy_min;
// If non empty, a decomposed view of the energy of this event.
// First value in each pair is x_size, second is y_size.
std::vector<LiteralValueValue> decomposed_energy;
};
// Stores the event for a rectangle along the two axis x and y.
// For a no_overlap constraint, y is always of size 1 between 0 and 1.
// For a cumulative constraint, y is the demand that must be between 0 and
// capacity_max.
struct CtEvent : BaseEvent {
CtEvent(int t, SchedulingConstraintHelper* x_helper);
// The lp value of the end of the x interval.
AffineExpression x_end;
double x_lp_end;
// Indicates if the events used the optional energy information from the
// model.
bool use_energy = false;
// Indicates if the cut is lifted, that is if it includes tasks that are not
// strictly contained in the current time window.
bool lifted = false;
// If we know that the size on y is fixed, we can use some heuristic to
// compute the maximum subset sums under the capacity and use that instead
// of the full capacity.
bool y_size_is_fixed = false;
std::string DebugString() const;
};
// Computes the minimum sum of the end min and the minimum sum of the end min
// weighted by y_size_min of all events. It returns false if no permatutation is
// valid w.r.t. the range of x_start.
//
// Note that this is an exhaustive algorithm, so the number of events should be
// small, like <= 10. They should also starts in index order.
//
// Optim: If both sums are proven <= to the corresponding threshold, we abort.
struct PermutableEvent {
PermutableEvent(int i, CtEvent e)
: index(i),
x_start_min(e.x_start_min),
x_start_max(e.x_start_max),
x_size_min(e.x_size_min),
y_size_min(e.y_size_min) {}
bool operator<(const PermutableEvent& o) const { return index < o.index; }
int index; // for < to be used by std::next_permutation().
IntegerValue x_start_min;
IntegerValue x_start_max;
IntegerValue x_size_min;
IntegerValue y_size_min;
};
bool ComputeMinSumOfWeightedEndMins(std::vector<PermutableEvent>& events,
IntegerValue capacity_max,
IntegerValue& min_sum_of_end_mins,
IntegerValue& min_sum_of_weighted_end_mins,
IntegerValue unweighted_threshold,
IntegerValue weighted_threshold);
} // namespace sat
} // namespace operations_research
#endif // OR_TOOLS_SAT_SCHEDULING_CUTS_H_