1 /**
2 * @file core/tree/rectangle_tree/rectangle_tree_impl.hpp
3 * @author Andrew Wells
4 *
5 * Implementation of generalized rectangle tree.
6 *
7 * mlpack is free software; you may redistribute it and/or modify it under the
8 * terms of the 3-clause BSD license. You should have received a copy of the
9 * 3-clause BSD license along with mlpack. If not, see
10 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
11 */
12 #ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_RECTANGLE_TREE_IMPL_HPP
13 #define MLPACK_CORE_TREE_RECTANGLE_TREE_RECTANGLE_TREE_IMPL_HPP
14
15 // In case it wasn't included already for some reason.
16 #include "rectangle_tree.hpp"
17
18 #include <mlpack/core/util/log.hpp>
19
20 namespace mlpack {
21 namespace tree {
22
23 // Build the statistics, bottom-up.
24 template<typename MetricType,
25 typename StatisticType,
26 typename MatType,
27 typename SplitType,
28 typename DescentType,
29 template<typename> class AuxiliaryInformationType>
30 void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
31 AuxiliaryInformationType>::
BuildStatistics(RectangleTree * node)32 BuildStatistics(RectangleTree* node)
33 {
34 // Recurse first.
35 for (size_t i = 0; i < node->NumChildren(); ++i)
36 BuildStatistics(&node->Child(i));
37
38 // Now build the statistic.
39 node->Stat() = StatisticType(*node);
40 }
41
42 template<typename MetricType,
43 typename StatisticType,
44 typename MatType,
45 typename SplitType,
46 typename DescentType,
47 template<typename> class AuxiliaryInformationType>
48 RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
49 AuxiliaryInformationType>::
RectangleTree(const MatType & data,const size_t maxLeafSize,const size_t minLeafSize,const size_t maxNumChildren,const size_t minNumChildren,const size_t firstDataIndex)50 RectangleTree(const MatType& data,
51 const size_t maxLeafSize,
52 const size_t minLeafSize,
53 const size_t maxNumChildren,
54 const size_t minNumChildren,
55 const size_t firstDataIndex) :
56 maxNumChildren(maxNumChildren),
57 minNumChildren(minNumChildren),
58 numChildren(0),
59 children(maxNumChildren + 1), // Add one to make splitting the node simpler.
60 parent(NULL),
61 begin(0),
62 count(0),
63 numDescendants(0),
64 maxLeafSize(maxLeafSize),
65 minLeafSize(minLeafSize),
66 bound(data.n_rows),
67 parentDistance(0),
68 dataset(new MatType(data)),
69 ownsDataset(true),
70 points(maxLeafSize + 1), // Add one to make splitting the node simpler.
71 auxiliaryInfo(this)
72 {
73 // For now, just insert the points in order.
74 RectangleTree* root = this;
75
76 for (size_t i = firstDataIndex; i < data.n_cols; ++i)
77 root->InsertPoint(i);
78
79 // Initialize statistic recursively after tree construction is complete.
80 BuildStatistics(this);
81 }
82
83 template<typename MetricType,
84 typename StatisticType,
85 typename MatType,
86 typename SplitType,
87 typename DescentType,
88 template<typename> class AuxiliaryInformationType>
89 RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
90 AuxiliaryInformationType>::
RectangleTree(MatType && data,const size_t maxLeafSize,const size_t minLeafSize,const size_t maxNumChildren,const size_t minNumChildren,const size_t firstDataIndex)91 RectangleTree(MatType&& data,
92 const size_t maxLeafSize,
93 const size_t minLeafSize,
94 const size_t maxNumChildren,
95 const size_t minNumChildren,
96 const size_t firstDataIndex) :
97 maxNumChildren(maxNumChildren),
98 minNumChildren(minNumChildren),
99 numChildren(0),
100 children(maxNumChildren + 1), // Add one to make splitting the node simpler.
101 parent(NULL),
102 begin(0),
103 count(0),
104 numDescendants(0),
105 maxLeafSize(maxLeafSize),
106 minLeafSize(minLeafSize),
107 bound(data.n_rows),
108 parentDistance(0),
109 dataset(new MatType(std::move(data))),
110 ownsDataset(true),
111 points(maxLeafSize + 1), // Add one to make splitting the node simpler.
112 auxiliaryInfo(this)
113 {
114 // For now, just insert the points in order.
115 RectangleTree* root = this;
116
117 for (size_t i = firstDataIndex; i < dataset->n_cols; ++i)
118 root->InsertPoint(i);
119
120 // Initialize statistic recursively after tree construction is complete.
121 BuildStatistics(this);
122 }
123
124 template<typename MetricType,
125 typename StatisticType,
126 typename MatType,
127 typename SplitType,
128 typename DescentType,
129 template<typename> class AuxiliaryInformationType>
130 RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
131 AuxiliaryInformationType>::
RectangleTree(RectangleTree<MetricType,StatisticType,MatType,SplitType,DescentType,AuxiliaryInformationType> * parentNode,const size_t numMaxChildren)132 RectangleTree(
133 RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
134 AuxiliaryInformationType>*
135 parentNode, const size_t numMaxChildren) :
136 maxNumChildren(numMaxChildren > 0 ? numMaxChildren :
137 parentNode->MaxNumChildren()),
138 minNumChildren(parentNode->MinNumChildren()),
139 numChildren(0),
140 children(maxNumChildren + 1),
141 parent(parentNode),
142 begin(0),
143 count(0),
144 numDescendants(0),
145 maxLeafSize(parentNode->MaxLeafSize()),
146 minLeafSize(parentNode->MinLeafSize()),
147 bound(parentNode->Bound().Dim()),
148 parentDistance(0),
149 dataset(&parentNode->Dataset()),
150 ownsDataset(false),
151 points(maxLeafSize + 1), // Add one to make splitting the node simpler.
152 auxiliaryInfo(this)
153 {
154 // Initialize statistic.
155 BuildStatistics(this);
156 }
157
158 /**
159 * Create a rectangle tree by copying the other tree. Be careful! This can
160 * take a long time and use a lot of memory.
161 */
162 template<typename MetricType,
163 typename StatisticType,
164 typename MatType,
165 typename SplitType,
166 typename DescentType,
167 template<typename> class AuxiliaryInformationType>
168 RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
169 AuxiliaryInformationType>::
RectangleTree(const RectangleTree & other,const bool deepCopy,RectangleTree * newParent)170 RectangleTree(
171 const RectangleTree& other,
172 const bool deepCopy,
173 RectangleTree* newParent) :
174 maxNumChildren(other.MaxNumChildren()),
175 minNumChildren(other.MinNumChildren()),
176 numChildren(other.NumChildren()),
177 children(maxNumChildren + 1, NULL),
178 parent(deepCopy ? newParent : other.Parent()),
179 begin(other.Begin()),
180 count(other.Count()),
181 numDescendants(other.numDescendants),
182 maxLeafSize(other.MaxLeafSize()),
183 minLeafSize(other.MinLeafSize()),
184 bound(other.bound),
185 stat(other.stat),
186 parentDistance(other.ParentDistance()),
187 dataset(deepCopy ?
188 (parent ? parent->dataset : new MatType(*other.dataset)) :
189 &other.Dataset()),
190 ownsDataset(deepCopy && (!parent)),
191 points(other.points),
192 auxiliaryInfo(other.auxiliaryInfo, this, deepCopy)
193 {
194 if (deepCopy)
195 {
196 if (numChildren > 0)
197 {
198 for (size_t i = 0; i < numChildren; ++i)
199 children[i] = new RectangleTree(other.Child(i), true, this);
200 }
201 }
202 else
203 children = other.children;
204 }
205
206 /**
207 * Move constructor.
208 */
209 template<typename MetricType,
210 typename StatisticType,
211 typename MatType,
212 typename SplitType,
213 typename DescentType,
214 template<typename> class AuxiliaryInformationType>
215 RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
216 AuxiliaryInformationType>::
RectangleTree(RectangleTree && other)217 RectangleTree(RectangleTree&& other) :
218 maxNumChildren(other.MaxNumChildren()),
219 minNumChildren(other.MinNumChildren()),
220 numChildren(other.NumChildren()),
221 children(std::move(other.children)),
222 parent(other.Parent()),
223 begin(other.Begin()),
224 count(other.Count()),
225 numDescendants(other.numDescendants),
226 maxLeafSize(other.MaxLeafSize()),
227 minLeafSize(other.MinLeafSize()),
228 bound(std::move(other.bound)),
229 stat(std::move(other.stat)),
230 parentDistance(other.ParentDistance()),
231 dataset(other.dataset),
232 ownsDataset(other.ownsDataset),
233 points(std::move(other.points)),
234 auxiliaryInfo(std::move(other.auxiliaryInfo))
235 {
236 if (parent)
237 {
238 size_t iChild = 0;
239 while (parent->children[iChild] != (&other))
240 iChild++;
241 assert(iChild < numChildren);
242 parent->children[iChild] = this;
243 }
244 if (!IsLeaf())
245 {
246 for (size_t i = 0; i < numChildren; ++i)
247 children[i]->parent = this;
248 }
249 // Now we are a clone of the other tree. But we must also clear the other
250 // tree's contents, so it doesn't delete anything when it is destructed.
251 other.maxNumChildren = 0;
252 other.minNumChildren = 0;
253 other.numChildren = 0;
254 other.parent = NULL;
255 other.begin = 0;
256 other.count = 0;
257 other.numDescendants = 0;
258 other.maxLeafSize = 0;
259 other.minLeafSize = 0;
260 other.parentDistance = 0;
261 other.dataset = NULL;
262 other.ownsDataset = false;
263 }
264
265 /**
266 * Copy assignment operator: copy the given other tree.
267 */
268 template<typename MetricType,
269 typename StatisticType,
270 typename MatType,
271 typename SplitType,
272 typename DescentType,
273 template<typename> class AuxiliaryInformationType>
274 RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
275 AuxiliaryInformationType>&
276 RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
277 AuxiliaryInformationType>::
operator =(const RectangleTree & other)278 operator=(const RectangleTree& other)
279 {
280 // Return if it's the same tree.
281 if (this == &other)
282 return *this;
283
284 // Freeing memory that will not be used anymore.
285 for (size_t i = 0; i < numChildren; ++i)
286 delete children[i];
287
288 if (ownsDataset)
289 delete dataset;
290
291 maxNumChildren = other.MaxNumChildren();
292 minNumChildren = other.MinNumChildren();
293 numChildren = other.NumChildren();
294 children.resize(maxNumChildren + 1, NULL);
295 parent = NULL;
296 begin = other.Begin();
297 count = other.Count();
298 numDescendants = other.numDescendants;
299 maxLeafSize = other.MaxLeafSize();
300 minLeafSize = other.MinLeafSize();
301 bound = other.bound;
302 stat = other.stat;
303 parentDistance = other.ParentDistance();
304 dataset = new MatType(*other.dataset);
305 ownsDataset = true;
306 points = other.points;
307 auxiliaryInfo = AuxiliaryInfoType(other.auxiliaryInfo, this, true);
308
309 if (numChildren > 0)
310 {
311 for (size_t i = 0; i < numChildren; ++i)
312 children[i] = new RectangleTree(other.Child(i), true, this);
313 }
314
315 return *this;
316 }
317
318 /**
319 * Move assignment operator: take ownership of the given tree.
320 */
321 template<typename MetricType,
322 typename StatisticType,
323 typename MatType,
324 typename SplitType,
325 typename DescentType,
326 template<typename> class AuxiliaryInformationType>
327 RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
328 AuxiliaryInformationType>&
329 RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
330 AuxiliaryInformationType>::
operator =(RectangleTree && other)331 operator=(RectangleTree&& other)
332 {
333 // Return if it's the same tree.
334 if (this == &other)
335 return *this;
336
337 // Freeing memory that will not be used anymore.
338 for (size_t i = 0; i < numChildren; ++i)
339 delete children[i];
340
341 if (ownsDataset)
342 delete dataset;
343
344 maxNumChildren = other.MaxNumChildren();
345 minNumChildren = other.MinNumChildren();
346 numChildren = other.NumChildren();
347 children = std::move(other.children);
348 parent = other.Parent();
349 begin = other.Begin();
350 count = other.Count();
351 numDescendants = other.numDescendants;
352 maxLeafSize = other.MaxLeafSize();
353 minLeafSize = other.MinLeafSize();
354 bound = std::move(other.bound);
355 stat = std::move(other.stat);
356 parentDistance = other.ParentDistance();
357 dataset = other.dataset;
358 ownsDataset = other.ownsDataset;
359 points = std::move(other.points);
360 auxiliaryInfo = std::move(other.auxiliaryInfo);
361
362 // Now we are a clone of the other tree. But we must also clear the other
363 // tree's contents, so it doesn't delete anything when it is destructed.
364 other.maxNumChildren = 0;
365 other.minNumChildren = 0;
366 other.numChildren = 0;
367 other.parent = NULL;
368 other.begin = 0;
369 other.count = 0;
370 other.numDescendants = 0;
371 other.maxLeafSize = 0;
372 other.minLeafSize = 0;
373 other.parentDistance = 0;
374 other.dataset = NULL;
375 other.ownsDataset = false;
376
377 return *this;
378 }
379
380 /**
381 * Construct the tree from a boost::serialization archive.
382 */
383 template<typename MetricType,
384 typename StatisticType,
385 typename MatType,
386 typename SplitType,
387 typename DescentType,
388 template<typename> class AuxiliaryInformationType>
389 template<typename Archive>
390 RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
391 AuxiliaryInformationType>::
RectangleTree(Archive & ar,const typename std::enable_if_t<Archive::is_loading::value> *)392 RectangleTree(
393 Archive& ar,
394 const typename std::enable_if_t<Archive::is_loading::value>*) :
395 RectangleTree() // Use default constructor.
396 {
397 // Now serialize.
398 ar >> BOOST_SERIALIZATION_NVP(*this);
399 }
400
401 /**
402 * Deletes this node, deallocating the memory for the children and calling
403 * their destructors in turn. This will invalidate any pointers or references
404 * to any nodes which are children of this one.
405 */
406 template<typename MetricType,
407 typename StatisticType,
408 typename MatType,
409 typename SplitType,
410 typename DescentType,
411 template<typename> class AuxiliaryInformationType>
412 RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
413 AuxiliaryInformationType>::
~RectangleTree()414 ~RectangleTree()
415 {
416 for (size_t i = 0; i < numChildren; ++i)
417 delete children[i];
418
419 if (ownsDataset)
420 delete dataset;
421 }
422
423 /**
424 * Deletes this node but leaves the children untouched. Needed for when we
425 * split nodes and remove nodes (inserting and deleting points).
426 */
427 template<typename MetricType,
428 typename StatisticType,
429 typename MatType,
430 typename SplitType,
431 typename DescentType,
432 template<typename> class AuxiliaryInformationType>
433 void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
434 AuxiliaryInformationType>::
SoftDelete()435 SoftDelete()
436 {
437 parent = NULL;
438
439 for (size_t i = 0; i < children.size(); ++i)
440 children[i] = NULL;
441
442 numChildren = 0;
443 delete this;
444 }
445
446 /**
447 * Nullify the auxiliary information.
448 */
449 template<typename MetricType,
450 typename StatisticType,
451 typename MatType,
452 typename SplitType,
453 typename DescentType,
454 template<typename> class AuxiliaryInformationType>
455 void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
456 AuxiliaryInformationType>::
NullifyData()457 NullifyData()
458 {
459 auxiliaryInfo.NullifyData();
460 }
461
462 /**
463 * Recurse through the tree and insert the point at the leaf node chosen
464 * by the heuristic.
465 */
466 template<typename MetricType,
467 typename StatisticType,
468 typename MatType,
469 typename SplitType,
470 typename DescentType,
471 template<typename> class AuxiliaryInformationType>
472 void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
473 AuxiliaryInformationType>::
InsertPoint(const size_t point)474 InsertPoint(const size_t point)
475 {
476 // Expand the bound regardless of whether it is a leaf node.
477 bound |= dataset->col(point);
478
479 numDescendants++;
480
481 std::vector<bool> lvls(TreeDepth(), true);
482
483 // If this is a leaf node, we stop here and add the point.
484 if (numChildren == 0)
485 {
486 if (!auxiliaryInfo.HandlePointInsertion(this, point))
487 points[count++] = point;
488
489 SplitNode(lvls);
490 return;
491 }
492
493 // If it is not a leaf node, we use the DescentHeuristic to choose a child
494 // to which we recurse.
495 auxiliaryInfo.HandlePointInsertion(this, point);
496 const size_t descentNode = DescentType::ChooseDescentNode(this, point);
497 children[descentNode]->InsertPoint(point, lvls);
498 }
499
500 /**
501 * Inserts a point into the tree, tracking which levels have been inserted into.
502 */
503 template<typename MetricType,
504 typename StatisticType,
505 typename MatType,
506 typename SplitType,
507 typename DescentType,
508 template<typename> class AuxiliaryInformationType>
509 void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
510 AuxiliaryInformationType>::
InsertPoint(const size_t point,std::vector<bool> & relevels)511 InsertPoint(const size_t point, std::vector<bool>& relevels)
512 {
513 // Expand the bound regardless of whether it is a leaf node.
514 bound |= dataset->col(point);
515
516 numDescendants++;
517
518 // If this is a leaf node, we stop here and add the point.
519 if (numChildren == 0)
520 {
521 if (!auxiliaryInfo.HandlePointInsertion(this, point))
522 points[count++] = point;
523
524 SplitNode(relevels);
525 return;
526 }
527
528 // If it is not a leaf node, we use the DescentHeuristic to choose a child
529 // to which we recurse.
530 auxiliaryInfo.HandlePointInsertion(this, point);
531 const size_t descentNode = DescentType::ChooseDescentNode(this, point);
532 children[descentNode]->InsertPoint(point, relevels);
533 }
534
535 /**
536 * Inserts a node into the tree, tracking which levels have been inserted into.
537 *
538 * @param node The node to be inserted.
539 * @param level The level on which this node should be inserted.
540 * @param relevels The levels that have been reinserted to on this top level
541 * insertion.
542 */
543 template<typename MetricType,
544 typename StatisticType,
545 typename MatType,
546 typename SplitType,
547 typename DescentType,
548 template<typename> class AuxiliaryInformationType>
549 void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
550 AuxiliaryInformationType>::
InsertNode(RectangleTree * node,const size_t level,std::vector<bool> & relevels)551 InsertNode(RectangleTree* node,
552 const size_t level,
553 std::vector<bool>& relevels)
554 {
555 // Expand the bound regardless of the level.
556 bound |= node->Bound();
557 numDescendants += node->numDescendants;
558 if (level == TreeDepth())
559 {
560 if (!auxiliaryInfo.HandleNodeInsertion(this, node, true))
561 {
562 children[numChildren++] = node;
563 node->Parent() = this;
564 }
565 SplitNode(relevels);
566 }
567 else
568 {
569 auxiliaryInfo.HandleNodeInsertion(this, node, false);
570 const size_t descentNode = DescentType::ChooseDescentNode(this, node);
571 children[descentNode]->InsertNode(node, level, relevels);
572 }
573 }
574
575 /**
576 * Recurse through the tree to remove the point. Once we find the point, we
577 * shrink the rectangles if necessary.
578 */
579 template<typename MetricType,
580 typename StatisticType,
581 typename MatType,
582 typename SplitType,
583 typename DescentType,
584 template<typename> class AuxiliaryInformationType>
585 bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
586 AuxiliaryInformationType>::
DeletePoint(const size_t point)587 DeletePoint(const size_t point)
588 {
589 // It is possible that this will cause a reinsertion, so we need to handle the
590 // levels properly.
591 RectangleTree* root = this;
592 while (root->Parent() != NULL)
593 root = root->Parent();
594
595 std::vector<bool> lvls(root->TreeDepth(), true);
596
597 if (numChildren == 0)
598 {
599 for (size_t i = 0; i < count; ++i)
600 {
601 if (points[i] == point)
602 {
603 if (!auxiliaryInfo.HandlePointDeletion(this, i))
604 points[i] = points[--count];
605
606 RectangleTree* tree = this;
607 while (tree != NULL)
608 {
609 tree->numDescendants--;
610 tree = tree->Parent();
611 }
612 // This function wil ensure that minFill is satisfied.
613 CondenseTree(dataset->col(point), lvls, true);
614 return true;
615 }
616 }
617 }
618
619 for (size_t i = 0; i < numChildren; ++i)
620 if (children[i]->Bound().Contains(dataset->col(point)))
621 if (children[i]->DeletePoint(point, lvls))
622 return true;
623
624 return false;
625 }
626
627 /**
628 * Recurse through the tree to remove the point. Once we find the point, we
629 * shrink the rectangles if necessary.
630 */
631 template<typename MetricType,
632 typename StatisticType,
633 typename MatType,
634 typename SplitType,
635 typename DescentType,
636 template<typename> class AuxiliaryInformationType>
637 bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
638 AuxiliaryInformationType>::
DeletePoint(const size_t point,std::vector<bool> & relevels)639 DeletePoint(const size_t point, std::vector<bool>& relevels)
640 {
641 if (numChildren == 0)
642 {
643 for (size_t i = 0; i < count; ++i)
644 {
645 if (points[i] == point)
646 {
647 if (!auxiliaryInfo.HandlePointDeletion(this, i))
648 points[i] = points[--count];
649
650 RectangleTree* tree = this;
651 while (tree != NULL)
652 {
653 tree->numDescendants--;
654 tree = tree->Parent();
655 }
656 // This function will ensure that minFill is satisfied.
657 CondenseTree(dataset->col(point), relevels, true);
658 return true;
659 }
660 }
661 }
662
663 for (size_t i = 0; i < numChildren; ++i)
664 if (children[i]->Bound().Contains(dataset->col(point)))
665 if (children[i]->DeletePoint(point, relevels))
666 return true;
667
668 return false;
669 }
670
671
672 /**
673 * Recurse through the tree to remove the node. Once we find the node, we
674 * shrink the rectangles if necessary.
675 */
676 template<typename MetricType,
677 typename StatisticType,
678 typename MatType,
679 typename SplitType,
680 typename DescentType,
681 template<typename> class AuxiliaryInformationType>
682 bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
683 AuxiliaryInformationType>::
RemoveNode(const RectangleTree * node,std::vector<bool> & relevels)684 RemoveNode(const RectangleTree* node, std::vector<bool>& relevels)
685 {
686 for (size_t i = 0; i < numChildren; ++i)
687 {
688 if (children[i] == node)
689 {
690 if (!auxiliaryInfo.HandleNodeRemoval(this, i))
691 {
692 children[i] = children[--numChildren]; // Decrement numChildren.
693 }
694 RectangleTree* tree = this;
695 while (tree != NULL)
696 {
697 tree->numDescendants -= node->numDescendants;
698 tree = tree->Parent();
699 }
700 CondenseTree(arma::vec(), relevels, false);
701 return true;
702 }
703
704 bool contains = true;
705 for (size_t j = 0; j < node->Bound().Dim(); ++j)
706 contains &= Child(i).Bound()[j].Contains(node->Bound()[j]);
707
708 if (contains)
709 if (children[i]->RemoveNode(node, relevels))
710 return true;
711 }
712
713 return false;
714 }
715
716 template<typename MetricType,
717 typename StatisticType,
718 typename MatType,
719 typename SplitType,
720 typename DescentType,
721 template<typename> class AuxiliaryInformationType>
722 size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
TreeSize() const723 DescentType, AuxiliaryInformationType>::TreeSize() const
724 {
725 int n = 0;
726 for (int i = 0; i < numChildren; ++i)
727 n += children[i]->TreeSize();
728
729 return n + 1; // Add one for this node.
730 }
731
732 template<typename MetricType,
733 typename StatisticType,
734 typename MatType,
735 typename SplitType,
736 typename DescentType,
737 template<typename> class AuxiliaryInformationType>
738 size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
TreeDepth() const739 DescentType, AuxiliaryInformationType>::TreeDepth() const
740 {
741 int n = 1;
742 RectangleTree* currentNode = const_cast<RectangleTree*> (this);
743
744 while (!currentNode->IsLeaf())
745 {
746 currentNode = currentNode->children[0];
747 n++;
748 }
749
750 return n;
751 }
752
753 template<typename MetricType,
754 typename StatisticType,
755 typename MatType,
756 typename SplitType,
757 typename DescentType,
758 template<typename> class AuxiliaryInformationType>
759 inline bool RectangleTree<MetricType, StatisticType, MatType, SplitType,
IsLeaf() const760 DescentType, AuxiliaryInformationType>::IsLeaf() const
761 {
762 return (numChildren == 0);
763 }
764
765 /**
766 * Return the index of the nearest child node to the given query point. If
767 * this is a leaf node, it will return NumChildren() (invalid index).
768 */
769 template<typename MetricType,
770 typename StatisticType,
771 typename MatType,
772 typename SplitType,
773 typename DescentType,
774 template<typename> class AuxiliaryInformationType>
775 template<typename VecType>
776 size_t RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
GetNearestChild(const VecType & point,typename std::enable_if_t<IsVector<VecType>::value> *)777 AuxiliaryInformationType>::GetNearestChild(
778 const VecType& point,
779 typename std::enable_if_t<IsVector<VecType>::value>*)
780 {
781 if (IsLeaf())
782 return 0;
783
784 ElemType bestDistance = std::numeric_limits<ElemType>::max();
785 size_t bestIndex = 0;
786 for (size_t i = 0; i < NumChildren(); ++i)
787 {
788 ElemType distance = Child(i).MinDistance(point);
789 if (distance <= bestDistance)
790 {
791 bestDistance = distance;
792 bestIndex = i;
793 }
794 }
795 return bestIndex;
796 }
797
798 /**
799 * Return the index of the furthest child node to the given query point. If
800 * this is a leaf node, it will return NumChildren() (invalid index).
801 */
802 template<typename MetricType,
803 typename StatisticType,
804 typename MatType,
805 typename SplitType,
806 typename DescentType,
807 template<typename> class AuxiliaryInformationType>
808 template<typename VecType>
809 size_t RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
GetFurthestChild(const VecType & point,typename std::enable_if_t<IsVector<VecType>::value> *)810 AuxiliaryInformationType>::GetFurthestChild(
811 const VecType& point,
812 typename std::enable_if_t<IsVector<VecType>::value>*)
813 {
814 if (IsLeaf())
815 return 0;
816
817 ElemType bestDistance = 0;
818 size_t bestIndex = 0;
819 for (size_t i = 0; i < NumChildren(); ++i)
820 {
821 ElemType distance = Child(i).MaxDistance(point);
822 if (distance >= bestDistance)
823 {
824 bestDistance = distance;
825 bestIndex = i;
826 }
827 }
828 return bestIndex;
829 }
830
831 /**
832 * Return the index of the nearest child node to the given query node. If it
833 * can't decide, it will return NumChildren() (invalid index).
834 */
835 template<typename MetricType,
836 typename StatisticType,
837 typename MatType,
838 typename SplitType,
839 typename DescentType,
840 template<typename> class AuxiliaryInformationType>
841 size_t RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
GetNearestChild(const RectangleTree & queryNode)842 AuxiliaryInformationType>::GetNearestChild(const RectangleTree& queryNode)
843 {
844 if (IsLeaf())
845 return 0;
846
847 ElemType bestDistance = std::numeric_limits<ElemType>::max();
848 size_t bestIndex = 0;
849 for (size_t i = 0; i < NumChildren(); ++i)
850 {
851 ElemType distance = Child(i).MinDistance(queryNode);
852 if (distance <= bestDistance)
853 {
854 bestDistance = distance;
855 bestIndex = i;
856 }
857 }
858 return bestIndex;
859 }
860
861 /**
862 * Return the index of the furthest child node to the given query node. If it
863 * can't decide, it will return NumChildren() (invalid index).
864 */
865 template<typename MetricType,
866 typename StatisticType,
867 typename MatType,
868 typename SplitType,
869 typename DescentType,
870 template<typename> class AuxiliaryInformationType>
871 size_t RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
GetFurthestChild(const RectangleTree & queryNode)872 AuxiliaryInformationType>::GetFurthestChild(const RectangleTree& queryNode)
873 {
874 if (IsLeaf())
875 return 0;
876
877 ElemType bestDistance = 0;
878 size_t bestIndex = 0;
879 for (size_t i = 0; i < NumChildren(); ++i)
880 {
881 ElemType distance = Child(i).MaxDistance(queryNode);
882 if (distance >= bestDistance)
883 {
884 bestDistance = distance;
885 bestIndex = i;
886 }
887 }
888 return bestIndex;
889 }
890
891 /**
892 * Return a bound on the furthest point in the node form the centroid.
893 * This returns 0 unless the node is a leaf.
894 */
895 template<typename MetricType,
896 typename StatisticType,
897 typename MatType,
898 typename SplitType,
899 typename DescentType,
900 template<typename> class AuxiliaryInformationType>
901 inline
902 typename RectangleTree<MetricType, StatisticType, MatType, SplitType,
903 DescentType, AuxiliaryInformationType>::ElemType
904 RectangleTree<MetricType, StatisticType, MatType, SplitType,
FurthestPointDistance() const905 DescentType, AuxiliaryInformationType>::FurthestPointDistance() const
906 {
907 if (!IsLeaf())
908 return 0.0;
909
910 // Otherwise return the distance from the centroid to a corner of the bound.
911 return 0.5 * bound.Diameter();
912 }
913
914 /**
915 * Return the furthest possible descendant distance. This returns the maximum
916 * distance from the centroid to the edge of the bound and not the empirical
917 * quantity which is the actual furthest descendant distance. So the actual
918 * furthest descendant distance may be less than what this method returns (but
919 * it will never be greater than this).
920 */
921 template<typename MetricType,
922 typename StatisticType,
923 typename MatType,
924 typename SplitType,
925 typename DescentType,
926 template<typename> class AuxiliaryInformationType>
927 inline
928 typename RectangleTree<MetricType, StatisticType, MatType, SplitType,
929 DescentType, AuxiliaryInformationType>::ElemType
930 RectangleTree<MetricType, StatisticType, MatType, SplitType,
FurthestDescendantDistance() const931 DescentType, AuxiliaryInformationType>::FurthestDescendantDistance() const
932 {
933 // Return the distance from the centroid to a corner of the bound.
934 return 0.5 * bound.Diameter();
935 }
936
937 /**
938 * Return the number of points contained in this node. Zero if it is a non-leaf
939 * node.
940 */
941 template<typename MetricType,
942 typename StatisticType,
943 typename MatType,
944 typename SplitType,
945 typename DescentType,
946 template<typename> class AuxiliaryInformationType>
947 inline size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
NumPoints() const948 DescentType, AuxiliaryInformationType>::NumPoints() const
949 {
950 if (numChildren != 0) // This is not a leaf node.
951 return 0;
952
953 return count;
954 }
955
956 /**
957 * Return the number of descendants under or in this node.
958 */
959 template<typename MetricType,
960 typename StatisticType,
961 typename MatType,
962 typename SplitType,
963 typename DescentType,
964 template<typename> class AuxiliaryInformationType>
965 inline size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
NumDescendants() const966 DescentType, AuxiliaryInformationType>::NumDescendants() const
967 {
968 return numDescendants;
969 }
970
971 /**
972 * Return the index of a particular descendant contained in this node.
973 */
974 template<typename MetricType,
975 typename StatisticType,
976 typename MatType,
977 typename SplitType,
978 typename DescentType,
979 template<typename> class AuxiliaryInformationType>
980 inline size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
Descendant(const size_t index) const981 DescentType, AuxiliaryInformationType>::Descendant(const size_t index) const
982 {
983 // I think this may be inefficient...
984 if (numChildren == 0)
985 {
986 return (points[index]);
987 }
988 else
989 {
990 size_t n = 0;
991 for (size_t i = 0; i < numChildren; ++i)
992 {
993 const size_t nd = children[i]->NumDescendants();
994 if (index - n < nd)
995 return children[i]->Descendant(index - n);
996 n += nd;
997 }
998
999 // I don't think this is valid.
1000 return children[numChildren - 1]->Descendant(index - n);
1001 }
1002 }
1003
1004 /**
1005 * Split the tree. This calls the SplitType code to split a node. This method
1006 * should only be called on a leaf node.
1007 */
1008 template<typename MetricType,
1009 typename StatisticType,
1010 typename MatType,
1011 typename SplitType,
1012 typename DescentType,
1013 template<typename> class AuxiliaryInformationType>
1014 void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
1015 AuxiliaryInformationType>::
SplitNode(std::vector<bool> & relevels)1016 SplitNode(std::vector<bool>& relevels)
1017 {
1018 if (numChildren == 0)
1019 {
1020 // We let the SplitType check if the node if overflowed
1021 // since an intermediate node of the R+ tree may be overflowed if the leaf
1022 // node contains only one point.
1023
1024 // The SplitType takes care of this and of moving up the tree if necessary.
1025 SplitType::SplitLeafNode(this, relevels);
1026 }
1027 else
1028 {
1029 // Check to see if we are full.
1030 if (numChildren <= maxNumChildren)
1031 return; // We don't need to split.
1032
1033 // If we are full, then we need to split (or at least try). The SplitType
1034 // takes care of this and of moving up the tree if necessary.
1035 SplitType::SplitNonLeafNode(this, relevels);
1036 }
1037 }
1038
1039 //! Default constructor for boost::serialization.
1040 template<typename MetricType,
1041 typename StatisticType,
1042 typename MatType,
1043 typename SplitType,
1044 typename DescentType,
1045 template<typename> class AuxiliaryInformationType>
1046 RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
1047 AuxiliaryInformationType>::
RectangleTree()1048 RectangleTree() :
1049 maxNumChildren(0), // Try to give sensible defaults, but it shouldn't matter
1050 minNumChildren(0), // because this tree isn't valid anyway and is only used
1051 numChildren(0), // by boost::serialization.
1052 parent(NULL),
1053 begin(0),
1054 count(0),
1055 numDescendants(0),
1056 maxLeafSize(0),
1057 minLeafSize(0),
1058 parentDistance(0.0),
1059 dataset(NULL),
1060 ownsDataset(false)
1061 {
1062 // Nothing to do.
1063 }
1064
1065 /**
1066 * Condense the tree. This shrinks the bounds and moves up the tree if
1067 * applicable. If a node goes below minimum fill, this code will deal with it.
1068 */
1069 template<typename MetricType,
1070 typename StatisticType,
1071 typename MatType,
1072 typename SplitType,
1073 typename DescentType,
1074 template<typename> class AuxiliaryInformationType>
1075 void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
1076 AuxiliaryInformationType>::
CondenseTree(const arma::vec & point,std::vector<bool> & relevels,const bool usePoint)1077 CondenseTree(const arma::vec& point,
1078 std::vector<bool>& relevels,
1079 const bool usePoint)
1080 {
1081 // First delete the node if we need to. There's no point in shrinking the
1082 // bound first.
1083 if (IsLeaf() && count < minLeafSize && parent != NULL)
1084 {
1085 // We can't delete the root node.
1086 for (size_t i = 0; i < parent->NumChildren(); ++i)
1087 {
1088 if (parent->children[i] == this)
1089 {
1090 // Decrement numChildren.
1091 if (!auxiliaryInfo.HandleNodeRemoval(parent, i))
1092 {
1093 parent->children[i] = parent->children[--parent->NumChildren()];
1094 }
1095
1096 // We find the root and shrink bounds at the same time.
1097 bool stillShrinking = true;
1098 RectangleTree* root = parent;
1099 while (root->Parent() != NULL)
1100 {
1101 if (stillShrinking)
1102 stillShrinking = root->ShrinkBoundForBound(bound);
1103 root = root->Parent();
1104 }
1105 if (stillShrinking)
1106 root->ShrinkBoundForBound(bound);
1107
1108 root = parent;
1109 while (root != NULL)
1110 {
1111 root->numDescendants -= numDescendants;
1112 root = root->Parent();
1113 }
1114
1115 stillShrinking = true;
1116 root = parent;
1117 while (root->Parent() != NULL)
1118 {
1119 if (stillShrinking)
1120 stillShrinking = root->AuxiliaryInfo().UpdateAuxiliaryInfo(root);
1121 root = root->Parent();
1122 }
1123 if (stillShrinking)
1124 root->AuxiliaryInfo().UpdateAuxiliaryInfo(root);
1125
1126 // Reinsert the points at the root node.
1127 for (size_t j = 0; j < count; ++j)
1128 root->InsertPoint(points[j], relevels);
1129
1130 // This will check the minFill of the parent.
1131 parent->CondenseTree(point, relevels, usePoint);
1132 // Now it should be safe to delete this node.
1133 SoftDelete();
1134
1135 return;
1136 }
1137 }
1138 // Control should never reach here.
1139 assert(false);
1140 }
1141 else if (!IsLeaf() && numChildren < minNumChildren)
1142 {
1143 if (parent != NULL)
1144 {
1145 // The normal case. We need to be careful with the root.
1146 for (size_t j = 0; j < parent->NumChildren(); ++j)
1147 {
1148 if (parent->children[j] == this)
1149 {
1150 // Decrement numChildren.
1151 if (!auxiliaryInfo.HandleNodeRemoval(parent, j))
1152 {
1153 parent->children[j] = parent->children[--parent->NumChildren()];
1154 }
1155 size_t level = TreeDepth();
1156
1157 // We find the root and shrink bounds at the same time.
1158 bool stillShrinking = true;
1159 RectangleTree* root = parent;
1160 while (root->Parent() != NULL)
1161 {
1162 if (stillShrinking)
1163 stillShrinking = root->ShrinkBoundForBound(bound);
1164 root = root->Parent();
1165 }
1166 if (stillShrinking)
1167 root->ShrinkBoundForBound(bound);
1168
1169 root = parent;
1170 while (root != NULL)
1171 {
1172 root->numDescendants -= numDescendants;
1173 root = root->Parent();
1174 }
1175
1176 stillShrinking = true;
1177 root = parent;
1178 while (root->Parent() != NULL)
1179 {
1180 if (stillShrinking)
1181 stillShrinking = root->AuxiliaryInfo().UpdateAuxiliaryInfo(root);
1182 root = root->Parent();
1183 }
1184 if (stillShrinking)
1185 root->AuxiliaryInfo().UpdateAuxiliaryInfo(root);
1186
1187 // Reinsert the nodes at the root node.
1188 for (size_t i = 0; i < numChildren; ++i)
1189 root->InsertNode(children[i], level, relevels);
1190
1191 // This will check the minFill of the point.
1192 parent->CondenseTree(point, relevels, usePoint);
1193 // Now it should be safe to delete this node.
1194 SoftDelete();
1195
1196 return;
1197 }
1198 }
1199 }
1200 else if (numChildren == 1)
1201 {
1202 // If there are multiple children, we can't do anything to the root.
1203 RectangleTree* child = children[0];
1204
1205 // Required for the X tree.
1206 if (child->NumChildren() > maxNumChildren)
1207 {
1208 maxNumChildren = child->MaxNumChildren();
1209 children.resize(maxNumChildren + 1);
1210 }
1211
1212 for (size_t i = 0; i < child->NumChildren(); ++i)
1213 {
1214 children[i] = child->children[i];
1215 children[i]->Parent() = this;
1216 child->children[i] = NULL;
1217 }
1218
1219 numChildren = child->NumChildren();
1220 child->NumChildren() = 0;
1221
1222 for (size_t i = 0; i < child->Count(); ++i)
1223 {
1224 // In case the tree has a height of two.
1225 points[i] = child->Point(i);
1226 }
1227
1228 auxiliaryInfo = child->AuxiliaryInfo();
1229
1230 count = child->Count();
1231 child->Count() = 0;
1232
1233 delete child;
1234 return;
1235 }
1236 }
1237
1238 // If we didn't delete it, shrink the bound if we need to.
1239 if (usePoint &&
1240 (ShrinkBoundForPoint(point) || auxiliaryInfo.UpdateAuxiliaryInfo(this)) &&
1241 parent != NULL)
1242 parent->CondenseTree(point, relevels, usePoint);
1243 else if (!usePoint &&
1244 (ShrinkBoundForBound(bound) ||
1245 auxiliaryInfo.UpdateAuxiliaryInfo(this)) &&
1246 parent != NULL)
1247 parent->CondenseTree(point, relevels, usePoint);
1248 }
1249
1250 /**
1251 * Shrink the bound so it fits tightly after the removal of this point.
1252 */
1253 template<typename MetricType,
1254 typename StatisticType,
1255 typename MatType,
1256 typename SplitType,
1257 typename DescentType,
1258 template<typename> class AuxiliaryInformationType>
1259 bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
1260 AuxiliaryInformationType>::
ShrinkBoundForPoint(const arma::vec & point)1261 ShrinkBoundForPoint(const arma::vec& point)
1262 {
1263 bool shrunk = false;
1264 if (IsLeaf())
1265 {
1266 for (size_t i = 0; i < bound.Dim(); ++i)
1267 {
1268 if (bound[i].Lo() == point[i])
1269 {
1270 ElemType min = std::numeric_limits<ElemType>::max();
1271 for (size_t j = 0; j < count; ++j)
1272 {
1273 if (dataset->col(points[j])[i] < min)
1274 min = dataset->col(points[j])[i];
1275 }
1276
1277 if (bound[i].Lo() < min)
1278 {
1279 shrunk = true;
1280 bound[i].Lo() = min;
1281 }
1282 else if (min < bound[i].Lo())
1283 {
1284 assert(false); // We have a problem.
1285 }
1286 }
1287 else if (bound[i].Hi() == point[i])
1288 {
1289 ElemType max = std::numeric_limits<ElemType>::lowest();
1290 for (size_t j = 0; j < count; ++j)
1291 {
1292 if (dataset->col(points[j])[i] > max)
1293 max = dataset->col(points[j])[i];
1294 }
1295
1296 if (bound[i].Hi() > max)
1297 {
1298 shrunk = true;
1299 bound[i].Hi() = max;
1300 }
1301 else if (max > bound[i].Hi())
1302 {
1303 assert(false); // We have a problem.
1304 }
1305 }
1306 }
1307 }
1308 else
1309 {
1310 for (size_t i = 0; i < bound.Dim(); ++i)
1311 {
1312 if (bound[i].Lo() == point[i])
1313 {
1314 ElemType min = std::numeric_limits<ElemType>::max();
1315 for (size_t j = 0; j < numChildren; ++j)
1316 {
1317 if (children[j]->Bound()[i].Lo() < min)
1318 min = children[j]->Bound()[i].Lo();
1319 }
1320
1321 if (bound[i].Lo() < min)
1322 {
1323 shrunk = true;
1324 bound[i].Lo() = min;
1325 }
1326 }
1327 else if (bound[i].Hi() == point[i])
1328 {
1329 ElemType max = std::numeric_limits<ElemType>::lowest();
1330 for (size_t j = 0; j < numChildren; ++j)
1331 {
1332 if (children[j]->Bound()[i].Hi() > max)
1333 max = children[j]->Bound()[i].Hi();
1334 }
1335
1336 if (bound[i].Hi() > max)
1337 {
1338 shrunk = true;
1339 bound[i].Hi() = max;
1340 }
1341 }
1342 }
1343 }
1344
1345 return shrunk;
1346 }
1347
1348 /**
1349 * Shrink the bound so it fits tightly after the removal of another bound.
1350 */
1351 template<typename MetricType,
1352 typename StatisticType,
1353 typename MatType,
1354 typename SplitType,
1355 typename DescentType,
1356 template<typename> class AuxiliaryInformationType>
1357 bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
1358 AuxiliaryInformationType>::
ShrinkBoundForBound(const bound::HRectBound<MetricType> &)1359 ShrinkBoundForBound(const bound::HRectBound<MetricType>& /* b */)
1360 {
1361 // Using the sum is safe since none of the dimensions can increase.
1362 ElemType sum = 0;
1363
1364 // I think it may be faster to just recalculate the whole thing.
1365 for (size_t i = 0; i < bound.Dim(); ++i)
1366 {
1367 sum += bound[i].Width();
1368 bound[i].Lo() = std::numeric_limits<ElemType>::max();
1369 bound[i].Hi() = std::numeric_limits<ElemType>::lowest();
1370 }
1371
1372 for (size_t i = 0; i < numChildren; ++i)
1373 {
1374 bound |= children[i]->Bound();
1375 }
1376
1377 ElemType sum2 = 0;
1378 for (size_t i = 0; i < bound.Dim(); ++i)
1379 sum2 += bound[i].Width();
1380
1381 return sum != sum2;
1382 }
1383
1384 /**
1385 * Serialize the tree.
1386 */
1387 template<typename MetricType,
1388 typename StatisticType,
1389 typename MatType,
1390 typename SplitType,
1391 typename DescentType,
1392 template<typename> class AuxiliaryInformationType>
1393 template<typename Archive>
1394 void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
serialize(Archive & ar,const unsigned int)1395 AuxiliaryInformationType>::serialize(
1396 Archive& ar,
1397 const unsigned int /* version */)
1398 {
1399 // Clean up memory, if necessary.
1400 if (Archive::is_loading::value)
1401 {
1402 for (size_t i = 0; i < numChildren; ++i)
1403 delete children[i];
1404 children.clear();
1405
1406 if (ownsDataset && dataset)
1407 delete dataset;
1408
1409 parent = NULL;
1410 }
1411
1412 ar & BOOST_SERIALIZATION_NVP(maxNumChildren);
1413 ar & BOOST_SERIALIZATION_NVP(minNumChildren);
1414 ar & BOOST_SERIALIZATION_NVP(numChildren);
1415 if (Archive::is_loading::value)
1416 children.resize(maxNumChildren + 1);
1417
1418 ar & BOOST_SERIALIZATION_NVP(begin);
1419 ar & BOOST_SERIALIZATION_NVP(count);
1420 ar & BOOST_SERIALIZATION_NVP(numDescendants);
1421 ar & BOOST_SERIALIZATION_NVP(maxLeafSize);
1422 ar & BOOST_SERIALIZATION_NVP(minLeafSize);
1423 ar & BOOST_SERIALIZATION_NVP(bound);
1424 ar & BOOST_SERIALIZATION_NVP(stat);
1425 ar & BOOST_SERIALIZATION_NVP(parentDistance);
1426 ar & BOOST_SERIALIZATION_NVP(dataset);
1427 ar & BOOST_SERIALIZATION_NVP(ownsDataset);
1428
1429 ar & BOOST_SERIALIZATION_NVP(points);
1430 ar & BOOST_SERIALIZATION_NVP(auxiliaryInfo);
1431
1432 // Since we may or may not be holding children, we need to serialize _only_
1433 // numChildren children.
1434 for (size_t i = 0; i < numChildren; ++i)
1435 {
1436 std::ostringstream oss;
1437 oss << "children" << i;
1438 ar & boost::serialization::make_nvp(oss.str().c_str(), children[i]);
1439
1440 if (Archive::is_loading::value)
1441 children[i]->parent = this;
1442 }
1443 for (size_t i = numChildren; i < maxNumChildren + 1; ++i)
1444 {
1445 children[i] = NULL;
1446 }
1447 }
1448
1449 } // namespace tree
1450 } // namespace mlpack
1451
1452 #endif
1453