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  dim.shrink_to_fit();
67  }
68  right.clear();
69  right.shrink_to_fit();
70  data.clear();
71  data.shrink_to_fit();
72  poolSize = -1;
73  poolPos = -1;
74  }
75 
76  int getNextNode() {
77  ++poolPos;
78  return poolPos;
79  }
80 
81  void build(int sizeData) {
82  poolSize = sizeData * 2 - 1;
83  for (auto &dim : dims) {
84  dim.resize(poolSize);
85  }
86  right.resize(poolSize);
87  data.resize(poolSize);
88  };
89 
90  constexpr bool isLeaf(int right) const {
91  // Valid values of right are always >= 2
92  // index 0 is the root, and 1 is the first left node
93  // Exploit index values 0 and 1 to mark which of dim1/dim2 is the
94  // current one in recSearch() at the depth of the leaf.
95  return right < 2;
96  }
97 
98  bool isLeafIndex(int index) const { return isLeaf(right[index]); }
99 };
100 
101 // Class that implements the KDTree partition of 2D space and
102 // a closest point search algorithme.
103 
104 template <typename DATA, unsigned int DIM = 2>
106 public:
107  // Dtor calls clear()
109 
110  // Here we build the KD tree from the "eltList" in the space define by "region".
112 
113  // Here we search in the KDTree for all points that would be
114  // contained in the given searchbox. The founded points are stored in resRecHitList.
115  void search(const KDTreeBox<DIM> &searchBox, std::vector<DATA> &resRecHitList);
116 
117  // This reurns true if the tree is empty
118  bool empty() { return nodePool_.empty(); }
119 
120  // This returns the number of nodes + leaves in the tree
121  // (nElements should be (size() +1)/2)
122  int size() { return nodePool_.size(); }
123 
124  // This method clears all allocated structures.
125  void clear() { clearTree(); }
126 
127 private:
128  // The node pool allow us to do just 1 call to new for each tree building.
130 
131  std::vector<DATA> *closestNeighbour;
132  std::vector<KDTreeNodeInfo<DATA, DIM> > *initialEltList;
133 
134  //Fast median search with Wirth algorithm in eltList between low and high indexes.
135  int medianSearch(int low, int high, int treeDepth) const;
136 
137  // Recursif kdtree builder. Is called by build()
138  int recBuild(int low, int hight, int depth);
139 
140  // Recursif kdtree search. Is called by search()
141  void recSearch(int current, const KDTreeBox<DIM> &trackBox, int depth = 0) const;
142 
143  // This method frees the KDTree.
144  void clearTree() { nodePool_.clear(); }
145 };
146 
147 //Implementation
148 
149 template <typename DATA, unsigned int DIM>
151  const KDTreeBox<DIM> &region) {
152  if (!eltList.empty()) {
153  initialEltList = &eltList;
154 
155  size_t size = initialEltList->size();
156  nodePool_.build(size);
157 
158  // Here we build the KDTree
159  int root = recBuild(0, size, 0);
160  assert(root == 0);
161 
162  initialEltList = nullptr;
163  }
164 }
165 
166 //Fast median search with Wirth algorithm in eltList between low and high indexes.
167 template <typename DATA, unsigned int DIM>
168 int KDTreeLinkerAlgo<DATA, DIM>::medianSearch(int low, int high, int treeDepth) const {
169  int nbrElts = high - low;
170  int median = (nbrElts & 1) ? nbrElts / 2 : nbrElts / 2 - 1;
171  median += low;
172 
173  int l = low;
174  int m = high - 1;
175 
176  while (l < m) {
177  KDTreeNodeInfo<DATA, DIM> elt = (*initialEltList)[median];
178  int i = l;
179  int j = m;
180 
181  do {
182  // The even depth is associated to dim1 dimension
183  // The odd one to dim2 dimension
184  const unsigned thedim = treeDepth % DIM;
185  while ((*initialEltList)[i].dims[thedim] < elt.dims[thedim])
186  ++i;
187  while ((*initialEltList)[j].dims[thedim] > elt.dims[thedim])
188  --j;
189 
190  if (i <= j) {
191  std::swap((*initialEltList)[i], (*initialEltList)[j]);
192  i++;
193  j--;
194  }
195  } while (i <= j);
196  if (j < median)
197  l = i;
198  if (i > median)
199  m = j;
200  }
201 
202  return median;
203 }
204 
205 template <typename DATA, unsigned int DIM>
206 void KDTreeLinkerAlgo<DATA, DIM>::search(const KDTreeBox<DIM> &trackBox, std::vector<DATA> &recHits) {
207  if (!empty()) {
208  closestNeighbour = &recHits;
209  recSearch(0, trackBox, 0);
210  closestNeighbour = nullptr;
211  }
212 }
213 
214 template <typename DATA, unsigned int DIM>
215 void KDTreeLinkerAlgo<DATA, DIM>::recSearch(int current, const KDTreeBox<DIM> &trackBox, int depth) const {
216  // Iterate until leaf is found, or there are no children in the
217  // search window. If search has to proceed on both children, proceed
218  // the search to left child via recursion. Swap search window
219  // dimension on alternate levels.
220  while (true) {
221  const int dimIndex = depth % DIM;
222  int right = nodePool_.right[current];
223  if (nodePool_.isLeaf(right)) {
224  // If point inside the rectangle/area
225  // Use intentionally bit-wise & instead of logical && for better
226  // performance. It is faster to always do all comparisons than to
227  // allow use of branches to not do some if any of the first ones
228  // is false.
229  bool isInside = true;
230  for (unsigned i = 0; i < DIM; ++i) {
231  float dimCurr = nodePool_.dims[i][current];
232  isInside &= (dimCurr >= trackBox.dimmin[i]) && (dimCurr <= trackBox.dimmax[i]);
233  }
234  if (isInside) {
235  closestNeighbour->push_back(nodePool_.data[current]);
236  }
237  break;
238  } else {
239  float median = nodePool_.dims[dimIndex][current];
240 
241  bool goLeft = (trackBox.dimmin[dimIndex] <= median);
242  bool goRight = (trackBox.dimmax[dimIndex] >= median);
243 
244  ++depth;
245  if (goLeft & goRight) {
246  int left = current + 1;
247  recSearch(left, trackBox, depth);
248  // continue with right
249  current = right;
250  } else if (goLeft) {
251  ++current;
252  } else if (goRight) {
253  current = right;
254  } else {
255  break;
256  }
257  }
258  }
259 }
260 
261 template <typename DATA, unsigned int DIM>
263  int portionSize = high - low;
264 
265  if (portionSize == 1) { // Leaf case
266  int leaf = nodePool_.getNextNode();
267  const KDTreeNodeInfo<DATA, DIM> &info = (*initialEltList)[low];
268  nodePool_.right[leaf] = 0;
269  for (unsigned i = 0; i < DIM; ++i) {
270  nodePool_.dims[i][leaf] = info.dims[i];
271  }
272  nodePool_.data[leaf] = info.data;
273  return leaf;
274 
275  } else { // Node case
276 
277  // The even depth is associated to dim1 dimension
278  // The odd one to dim2 dimension
279  int medianId = medianSearch(low, high, depth);
280  int dimIndex = depth % DIM;
281  float medianVal = (*initialEltList)[medianId].dims[dimIndex];
282 
283  // We create the node
284  int nodeInd = nodePool_.getNextNode();
285 
286  ++depth;
287  ++medianId;
288 
289  // We recursively build the son nodes
290  int left = recBuild(low, medianId, depth);
291  assert(nodeInd + 1 == left);
292  int right = recBuild(medianId, high, depth);
293  nodePool_.right[nodeInd] = right;
294 
295  nodePool_.dims[dimIndex][nodeInd] = medianVal;
296 
297  return nodeInd;
298  }
299 }
300 
301 #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
#define DATA
Definition: cutflowutil.h:20
std::vector< int > right
bool empty() const
bool operator>(const KDTreeNodeInfo &rhs) const