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 {
22 const std::array<unsigned int, 32> MultiplyDeBruijnBitPosition
23 {
24 { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27,
25  23, 6, 26, 5, 4, 31 } };
26 unsigned int ilog2(unsigned int v)
27 {
28  v |= v >> 1; // first round down to one less than a power of 2
29  v |= v >> 2;
30  v |= v >> 4;
31  v |= v >> 8;
32  v |= v >> 16;
33  return MultiplyDeBruijnBitPosition[(unsigned int) (v * 0x07C4ACDDU) >> 27];
34 }
35 }
36 
37 template<class TYPE, unsigned int numberOfDimensions>
38 class FKDTree
39 {
40 
41  public:
42 
44  {
46  theDepth = 0;
47  }
48 
49  bool empty()
50  {
51  return theNumberOfPoints == 0;
52  }
53 
54  // One can search for all the points which are contained in a k-dimensional box.
55  // Searching is done by providing the two k-dimensional points in the minimum and maximum corners.
56  // The vector that will contain the indices of the points that lay inside the box is also needed.
57  // Indices are pushed into foundPoints, which is not checked for emptiness at the beginning,
58  // nor memory is reserved for it.
59  // Searching is done using a Breadth-first search, level after level.
61  const FKDPoint<TYPE, numberOfDimensions>& maxPoint,
62  std::vector<unsigned int>& foundPoints)
63  {
64  //going down the FKDTree, one needs track which indices have to be visited in the following level.
65  //a custom queue is created, since std::queue is based on lists which are sometimes not performing
66  // well on computing accelerators
67  // The initial size of the queue has to be a power of two for allowing fast modulo % operation.
68  FQueue<unsigned int> indicesToVisit(16);
69 
70  //The root element is pushed first
71  indicesToVisit.push_back(0);
72  unsigned int index;
73  bool intersection;
74  unsigned int dimension;
75  unsigned int numberOfindicesToVisitThisDepth;
76  unsigned int numberOfSonsToVisitNext;
77  unsigned int firstSonToVisitNext;
78 
79  //The loop over levels of the FKDTree starts here
80  for (unsigned int depth = 0; depth < theDepth + 1; ++depth)
81  {
82  // At each level, comparisons are performed for a different dimension in round robin.
83  dimension = depth % numberOfDimensions;
84  numberOfindicesToVisitThisDepth = indicesToVisit.size();
85  // Loop over the nodes to be visit at this level
86  for (unsigned int visitedindicesThisDepth = 0;
87  visitedindicesThisDepth < numberOfindicesToVisitThisDepth;
88  visitedindicesThisDepth++)
89  {
90 
91  index = indicesToVisit[visitedindicesThisDepth];
92  // check if the element's dimension lays between the two box borders
93  intersection = intersects(index, minPoint, maxPoint, dimension);
94  firstSonToVisitNext = leftSonIndex(index);
95 
96 
97  if (intersection)
98  {
99  // Check if the element is contained in the box and push it to the result
100  if (is_in_the_box(index, minPoint, maxPoint))
101  {
102  foundPoints.emplace_back(theIds[index]);
103  }
104  //if the element is between the box borders, both the its sons have to be visited
105  numberOfSonsToVisitNext = (firstSonToVisitNext < theNumberOfPoints)
106  + ((firstSonToVisitNext + 1) < theNumberOfPoints);
107  }
108  else
109  {
110  // depending on the position of the element wrt the box, one son will be visited (if it exists)
111  firstSonToVisitNext += (theDimensions[dimension][index]
112  < minPoint[dimension]);
113 
114  numberOfSonsToVisitNext = std::min(
115  (firstSonToVisitNext < theNumberOfPoints)
116  + ((firstSonToVisitNext + 1) < theNumberOfPoints), 1);
117  }
118 
119  // the indices of the sons to be visited in the next iteration are pushed in the queue
120  for (unsigned int whichSon = 0; whichSon < numberOfSonsToVisitNext; ++whichSon)
121  {
122  indicesToVisit.push_back(firstSonToVisitNext + whichSon);
123  }
124  }
125  // a new element is popped from the queue
126  indicesToVisit.pop_front(numberOfindicesToVisitThisDepth);
127  }
128 
129  }
130 
131  // A vector of K-dimensional points needs to be passed in order to build the kdtree.
132  // The order of the elements in the vector will be modified.
134  {
135  // initialization of the data members
136  theNumberOfPoints = points.size();
137  theDepth = ilog2(theNumberOfPoints);
140  for (unsigned int i = 0; i < numberOfDimensions; ++i)
142  theIds.resize(theNumberOfPoints);
143 
144  // building is done by reordering elements in a partition starting at theIntervalMin
145  // for a number of elements theIntervalLength
146  unsigned int dimension;
147  theIntervalMin[0] = 0;
149 
150  // building for each level starts here
151  for (unsigned int depth = 0; depth < theDepth; ++depth)
152  {
153  // A heapified left-balanced tree can be represented in memory as an array.
154  // Each level contains a power of two number of elements and starts from element 2^depth -1
155  dimension = depth % numberOfDimensions;
156  unsigned int firstIndexInDepth = (1 << depth) - 1;
157  unsigned int maxDepth = (1 << depth);
158  for (unsigned int indexInDepth = 0; indexInDepth < maxDepth; ++indexInDepth)
159  {
160  unsigned int indexInArray = firstIndexInDepth + indexInDepth;
161  unsigned int leftSonIndexInArray = 2 * indexInArray + 1;
162  unsigned int rightSonIndexInArray = leftSonIndexInArray + 1;
163 
164  // partitioning is done by choosing the pivotal element that keeps the tree heapified
165  // and left-balanced
166  unsigned int whichElementInInterval = partition_complete_kdtree(
167  theIntervalLength[indexInArray]);
168  // the elements have been partitioned in two unsorted subspaces (one containing the elements
169  // smaller than the pivot, the other containing those greater than the pivot)
170  std::nth_element(points.begin() + theIntervalMin[indexInArray],
171  points.begin() + theIntervalMin[indexInArray] + whichElementInInterval,
172  points.begin() + theIntervalMin[indexInArray]
173  + theIntervalLength[indexInArray],
175  const FKDPoint<TYPE,numberOfDimensions> & b) -> bool
176  {
177  if(a[dimension] == b[dimension])
178  return a.getId() < b.getId();
179  else
180  return a[dimension] < b[dimension];
181  });
182  // the element is placed in its final position in the internal array representation
183  // of the tree
184  add_at_position(points[theIntervalMin[indexInArray] + whichElementInInterval],
185  indexInArray);
186  if (leftSonIndexInArray < theNumberOfPoints)
187  {
188  theIntervalMin[leftSonIndexInArray] = theIntervalMin[indexInArray];
189  theIntervalLength[leftSonIndexInArray] = whichElementInInterval;
190  }
191 
192  if (rightSonIndexInArray < theNumberOfPoints)
193  {
194  theIntervalMin[rightSonIndexInArray] = theIntervalMin[indexInArray]
195  + whichElementInInterval + 1;
196  theIntervalLength[rightSonIndexInArray] = (theIntervalLength[indexInArray]
197  - 1 - whichElementInInterval);
198  }
199  }
200  }
201  // the last level of the tree may not be complete and needs special treatment
202  dimension = theDepth % numberOfDimensions;
203  unsigned int firstIndexInDepth = (1 << theDepth) - 1;
204  for (unsigned int indexInArray = firstIndexInDepth; indexInArray < theNumberOfPoints;
205  ++indexInArray)
206  {
207  add_at_position(points[theIntervalMin[indexInArray]], indexInArray);
208  }
209 
210  }
211  // returns the number of points in the FKDTree
212  std::size_t size() const
213  {
214  return theNumberOfPoints;
215  }
216 
217  private:
218  // returns the index of the element which makes the FKDtree a left-complete heap
219  // e.g.: if we have 6 elements, the tree will be shaped like
220  // O
221  // / '\'
222  // O O
223  // /'\' /
224  // O OO
225  //
226  // This will return for a length of 6 the 4th element, which will partition the tree so that
227  // 3 elements are on its left and 2 elements are on its right
228  unsigned int partition_complete_kdtree(unsigned int length)
229  {
230  if (length == 1)
231  return 0;
232  unsigned int index = 1 << (ilog2(length));
233 
234  if ((index / 2) - 1 <= length - index)
235  return index - 1;
236  else
237  return length - index / 2;
238  }
239 
240  // returns the index of an element left son in the array representation
241  unsigned int leftSonIndex(unsigned int index) const
242  {
243  return 2 * index + 1;
244  }
245  // returns the index of an element right son in the array representation
246  unsigned int rightSonIndex(unsigned int index) const
247  {
248  return 2 * index + 2;
249  }
250 
251  //check if one element's dimension is between minPoint's and maxPoint's dimension
252  bool intersects(unsigned int index, const FKDPoint<TYPE, numberOfDimensions>& minPoint,
253  const FKDPoint<TYPE, numberOfDimensions>& maxPoint, int dimension) const
254  {
255  return (theDimensions[dimension][index] <= maxPoint[dimension]
256  && theDimensions[dimension][index] >= minPoint[dimension]);
257  }
258 
259  // check if an element is completely in the box
260  bool is_in_the_box(unsigned int index, const FKDPoint<TYPE, numberOfDimensions>& minPoint,
261  const FKDPoint<TYPE, numberOfDimensions>& maxPoint) const
262  {
263  for (unsigned int i = 0; i < numberOfDimensions; ++i)
264  {
265  if ((theDimensions[i][index] <= maxPoint[i]
266  && theDimensions[i][index] >= minPoint[i]) == false)
267  return false;
268  }
269  return true;
270  }
271 
272  // places an element at the specified position in the internal data structure
274  const unsigned int position)
275  {
276  for (unsigned int i = 0; i < numberOfDimensions; ++i)
277  theDimensions[i][position] = point[i];
278  theIds[position] = point.getId();
279  }
280 
282  const unsigned int position)
283  {
284  for (unsigned int i = 0; i < numberOfDimensions; ++i)
285  theDimensions[i][position] = point[i];
286  theIds[position] = point.getId();
287  }
288 
289  unsigned int theNumberOfPoints;
290  unsigned int theDepth;
291 
292  // a SoA containing all the dimensions for each point
293  std::array<std::vector<TYPE>, numberOfDimensions> theDimensions;
294  std::vector<unsigned int> theIntervalLength;
295  std::vector<unsigned int> theIntervalMin;
296  std::vector<unsigned int> theIds;
297 
298 };
299 
300 #endif
std::array< std::vector< TYPE >, numberOfDimensions > theDimensions
Definition: FKDTree.h:293
bool intersects(unsigned int index, const FKDPoint< TYPE, numberOfDimensions > &minPoint, const FKDPoint< TYPE, numberOfDimensions > &maxPoint, int dimension) const
Definition: FKDTree.h:252
unsigned int getId() const
Definition: FKDPoint.h:67
bool empty()
Definition: FKDTree.h:49
unsigned int theDepth
Definition: FKDTree.h:290
std::vector< unsigned int > theIds
Definition: FKDTree.h:296
std::vector< unsigned int > theIntervalMin
Definition: FKDTree.h:295
unsigned int rightSonIndex(unsigned int index) const
Definition: FKDTree.h:246
Definition: FQueue.h:7
unsigned int partition_complete_kdtree(unsigned int length)
Definition: FKDTree.h:228
unsigned int size() const
Definition: FQueue.h:27
void search(const FKDPoint< TYPE, numberOfDimensions > &minPoint, const FKDPoint< TYPE, numberOfDimensions > &maxPoint, std::vector< unsigned int > &foundPoints)
Definition: FKDTree.h:60
T min(T a, T b)
Definition: MathUtil.h:58
bool is_in_the_box(unsigned int index, const FKDPoint< TYPE, numberOfDimensions > &minPoint, const FKDPoint< TYPE, numberOfDimensions > &maxPoint) const
Definition: FKDTree.h:260
void build(std::vector< FKDPoint< TYPE, numberOfDimensions > > &points)
Definition: FKDTree.h:133
void push_back(const T &value)
Definition: FQueue.h:52
unsigned int theNumberOfPoints
Definition: FKDTree.h:289
void add_at_position(const FKDPoint< TYPE, numberOfDimensions > &point, const unsigned int position)
Definition: FKDTree.h:273
double b
Definition: hdecay.h:120
void add_at_position(FKDPoint< TYPE, numberOfDimensions > &&point, const unsigned int position)
Definition: FKDTree.h:281
FKDTree()
Definition: FKDTree.h:43
unsigned int leftSonIndex(unsigned int index) const
Definition: FKDTree.h:241
double a
Definition: hdecay.h:121
static int position[264][3]
Definition: ReadPGInfo.cc:509
std::vector< unsigned int > theIntervalLength
Definition: FKDTree.h:294
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
std::size_t size() const
Definition: FKDTree.h:212
void pop_front()
Definition: FQueue.h:77