Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
theta_tree.h
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef OR_TOOLS_SAT_THETA_TREE_H_
15#define OR_TOOLS_SAT_THETA_TREE_H_
16
17#include <cstdint>
18#include <vector>
19
21#include "ortools/sat/integer.h"
22
23namespace operations_research {
24namespace sat {
25
26// The Theta-Lambda tree can be used to implement several scheduling algorithms.
27//
28// This template class is instantiated only for IntegerValue and int64_t.
29//
30// The tree structure itself is a binary tree coded in a vector, where node 0 is
31// unused, node 1 is the root, node 2 is the left child of the root, node 3 its
32// right child, etc.
33//
34// The API gives access to rightmost events that realize a given envelope.
35//
36// See:
37// _ (0) Petr Vilim's PhD thesis "Global Constraints in Scheduling".
38// _ (1) Petr Vilim "Edge Finding Filtering Algorithm for Discrete Cumulative
39// Resources in O(kn log n)"
40// _ (2) Petr Vilim "Max energy filtering algorithm for discrete cumulative
41// resources".
42// _ (3) Wolf & Schrader "O(n log n) Overload Checking for the Cumulative
43// Constraint and Its Application".
44// _ (4) Kameugne & Fotso "A cumulative not-first/not-last filtering algorithm
45// in O(n^2 log n)".
46// _ (5) Ouellet & Quimper "Time-table extended-edge-finding for the cumulative
47// constraint".
48//
49// Instead of providing one declination of the theta-tree per possible filtering
50// algorithm, this generalization intends to provide a data structure that can
51// fit several algorithms.
52// This tree is based around the notion of events. It has events at its leaves
53// that can be present or absent, and present events come with an
54// initial_envelope, a minimal and a maximal energy.
55// All nodes maintain values on the set of present events under them:
56// _ sum_energy_min(node) = sum_{leaf \in leaves(node)} energy_min(leaf)
57// _ envelope(node) =
58// max_{leaf \in leaves(node)}
59// initial_envelope(leaf) +
60// sum_{leaf' \in leaves(node), leaf' >= leaf} energy_min(leaf').
61//
62// Thus, the envelope of a leaf representing an event, when present, is
63// initial_envelope(event) + sum_energy_min(event).
64//
65// We also maintain envelope_opt with is the maximum envelope a node could take
66// if at most one of the events were at its maximum energy.
67// _ energy_delta(leaf) = energy_max(leaf) - energy_min(leaf)
68// _ max_energy_delta(node) = max_{leaf \in leaves(node)} energy_delta(leaf)
69// _ envelope_opt(node) =
70// max_{leaf \in leaves(node)}
71// initial_envelope(leaf) +
72// sum_{leaf' \in leaves(node), leaf' >= leaf} energy_min(leaf') +
73// max_{leaf' \in leaves(node), leaf' >= leaf} energy_delta(leaf');
74//
75// Most articles using theta-tree variants hack Vilim's original theta tree
76// for the disjunctive resource constraint by manipulating envelope and
77// energy:
78// _ in (0), initial_envelope = start_min, energy = duration
79// _ in (3), initial_envelope = C * start_min, energy = demand * duration
80// _ in (5), there are several trees in parallel:
81// initial_envelope = C * start_min or (C - h) * start_min
82// energy = demand * duration, h * (Horizon - start_min),
83// or h * (end_min).
84// _ in (2), same as (3), but putting the max energy instead of min in lambda.
85// _ in OscaR's TimeTableOverloadChecker,
86// initial_envelope = C * start_min -
87// energy of mandatory profile before start_min,
88// energy = demand * duration
89//
90// There is hope to unify the variants of these algorithms by abstracting the
91// tasks away to reason only on events.
92
93// The minimal value of an envelope, for instance the envelope of the empty set.
94template <typename IntegerType>
95constexpr IntegerType IntegerTypeMinimumValue() {
96 return std::numeric_limits<IntegerType>::min();
97}
98template <>
99constexpr IntegerValue IntegerTypeMinimumValue() {
100 return kMinIntegerValue;
101}
102
103template <typename IntegerType>
105 public:
106 // Builds a reusable tree. Initialization is done with Reset().
108
109 // Initializes this class for events in [0, num_events) and makes all of them
110 // absent. Instead of allocating and de-allocating trees at every usage, i.e.
111 // at every Propagate() of the scheduling algorithms that uses it, this class
112 // allows to keep the same memory for each call.
113 void Reset(int num_events);
114
115 // Recomputes the values of internal nodes of the tree from the values in the
116 // leaves. We enable batching modifications to the tree by providing
117 // DelayedXXX() methods that run in O(1), but those methods do not
118 // update internal nodes. This breaks tree invariants, so that GetXXX()
119 // methods will not reflect modifications made to events.
120 // RecomputeTreeForDelayedOperations() restores those invariants in O(n).
121 // Thus, batching operations can be done by first doing calls to DelayedXXX()
122 // methods, then calling RecomputeTreeForDelayedOperations() once.
124
125 // Makes event present and updates its initial envelope and min/max energies.
126 // The initial_envelope must be >= ThetaLambdaTreeNegativeInfinity().
127 // This updates the tree in O(log n).
128 void AddOrUpdateEvent(int event, IntegerType initial_envelope,
129 IntegerType energy_min, IntegerType energy_max);
130
131 // Delayed version of AddOrUpdateEvent(),
132 // see RecomputeTreeForDelayedOperations().
133 void DelayedAddOrUpdateEvent(int event, IntegerType initial_envelope,
134 IntegerType energy_min, IntegerType energy_max);
135
136 // Adds event to the lambda part of the tree only.
137 // This will leave GetEnvelope() unchanged, only GetOptionalEnvelope() can
138 // be affected. This is done by setting envelope to IntegerTypeMinimumValue(),
139 // energy_min to 0, and initial_envelope_opt and energy_max to the parameters.
140 // This updates the tree in O(log n).
141 void AddOrUpdateOptionalEvent(int event, IntegerType initial_envelope_opt,
142 IntegerType energy_max);
143
144 // Delayed version of AddOrUpdateOptionalEvent(),
145 // see RecomputeTreeForDelayedOperations().
146 void DelayedAddOrUpdateOptionalEvent(int event,
147 IntegerType initial_envelope_opt,
148 IntegerType energy_max);
149
150 // Makes event absent, compute the new envelope in O(log n).
151 void RemoveEvent(int event);
152
153 // Delayed version of RemoveEvent(), see RecomputeTreeForDelayedOperations().
154 void DelayedRemoveEvent(int event);
155
156 // Returns the maximum envelope using all the energy_min in O(1).
157 // If theta is empty, returns ThetaLambdaTreeNegativeInfinity().
158 IntegerType GetEnvelope() const;
159
160 // Returns the maximum envelope using the energy min of all task but
161 // one and the energy max of the last one in O(1).
162 // If theta and lambda are empty, returns ThetaLambdaTreeNegativeInfinity().
163 IntegerType GetOptionalEnvelope() const;
164
165 // Computes the maximum event s.t. GetEnvelopeOf(event) > envelope_max.
166 // There must be such an event, i.e. GetEnvelope() > envelope_max.
167 // This finds the maximum event e such that
168 // initial_envelope(e) + sum_{e' >= e} energy_min(e') > target_envelope.
169 // This operation is O(log n).
170 int GetMaxEventWithEnvelopeGreaterThan(IntegerType target_envelope) const;
171
172 // Returns initial_envelope(event) + sum_{event' >= event} energy_min(event'),
173 // in time O(log n).
174 IntegerType GetEnvelopeOf(int event) const;
175
176 // Computes a pair of events (critical_event, optional_event) such that
177 // if optional_event was at its maximum energy, the envelope of critical_event
178 // would be greater than target_envelope.
179 //
180 // This assumes that such a pair exists, i.e. GetOptionalEnvelope() should be
181 // greater than target_envelope. More formally, this finds events such that:
182 // initial_envelope(critical_event) +
183 // sum_{event' >= critical_event} energy_min(event') +
184 // max_{optional_event >= critical_event} energy_delta(optional_event)
185 // > target envelope.
186 //
187 // For efficiency reasons, this also fills available_energy with the maximum
188 // energy the optional task can take such that the optional envelope of the
189 // pair would be target_envelope, i.e.
190 // target_envelope - GetEnvelopeOf(event) + energy_min(optional_event).
191 //
192 // This operation is O(log n).
194 IntegerType target_envelope, int* critical_event, int* optional_event,
195 IntegerType* available_energy) const;
196
197 // Getters.
198 IntegerType EnergyMin(int event) const {
199 return tree_[GetLeafFromEvent(event)].sum_of_energy_min;
200 }
201
202 private:
203 struct TreeNode {
204 IntegerType envelope;
205 IntegerType envelope_opt;
206 IntegerType sum_of_energy_min;
207 IntegerType max_of_energy_delta;
208 };
209
210 TreeNode ComposeTreeNodes(const TreeNode& left, const TreeNode& right);
211
212 int GetLeafFromEvent(int event) const;
213 int GetEventFromLeaf(int leaf) const;
214
215 // Propagates the change of leaf energies and envelopes towards the root.
216 void RefreshNode(int node);
217
218 // Finds the maximum leaf under node such that
219 // initial_envelope(leaf) + sum_{leaf' >= leaf} energy_min(leaf')
220 // > target_envelope.
221 // Fills extra with the difference.
222 int GetMaxLeafWithEnvelopeGreaterThan(int node, IntegerType target_envelope,
223 IntegerType* extra) const;
224
225 // Returns the leaf with maximum energy delta under node.
226 int GetLeafWithMaxEnergyDelta(int node) const;
227
228 // Finds the leaves and energy relevant for
229 // GetEventsWithOptionalEnvelopeGreaterThan().
230 void GetLeavesWithOptionalEnvelopeGreaterThan(
231 IntegerType target_envelope, int* critical_leaf, int* optional_leaf,
232 IntegerType* available_energy) const;
233
234 // Number of events of the last Reset().
235 int num_events_;
236 int num_leaves_;
237 int power_of_two_;
238
239 // A bool used in debug mode, to check that sequences of delayed operations
240 // are ended by Reset() or RecomputeTreeForDelayedOperations().
241 bool leaf_nodes_have_delayed_operations_ = false;
242
243 // Envelopes and energies of nodes.
244 std::vector<TreeNode> tree_;
245};
246
247// Explicit instantiations in theta_tree.cc.
248extern template class ThetaLambdaTree<IntegerValue>;
249extern template class ThetaLambdaTree<int64_t>;
250
251} // namespace sat
252} // namespace operations_research
253
254#endif // OR_TOOLS_SAT_THETA_TREE_H_
void DelayedRemoveEvent(int event)
Delayed version of RemoveEvent(), see RecomputeTreeForDelayedOperations().
IntegerType EnergyMin(int event) const
Getters.
Definition theta_tree.h:198
ThetaLambdaTree()
Builds a reusable tree. Initialization is done with Reset().
void DelayedAddOrUpdateOptionalEvent(int event, IntegerType initial_envelope_opt, IntegerType energy_max)
void RemoveEvent(int event)
Makes event absent, compute the new envelope in O(log n).
void AddOrUpdateEvent(int event, IntegerType initial_envelope, IntegerType energy_min, IntegerType energy_max)
void GetEventsWithOptionalEnvelopeGreaterThan(IntegerType target_envelope, int *critical_event, int *optional_event, IntegerType *available_energy) const
int GetMaxEventWithEnvelopeGreaterThan(IntegerType target_envelope) const
IntegerType GetEnvelopeOf(int event) const
void AddOrUpdateOptionalEvent(int event, IntegerType initial_envelope_opt, IntegerType energy_max)
void DelayedAddOrUpdateEvent(int event, IntegerType initial_envelope, IntegerType energy_min, IntegerType energy_max)
Definition theta_tree.cc:99
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
constexpr IntegerType IntegerTypeMinimumValue()
The minimal value of an envelope, for instance the envelope of the empty set.
Definition theta_tree.h:95
In SWIG mode, we don't want anything besides these top-level includes.