Belle II Software development
QuadTreeProcessor.h
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8#pragma once
9
10#include <tracking/trackFindingCDC/legendre/quadtree/QuadTreeNode.h>
11#include <tracking/trackFindingCDC/legendre/quadtree/QuadTreeItem.h>
12
13#include <tracking/trackingUtilities/utilities/Algorithms.h>
14
15#include <framework/logging/Logger.h>
16
17#include <algorithm>
18#include <array>
19#include <functional>
20#include <memory>
21#include <map>
22#include <vector>
23#include <deque>
24#include <utility>
25
26namespace Belle2 {
31 namespace TrackingUtilities {
32 class CDCWireHit;
33 }
34 namespace TrackFindingCDC {
35
42 template<typename AX, typename AY, class AData>
44
45 private:
47 static const int c_maxNChildren = 16;
48
49 public:
52
55
57 using XSpan = typename QuadTree::XSpan;
58
60 using YSpan = typename QuadTree::YSpan;
61
63 using XYSpans = std::pair<XSpan, YSpan>;
64
67
69 using CandidateReceiver = std::function<void(const std::vector<AData*>&, QuadTree*)>;
70
71 public:
79 QuadTreeProcessor(int lastLevel,
80 int seedLevel,
81 const XYSpans& xySpans,
82 bool debugOutput = false)
83 : m_quadTree{std::make_unique<QuadTree>(xySpans.first, xySpans.second, 0, nullptr)}
84 , m_lastLevel(lastLevel)
85 , m_seedLevel(seedLevel)
86 , m_debugOutput(debugOutput)
88 {
89 }
90
95 {
96 clear();
97 }
98
102 void clear()
103 {
104 m_seededTrees.clear();
105 m_quadTree->clearChildren();
106 m_quadTree->clearItems();
107 m_items.clear();
108 }
109
113 void seed(const std::vector<AData*>& datas)
114 {
115 // Create the items
116 for (AData* data : datas) {
117 m_items.emplace_back(data);
118 }
119
120 // Creating the seed level
121 long nSeedBins = pow(2, m_seedLevel);
122 m_seededTrees.reserve(nSeedBins * nSeedBins);
123
124 // Expand the first levels to the seed sectors
125 m_seededTrees.push_back(m_quadTree.get());
126 std::vector<QuadTree*> nextSeededTrees;
127
128 for (int level = 0; level < m_seedLevel; ++level) {
129 for (QuadTree* node : m_seededTrees) {
130 if (node->getChildren().empty()) {
131 this->createChildren(node, node->getChildren());
132 }
133 for (QuadTree& child : node->getChildren()) {
134 nextSeededTrees.push_back(&child);
135 }
136 }
137 std::swap(nextSeededTrees, m_seededTrees);
138 nextSeededTrees.clear();
139 }
140
141 // Fill the seed level with the items
142 m_itemPtrs.clear();
143 m_itemPtrs.reserve(m_items.size());
144 for (Item& item : m_items) {
145 m_itemPtrs.push_back(&item);
146 }
147
148 for (QuadTree* seededTree : m_seededTrees) {
149 seededTree->reserveItems(m_items.size());
150 }
151
152 this->insertItemsInNodes(m_seededTrees, m_itemPtrs);
153 }
154
155 public:
160 std::vector<AData*> getAssignedItems()
161 {
162 std::vector<const TrackingUtilities::CDCWireHit*> result;
163 for (QuadTree* seededTree : m_seededTrees) {
164 for (Item* item : seededTree->getItems()) {
165 result.push_back(item->getPointer());
166 }
167 }
168 std::sort(result.begin(), result.end());
169 result.erase(std::unique(result.begin(), result.end()), result.end());
170 return result;
171 }
172
173 public:
179 void fill(const CandidateReceiver& candidateReceiver, int nHitsThreshold)
180 {
181 fill(candidateReceiver, nHitsThreshold, std::numeric_limits<AY>::max());
182 }
183
190 void fill(const CandidateReceiver& candidateReceiver, int nHitsThreshold, float yLimit)
191 {
192 std::vector<QuadTree*> quadTrees = m_seededTrees;
193 std::sort(quadTrees.begin(), quadTrees.end(), [](const QuadTree * quadTree1, const QuadTree * quadTree2) {
194 return quadTree1->getNItems() > quadTree2->getNItems();
195 });
196
197 for (QuadTree* tree : quadTrees) {
198 erase_remove_if(tree->getItems(), [](Item * hit) { return hit->isUsed(); });
199 fillGivenTree(tree, candidateReceiver, nHitsThreshold, yLimit);
200 }
201 }
202
203 private:
209 const CandidateReceiver& candidateReceiver,
210 int nItemsThreshold,
211 AY yLimit)
212 {
213 if (node->getNItems() < nItemsThreshold) {
214 return;
215 }
216
217 if ((node->getYMin() > yLimit) or (-node->getYMax() > yLimit)) {
218 return;
219 }
220
221 if (isLeaf(node)) {
222 callResultFunction(node, candidateReceiver);
223 return;
224 }
225
226 if (node->getChildren().empty()) {
227 this->createChildren(node, node->getChildren());
228 }
229
230 if (!node->checkFilled()) {
231 fillChildren(node, node->getItems());
232 node->setFilled();
233 }
234
235 // Kept on the stack - this function is called millions of times per event
236 std::array<QuadTree*, c_maxNChildren> children;
237 int nChildren = 0;
238 for (QuadTree& child : node->getChildren()) {
239 B2ASSERT("More children than the quad tree processor supports", nChildren < c_maxNChildren);
240 children[nChildren++] = &child;
241 }
242 const auto compareNItems = [](const QuadTree * lhs, const QuadTree * rhs) {
243 return lhs->getNItems() < rhs->getNItems();
244 };
245
246 // Explicitly count down the children
247 for (int nRemaining = nChildren; nRemaining > 0; --nRemaining) {
248 auto itHeaviestChild =
249 std::max_element(children.begin(), children.begin() + nRemaining, compareNItems);
250 QuadTree* heaviestChild = *itHeaviestChild;
251 // Drop the heaviest child from the list keeping the order of the remaining ones
252 std::move(itHeaviestChild + 1, children.begin() + nRemaining, itHeaviestChild);
253 // After we have processed some children we need to get rid of the already used hits in all the children,
254 // because this can change the number of items drastically
255 erase_remove_if(heaviestChild->getItems(), [&](Item * hit) { return hit->isUsed(); });
256 this->fillGivenTree(heaviestChild, candidateReceiver, nItemsThreshold, yLimit);
257 }
258 }
259
264 void createChildren(QuadTree* node, QuadTreeChildren& m_children) const
265 {
266 m_children.reserve(node->getXNbins() * node->getYNbins());
267 for (int i = 0; i < node->getXNbins(); ++i) {
268 for (int j = 0; j < node->getYNbins(); ++j) {
269 const XYSpans& xySpans = createChild(node, i, j);
270 const XSpan& xSpan = xySpans.first;
271 const YSpan& ySpan = xySpans.second;
272 m_children.push_back(QuadTree(xSpan, ySpan, node->getLevel() + 1, node));
273 }
274 }
275 }
276
281 void fillChildren(QuadTree* node, const std::vector<Item*>& items)
282 {
283 // An item can be inserted into each child at most once
284 const size_t neededSize = items.size();
285 m_childPtrs.clear();
286 m_childPtrs.reserve(node->getChildren().size());
287 for (QuadTree& child : node->getChildren()) {
288 child.reserveItems(neededSize);
289 m_childPtrs.push_back(&child);
290 }
291
292 this->insertItemsInNodes(m_childPtrs, items);
293
295 }
296
301 static void callResultFunction(QuadTree* node, const CandidateReceiver& candidateReceiver)
302 {
303 const std::vector<Item*>& foundItems = node->getItems();
304 std::vector<AData*> candidate;
305 candidate.reserve(foundItems.size());
306
307 for (Item* item : foundItems) {
308 item->setUsedFlag();
309 candidate.push_back(item->getPointer());
310 }
311
312 candidateReceiver(candidate, node);
313 }
314
315 protected: // Section of specialisable functions
324 virtual XYSpans createChild(QuadTree* node, int iX, int iY) const
325 {
326 AX xMin = node->getXLowerBound(iX);
327 AX xMax = node->getXUpperBound(iX);
328 AY yMin = node->getYLowerBound(iY);
329 AY yMax = node->getYUpperBound(iY);
330 return XYSpans({xMin, xMax}, {yMin, yMax});
331 }
332
340 virtual bool isInNode(QuadTree* node, AData* item) const = 0;
341
351 virtual void insertItemsInNodes(const std::vector<QuadTree*>& nodes,
352 const std::vector<Item*>& items)
353 {
354 for (Item* item : items) {
355 if (item->isUsed()) continue;
356
357 for (QuadTree* node : nodes) {
358 if (isInNode(node, item->getPointer())) {
359 node->insertItem(item);
360 }
361 }
362 }
363 }
364
370 virtual bool isLeaf(QuadTree* node) const
371 {
372 if (node->getLevel() >= m_lastLevel) {
373 return true;
374 } else {
375 return false;
376 }
377 }
378
382 int getLastLevel() const
383 {
384 return m_lastLevel;
385 }
386
387 public: // debug stuff
392 virtual void afterFillDebugHook(QuadTreeChildren& children)
393 {
394 if (not m_debugOutput) return;
395 for (const QuadTree& childNode : children) {
396 if (childNode.getLevel() != getLastLevel()) continue; // Only write the lowest level
397 //m_debugOutputMap[ {childNode.getXMean(), childNode.getYMean()}] = childNode.getItems();
398 }
399 }
400
404 const std::map<std::pair<AX, AY>, std::vector<Item*>>& getDebugInformation() const
405 {
406 return m_debugOutputMap;
407 }
408
409 protected:
411 std::unique_ptr<QuadTree> m_quadTree;
412
414 std::deque<Item> m_items;
415
417 std::vector<Item*> m_itemPtrs;
418
420 std::vector<QuadTree*> m_childPtrs;
421
427 std::vector<QuadTree*> m_seededTrees;
428
429 private:
432
435
438
440 std::map<std::pair<AX, AY>, std::vector<Item*>> m_debugOutputMap;
441 };
442 }
444}
This class serves as a wrapper around all things that should go into a QuadTree.
Class which holds quadtree structure.
Children & getChildren()
Returns the children structure of this node.
std::vector< AItem * > & getItems()
Get items from node.
AY getYMax() const
Get maximal "r" value of the node.
void setFilled()
Set status of node to "filled" (children nodes has been filled)
AY getYMin() const
Get minimal "r" value of the node.
int getLevel() const
Returns level of the node in tree (i.e., how much ancestors the node has)
AX getXLowerBound(int iBin) const
Get lower "Theta" value of given bin.
AY getYUpperBound(int iBin) const
Get upper "r" value of given bin.
AY getYLowerBound(int iBin) const
Get lower "r" value of given bin.
bool checkFilled() const
Check whether node has been processed, i.e.
int getNItems() const
Check if the node passes threshold on number of hits.
constexpr int getXNbins() const
Get number of bins in "Theta" direction.
constexpr int getYNbins() const
Get number of bins in "r" direction.
AX getXUpperBound(int iBin) const
Get upper "Theta" value of given bin.
void fill(const CandidateReceiver &candidateReceiver, int nHitsThreshold, float yLimit)
Fill vector of QuadTree instances with hits.
virtual void insertItemsInNodes(const std::vector< QuadTree * > &nodes, const std::vector< Item * > &items)
Insert each of the given items into every one of the given nodes it belongs to.
QuadTreeNode< AX, AY, Item > QuadTree
The used QuadTree.
static void callResultFunction(QuadTree *node, const CandidateReceiver &candidateReceiver)
When a node is accepted as a result, we extract a vector with the items (back transformed to AData*) ...
typename QuadTree::Children QuadTreeChildren
Alias for the QuadTree Children.
void createChildren(QuadTree *node, QuadTreeChildren &m_children) const
Creates the sub node of a given node.
void fillChildren(QuadTree *node, const std::vector< Item * > &items)
This function is called by fillGivenTree and fills the items into the corresponding children.
std::vector< Item * > m_itemPtrs
Reusable buffer with pointers to all items - used to seed the tree.
int m_lastLevel
The last level to be filled.
void fillGivenTree(QuadTree *node, const CandidateReceiver &candidateReceiver, int nItemsThreshold, AY yLimit)
Internal function to do the real quad tree search: fill the nodes, check which of the n*m bins we nee...
virtual ~QuadTreeProcessor()
Destructor deletes the quad tree.
const std::map< std::pair< AX, AY >, std::vector< Item * > > & getDebugInformation() const
Return the debug information if collected.
std::pair< XSpan, YSpan > XYSpans
This pair of spans describes the span of a node.
QuadTreeProcessor(int lastLevel, int seedLevel, const XYSpans &xySpans, bool debugOutput=false)
Constructor is very simple.
std::vector< QuadTree * > m_seededTrees
Vector of QuadTrees QuadTree instances (which are filled in the vector) cover the whole Legendre phas...
bool m_debugOutput
A flag to control the creation of the debug output.
int getLastLevel() const
Return the parameter last level.
virtual XYSpans createChild(QuadTree *node, int iX, int iY) const
Implement that function if you want to provide a new processor.
virtual bool isLeaf(QuadTree *node) const
Function which checks if given node is leaf Implemented as virtual to keep possibility of changing la...
typename QuadTree::XSpan XSpan
This pair describes the span in X for a node.
std::deque< Item > m_items
Storage space for the items that are referenced by the quad tree nodes.
std::vector< QuadTree * > m_childPtrs
Reusable buffer with pointers to the children of the node currently being filled.
std::vector< AData * > getAssignedItems()
Get items that have been assigned to the seed level The returned elements are unique even if items ar...
typename QuadTree::YSpan YSpan
This pair describes the span in Y for a node.
void fill(const CandidateReceiver &candidateReceiver, int nHitsThreshold)
Start filling the already created tree.
void clear()
Delete all the QuadTreeItems in the tree and clear the tree.
int m_seedLevel
The first level to be filled, effectively skip forward to this higher granularity level.
void seed(const std::vector< AData * > &datas)
Fill in the items in the given vector.
std::function< void(const std::vector< AData * > &, QuadTree *)> CandidateReceiver
This lambda function can be used for postprocessing.
QuadTreeItem< AData > Item
The QuadTree will only see items of this type.
virtual bool isInNode(QuadTree *node, AData *item) const =0
Implement that function if you want to provide a new processor.
std::map< std::pair< AX, AY >, std::vector< Item * > > m_debugOutputMap
The calculated debug map.
virtual void afterFillDebugHook(QuadTreeChildren &children)
Override that function if you want to receive debug output whenever the children of a node are filled...
static const int c_maxNChildren
Upper bound on the number of children of a node - they are kept on the stack.
std::unique_ptr< QuadTree > m_quadTree
The quad tree we work with.
Class representing a hit wire in the central drift chamber.
Definition CDCWireHit.h:56
Abstract base class for different kinds of events.
STL namespace.