KDTreeLinkerAlgoT.h
Go to the documentation of this file.
1 /**
2  * @file larpandoracontent/LArUtility/KDTreeLinkerAlgoT.h
3  *
4  * @brief Header file for the kd tree linker algo template class
5  *
6  * $Log: $
7  */
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 
18 /**
19  * @brief Class that implements the KDTree partition of 2D space and a closest point search algorithm
20  */
21 template <typename DATA, unsigned DIM = 2>
22 class KDTreeLinkerAlgo
23 {
24 public:
25  /**
26  * @brief Default constructor
27  */
29 
30  /**
31  * @brief Destructor calls clear
32  */
34 
35  /**
36  * @brief Build the KD tree from the "eltList" in the space define by "region"
37  *
38  * @param eltList
39  * @param region
40  */
41  void build(std::vector<KDTreeNodeInfoT<DATA, DIM>> &eltList, const KDTreeBoxT<DIM> &region);
42 
43  /**
44  * @brief Search in the KDTree for all points that would be contained in the given searchbox
45  * The founded points are stored in resRecHitList
46  *
47  * @param searchBox
48  * @param resRecHitList
49  */
50  void search(const KDTreeBoxT<DIM> &searchBox, std::vector<KDTreeNodeInfoT<DATA, DIM>> &resRecHitList);
51 
52  /**
53  * @brief findNearestNeighbour
54  *
55  * @param point
56  * @param result
57  * @param distance
58  */
59  void findNearestNeighbour(const KDTreeNodeInfoT<DATA, DIM> &point, const KDTreeNodeInfoT<DATA, DIM> *&result, float &distance);
60 
61  /**
62  * @brief Whether the tree is empty
63  *
64  * @return boolean
65  */
66  bool empty();
67 
68  /**
69  * @brief Return the number of nodes + leaves in the tree (nElements should be (size() +1) / 2)
70  *
71  * @return the number of nodes + leaves in the tree
72  */
73  int size();
74 
75  /**
76  * @brief Clear all allocated structures
77  */
78  void clear();
79 
80 private:
81  /**
82  * @brief Get the next node from the node pool
83  *
84  * @return the next node from the node pool
85  */
86  KDTreeNodeT<DATA, DIM> *getNextNode();
87 
88  /**
89  * @brief Fast median search with Wirth algorithm in eltList between low and high indexes.
90  *
91  * @param low
92  * @param high
93  * @param treeDepth
94  */
95  int medianSearch(int low, int high, int treeDepth);
96 
97  /**
98  * @brief Recursive kdtree builder. Is called by build()
99  *
100  * @param low
101  * @param high
102  * @param depth
103  * @param region
104  */
105  KDTreeNodeT<DATA, DIM> *recBuild(int low, int high, int depth, const KDTreeBoxT<DIM> &region);
106 
107  /**
108  * @brief Recursive kdtree search. Is called by search()
109  *
110  * @param current
111  * @param trackBox
112  */
113  void recSearch(const KDTreeNodeT<DATA, DIM> *current, const KDTreeBoxT<DIM> &trackBox);
114 
115  /**
116  * @brief Recursive nearest neighbour search. Is called by findNearestNeighbour()
117  *
118  * @param depth
119  * @param current
120  * @param point
121  * @param best_match
122  * @param best_dist
123  */
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 
127  /**
128  * @brief Add all elements of an subtree to the closest elements. Used during the recSearch().
129  *
130  * @param current
131  */
132  void addSubtree(const KDTreeNodeT<DATA, DIM> *current);
133 
134  /**
135  * @brief dist2
136  *
137  * @param a
138  * @param b
139  *
140  * @return dist2
141  */
142  float dist2(const KDTreeNodeInfoT<DATA, DIM> &a, const KDTreeNodeInfoT<DATA, DIM> &b) const;
143 
144  /**
145  * @brief Frees the KDTree.
146  */
147  void clearTree();
148 
149  KDTreeNodeT<DATA, DIM> *root_; ///< The KDTree root
150  KDTreeNodeT<DATA, DIM> *nodePool_; ///< Node pool allows us to do just 1 call to new for each tree building
151  int nodePoolSize_; ///< The node pool size
152  int nodePoolPos_; ///< The node pool position
153 
154  std::vector<KDTreeNodeInfoT<DATA, DIM>> *closestNeighbour; ///< The closest neighbour
155  std::vector<KDTreeNodeInfoT<DATA, DIM>> *initialEltList; ///< The initial element list
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  {
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>
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>
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.
static QCString result
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.
struct vector vector
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.
static QStrList * l
Definition: config.cpp:1044
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...
void clearTree()
Frees the KDTree.
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 swap(Handle< T > &a, Handle< T > &b)
const double a
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".
static Entry * current
double distance(double x1, double y1, double z1, double x2, double y2, double z2)
static int max(int a, int b)
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
static bool * b
Definition: config.cpp:1043
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()
double median(sqlite3 *db, std::string const &table_name, std::string const &column_name)
Definition: statistics.cc:26
Header file for the kd tree linker tools template class.