theta_tree.h 10.6 KB
Newer Older
Valentin Platzgummer's avatar
Valentin Platzgummer committed
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
// Copyright 2010-2018 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_THETA_TREE_H_
#define OR_TOOLS_SAT_THETA_TREE_H_

#include <vector>

#include "ortools/base/logging.h"
#include "ortools/sat/integer.h"

namespace operations_research {
namespace sat {

// The Theta-Lambda tree can be used to implement several scheduling algorithms.
//
// This template class is instantiated only for IntegerValue and int64.
//
// The tree structure itself is a binary tree coded in a vector, where node 0 is
// unused, node 1 is the root, node 2 is the left child of the root, node 3 its
// right child, etc.
//
// The API gives access to rightmost events that realize a given envelope.
//
// See:
// _ (0) Petr Vilim's PhD thesis "Global Constraints in Scheduling".
// _ (1) Petr Vilim "Edge Finding Filtering Algorithm for Discrete Cumulative
//   Resources in O(kn log n)"
// _ (2) Petr Vilim "Max energy filtering algorithm for discrete cumulative
//   resources".
// _ (3) Wolf & Schrader "O(n log n) Overload Checking for the Cumulative
//   Constraint and Its Application".
// _ (4) Kameugne & Fotso "A cumulative not-first/not-last filtering algorithm
//   in O(n^2 log n)".
// _ (5) Ouellet & Quimper "Time-table extended-edge-finding for the cumulative
//   constraint".
//
// Instead of providing one declination of the theta-tree per possible filtering
// algorithm, this generalization intends to provide a data structure that can
// fit several algorithms.
// This tree is based around the notion of events. It has events at its leaves
// that can be present or absent, and present events come with an
// initial_envelope, a minimal and a maximal energy.
// All nodes maintain values on the set of present events under them:
// _ sum_energy_min(node) = sum_{leaf \in leaves(node)} energy_min(leaf)
// _ envelope(node) =
//     max_{leaf \in leaves(node)}
//       initial_envelope(leaf) +
//       sum_{leaf' \in leaves(node), leaf' >= leaf} energy_min(leaf').
//
// Thus, the envelope of a leaf representing an event, when present, is
//   initial_envelope(event) + sum_energy_min(event).
//
// We also maintain envelope_opt with is the maximum envelope a node could take
// if at most one of the events were at its maximum energy.
// _ energy_delta(leaf) = energy_max(leaf) - energy_min(leaf)
// _ max_energy_delta(node) = max_{leaf \in leaves(node)} energy_delta(leaf)
// _ envelope_opt(node) =
//     max_{leaf \in leaves(node)}
//       initial_envelope(leaf) +
//       sum_{leaf' \in leaves(node), leaf' >= leaf} energy_min(leaf') +
//       max_{leaf' \in leaves(node), leaf' >= leaf} energy_delta(leaf');
//
// Most articles using theta-tree variants hack Vilim's original theta tree
// for the disjunctive resource constraint by manipulating envelope and
// energy:
// _ in (0), initial_envelope = start_min, energy = duration
// _ in (3), initial_envelope = C * start_min, energy = demand * duration
// _ in (5), there are several trees in parallel:
//           initial_envelope = C * start_min or (C - h) * start_min
//           energy = demand * duration, h * (Horizon - start_min),
//                    or h * (end_min).
// _ in (2), same as (3), but putting the max energy instead of min in lambda.
// _ in OscaR's TimeTableOverloadChecker,
//   initial_envelope = C * start_min -
//                      energy of mandatory profile before start_min,
//   energy = demand * duration
//
// There is hope to unify the variants of these algorithms by abstracting the
// tasks away to reason only on events.

92
// The minimal value of an envelope, for instance the envelope of the empty set.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
template <typename IntegerType>
constexpr IntegerType IntegerTypeMinimumValue() {
  return std::numeric_limits<IntegerType>::min();
}
template <>
constexpr IntegerValue IntegerTypeMinimumValue() {
  return kMinIntegerValue;
}

template <typename IntegerType>
class ThetaLambdaTree {
 public:
  // Builds a reusable tree. Initialization is done with Reset().
  ThetaLambdaTree();

