mlpack
single_tree_traverser_impl.hpp
Go to the documentation of this file.
1 
13 #ifndef MLPACK_CORE_TREE_COVER_TREE_SINGLE_TREE_TRAVERSER_IMPL_HPP
14 #define MLPACK_CORE_TREE_COVER_TREE_SINGLE_TREE_TRAVERSER_IMPL_HPP
15 
16 // In case it hasn't been included yet.
18 
19 #include <queue>
20 
21 namespace mlpack {
22 namespace tree {
23 
25 template<
26  typename MetricType,
27  typename StatisticType,
28  typename MatType,
29  typename RootPointPolicy
30 >
32 {
36  double score;
38  size_t parent;
40  double baseCase;
41 
43  bool operator<(const CoverTreeMapEntry& other) const
44  {
45  return (score < other.score);
46  }
47 };
48 
49 template<
50  typename MetricType,
51  typename StatisticType,
52  typename MatType,
53  typename RootPointPolicy
54 >
55 template<typename RuleType>
58  rule(rule),
59  numPrunes(0)
60 { /* Nothing to do. */ }
61 
62 template<
63  typename MetricType,
64  typename StatisticType,
65  typename MatType,
66  typename RootPointPolicy
67 >
68 template<typename RuleType>
71  const size_t queryIndex,
72  CoverTree& referenceNode)
73 {
74  // This is a non-recursive implementation (which should be faster than a
75  // recursive implementation).
77  MapEntryType;
78 
79  // We will use this map as a priority queue. Each key represents the scale,
80  // and then the vector is all the nodes in that scale which need to be
81  // investigated. Because no point in a scale can add a point in its own
82  // scale, we know that the vector for each scale is final when we get to it.
83  // In addition, the map is organized in such a way that begin() will return
84  // the largest scale.
85  std::map<int, std::vector<MapEntryType>, std::greater<int>> mapQueue;
86 
87  // Create the score for the children.
88  double rootChildScore = rule.Score(queryIndex, referenceNode);
89 
90  if (rootChildScore == DBL_MAX)
91  {
92  numPrunes += referenceNode.NumChildren();
93  }
94  else
95  {
96  // Manually add the children of the first node.
97  // Often, a ruleset will return without doing any computation on cover trees
98  // using TreeTraits::FirstPointIsCentroid; this is an optimization that
99  // (theoretically) the compiler should get right.
100  double rootBaseCase = rule.BaseCase(queryIndex, referenceNode.Point());
101 
102  // Don't add the self-leaf.
103  size_t i = 0;
104  if (referenceNode.Child(0).NumChildren() == 0)
105  {
106  ++numPrunes;
107  i = 1;
108  }
109 
110  for (/* i was set above. */; i < referenceNode.NumChildren(); ++i)
111  {
112  MapEntryType newFrame;
113  newFrame.node = &referenceNode.Child(i);
114  newFrame.score = rootChildScore;
115  newFrame.baseCase = rootBaseCase;
116  newFrame.parent = referenceNode.Point();
117 
118  // Put it into the map.
119  mapQueue[newFrame.node->Scale()].push_back(newFrame);
120  }
121  }
122 
123  // Now begin the iteration through the map, but only if it has anything in it.
124  if (mapQueue.empty())
125  return;
126  int maxScale = mapQueue.cbegin()->first;
127 
128  // We will treat the leaves differently (below).
129  while (maxScale != INT_MIN)
130  {
131  // Get a reference to the current scale.
132  std::vector<MapEntryType>& scaleVector = mapQueue[maxScale];
133 
134  // Before traversing all the points in this scale, sort by score.
135  std::sort(scaleVector.begin(), scaleVector.end());
136 
137  // Now loop over each element.
138  for (size_t i = 0; i < scaleVector.size(); ++i)
139  {
140  // Get a reference to the current element.
141  const MapEntryType& frame = scaleVector.at(i);
142 
143  CoverTree* node = frame.node;
144  const double score = frame.score;
145  const size_t parent = frame.parent;
146  const size_t point = node->Point();
147  double baseCase = frame.baseCase;
148 
149  // First we recalculate the score of this node to find if we can prune it.
150  if (rule.Rescore(queryIndex, *node, score) == DBL_MAX)
151  {
152  ++numPrunes;
153  continue;
154  }
155 
156  // Create the score for the children.
157  const double childScore = rule.Score(queryIndex, *node);
158 
159  // Now if this childScore is DBL_MAX we can prune all children. In this
160  // recursion setup pruning is all or nothing for children.
161  if (childScore == DBL_MAX)
162  {
163  numPrunes += node->NumChildren();
164  continue;
165  }
166 
167  // If we are a self-child, the base case has already been evaluated.
168  // Often, a ruleset will return without doing any computation on cover
169  // trees using TreeTraits::FirstPointIsCentroid; this is an optimization
170  // that (theoretically) the compiler should get right.
171  if (point != parent)
172  {
173  baseCase = rule.BaseCase(queryIndex, point);
174  }
175 
176  // Don't add the self-leaf.
177  size_t j = 0;
178  if (node->Child(0).NumChildren() == 0)
179  {
180  ++numPrunes;
181  j = 1;
182  }
183 
184  for (/* j is already set. */; j < node->NumChildren(); ++j)
185  {
186  MapEntryType newFrame;
187  newFrame.node = &node->Child(j);
188  newFrame.score = childScore;
189  newFrame.baseCase = baseCase;
190  newFrame.parent = point;
191 
192  mapQueue[newFrame.node->Scale()].push_back(newFrame);
193  }
194  }
195 
196  // Now clear the memory for this scale; it isn't needed anymore.
197  mapQueue.erase(maxScale);
198  maxScale = mapQueue.begin()->first;
199  }
200 
201  // Now deal with the leaves.
202  for (size_t i = 0; i < mapQueue[INT_MIN].size(); ++i)
203  {
204  const MapEntryType& frame = mapQueue[INT_MIN].at(i);
205 
206  CoverTree* node = frame.node;
207  const double score = frame.score;
208  const size_t point = node->Point();
209 
210  // First, recalculate the score of this node to find if we can prune it.
211  double rescore = rule.Rescore(queryIndex, *node, score);
212 
213  if (rescore == DBL_MAX)
214  {
215  ++numPrunes;
216  continue;
217  }
218 
219  // For this to be a valid dual-tree algorithm, we *must* evaluate the
220  // combination, even if pruning it will make no difference. It's the
221  // definition.
222  const double actualScore = rule.Score(queryIndex, *node);
223 
224  if (actualScore == DBL_MAX)
225  {
226  ++numPrunes;
227  continue;
228  }
229  else
230  {
231  // Evaluate the base case, since the combination was not pruned.
232  // Often, a ruleset will return without doing any computation on cover
233  // trees using TreeTraits::FirstPointIsCentroid; this is an optimization
234  // that (theoretically) the compiler should get right.
235  rule.BaseCase(queryIndex, point);
236  }
237  }
238 }
239 
240 } // namespace tree
241 } // namespace mlpack
242 
243 #endif
size_t parent
The index of the parent node.
Definition: single_tree_traverser_impl.hpp:38
bool operator<(const CoverTreeMapEntry &other) const
Comparison operator.
Definition: single_tree_traverser_impl.hpp:43
Linear algebra utility functions, generally performed on matrices or vectors.
Definition: cv.hpp:1
double baseCase
The base case evaluation.
Definition: single_tree_traverser_impl.hpp:40
CoverTree< MetricType, StatisticType, MatType, RootPointPolicy > * node
The node this entry refers to.
Definition: single_tree_traverser_impl.hpp:34
double score
The score of the node.
Definition: single_tree_traverser_impl.hpp:36
size_t Point() const
Get the index of the point which this node represents.
Definition: cover_tree.hpp:286
const CoverTree & Child(const size_t index) const
Get a particular child node.
Definition: cover_tree.hpp:294
size_t NumChildren() const
Get the number of children.
Definition: cover_tree.hpp:301
This is the structure the cover tree map will use for traversal.
Definition: single_tree_traverser_impl.hpp:31
A cover tree is a tree specifically designed to speed up nearest-neighbor computation in high-dimensi...
Definition: cover_tree.hpp:99