Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
monoid_operation_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_UTIL_MONOID_OPERATION_TREE_H_
15#define OR_TOOLS_UTIL_MONOID_OPERATION_TREE_H_
16
17#include <algorithm>
18#include <string>
19#include <vector>
20
21#include "absl/strings/str_format.h"
23
24namespace operations_research {
25
26// A monoid is an algebraic structure consisting of a set S with an associative
27// binary operation * :S x S -> S that has an identity element.
28// Associative means a*(b*c) = (a*b)*c for all a,b,c in S.
29// An identity element is an element e in S such that for all a in S,
30// e*a = a*e = a.
31// See http://en.wikipedia.org/wiki/Monoid for more details.
32//
33// A MonoidOperationTree is a data structure that maintains a product
34// a_1 * a_2 * ... * a_n for a given (fixed) n, and that supports the
35// following functions:
36// - Setting the k-th operand to a given value in O(log n) calls to the *
37// operation
38// - Querying the result in O(1)
39//
40// Note that the monoid is not required to be commutative.
41//
42// The parameter class T represents an element of the set S.
43// It must:
44// * Have a public no-argument constructor producing the identity element.
45// * Have a = operator method that sets its value to the given one.
46// * Have a Compute(const T& left, const T& right) method that sets its value
47// to the result of the binary operation for the two given operands.
48// * Have a string DebugString() const method.
49//
50// Possible use cases are:
51// * Maintain a sum or a product of doubles, with a guarantee that the queried
52// result is independent of the order of past numerical issues
53// * Maintain a product of identically sized square matrices, which is an
54// example of use with non-commutative operations.
55template <class T>
57 public:
58 // Constructs a MonoidOperationTree able to store 'size' operands.
59 explicit MonoidOperationTree(int size);
60
61 // This type is neither copyable nor movable.
64
65 // Returns the root of the tree, containing the result of the operation.
66 const T& result() const { return *result_; }
67
68 // Resets the argument of given index.
69 void Reset(int argument_index);
70
71 // Sets the argument of given index.
72 void Set(int argument_index, const T& argument);
73
74 // Resets all arguments.
75 void Clear();
76
77 // Returns the leaf node corresponding to the given argument index.
78 const T& GetOperand(int argument_index) const {
79 return nodes_[PositionOfLeaf(argument_index)];
80 }
81
82 // Dive down a branch of the operation tree, and then come back up.
83 template <class Diver>
84 void DiveInTree(Diver* const diver) const {
85 DiveInTree(0, diver);
86 }
87
88 std::string DebugString() const;
89
90 private:
91 // Computes the index of the first leaf for the given size.
92 static int ComputeLeafOffset(int size);
93
94 // Computes the total number of nodes we need to store non-leaf nodes and
95 // leaf nodes.
96 static int ComputeNumberOfNodes(int leaf_offset);
97
98 // Computes the whole path from the node of given position up to the root,
99 // excluding the bottom node.
100 void ComputeAbove(int position);
101
102 // Computes the node of given position, and no other.
103 void Compute(int position);
104
105 // Returns the position of the leaf node of given index.
106 int PositionOfLeaf(int index) const { return leaf_offset_ + index; }
107
108 // Returns true if the node of given position is a leaf.
109 bool IsLeaf(int position) const { return position >= leaf_offset_; }
110
111 // Returns the index of the argument stored in the node of given position.
112 int ArgumentIndexOfLeafPosition(int position) const {
113 DCHECK(IsLeaf(position));
114 return position - leaf_offset_;
115 }
116
117 template <class Diver>
118 void DiveInTree(int position, Diver* diver) const;
119
120 static int father(int pos) { return (pos - 1) >> 1; }
121 static int left(int pos) { return (pos << 1) + 1; }
122 static int right(int pos) { return (pos + 1) << 1; }
123
124 // The number of arguments that can be stored in this tree. That is, the
125 // number of used leaves. (There may be unused leaves, too)
126 const int size_;
127
128 // The index of the first leaf.
129 const int leaf_offset_;
130
131 // Number of nodes, both non-leaves and leaves.
132 const int num_nodes_;
133
134 // All the nodes, both non-leaves and leaves.
135 std::vector<T> nodes_;
136
137 // A pointer to the root node
138 T const* result_;
139};
140
141// --------------------------------------------------------------------- //
142// Implementation
143// --------------------------------------------------------------------- //
144
145template <class T>
146int MonoidOperationTree<T>::ComputeLeafOffset(int size) {
147 int smallest_pow_two_not_less_than_size = 1;
148 while (smallest_pow_two_not_less_than_size < size) {
149 smallest_pow_two_not_less_than_size <<= 1;
150 }
151 return std::max(1, smallest_pow_two_not_less_than_size - 1);
152}
153
154template <class T>
155int MonoidOperationTree<T>::ComputeNumberOfNodes(int leaf_offset) {
156 // leaf_offset should be a power of 2 minus 1.
157 DCHECK_EQ(0, (leaf_offset) & (leaf_offset + 1));
158 const int num_leaves = leaf_offset + 1;
159 const int num_nodes = leaf_offset + num_leaves;
160 DCHECK_GE(num_nodes, 3); // We need at least the root and its 2 children
161 return num_nodes;
162}
163
164template <class T>
166 : size_(size),
167 leaf_offset_(ComputeLeafOffset(size)),
168 num_nodes_(ComputeNumberOfNodes(leaf_offset_)),
169 nodes_(num_nodes_, T()),
170 result_(&(nodes_[0])) {}
171
172template <class T>
174 const int size = nodes_.size();
175 nodes_.assign(size, T());
176}
177
178template <class T>
179void MonoidOperationTree<T>::Reset(int argument_index) {
180 Set(argument_index, T());
181}
182
183template <class T>
184void MonoidOperationTree<T>::Set(int argument_index, const T& argument) {
185 CHECK_LT(argument_index, size_);
186 const int position = leaf_offset_ + argument_index;
187 nodes_[position] = argument;
188 ComputeAbove(position);
189}
190
191template <class T>
192void MonoidOperationTree<T>::ComputeAbove(int position) {
193 int pos = father(position);
194 while (pos > 0) {
195 Compute(pos);
196 pos = father(pos);
197 }
198 Compute(0);
199}
200
201template <class T>
202void MonoidOperationTree<T>::Compute(int position) {
203 const T& left_child = nodes_[left(position)];
204 const T& right_child = nodes_[right(position)];
205 nodes_[position].Compute(left_child, right_child);
206}
207
208template <class T>
210 std::string out;
211 int layer = 0;
212 for (int i = 0; i < num_nodes_; ++i) {
213 if (((i + 1) & i) == 0) {
214 // New layer
215 absl::StrAppendFormat(&out, "-------------- Layer %d ---------------\n",
216 layer);
217 ++layer;
218 }
219 absl::StrAppendFormat(&out, "Position %d: %s\n", i,
220 nodes_[i].DebugString());
221 }
222 return out;
223}
224
225template <class T>
226template <class Diver>
227void MonoidOperationTree<T>::DiveInTree(int position, Diver* diver) const {
228 // Are we at a leaf?
229 if (IsLeaf(position)) {
230 const int index = ArgumentIndexOfLeafPosition(position);
231 const T& argument = nodes_[position];
232 diver->OnArgumentReached(index, argument);
233 } else {
234 const T& current = nodes_[position];
235 const T& left_child = nodes_[left(position)];
236 const T& right_child = nodes_[right(position)];
237 if (diver->ChooseGoLeft(current, left_child, right_child)) {
238 // Go left
239 DiveInTree(left(position), diver);
240 // Come back up
241 diver->OnComeBackFromLeft(current, left_child, right_child);
242 } else {
243 // Go right
244 DiveInTree(right(position), diver);
245 // Come back up
246 diver->OnComeBackFromRight(current, left_child, right_child);
247 }
248 }
249}
250
251} // namespace operations_research
252
253#endif // OR_TOOLS_UTIL_MONOID_OPERATION_TREE_H_
IntegerValue size
int right_child
const T & GetOperand(int argument_index) const
Returns the leaf node corresponding to the given argument index.
MonoidOperationTree(const MonoidOperationTree &)=delete
This type is neither copyable nor movable.
void Reset(int argument_index)
Resets the argument of given index.
void Set(int argument_index, const T &argument)
Sets the argument of given index.
MonoidOperationTree & operator=(const MonoidOperationTree &)=delete
MonoidOperationTree(int size)
Constructs a MonoidOperationTree able to store 'size' operands.
void DiveInTree(Diver *const diver) const
Dive down a branch of the operation tree, and then come back up.
const T & result() const
Returns the root of the tree, containing the result of the operation.
int index
In SWIG mode, we don't want anything besides these top-level includes.