CMS 3D CMS Logo

FKDTree.h
Go to the documentation of this file.
1 #ifndef COMMONTOOLS_RECOALGOS_FKDTREE_H
2 #define COMMONTOOLS_RECOALGOS_FKDTREE_H
3 
4 #include <vector>
5 #include <array>
6 #include <algorithm>
7 #include <cmath>
8 #include <utility>
9 #include "FKDPoint.h"
10 #include "FQueue.h"
11 
12 // Author: Felice Pantaleo
13 // email: felice.pantaleo@cern.ch
14 // date: 08/05/2017
15 // Description: This class provides a k-d tree implementation targeting modern architectures.
16 // Building each level of the FKDTree can be done in parallel by different threads.
17 // It produces a compact array of nodes in memory thanks to the different space partitioning method used.
18 
19 // Fast version of the integer logarithm
20 namespace {
21  const std::array<unsigned int, 32> MultiplyDeBruijnBitPosition{{0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16,
22  18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17,
23  24, 7, 19, 27, 23, 6, 26, 5, 4, 31}};
24  unsigned int ilog2(unsigned int v) {
25  v |= v >> 1; // first round down to one less than a power of 2
26  v |= v >> 2;
27  v |= v >> 4;
28  v |= v >> 8;
29  v |= v >> 16;
30  return MultiplyDeBruijnBitPosition[(unsigned int)(v * 0x07C4ACDDU) >> 27];
31  }
32 } // namespace
33 
34 template <class TYPE, unsigned int numberOfDimensions>
35 class FKDTree {
36 public:
37  FKDTree() {
39  theDepth = 0;
40  }
41 
42  bool empty() { return theNumberOfPoints == 0; }
43 
44  // One can search for all the points which are contained in a k-dimensional box.
45  // Searching is done by providing the two k-dimensional points in the minimum and maximum corners.
46  // The vector that will contain the indices of the points that lay inside the box is also needed.
47  // Indices are pushed into foundPoints, which is not checked for emptiness at the beginning,
48  // nor memory is reserved for it.
49  // Searching is done using a Breadth-first search, level after level.
51  const FKDPoint<TYPE, numberOfDimensions>& maxPoint,
52  std::vector<unsigned int>& foundPoints) {
53  //going down the FKDTree, one needs track which indices have to be visited in the following level.
54  //a custom queue is created, since std::queue is based on lists which are sometimes not performing
55  // well on computing accelerators
56  // The initial size of the queue has to be a power of two for allowing fast modulo % operation.
57  FQueue<unsigned int> indicesToVisit(16);
58 
59  //The root element is pushed first
60  indicesToVisit.push_back(0);
61  unsigned int index;
62  bool intersection;
63  unsigned int dimension;
64  unsigned int numberOfindicesToVisitThisDepth;
65  unsigned int numberOfSonsToVisitNext;
66  unsigned int firstSonToVisitNext;
67 
68  //The loop over levels of the FKDTree starts here
69  for (unsigned int depth = 0; depth < theDepth + 1; ++depth) {
70  // At each level, comparisons are performed for a different dimension in round robin.
71  dimension = depth % numberOfDimensions;
72  numberOfindicesToVisitThisDepth = indicesToVisit.size();
73  // Loop over the nodes to be visit at this level
74  for (unsigned int visitedindicesThisDepth = 0; visitedindicesThisDepth < numberOfindicesToVisitThisDepth;
75  visitedindicesThisDepth++) {
76  index = indicesToVisit[visitedindicesThisDepth];
77  // check if the element's dimension lays between the two box borders
78  intersection = intersects(index, minPoint, maxPoint, dimension);
79  firstSonToVisitNext = leftSonIndex(index);
80 
81  if (intersection) {
82  // Check if the element is contained in the box and push it to the result
83  if (is_in_the_box(index, minPoint, maxPoint)) {
84  foundPoints.emplace_back(theIds[index]);
85  }
86  //if the element is between the box borders, both the its sons have to be visited
87  numberOfSonsToVisitNext =
88  (firstSonToVisitNext < theNumberOfPoints) + ((firstSonToVisitNext + 1) < theNumberOfPoints);
89  } else {
90  // depending on the position of the element wrt the box, one son will be visited (if it exists)
91  firstSonToVisitNext += (theDimensions[dimension][index] < minPoint[dimension]);
92 
93  numberOfSonsToVisitNext =
94  std::min((firstSonToVisitNext < theNumberOfPoints) + ((firstSonToVisitNext + 1) < theNumberOfPoints), 1);
95  }
96 
97  // the indices of the sons to be visited in the next iteration are pushed in the queue
98  for (unsigned int whichSon = 0; whichSon < numberOfSonsToVisitNext; ++whichSon) {
99  indicesToVisit.push_back(firstSonToVisitNext + whichSon);
100  }
101  }
102  // a new element is popped from the queue
103  indicesToVisit.pop_front(numberOfindicesToVisitThisDepth);
104  }
105  }
106 
107  // A vector of K-dimensional points needs to be passed in order to build the kdtree.
108  // The order of the elements in the vector will be modified.
110  // initialization of the data members
111  theNumberOfPoints = points.size();
115  for (unsigned int i = 0; i < numberOfDimensions; ++i)
117  theIds.resize(theNumberOfPoints);
118 
119  // building is done by reordering elements in a partition starting at theIntervalMin
120  // for a number of elements theIntervalLength
121  unsigned int dimension;
122  theIntervalMin[0] = 0;
124 
125  // building for each level starts here
126  for (unsigned int depth = 0; depth < theDepth; ++depth) {
127  // A heapified left-balanced tree can be represented in memory as an array.
128  // Each level contains a power of two number of elements and starts from element 2^depth -1
129  dimension = depth % numberOfDimensions;
130  unsigned int firstIndexInDepth = (1 << depth) - 1;
131  unsigned int maxDepth = (1 << depth);
132  for (unsigned int indexInDepth = 0; indexInDepth < maxDepth; ++indexInDepth) {
133  unsigned int indexInArray = firstIndexInDepth + indexInDepth;
134  unsigned int leftSonIndexInArray = 2 * indexInArray + 1;
135  unsigned int rightSonIndexInArray = leftSonIndexInArray + 1;
136 
137  // partitioning is done by choosing the pivotal element that keeps the tree heapified
138  // and left-balanced
139  unsigned int whichElementInInterval = partition_complete_kdtree(theIntervalLength[indexInArray]);
140  // the elements have been partitioned in two unsorted subspaces (one containing the elements
141  // smaller than the pivot, the other containing those greater than the pivot)
142  std::nth_element(points.begin() + theIntervalMin[indexInArray],
143  points.begin() + theIntervalMin[indexInArray] + whichElementInInterval,
144  points.begin() + theIntervalMin[indexInArray] + theIntervalLength[indexInArray],
146  const FKDPoint<TYPE, numberOfDimensions>& b) -> bool {
147  if (a[dimension] == b[dimension])
148  return a.getId() < b.getId();
149  else
150  return a[dimension] < b[dimension];
151  });
152  // the element is placed in its final position in the internal array representation
153  // of the tree
154  add_at_position(points[theIntervalMin[indexInArray] + whichElementInInterval], indexInArray);
155  if (leftSonIndexInArray < theNumberOfPoints) {
156  theIntervalMin[leftSonIndexInArray] = theIntervalMin[indexInArray];
157  theIntervalLength[leftSonIndexInArray] = whichElementInInterval;
158  }
159 
160  if (rightSonIndexInArray < theNumberOfPoints) {
161  theIntervalMin[rightSonIndexInArray] = theIntervalMin[indexInArray] + whichElementInInterval + 1;
162  theIntervalLength[rightSonIndexInArray] = (theIntervalLength[indexInArray] - 1 - whichElementInInterval);
163  }
164  }
165  }
166  // the last level of the tree may not be complete and needs special treatment
167  dimension = theDepth % numberOfDimensions;
168  unsigned int firstIndexInDepth = (1 << theDepth) - 1;
169  for (unsigned int indexInArray = firstIndexInDepth; indexInArray < theNumberOfPoints; ++indexInArray) {
170  add_at_position(points[theIntervalMin[indexInArray]], indexInArray);
171  }
172  }
173  // returns the number of points in the FKDTree
174  std::size_t size() const { return theNumberOfPoints; }
175 
176 private:
177  // returns the index of the element which makes the FKDtree a left-complete heap
178  // e.g.: if we have 6 elements, the tree will be shaped like
179  // O
180  // / '\'
181  // O O
182  // /'\' /
183  // O OO
184  //
185  // This will return for a length of 6 the 4th element, which will partition the tree so that
186  // 3 elements are on its left and 2 elements are on its right
187  unsigned int partition_complete_kdtree(unsigned int length) {
188  if (length == 1)
189  return 0;
190  unsigned int index = 1 << (ilog2(length));
191 
192  if ((index / 2) - 1 <= length - index)
193  return index - 1;
194  else
195  return length - index / 2;
196  }
197 
198  // returns the index of an element left son in the array representation
199  unsigned int leftSonIndex(unsigned int index) const { return 2 * index + 1; }
200  // returns the index of an element right son in the array representation
201  unsigned int rightSonIndex(unsigned int index) const { return 2 * index + 2; }
202 
203  //check if one element's dimension is between minPoint's and maxPoint's dimension
204  bool intersects(unsigned int index,
205  const FKDPoint<TYPE, numberOfDimensions>& minPoint,
206  const FKDPoint<TYPE, numberOfDimensions>& maxPoint,
207  int dimension) const {
208  return (theDimensions[dimension][index] <= maxPoint[dimension] &&
209  theDimensions[dimension][index] >= minPoint[dimension]);
210  }
211 
212  // check if an element is completely in the box
213  bool is_in_the_box(unsigned int index,
214  const FKDPoint<TYPE, numberOfDimensions>& minPoint,
215  const FKDPoint<TYPE, numberOfDimensions>& maxPoint) const {
216  for (unsigned int i = 0; i < numberOfDimensions; ++i) {
217  if ((theDimensions[i][index] <= maxPoint[i] && theDimensions[i][index] >= minPoint[i]) == false)
218  return false;
219  }
220  return true;
221  }
222 
223  // places an element at the specified position in the internal data structure
225  for (unsigned int i = 0; i < numberOfDimensions; ++i)
227  theIds[position] = point.getId();
228  }
229 
231  for (unsigned int i = 0; i < numberOfDimensions; ++i)
233  theIds[position] = point.getId();
234  }
235 
236  unsigned int theNumberOfPoints;
237  unsigned int theDepth;
238 
239  // a SoA containing all the dimensions for each point
240  std::array<std::vector<TYPE>, numberOfDimensions> theDimensions;
241  std::vector<unsigned int> theIntervalLength;
242  std::vector<unsigned int> theIntervalMin;
243  std::vector<unsigned int> theIds;
244 };
245 
246 #endif
std::array< std::vector< TYPE >, numberOfDimensions > theDimensions
Definition: FKDTree.h:240
unsigned int rightSonIndex(unsigned int index) const
Definition: FKDTree.h:201
bool empty()
Definition: FKDTree.h:42
unsigned int theDepth
Definition: FKDTree.h:237
std::size_t size() const
Definition: FKDTree.h:174
unsigned int size() const
Definition: FQueue.h:24
std::vector< unsigned int > theIds
Definition: FKDTree.h:243
std::vector< unsigned int > theIntervalMin
Definition: FKDTree.h:242
Definition: FQueue.h:7
int ilog2(double factor)
Definition: Util.h:106
unsigned int partition_complete_kdtree(unsigned int length)
Definition: FKDTree.h:187
void search(const FKDPoint< TYPE, numberOfDimensions > &minPoint, const FKDPoint< TYPE, numberOfDimensions > &maxPoint, std::vector< unsigned int > &foundPoints)
Definition: FKDTree.h:50
void build(std::vector< FKDPoint< TYPE, numberOfDimensions > > &points)
Definition: FKDTree.h:109
void push_back(const T &value)
Definition: FQueue.h:34
unsigned int theNumberOfPoints
Definition: FKDTree.h:236
void add_at_position(const FKDPoint< TYPE, numberOfDimensions > &point, const unsigned int position)
Definition: FKDTree.h:224
double b
Definition: hdecay.h:118
void add_at_position(FKDPoint< TYPE, numberOfDimensions > &&point, const unsigned int position)
Definition: FKDTree.h:230
FKDTree()
Definition: FKDTree.h:37
double a
Definition: hdecay.h:119
static int position[264][3]
Definition: ReadPGInfo.cc:289
std::vector< unsigned int > theIntervalLength
Definition: FKDTree.h:241
bool intersects(unsigned int index, const FKDPoint< TYPE, numberOfDimensions > &minPoint, const FKDPoint< TYPE, numberOfDimensions > &maxPoint, int dimension) const
Definition: FKDTree.h:204
unsigned int leftSonIndex(unsigned int index) const
Definition: FKDTree.h:199
uint32_t dimension(pat::CandKinResolution::Parametrization parametrization)
Returns the number of free parameters in a parametrization (3 or 4)
*vegas h *****************************************************used in the default bin number in original ***version of VEGAS is ***a higher bin number might help to derive a more precise ***grade subtle point
Definition: invegas.h:5
bool is_in_the_box(unsigned int index, const FKDPoint< TYPE, numberOfDimensions > &minPoint, const FKDPoint< TYPE, numberOfDimensions > &maxPoint) const
Definition: FKDTree.h:213
void pop_front()
Definition: FQueue.h:52