LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
KDTreeLinkerAlgoT.h
Go to the documentation of this file.
1 
8 #ifndef LAR_KD_TREE_LINKER_ALGO_TEMPLATED_H
9 #define LAR_KD_TREE_LINKER_ALGO_TEMPLATED_H
10 
11 #include "KDTreeLinkerToolsT.h"
12 
13 #include <vector>
14 
15 namespace lar_content
16 {
17 
21 template <typename DATA, unsigned DIM = 2>
22 class KDTreeLinkerAlgo
23 {
24 public:
29 
34 
41  void build(std::vector<KDTreeNodeInfoT<DATA, DIM> > &eltList, const KDTreeBoxT<DIM> &region);
42 
50  void search(const KDTreeBoxT<DIM> &searchBox, std::vector<KDTreeNodeInfoT<DATA, DIM> > &resRecHitList);
51 
59  void findNearestNeighbour(const KDTreeNodeInfoT<DATA, DIM> &point, const KDTreeNodeInfoT<DATA, DIM> *&result, float &distance);
60 
66  bool empty();
67 
73  int size();
74 
78  void clear();
79 
80 private:
86  KDTreeNodeT<DATA, DIM> *getNextNode();
87 
95  int medianSearch(int low, int high, int treeDepth);
96 
105  KDTreeNodeT<DATA, DIM> *recBuild(int low, int high, int depth, const KDTreeBoxT<DIM> &region);
106 
113  void recSearch(const KDTreeNodeT<DATA, DIM> *current, const KDTreeBoxT<DIM> &trackBox);
114 
124  void recNearestNeighbour(unsigned depth, const KDTreeNodeT<DATA, DIM> *current, const KDTreeNodeInfoT<DATA, DIM> &point,
125  const KDTreeNodeT<DATA, DIM> *&best_match, float &best_dist);
126 
132  void addSubtree(const KDTreeNodeT<DATA, DIM> *current);
133 
142  float dist2(const KDTreeNodeInfoT<DATA, DIM> &a, const KDTreeNodeInfoT<DATA, DIM> &b) const;
143 
147  void clearTree();
148 
153 
154  std::vector<KDTreeNodeInfoT<DATA, DIM> > *closestNeighbour;
155  std::vector<KDTreeNodeInfoT<DATA, DIM> > *initialEltList;
156 };
157 
158 //------------------------------------------------------------------------------------------------------------------------------------------
159 //------------------------------------------------------------------------------------------------------------------------------------------
160 
161 template <typename DATA, unsigned DIM>
163  root_(nullptr),
164  nodePool_(nullptr),
165  nodePoolSize_(-1),
166  nodePoolPos_(-1),
167  closestNeighbour(nullptr),
168  initialEltList(nullptr)
169 {
170 }
171 
172 //------------------------------------------------------------------------------------------------------------------------------------------
173 
174 template <typename DATA, unsigned DIM>
176 {
177  this->clear();
178 }
179 
180 //------------------------------------------------------------------------------------------------------------------------------------------
181 
182 template <typename DATA, unsigned DIM>
184 {
185  if (eltList.size())
186  {
187  initialEltList = &eltList;
188  const size_t mysize = initialEltList->size();
189 
190  nodePoolSize_ = mysize * 2 - 1;
192 
193  // Here we build the KDTree
194  root_ = this->recBuild(0, mysize, 0, region);
195  initialEltList = nullptr;
196  }
197 }
198 
199 //------------------------------------------------------------------------------------------------------------------------------------------
200 
201 template <typename DATA, unsigned DIM>
202 inline int KDTreeLinkerAlgo<DATA, DIM>::medianSearch(int low, int high, int treeDepth)
203 {
204  // We should have at least 1 element to calculate the median...
205  //assert(low < high);
206 
207  const int nbrElts = high - low;
208  int median = nbrElts / 2 - (1 - 1 * (nbrElts & 1));
209  median += low;
210 
211  int l = low;
212  int m = high - 1;
213 
214  while (l < m)
215  {
216  KDTreeNodeInfoT<DATA, DIM> elt = (*initialEltList)[median];
217  int i = l;
218  int j = m;
219 
220  do
221  {
222  // The even depth is associated to dim1 dimension, the odd one to dim2 dimension
223  const unsigned thedim = treeDepth % DIM;
224  while ((*initialEltList)[i].dims[thedim] < elt.dims[thedim]) ++i;
225  while ((*initialEltList)[j].dims[thedim] > elt.dims[thedim]) --j;
226 
227  if (i <= j)
228  {
229  std::swap((*initialEltList)[i], (*initialEltList)[j]);
230  i++;
231  j--;
232  }
233  }
234  while (i <= j);
235 
236  if (j < median) l = i;
237  if (i > median) m = j;
238  }
239 
240  return median;
241 }
242 
243 //------------------------------------------------------------------------------------------------------------------------------------------
244 
245 template <typename DATA, unsigned DIM>
247 {
248  if (root_)
249  {
250  closestNeighbour = &recHits;
251  this->recSearch(root_, trackBox);
252  closestNeighbour = nullptr;
253  }
254 }
255 
256 //------------------------------------------------------------------------------------------------------------------------------------------
257 
258 template <typename DATA, unsigned DIM>
260 {
261  // By construction, current can't be null
262  //assert(current != 0);
263  // By Construction, a node can't have just 1 son.
264  //assert (!(((current->left == 0) && (current->right != 0)) || ((current->left != 0) && (current->right == 0))));
265 
266  if ((current->left == nullptr) && (current->right == nullptr))
267  {
268  // Leaf case
269  // If point inside the rectangle/area
270  bool isInside = true;
271 
272  for (unsigned i = 0; i < DIM; ++i)
273  {
274  const auto thedim = current->info.dims[i];
275  isInside *= thedim >= trackBox.dimmin[i] && thedim <= trackBox.dimmax[i];
276  }
277 
278  if (isInside)
279  closestNeighbour->push_back(current->info);
280  }
281  else
282  {
283  // Node case
284  // If region( v->left ) is fully contained in the rectangle
285  bool isFullyContained = true;
286  bool hasIntersection = true;
287 
288  for (unsigned i = 0; i < DIM; ++i)
289  {
290  const auto regionmin = current->left->region.dimmin[i];
291  const auto regionmax = current->left->region.dimmax[i];
292  isFullyContained *= (regionmin >= trackBox.dimmin[i] && regionmax <= trackBox.dimmax[i]);
293  hasIntersection *= (regionmin < trackBox.dimmax[i] && regionmax > trackBox.dimmin[i]);
294  }
295 
296  if (isFullyContained)
297  {
298  this->addSubtree(current->left);
299  }
300  else if (hasIntersection)
301  {
302  this->recSearch(current->left, trackBox);
303  }
304 
305  //if region( v->right ) is fully contained in the rectangle
306  isFullyContained = true;
307  hasIntersection = true;
308 
309  for (unsigned i = 0; i < DIM; ++i)
310  {
311  const auto regionmin = current->right->region.dimmin[i];
312  const auto regionmax = current->right->region.dimmax[i];
313  isFullyContained *= (regionmin >= trackBox.dimmin[i] && regionmax <= trackBox.dimmax[i]);
314  hasIntersection *= (regionmin < trackBox.dimmax[i] && regionmax > trackBox.dimmin[i]);
315  }
316 
317  if (isFullyContained)
318  {
319  this->addSubtree(current->right);
320  }
321  else if (hasIntersection)
322  {
323  this->recSearch(current->right, trackBox);
324  }
325  }
326 }
327 
328 //------------------------------------------------------------------------------------------------------------------------------------------
329 
330 template <typename DATA, unsigned DIM>
332  float &distance)
333 {
334  if (nullptr != result || distance != std::numeric_limits<float>::max())
335  {
336  result = nullptr;
337  distance = std::numeric_limits<float>::max();
338  }
339 
340  if (root_)
341  {
342  const KDTreeNodeT<DATA, DIM> *best_match = nullptr;
343  this->recNearestNeighbour(0, root_, point, best_match, distance);
344 
345  if (distance != std::numeric_limits<float>::max())
346  {
347  result = &(best_match->info);
348  distance = std::sqrt(distance);
349  }
350  }
351 }
352 
353 //------------------------------------------------------------------------------------------------------------------------------------------
354 
355 template <typename DATA, unsigned DIM>
356 inline void KDTreeLinkerAlgo<DATA, DIM>::recNearestNeighbour(unsigned int depth, const KDTreeNodeT<DATA, DIM> *current,
357  const KDTreeNodeInfoT<DATA, DIM> &point, const KDTreeNodeT<DATA, DIM> *&best_match, float &best_dist)
358 {
359  const unsigned int current_dim = depth % DIM;
360 
361  if (current->left == nullptr && current->right == nullptr)
362  {
363  best_match = current;
364  best_dist = this->dist2(point, best_match->info);
365  return;
366  }
367  else
368  {
369  const float dist_to_axis = point.dims[current_dim] - current->info.dims[current_dim];
370 
371  if (dist_to_axis < 0.f)
372  {
373  this->recNearestNeighbour(depth + 1, current->left, point, best_match, best_dist);
374  }
375  else
376  {
377  this->recNearestNeighbour(depth + 1, current->right, point, best_match, best_dist);
378  }
379 
380  // If we're here we're returned so best_dist is filled. Compare to this node and see if it's a better match. If it is, update result
381  const float dist_current = this->dist2(point, current->info);
382 
383  if (dist_current < best_dist)
384  {
385  best_dist = dist_current;
386  best_match = current;
387  }
388 
389  // Now we see if the radius to best crosses the splitting axis
390  if (best_dist > dist_to_axis * dist_to_axis)
391  {
392  // if it does we traverse the other side of the axis to check for a new best
393  const KDTreeNodeT<DATA, DIM> *check_best = best_match;
394  float check_dist = best_dist;
395 
396  if (dist_to_axis < 0.f)
397  {
398  this->recNearestNeighbour(depth + 1, current->right, point, check_best, check_dist);
399  }
400  else
401  {
402  this->recNearestNeighbour(depth + 1, current->left, point, check_best, check_dist);
403  }
404 
405  if (check_dist < best_dist)
406  {
407  best_dist = check_dist;
408  best_match = check_best;
409  }
410  }
411  return;
412  }
413 }
414 
415 //------------------------------------------------------------------------------------------------------------------------------------------
416 
417 template < typename DATA, unsigned DIM >
419 {
420  // By construction, current can't be null
421  //assert(current != 0);
422 
423  if ((current->left == nullptr) && (current->right == nullptr))
424  {
425  // Leaf case
426  closestNeighbour->push_back(current->info);
427  }
428  else
429  {
430  // Node case
431  this->addSubtree(current->left);
432  this->addSubtree(current->right);
433  }
434 }
435 
436 //------------------------------------------------------------------------------------------------------------------------------------------
437 
438 template <typename DATA, unsigned DIM>
440 {
441  double d = 0.;
442 
443  for (unsigned i = 0 ; i < DIM; ++i)
444  {
445  const double diff = a.dims[i] - b.dims[i];
446  d += diff * diff;
447  }
448 
449  return (float)d;
450 }
451 
452 //------------------------------------------------------------------------------------------------------------------------------------------
453 
454 template <typename DATA, unsigned DIM>
456 {
457  delete[] nodePool_;
458  nodePool_ = nullptr;
459  root_ = nullptr;
460  nodePoolSize_ = -1;
461  nodePoolPos_ = -1;
462 }
463 
464 //------------------------------------------------------------------------------------------------------------------------------------------
465 
466 template <typename DATA, unsigned DIM>
468 {
469  return (nodePoolPos_ == -1);
470 }
471 
472 //------------------------------------------------------------------------------------------------------------------------------------------
473 
474 template <typename DATA, unsigned DIM>
476 {
477  return (nodePoolPos_ + 1);
478 }
479 
480 //------------------------------------------------------------------------------------------------------------------------------------------
481 
482 template <typename DATA, unsigned DIM>
484 {
485  if (root_)
486  this->clearTree();
487 }
488 
489 //------------------------------------------------------------------------------------------------------------------------------------------
490 
491 template <typename DATA, unsigned DIM>
493 {
494  ++nodePoolPos_;
495 
496  // The tree size is exactly 2 * nbrElts - 1 and this is the total allocated memory.
497  // If we have used more than that....there is a big problem.
498  //assert(nodePoolPos_ < nodePoolSize_);
499 
500  return &(nodePool_[nodePoolPos_]);
501 }
502 
503 //------------------------------------------------------------------------------------------------------------------------------------------
504 
505 template <typename DATA, unsigned DIM>
506 inline KDTreeNodeT<DATA, DIM> *KDTreeLinkerAlgo<DATA, DIM>::recBuild(int low, int high, int depth, const KDTreeBoxT<DIM> &region)
507 {
508  const int portionSize = high - low;
509 
510  // By construction, portionSize > 0 can't happen.
511  //assert(portionSize > 0);
512 
513  if (portionSize == 1)
514  {
515  // Leaf case
516  KDTreeNodeT<DATA, DIM> *leaf = this->getNextNode();
517  leaf->setAttributs(region, (*initialEltList)[low]);
518  return leaf;
519  }
520  else
521  {
522  // The even depth is associated to dim1 dimension, the odd one to dim2 dimension
523  int medianId = this->medianSearch(low, high, depth);
524 
525  // We create the node
526  KDTreeNodeT<DATA, DIM> *node = this->getNextNode();
527  node->setAttributs(region);
528  node->info = (*initialEltList)[medianId];
529 
530  // Here we split into 2 halfplanes the current plane
531  KDTreeBoxT<DIM> leftRegion = region;
532  KDTreeBoxT<DIM> rightRegion = region;
533 
534  const unsigned thedim = depth % DIM;
535  auto medianVal = (*initialEltList)[medianId].dims[thedim];
536  leftRegion.dimmax[thedim] = medianVal;
537  rightRegion.dimmin[thedim] = medianVal;
538 
539  ++depth;
540  ++medianId;
541 
542  // We recursively build the son nodes
543  node->left = this->recBuild(low, medianId, depth, leftRegion);
544  node->right = this->recBuild(medianId, high, depth, rightRegion);
545  return node;
546  }
547 }
548 
549 } // namespace lar_content
550 
551 #endif // LAR_KD_TREE_LINKER_ALGO_TEMPLATED_H
int nodePoolPos_
The node pool position.
KDTreeNodeT< DATA, DIM > * root_
The KDTree root.
std::vector< KDTreeNodeInfoT< DATA, DIM > > * initialEltList
The initial element list.
void setAttributs(const KDTreeBoxT< DIM > &regionBox, const KDTreeNodeInfoT< DATA, DIM > &infoToStore)
setAttributs
Box structure used to define 2D field. It&#39;s used in KDTree building step to divide the detector space...
KDTreeNodeInfoT< DATA, DIM > info
Data.
int nodePoolSize_
The node pool size.
std::array< float, DIM > dims
KDTreeLinkerAlgo()
Default constructor.
int size()
Return the number of nodes + leaves in the tree (nElements should be (size() +1) / 2) ...
void clear()
Clear all allocated structures.
KDTreeNodeT< DATA, DIM > * right
Right son.
bool empty()
Whether the tree is empty.
void recNearestNeighbour(unsigned depth, const KDTreeNodeT< DATA, DIM > *current, const KDTreeNodeInfoT< DATA, DIM > &point, const KDTreeNodeT< DATA, DIM > *&best_match, float &best_dist)
Recursive nearest neighbour search. Is called by findNearestNeighbour()
void findNearestNeighbour(const KDTreeNodeInfoT< DATA, DIM > &point, const KDTreeNodeInfoT< DATA, DIM > *&result, float &distance)
findNearestNeighbour
Data stored in each KDTree node. The dim1/dim2 fields are usually the duplication of some PFRecHit va...
TFile f
Definition: plotHisto.C:6
void clearTree()
Frees the KDTree.
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
KDTreeNodeT< DATA, DIM > * left
Left son.
KDTreeNodeT< DATA, DIM > * getNextNode()
Get the next node from the node pool.
Int_t max
Definition: plot.C:27
~KDTreeLinkerAlgo()
Destructor calls clear.
void search(const KDTreeBoxT< DIM > &searchBox, std::vector< KDTreeNodeInfoT< DATA, DIM > > &resRecHitList)
Search in the KDTree for all points that would be contained in the given searchbox The founded points...
float dist2(const KDTreeNodeInfoT< DATA, DIM > &a, const KDTreeNodeInfoT< DATA, DIM > &b) const
dist2
void addSubtree(const KDTreeNodeT< DATA, DIM > *current)
Add all elements of an subtree to the closest elements. Used during the recSearch().
Float_t d
Definition: plot.C:237
KDTreeNodeT< DATA, DIM > * nodePool_
Node pool allows us to do just 1 call to new for each tree building.
std::vector< KDTreeNodeInfoT< DATA, DIM > > * closestNeighbour
The closest neighbour.
std::array< float, DIM > dimmin
KDTreeNodeT< DATA, DIM > * recBuild(int low, int high, int depth, const KDTreeBoxT< DIM > &region)
Recursive kdtree builder. Is called by build()
std::array< float, DIM > dimmax
int medianSearch(int low, int high, int treeDepth)
Fast median search with Wirth algorithm in eltList between low and high indexes.
void recSearch(const KDTreeNodeT< DATA, DIM > *current, const KDTreeBoxT< DIM > &trackBox)
Recursive kdtree search. Is called by search()
Header file for the kd tree linker tools template class.
void build(std::vector< KDTreeNodeInfoT< DATA, DIM > > &eltList, const KDTreeBoxT< DIM > &region)
Build the KD tree from the "eltList" in the space define by "region".