  // Initializes this class for events in [0, num_events) and makes all of them
  // absent. Instead of allocating and de-allocating trees at every usage, i.e.
  // at every Propagate() of the scheduling algorithms that uses it, this class
  // allows to keep the same memory for each call.
  void Reset(int num_events);

114 115 116 117 118 119 120 121 122 123
  // Recomputes the values of internal nodes of the tree from the values in the
  // leaves. We enable batching modifications to the tree by providing
  // DelayedXXX() methods that run in O(1), but those methods do not
  // update internal nodes. This breaks tree invariants, so that GetXXX()
  // methods will not reflect modifications made to events.
  // RecomputeTreeForDelayedOperations() restores those invariants in O(n).
  // Thus, batching operations can be done by first doing calls to DelayedXXX()
  // methods, then calling RecomputeTreeForDelayedOperations() once.
  void RecomputeTreeForDelayedOperations();

Valentin Platzgummer's avatar
Valentin Platzgummer committed
124 125 126 127 128 129
  // Makes event present and updates its initial envelope and min/max energies.
  // The initial_envelope must be >= ThetaLambdaTreeNegativeInfinity().
  // This updates the tree in O(log n).
  void AddOrUpdateEvent(int event, IntegerType initial_envelope,
                        IntegerType energy_min, IntegerType energy_max);

130 131 132 133 134
  // Delayed version of AddOrUpdateEvent(),
  // see RecomputeTreeForDelayedOperations().
  void DelayedAddOrUpdateEvent(int event, IntegerType initial_envelope,
                               IntegerType energy_min, IntegerType energy_max);

Valentin Platzgummer's avatar
Valentin Platzgummer committed
135 136 137 138 139 140 141 142
  // Adds event to the lambda part of the tree only.
  // This will leave GetEnvelope() unchanged, only GetOptionalEnvelope() can
  // be affected. This is done by setting envelope to IntegerTypeMinimumValue(),
  // energy_min to 0, and initial_envelope_opt and energy_max to the parameters.
  // This updates the tree in O(log n).
  void AddOrUpdateOptionalEvent(int event, IntegerType initial_envelope_opt,
                                IntegerType energy_max);

143 144 145 146 147 148
  // Delayed version of AddOrUpdateOptionalEvent(),
  // see RecomputeTreeForDelayedOperations().
  void DelayedAddOrUpdateOptionalEvent(int event,
                                       IntegerType initial_envelope_opt,
                                       IntegerType energy_max);

Valentin Platzgummer's avatar
Valentin Platzgummer committed
149 150 151
  // Makes event absent, compute the new envelope in O(log n).
  void RemoveEvent(int event);

152 153 154
  // Delayed version of RemoveEvent(), see RecomputeTreeForDelayedOperations().
  void DelayedRemoveEvent(int event);

Valentin Platzgummer's avatar
Valentin Platzgummer committed
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 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
  // Returns the maximum envelope using all the energy_min in O(1).
  // If theta is empty, returns ThetaLambdaTreeNegativeInfinity().
  IntegerType GetEnvelope() const;

  // Returns the maximum envelope using the energy min of all task but
  // one and the energy max of the last one in O(1).
  // If theta and lambda are empty, returns ThetaLambdaTreeNegativeInfinity().
  IntegerType GetOptionalEnvelope() const;

  // Computes the maximum event s.t. GetEnvelopeOf(event) > envelope_max.
  // There must be such an event, i.e. GetEnvelope() > envelope_max.
  // This finds the maximum event e such that
  // initial_envelope(e) + sum_{e' >= e} energy_min(e') > target_envelope.
  // This operation is O(log n).
  int GetMaxEventWithEnvelopeGreaterThan(IntegerType target_envelope) const;

  // Returns initial_envelope(event) + sum_{event' >= event} energy_min(event'),
  // in time O(log n).
  IntegerType GetEnvelopeOf(int event) const;

  // Computes a pair of events (critical_event, optional_event) such that
  // if optional_event was at its maximum energy, the envelope of critical_event
  // would be greater than target_envelope.
  //
  // This assumes that such a pair exists, i.e. GetOptionalEnvelope() should be
  // greater than target_envelope. More formally, this finds events such that:
  //   initial_envelope(critical_event) +
  //   sum_{event' >= critical_event} energy_min(event') +
  //   max_{optional_event >= critical_event} energy_delta(optional_event)
  //     > target envelope.
  //
  // For efficiency reasons, this also fills available_energy with the maximum
  // energy the optional task can take such that the optional envelope of the
  // pair would be target_envelope, i.e.
  //   target_envelope - GetEnvelopeOf(event) + energy_min(optional_event).
  //
  // This operation is O(log n).
  void GetEventsWithOptionalEnvelopeGreaterThan(
      IntegerType target_envelope, int* critical_event, int* optional_event,
      IntegerType* available_energy) const;

  // Getters.
  IntegerType EnergyMin(int event) const {
    return tree_[GetLeafFromEvent(event)].sum_of_energy_min;
  }

 private:
202 203 204 205 206 207 208 209 210
  struct TreeNode {
    IntegerType envelope;
    IntegerType envelope_opt;
    IntegerType sum_of_energy_min;
    IntegerType max_of_energy_delta;
  };

  TreeNode ComposeTreeNodes(TreeNode left, TreeNode right);

Valentin Platzgummer's avatar
Valentin Platzgummer committed
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
  int GetLeafFromEvent(int event) const;
  int GetEventFromLeaf(int leaf) const;

  // Propagates the change of leaf energies and envelopes towards the root.
  void RefreshNode(int node);

  // Finds the maximum leaf under node such that
  // initial_envelope(leaf) + sum_{leaf' >= leaf} energy_min(leaf')
  //   > target_envelope.
  // Fills extra with the difference.
  int GetMaxLeafWithEnvelopeGreaterThan(int node, IntegerType target_envelope,
                                        IntegerType* extra) const;

  // Returns the leaf with maximum energy delta under node.
  int GetLeafWithMaxEnergyDelta(int node) const;

  // Finds the leaves and energy relevant for
  // GetEventsWithOptionalEnvelopeGreaterThan().
  void GetLeavesWithOptionalEnvelopeGreaterThan(
      IntegerType target_envelope, int* critical_leaf, int* optional_leaf,
      IntegerType* available_energy) const;

  // Number of events of the last Reset().
  int num_events_;
  int num_leaves_;
  int power_of_two_;

238 239 240 241
  // A bool used in debug mode, to check that sequences of delayed operations
  // are ended by Reset() or RecomputeTreeForDelayedOperations().
  bool leaf_nodes_have_delayed_operations_ = false;

Valentin Platzgummer's avatar
Valentin Platzgummer committed
242 243 244 245 246 247 248 249 250 251 252 253
  // Envelopes and energies of nodes.
  std::vector<TreeNode> tree_;
};

// Explicit instantiations in theta_Tree.cc.
extern template class ThetaLambdaTree<IntegerValue>;
extern template class ThetaLambdaTree<int64>;

}  // namespace sat
}  // namespace operations_research

#endif  // OR_TOOLS_SAT_THETA_TREE_H_