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