LArSoft  v09_90_00
Liquid Argon Software toolkit - https://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])
225  ++i;
226  while ((*initialEltList)[j].dims[thedim] > elt.dims[thedim])
227  --j;
228 
229  if (i <= j)
230  {
231  std::swap((*initialEltList)[i], (*initialEltList)[j]);
232  i++;
233  j--;
234  }
235  } while (i <= j);
236 
237  if (j < median)
238  l = i;
239  if (i > median)
240  m = j;
241  }
242 
243  return median;
244 }
245 
246 //------------------------------------------------------------------------------------------------------------------------------------------
247 
248 template <typename DATA, unsigned DIM>
250 {
251  if (root_)
252  {
253  closestNeighbour = &recHits;
254  this->recSearch(root_, trackBox);
255  closestNeighbour = nullptr;
256  }
257 }
258 
259 //------------------------------------------------------------------------------------------------------------------------------------------
260 
261 template <typename DATA, unsigned DIM>
263 {
264  // By construction, current can't be null
265  //assert(current != 0);
266  // By Construction, a node can't have just 1 son.
267  //assert (!(((current->left == 0) && (current->right != 0)) || ((current->left != 0) && (current->right == 0))));
268 
269  if ((current->left == nullptr) && (current->right == nullptr))
270  {
271  // Leaf case
272  // If point inside the rectangle/area
273  bool isInside = true;
274 
275  for (unsigned i = 0; i < DIM; ++i)
276  {
277  const auto thedim = current->info.dims[i];
278  isInside = isInside && thedim >= trackBox.dimmin[i] && thedim <= trackBox.dimmax[i];
279  }
280 
281  if (isInside)
282  closestNeighbour->push_back(current->info);
283  }
284  else
285  {
286  // Node case
287  // If region( v->left ) is fully contained in the rectangle
288  bool isFullyContained = true;
289  bool hasIntersection = true;
290 
291  for (unsigned i = 0; i < DIM; ++i)
292  {
293  const auto regionmin = current->left->region.dimmin[i];
294  const auto regionmax = current->left->region.dimmax[i];
295  isFullyContained = isFullyContained && (regionmin >= trackBox.dimmin[i] && regionmax <= trackBox.dimmax[i]);
296  hasIntersection = hasIntersection && (regionmin < trackBox.dimmax[i] && regionmax > trackBox.dimmin[i]);
297  }
298 
299  if (isFullyContained)
300  {
301  this->addSubtree(current->left);
302  }
303  else if (hasIntersection)
304  {
305  this->recSearch(current->left, trackBox);
306  }
307 
308  //if region( v->right ) is fully contained in the rectangle
309  isFullyContained = true;
310  hasIntersection = true;
311 
312  for (unsigned i = 0; i < DIM; ++i)
313  {
314  const auto regionmin = current->right->region.dimmin[i];
315  const auto regionmax = current->right->region.dimmax[i];
316  isFullyContained = isFullyContained && (regionmin >= trackBox.dimmin[i] && regionmax <= trackBox.dimmax[i]);
317  hasIntersection = hasIntersection && (regionmin < trackBox.dimmax[i] && regionmax > trackBox.dimmin[i]);
318  }
319 
320  if (isFullyContained)
321  {
322  this->addSubtree(current->right);
323  }
324  else if (hasIntersection)
325  {
326  this->recSearch(current->right, trackBox);
327  }
328  }
329 }
330 
331 //------------------------------------------------------------------------------------------------------------------------------------------
332 
333 template <typename DATA, unsigned DIM>
335  const KDTreeNodeInfoT<DATA, DIM> &point, const KDTreeNodeInfoT<DATA, DIM> *&result, float &distance)
336 {
337  if (nullptr != result || distance != std::numeric_limits<float>::max())
338  {
339  result = nullptr;
340  distance = std::numeric_limits<float>::max();
341  }
342 
343  if (root_)
344  {
345  const KDTreeNodeT<DATA, DIM> *best_match = nullptr;
346  this->recNearestNeighbour(0, root_, point, best_match, distance);
347 
348  if (distance != std::numeric_limits<float>::max())
349  {
350  result = &(best_match->info);
351  distance = std::sqrt(distance);
352  }
353  }
354 }
355 
356 //------------------------------------------------------------------------------------------------------------------------------------------
357 
358 template <typename DATA, unsigned DIM>
359 inline void KDTreeLinkerAlgo<DATA, DIM>::recNearestNeighbour(unsigned int depth, const KDTreeNodeT<DATA, DIM> *current,
360  const KDTreeNodeInfoT<DATA, DIM> &point, const KDTreeNodeT<DATA, DIM> *&best_match, float &best_dist)
361 {
362  const unsigned int current_dim = depth % DIM;
363 
364  if (current->left == nullptr && current->right == nullptr)
365  {
366  best_match = current;
367  best_dist = this->dist2(point, best_match->info);
368  return;
369  }
370  else
371  {
372  const float dist_to_axis = point.dims[current_dim] - current->info.dims[current_dim];
373 
374  if (dist_to_axis < 0.f)
375  {
376  this->recNearestNeighbour(depth + 1, current->left, point, best_match, best_dist);
377  }
378  else
379  {
380  this->recNearestNeighbour(depth + 1, current->right, point, best_match, best_dist);
381  }
382 
383  // 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
384  const float dist_current = this->dist2(point, current->info);
385 
386  if (dist_current < best_dist)
387  {
388  best_dist = dist_current;
389  best_match = current;
390  }
391 
392  // Now we see if the radius to best crosses the splitting axis
393  if (best_dist > dist_to_axis * dist_to_axis)
394  {
395  // if it does we traverse the other side of the axis to check for a new best
396  const KDTreeNodeT<DATA, DIM> *check_best = best_match;
397  float check_dist = best_dist;
398 
399  if (dist_to_axis < 0.f)
400  {
401  this->recNearestNeighbour(depth + 1, current->right, point, check_best, check_dist);
402  }
403  else
404  {
405  this->recNearestNeighbour(depth + 1, current->left, point, check_best, check_dist);
406  }
407 
408  if (check_dist < best_dist)
409  {
410  best_dist = check_dist;
411  best_match = check_best;
412  }
413  }
414  return;
415  }
416 }
417 
418 //------------------------------------------------------------------------------------------------------------------------------------------
419 
420 template <typename DATA, unsigned DIM>
422 {
423  // By construction, current can't be null
424  //assert(current != 0);
425 
426  if ((current->left == nullptr) && (current->right == nullptr))
427  {
428  // Leaf case
429  closestNeighbour->push_back(current->info);
430  }
431  else
432  {
433  // Node case
434  this->addSubtree(current->left);
435  this->addSubtree(current->right);
436  }
437 }
438 
439 //------------------------------------------------------------------------------------------------------------------------------------------
440 
441 template <typename DATA, unsigned DIM>
443 {
444  double d = 0.;
445 
446  for (unsigned i = 0; i < DIM; ++i)
447  {
448  const double diff = a.dims[i] - b.dims[i];
449  d += diff * diff;
450  }
451 
452  return (float)d;
453 }
454 
455 //------------------------------------------------------------------------------------------------------------------------------------------
456 
457 template <typename DATA, unsigned DIM>
459 {
460  delete[] nodePool_;
461  nodePool_ = nullptr;
462  root_ = nullptr;
463  nodePoolSize_ = -1;
464  nodePoolPos_ = -1;
465 }
466 
467 //------------------------------------------------------------------------------------------------------------------------------------------
468 
469 template <typename DATA, unsigned DIM>
471 {
472  return (nodePoolPos_ == -1);
473 }
474 
475 //------------------------------------------------------------------------------------------------------------------------------------------
476 
477 template <typename DATA, unsigned DIM>
479 {
480  return (nodePoolPos_ + 1);
481 }
482 
483 //------------------------------------------------------------------------------------------------------------------------------------------
484 
485 template <typename DATA, unsigned DIM>
487 {
488  if (root_)
489  this->clearTree();
490 }
491 
492 //------------------------------------------------------------------------------------------------------------------------------------------
493 
494 template <typename DATA, unsigned DIM>
496 {
497  ++nodePoolPos_;
498 
499  // The tree size is exactly 2 * nbrElts - 1 and this is the total allocated memory.
500  // If we have used more than that....there is a big problem.
501  //assert(nodePoolPos_ < nodePoolSize_);
502 
503  return &(nodePool_[nodePoolPos_]);
504 }
505 
506 //------------------------------------------------------------------------------------------------------------------------------------------
507 
508 template <typename DATA, unsigned DIM>
509 inline KDTreeNodeT<DATA, DIM> *KDTreeLinkerAlgo<DATA, DIM>::recBuild(int low, int high, int depth, const KDTreeBoxT<DIM> &region)
510 {
511  const int portionSize = high - low;
512 
513  // By construction, portionSize > 0 can't happen.
514  //assert(portionSize > 0);
515 
516  if (portionSize == 1)
517  {
518  // Leaf case
519  KDTreeNodeT<DATA, DIM> *leaf = this->getNextNode();
520  leaf->setAttributs(region, (*initialEltList)[low]);
521  return leaf;
522  }
523  else
524  {
525  // The even depth is associated to dim1 dimension, the odd one to dim2 dimension
526  int medianId = this->medianSearch(low, high, depth);
527 
528  // We create the node
529  KDTreeNodeT<DATA, DIM> *node = this->getNextNode();
530  node->setAttributs(region);
531  node->info = (*initialEltList)[medianId];
532 
533  // Here we split into 2 halfplanes the current plane
534  KDTreeBoxT<DIM> leftRegion = region;
535  KDTreeBoxT<DIM> rightRegion = region;
536 
537  const unsigned thedim = depth % DIM;
538  auto medianVal = (*initialEltList)[medianId].dims[thedim];
539  leftRegion.dimmax[thedim] = medianVal;
540  rightRegion.dimmin[thedim] = medianVal;
541 
542  ++depth;
543  ++medianId;
544 
545  // We recursively build the son nodes
546  node->left = this->recBuild(low, medianId, depth, leftRegion);
547  node->right = this->recBuild(medianId, high, depth, rightRegion);
548  return node;
549  }
550 }
551 
552 } // namespace lar_content
553 
554 #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:289
KDTreeNodeT< DATA, DIM > * left
Left son.
KDTreeNodeT< DATA, DIM > * getNextNode()
Get the next node from the node pool.
~KDTreeLinkerAlgo()
Destructor calls clear.
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().
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".
Float_t d
Definition: plot.C:235
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 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...
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.