CMS 3D CMS Logo

KDTreeLinkerAlgo.h
Go to the documentation of this file.
1 #ifndef KDTreeLinkerAlgoTemplated_h
2 #define KDTreeLinkerAlgoTemplated_h
3 
4 #include <cassert>
5 #include <vector>
6 #include <array>
7 #include <algorithm>
8 
9 // Box structure used to define 2D field.
10 // It's used in KDTree building step to divide the detector
11 // space (ECAL, HCAL...) and in searching step to create a bounding
12 // box around the demanded point (Track collision point, PS projection...).
13 template <unsigned DIM = 2>
14 struct KDTreeBox {
15  std::array<float, DIM> dimmin, dimmax;
16 
17  template <typename... Ts>
18  KDTreeBox(Ts... dimargs) {
19  static_assert(sizeof...(dimargs) == 2 * DIM, "Constructor requires 2*DIM args");
20  std::vector<float> dims = {dimargs...};
21  for (unsigned i = 0; i < DIM; ++i) {
22  dimmin[i] = dims[2 * i];
23  dimmax[i] = dims[2 * i + 1];
24  }
25  }
26 
27  KDTreeBox() {}
28 };
29 
30 // Data stored in each KDTree node.
31 // The dim1/dim2 fields are usually the duplication of some PFRecHit values
32 // (eta/phi or x/y). But in some situations, phi field is shifted by +-2.Pi
33 template <typename DATA, unsigned DIM = 2>
36  std::array<float, DIM> dims;
37 
38 public:
40 
41  template <typename... Ts>
42  KDTreeNodeInfo(const DATA &d, Ts... dimargs) : data(d), dims{{dimargs...}} {}
43  template <typename... Ts>
44  bool operator>(const KDTreeNodeInfo &rhs) const {
45  return (data > rhs.data);
46  }
47 };
48 
49 template <typename DATA, unsigned DIM = 2>
50 struct KDTreeNodes {
51  std::array<std::vector<float>, DIM> dims;
52  std::vector<int> right;
53  std::vector<DATA> data;
54 
55  int poolSize;
56  int poolPos;
57 
59 
60  bool empty() const { return poolPos == -1; }
61  int size() const { return poolPos + 1; }
62 
63  void clear() {
64  for (auto &dim : dims) {
65  dim.clear();
66  }
67  right.clear();
68  data.clear();
69  poolSize = -1;
70  poolPos = -1;
71  }
72 
73  int getNextNode() {
74  ++poolPos;
75  return poolPos;
76  }
77 
78  void build(int sizeData) {
79  poolSize = sizeData * 2 - 1;
80  for (auto &dim : dims) {
81  dim.resize(poolSize);
82  }
83  right.resize(poolSize);
84  data.resize(poolSize);
85  };
86 
87  constexpr bool isLeaf(int right) const {
88  // Valid values of right are always >= 2
89  // index 0 is the root, and 1 is the first left node
90  // Exploit index values 0 and 1 to mark which of dim1/dim2 is the
91  // current one in recSearch() at the depth of the leaf.
92  return right < 2;
93  }
94 
95  bool isLeafIndex(int index) const { return isLeaf(right[index]); }
96 };
97 
98 // Class that implements the KDTree partition of 2D space and
99 // a closest point search algorithme.
100 
101 template <typename DATA, unsigned int DIM = 2>
103 public:
104  // Dtor calls clear()
106 
107  // Here we build the KD tree from the "eltList" in the space define by "region".
109 
110  // Here we search in the KDTree for all points that would be
111  // contained in the given searchbox. The founded points are stored in resRecHitList.
112  void search(const KDTreeBox<DIM> &searchBox, std::vector<DATA> &resRecHitList);
113 
114  // This reurns true if the tree is empty
115  bool empty() { return nodePool_.empty(); }
116 
117  // This returns the number of nodes + leaves in the tree
118  // (nElements should be (size() +1)/2)
119  int size() { return nodePool_.size(); }
120 
121  // This method clears all allocated structures.
122  void clear() { clearTree(); }
123 
124 private:
125  // The node pool allow us to do just 1 call to new for each tree building.
127 
128  std::vector<DATA> *closestNeighbour;
129  std::vector<KDTreeNodeInfo<DATA, DIM> > *initialEltList;
130 
131  //Fast median search with Wirth algorithm in eltList between low and high indexes.
132  int medianSearch(int low, int high, int treeDepth) const;
133 
134  // Recursif kdtree builder. Is called by build()
135  int recBuild(int low, int hight, int depth);
136 
137  // Recursif kdtree search. Is called by search()
138  void recSearch(int current, const KDTreeBox<DIM> &trackBox, int depth = 0) const;
139 
140  // This method frees the KDTree.
141  void clearTree() { nodePool_.clear(); }
142 };
143 
144 //Implementation
145 
146 template <typename DATA, unsigned int DIM>
148  const KDTreeBox<DIM> &region) {
149  if (!eltList.empty()) {
150  initialEltList = &eltList;
151 
152  size_t size = initialEltList->size();
153  nodePool_.build(size);
154 
155  // Here we build the KDTree
156  int root = recBuild(0, size, 0);
157  assert(root == 0);
158 
159  initialEltList = nullptr;
160  }
161 }
162 
163 //Fast median search with Wirth algorithm in eltList between low and high indexes.
164 template <typename DATA, unsigned int DIM>
165 int KDTreeLinkerAlgo<DATA, DIM>::medianSearch(int low, int high, int treeDepth) const {
166  int nbrElts = high - low;
167  int median = (nbrElts & 1) ? nbrElts / 2 : nbrElts / 2 - 1;
168  median += low;
169 
170  int l = low;
171  int m = high - 1;
172 
173  while (l < m) {
174  KDTreeNodeInfo<DATA, DIM> elt = (*initialEltList)[median];
175  int i = l;
176  int j = m;
177 
178  do {
179  // The even depth is associated to dim1 dimension
180  // The odd one to dim2 dimension
181  const unsigned thedim = treeDepth % DIM;
182  while ((*initialEltList)[i].dims[thedim] < elt.dims[thedim])
183  ++i;
184  while ((*initialEltList)[j].dims[thedim] > elt.dims[thedim])
185  --j;
186 
187  if (i <= j) {
188  std::swap((*initialEltList)[i], (*initialEltList)[j]);
189  i++;
190  j--;
191  }
192  } while (i <= j);
193  if (j < median)
194  l = i;
195  if (i > median)
196  m = j;
197  }
198 
199  return median;
200 }
201 
202 template <typename DATA, unsigned int DIM>
203 void KDTreeLinkerAlgo<DATA, DIM>::search(const KDTreeBox<DIM> &trackBox, std::vector<DATA> &recHits) {
204  if (!empty()) {
205  closestNeighbour = &recHits;
206  recSearch(0, trackBox, 0);
207  closestNeighbour = nullptr;
208  }
209 }
210 
211 template <typename DATA, unsigned int DIM>
212 void KDTreeLinkerAlgo<DATA, DIM>::recSearch(int current, const KDTreeBox<DIM> &trackBox, int depth) const {
213  // Iterate until leaf is found, or there are no children in the
214  // search window. If search has to proceed on both children, proceed
215  // the search to left child via recursion. Swap search window
216  // dimension on alternate levels.
217  while (true) {
218  const int dimIndex = depth % DIM;
219  int right = nodePool_.right[current];
220  if (nodePool_.isLeaf(right)) {
221  // If point inside the rectangle/area
222  // Use intentionally bit-wise & instead of logical && for better
223  // performance. It is faster to always do all comparisons than to
224  // allow use of branches to not do some if any of the first ones
225  // is false.
226  bool isInside = true;
227  for (unsigned i = 0; i < DIM; ++i) {
228  float dimCurr = nodePool_.dims[i][current];
229  isInside &= (dimCurr >= trackBox.dimmin[i]) && (dimCurr <= trackBox.dimmax[i]);
230  }
231  if (isInside) {
232  closestNeighbour->push_back(nodePool_.data[current]);
233  }
234  break;
235  } else {
236  float median = nodePool_.dims[dimIndex][current];
237 
238  bool goLeft = (trackBox.dimmin[dimIndex] <= median);
239  bool goRight = (trackBox.dimmax[dimIndex] >= median);
240 
241  ++depth;
242  if (goLeft & goRight) {
243  int left = current + 1;
244  recSearch(left, trackBox, depth);
245  // continue with right
246  current = right;
247  } else if (goLeft) {
248  ++current;
249  } else if (goRight) {
250  current = right;
251  } else {
252  break;
253  }
254  }
255  }
256 }
257 
258 template <typename DATA, unsigned int DIM>
260  int portionSize = high - low;
261 
262  if (portionSize == 1) { // Leaf case
263  int leaf = nodePool_.getNextNode();
264  const KDTreeNodeInfo<DATA, DIM> &info = (*initialEltList)[low];
265  nodePool_.right[leaf] = 0;
266  for (unsigned i = 0; i < DIM; ++i) {
267  nodePool_.dims[i][leaf] = info.dims[i];
268  }
269  nodePool_.data[leaf] = info.data;
270  return leaf;
271 
272  } else { // Node case
273 
274  // The even depth is associated to dim1 dimension
275  // The odd one to dim2 dimension
276  int medianId = medianSearch(low, high, depth);
277  int dimIndex = depth % DIM;
278  float medianVal = (*initialEltList)[medianId].dims[dimIndex];
279 
280  // We create the node
281  int nodeInd = nodePool_.getNextNode();
282 
283  ++depth;
284  ++medianId;
285 
286  // We recursively build the son nodes
287  int left = recBuild(low, medianId, depth);
288  assert(nodeInd + 1 == left);
289  int right = recBuild(medianId, high, depth);
290  nodePool_.right[nodeInd] = right;
291 
292  nodePool_.dims[dimIndex][nodeInd] = medianVal;
293 
294  return nodeInd;
295  }
296 }
297 
298 #endif
size
Write out results.
static const TGPicture * info(bool iBackgroundIsBlack)
KDTreeNodes< DATA, DIM > nodePool_
void search(const KDTreeBox< DIM > &searchBox, std::vector< DATA > &resRecHitList)
std::array< std::vector< float >, DIM > dims
int recBuild(int low, int hight, int depth)
std::vector< DATA > * closestNeighbour
std::vector< DATA > data
#define DIM(a)
std::array< float, DIM > dimmin
constexpr KDTreeNodes()
assert(be >=bs)
void swap(Association< C > &lhs, Association< C > &rhs)
Definition: Association.h:112
void build(int sizeData)
bool isLeafIndex(int index) const
void recSearch(int current, const KDTreeBox< DIM > &trackBox, int depth=0) const
constexpr bool isLeaf(int right) const
int medianSearch(int low, int high, int treeDepth) const
KDTreeBox(Ts... dimargs)
d
Definition: ztail.py:151
std::array< float, DIM > dims
void build(std::vector< KDTreeNodeInfo< DATA, DIM > > &eltList, const KDTreeBox< DIM > &region)
std::vector< KDTreeNodeInfo< DATA, DIM > > * initialEltList
KDTreeNodeInfo(const DATA &d, Ts... dimargs)
std::array< float, DIM > dimmax
T median(std::vector< T > values)
Definition: median.h:16
int size() const
std::vector< int > right
bool empty() const
bool operator>(const KDTreeNodeInfo &rhs) const