Document RectangleTree and RTree (#3885)
* First attempt at documenting the RectangleTree. * Add Insert() and Delete() to RectangleTree; they have a nicer user interface than what was previously there. * Add R-tree documentation. * Fix default arguments for RectangleTree typedefs. * Fix some minor documentation display issues. * Fix link. * Fix links. * Try to clarify what trees are a little bit. * Apply suggestions from code review Co-authored-by: Dirk Eddelbuettel <edd@debian.org> * Update doc/user/core/trees/r_tree.md Co-authored-by: Dirk Eddelbuettel <edd@debian.org> --------- Co-authored-by: Dirk Eddelbuettel <edd@debian.org>
This commit is contained in:
co-authored by
Dirk Eddelbuettel
parent
b6ff6e0663
commit
e30f83c028
@@ -1,9 +1,15 @@
|
||||
# The TreeType policy in mlpack
|
||||
|
||||
Trees are an important data structure in mlpack and are used in a number of the
|
||||
machine learning algorithms that mlpack implements. Often, the use of trees can
|
||||
allow significant acceleration of an algorithm; this is generally done by
|
||||
pruning away large parts of the tree during computation.
|
||||
machine learning algorithms that mlpack implements. Trees in mlpack are
|
||||
hierarchical structures that organize data points: "nearby" points (with respect
|
||||
to a distance metric) are generally grouped in the same node or branch of a
|
||||
tree.
|
||||
|
||||
For certain machine learning algorithms, this hierarchical organization of data
|
||||
points into trees can allow significant computational acceleration. This
|
||||
speedup is typically achieved by pruning away large parts of the tree during
|
||||
computation.
|
||||
|
||||
Most mlpack algorithms that use trees are not tied to a specific tree but
|
||||
instead allow the user to choose a tree via the `TreeType` template parameter.
|
||||
|
||||
@@ -155,6 +155,16 @@ when the sidebar is built for each page.
|
||||
<code>Octree</code>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="LINKROOTuser/core/trees/r_tree.html">
|
||||
<code>RTree</code>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="LINKROOTuser/core/trees/rectangle_tree.html">
|
||||
<code>RectangleTree</code>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
# Trees
|
||||
|
||||
mlpack includes a number of space partitioning trees and other trees for its
|
||||
geometric techniques. All of mlpack's trees implement
|
||||
geometric techniques. These trees are built on [data matrices](../matrices.md)
|
||||
where each column in the matrix is a point in the tree. Trees are organized
|
||||
such that "nearby" points (with respect to a given distance metric) are
|
||||
generally grouped in the same node or branch of the tree.
|
||||
|
||||
All trees in mlpack implement
|
||||
the [same API](../../developer/trees.md), allowing easy plug-and-play usage of
|
||||
different trees. The following tree types are available in mlpack:
|
||||
|
||||
|
||||
@@ -141,8 +141,8 @@ different.
|
||||
is not supported, because this generally results in a ball tree with very
|
||||
loose bounding balls. It is better to simply build a new `BallTree` on the
|
||||
modified dataset. For trees that support individual insertion and deletions,
|
||||
see the `RectangleTree` class and all its variants (e.g. `RTree`,
|
||||
`RStarTree`, etc.).
|
||||
see the [`RectangleTree`](rectangle_tree.md) class and all its variants (e.g.
|
||||
[`RTree`](r_tree.md), `RStarTree`, etc.).
|
||||
|
||||
- See also the
|
||||
[developer documentation on tree constructors](../../../developer/trees.md#constructors-and-destructors).
|
||||
@@ -391,7 +391,7 @@ nodes. The following functions can be used for these tasks.
|
||||
`arma::fmat`, and the returned type is
|
||||
[`RangeType<float>`](../math.md#range)).
|
||||
|
||||
### Tree traversals
|
||||
## Tree traversals
|
||||
|
||||
Like every mlpack tree, the `BallTree` class provides a [single-tree and
|
||||
dual-tree traversal](../../../developer/trees.md#traversals) that can be paired
|
||||
|
||||
@@ -9,6 +9,12 @@ instead:
|
||||
|
||||
* [`KDTree`](kdtree.md)
|
||||
* [`MeanSplitKDTree`](mean_split_kdtree.md)
|
||||
* [`BallTree`](ball_tree.md)
|
||||
* [`MeanSplitBallTree`](mean_split_ball_tree.md)
|
||||
* [`VPTree`](vptree.md)
|
||||
* [`RPTree`](rp_tree.md)
|
||||
* [`MaxRPTree`](max_rp_tree.md)
|
||||
* [`UBTree`](ub_tree.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -161,8 +167,8 @@ different.
|
||||
`BinarySpaceTree` is not supported, because this generally results in a tree
|
||||
with very loose bounding boxes. It is better to simply build a new
|
||||
`BinarySpaceTree` on the modified dataset. For trees that support individual
|
||||
insertion and deletions, see the `RectangleTree` class and all its variants
|
||||
(e.g. `RTree`, `RStarTree`, etc.).
|
||||
insertion and deletions, see the [`RectangleTree`](rectangle_tree.md) class
|
||||
and all its variants (e.g. [`RTree`](r_tree.md), `RStarTree`, etc.).
|
||||
|
||||
- See also the
|
||||
[developer documentation on tree constructors](../../../developer/trees.md#constructors-and-destructors).
|
||||
@@ -430,9 +436,6 @@ write a custom `BoundType` for use with `BinarySpaceTree`:
|
||||
* [Custom `BoundType`s](#custom-boundtypes): implement a fully custom
|
||||
`BoundType`
|
||||
|
||||
*Note:* this section is still under construction---not all bound types are
|
||||
documented yet.
|
||||
|
||||
### `HRectBound`
|
||||
|
||||
The `HRectBound` class represents a hyper-rectangle bound; that is, a
|
||||
@@ -551,10 +554,12 @@ operations with data points or other bounds.
|
||||
[column-major `arma::mat`](../../matrices.md#representing-data-in-mlpack).
|
||||
The expansion operation is minimal, so `b` is not expanded any more than
|
||||
necessary.
|
||||
- If the dimensionality of `b` is `0`, it is set to `data.n_rows`.
|
||||
|
||||
* `b |= bound` expands `b` to fully include `bound`, where `bound` is another
|
||||
`HRectBound`. The expansion/union operation is minimal, so `b` is not
|
||||
expanded any more than necessary.
|
||||
- If the dimensionality of `b` is `0`, it is set to `bound.Dim()`.
|
||||
|
||||
* `b & bound` returns a new `HRectBound` whose bounding hyper-rectangle is the
|
||||
intersection of the bounding hyperrectangles of `b` and `bound`. If `b` and
|
||||
@@ -1978,9 +1983,6 @@ to write a fully custom split:
|
||||
* [Custom `SplitType`s](#custom-splittypes): implement a fully custom
|
||||
`SplitType` class
|
||||
|
||||
*Note:* this section is still under construction---not all split types are
|
||||
documented yet.
|
||||
|
||||
### `MidpointSplit`
|
||||
|
||||
The `MidpointSplit` class is a splitting strategy that can be used by
|
||||
@@ -2412,7 +2414,7 @@ while (!stack.empty())
|
||||
// stack is the better option here.
|
||||
|
||||
// Print the results.
|
||||
std::cout << leafCount << " out of " << totalLeafCount << " leaves have less "
|
||||
std::cout << leafCount << " out of " << totalLeafCount << " leaves have fewer "
|
||||
<< "than 10 points." << std::endl;
|
||||
```
|
||||
|
||||
|
||||
@@ -343,7 +343,7 @@ nodes. The following functions can be used for these tasks.
|
||||
`arma::fmat`, and the returned type is
|
||||
[`RangeType<float>`](../math.md#range)).
|
||||
|
||||
### Tree traversals
|
||||
## Tree traversals
|
||||
|
||||
Like every mlpack tree, the `CoverTree` class provides a [single-tree and
|
||||
dual-tree traversal](../../../developer/trees.md#traversals) that can be paired
|
||||
|
||||
@@ -125,7 +125,8 @@ different.
|
||||
not supported, because this generally results in a kd-tree with very loose
|
||||
bounding boxes. It is better to simply build a new `KDTree` on the modified
|
||||
dataset. For trees that support individual insertion and deletions, see the
|
||||
`RectangleTree` class and all its variants (e.g. `RTree`, `RStarTree`, etc.).
|
||||
[`RectangleTree`](rectangle_tree.md) class and all its variants (e.g.
|
||||
[`RTree`](r_tree.md), `RStarTree`, etc.).
|
||||
|
||||
- See also the
|
||||
[developer documentation on tree constructors](../../../developer/trees.md#constructors-and-destructors).
|
||||
@@ -378,7 +379,7 @@ nodes. The following functions can be used for these tasks.
|
||||
`arma::fmat`, and the returned type is
|
||||
[`RangeType<float>`](../math.md#range)).
|
||||
|
||||
### Tree traversals
|
||||
## Tree traversals
|
||||
|
||||
Like every mlpack tree, the `KDTree` class provides a [single-tree and dual-tree
|
||||
traversal](../../../developer/trees.md#traversals) that can be paired with a
|
||||
|
||||
@@ -136,8 +136,8 @@ different.
|
||||
is not supported, because this generally results in a random projection tree
|
||||
with very loose bounding boxes. It is better to simply build a new
|
||||
`MaxRPTree` on the modified dataset. For trees that support individual
|
||||
insertion and deletions, see the `RectangleTree` class and all its variants
|
||||
(e.g. `RTree`, `RStarTree`, etc.).
|
||||
insertion and deletions, see the [`RectangleTree`](rectangle_tree.md) class
|
||||
and all its variants (e.g. [`RTree`](r_tree.md), `RStarTree`, etc.).
|
||||
|
||||
- See also the
|
||||
[developer documentation on tree constructors](../../../developer/trees.md#constructors-and-destructors).
|
||||
@@ -390,7 +390,7 @@ nodes. The following functions can be used for these tasks.
|
||||
`arma::fmat`, and the returned type is
|
||||
[`RangeType<float>`](../math.md#range)).
|
||||
|
||||
### Tree traversals
|
||||
## Tree traversals
|
||||
|
||||
Like every mlpack tree, the `MaxRPTree` class provides a [single-tree and
|
||||
dual-tree traversal](../../../developer/trees.md#traversals) that can be paired
|
||||
|
||||
@@ -138,8 +138,9 @@ may be different.
|
||||
`MeanSplitBallTree` is not supported, because this generally results in a
|
||||
ball tree with very loose bounding balls. It is better to simply build a new
|
||||
`MeanSplitBallTree` on the modified dataset. For trees that support
|
||||
individual insertion and deletions, see the `RectangleTree` class and all its
|
||||
variants (e.g. `RTree`, `RStarTree`, etc.).
|
||||
individual insertion and deletions, see the
|
||||
[`RectangleTree`](rectangle_tree.md) class and all its variants (e.g.
|
||||
[`RTree`](r_tree.md), `RStarTree`, etc.).
|
||||
|
||||
- See also the
|
||||
[developer documentation on tree constructors](../../../developer/trees.md#constructors-and-destructors).
|
||||
@@ -389,7 +390,7 @@ nodes. The following functions can be used for these tasks.
|
||||
`arma::fmat`, and the returned type is
|
||||
[`RangeType<float>`](../math.md#range)).
|
||||
|
||||
### Tree traversals
|
||||
## Tree traversals
|
||||
|
||||
Like every mlpack tree, the `MeanSplitBallTree` class provides a [single-tree
|
||||
and dual-tree traversal](../../../developer/trees.md#traversals) that can be
|
||||
|
||||
@@ -135,8 +135,9 @@ different.
|
||||
`MeanSplitKDTree` is not supported, because this generally results in a
|
||||
mean-split kd-tree with very loose bounding boxes. It is better to simply
|
||||
build a new `MeanSplitKDTree` on the modified dataset. For trees that
|
||||
support individual insertion and deletions, see the `RectangleTree` class and
|
||||
all its variants (e.g. `RTree`, `RStarTree`, etc.).
|
||||
support individual insertion and deletions, see the
|
||||
[`RectangleTree`](rectangle_tree.md) class and all its variants (e.g.
|
||||
[`RTree`](r_tree.md), `RStarTree`, etc.).
|
||||
|
||||
- See also the
|
||||
[developer documentation on tree constructors](../../../developer/trees.md#constructors-and-destructors).
|
||||
@@ -391,7 +392,7 @@ nodes. The following functions can be used for these tasks.
|
||||
`arma::fmat`, and the returned type is
|
||||
[`RangeType<float>`](../math.md#range)).
|
||||
|
||||
### Tree traversals
|
||||
## Tree traversals
|
||||
|
||||
Like every mlpack tree, the `MeanSplitKDTree` class provides a [single-tree and
|
||||
dual-tree traversal](../../../developer/trees.md#traversals) that can be paired
|
||||
|
||||
@@ -0,0 +1,599 @@
|
||||
# `RTree`
|
||||
|
||||
The `RTree` class implements the R tree, a well-known multidimensional space
|
||||
partitioning tree that can insert and remove points dynamically.
|
||||
|
||||
The `RTree` implementation in mlpack supports three template parameters for
|
||||
configurable behavior, and implements all the functionality required by the
|
||||
[TreeType API](../../../developer/trees.md#the-treetype-api), plus some
|
||||
additional functionality specific to R trees.
|
||||
|
||||
The R tree is generally less efficient for machine learning tasks than other
|
||||
trees such as the [`KDTree`](kdtree.md) or [`Octree`](octree.md), but those
|
||||
trees do not support dynamic insertion or deletion of points. If insert/delete
|
||||
functionality is required, then the R tree or other variants of
|
||||
[`RectangleTree`](rectangle_tree.md) should be chosen instead.
|
||||
|
||||
* [Template parameters](#template-parameters)
|
||||
* [Constructors](#constructors)
|
||||
* [Basic tree properties](#basic-tree-properties)
|
||||
* [Bounding distances with the tree](#bounding-distances-with-the-tree)
|
||||
* [Tree traversals](#tree-traversals)
|
||||
* [Example usage](#example-usage)
|
||||
|
||||
## See also
|
||||
|
||||
<!-- TODO: add links to all distance-based algorithms and other trees? -->
|
||||
|
||||
* [`RectangleTree`](rectangle_tree.md)
|
||||
* [R-Tree on Wikipedia](https://en.wikipedia.org/wiki/R-tree)
|
||||
* [R-Trees: A Dynamic Index Structure for Spatial Searching (pdf)](http://www-db.deis.unibo.it/courses/SI-LS/papers/Gut84.pdf)
|
||||
* [Tree-Independent Dual-Tree Algorithms (pdf)](https://www.ratml.org/pub/pdf/2013tree.pdf)
|
||||
|
||||
## Template parameters
|
||||
|
||||
In accordance with the [TreeType
|
||||
API](../../../developer/trees.md#template-parameters-required-by-the-treetype-policy)
|
||||
(see also [this more detailed section](../../../developer/trees.md#template-parameters)),
|
||||
the `RTree` class takes three template parameters:
|
||||
|
||||
```
|
||||
RTree<DistanceType, StatisticType, MatType>
|
||||
```
|
||||
|
||||
* `DistanceType`: the [distance metric](../distances.md) to use for distance
|
||||
computations. `RTree` requires that this is
|
||||
[`EuclideanDistance`](../distances.md#lmetric), and a compilation error will
|
||||
be thrown if any other `DistanceType` is specified.
|
||||
|
||||
* `StatisticType`: this holds auxiliary information in each tree node. By
|
||||
default, [`EmptyStatistic`](rectangle_tree.md#emptystatistic) is used, which
|
||||
holds no information.
|
||||
- See the [`StatisticType`](rectangle_tree.md#statistictype) section for more
|
||||
details.
|
||||
|
||||
* `MatType`: the type of matrix used to represent points. Must be a type
|
||||
matching the [Armadillo API](../../matrices.md). By default, `arma::mat` is
|
||||
used, but other types such as `arma::fmat` or similar will work just fine.
|
||||
|
||||
The `RTree` class itself is a convenience typedef of the generic
|
||||
[`RectangleTree`](rectangle_tree.md) class, using the
|
||||
[`RTreeSplit`](rectangle_tree.md#rtreesplit) class as the split strategy, the
|
||||
[`RTreeDescentHeuristic`](rectangle_tree.md#rtreedescentheuristic) class as the
|
||||
descent strategy, and
|
||||
[`NoAuxiliaryInformation`](rectangle_tree.md#auxiliaryinformationtype) as the
|
||||
auxiliary information type.
|
||||
|
||||
If no template parameters are explicitly specified, then defaults are used:
|
||||
|
||||
```
|
||||
RTree<> = RTree<EuclideanDistance, EmptyStatistic, arma::mat>
|
||||
```
|
||||
|
||||
## Constructors
|
||||
|
||||
`RTree`s are constructed by inserting points in a dataset sequentially.
|
||||
The dataset is not permuted during the construction process.
|
||||
|
||||
---
|
||||
|
||||
* `node = RTree(data)`
|
||||
* `node = RTree(data, maxLeafSize=20, minLeafSize=8)`
|
||||
* `node = RTree(data, maxLeafSize=20, minLeafSize=8, maxNumChildren=5, minNumChildren=2)`
|
||||
- Construct an `RTree` on the given `data` with the given construction
|
||||
parameters.
|
||||
- By default, `data` is copied. Avoid a copy by using `std::move()` (e.g.
|
||||
`std::move(data)`); when doing this, `data` will be set to an empty matrix.
|
||||
|
||||
---
|
||||
|
||||
* `node = RTree<DistanceType, StatisticType, MatType>(data)`
|
||||
* `node = RTree<DistanceType, StatisticType, MatType>(data, maxLeafSize=20, minLeafSize=8)`
|
||||
* `node = RTree<DistanceType, StatisticType, MatType>(data, maxLeafSize=20, minLeafSize=8, maxNumChildren=5, minNumChildren=2)`
|
||||
- Construct an `RTree` on the given `data`, using custom template parameters
|
||||
to control the behavior of the tree and the given construction parameters.
|
||||
- By default, `data` is copied. Avoid a copy by using `std::move()` (e.g.
|
||||
`std::move(data)`); when doing this, `data` will be set to an empty matrix.
|
||||
|
||||
---
|
||||
|
||||
* `node = RTree(dimensionality)`
|
||||
- Construct an empty `RTree` with no children, no points, and
|
||||
default template parameters.
|
||||
- Use `node.Insert()` to insert points into the tree. All points must have
|
||||
dimensionality `dimensionality`.
|
||||
|
||||
---
|
||||
|
||||
* `node.Insert(x)`
|
||||
- Insert the point `x` into the tree.
|
||||
- `x` should have vector type compatible with the chosen `MatType`; so, for
|
||||
default `MatType`, `arma::vec` is the expected type.
|
||||
- If a custom `MatType` is specified (e.g. `arma::fmat`), then `x` should
|
||||
have type equivalent to the corresponding column vector type (e.g.
|
||||
`arma::fvec`).
|
||||
- Due to tree rebalancing, this may change the internal structure of the
|
||||
tree; so references and pointers to children of `node` may become invalid.
|
||||
- ***Warning:*** This will throw an exception if `node` is not the root of
|
||||
the tree!
|
||||
|
||||
* `node.Delete(i)
|
||||
- Delete the point with index `i` from the tree.
|
||||
- The point to be deleted from the tree will be `node.Dataset().col(i)`;
|
||||
after deleting, the column will be removed from `node.Dataset()` and all
|
||||
indexes held in all tree nodes will be updated. (Thus, this operation can
|
||||
be expensive!)
|
||||
- Due to tree rebalancing, this may change the internal structure of the
|
||||
tree; so references and pointers to children of `node` may become invalid.
|
||||
- ***Warning:*** This will throw an exception if `node` is not the root of
|
||||
the tree!
|
||||
|
||||
---
|
||||
|
||||
***Notes:***
|
||||
|
||||
- The name `node` is used here for `RTree` objects instead of `tree`, because
|
||||
each `RTree` object is a single node in the tree. The constructor returns
|
||||
the node that is the root of the tree.
|
||||
|
||||
- See also the
|
||||
[developer documentation on tree constructors](../../../developer/trees.md#constructors-and-destructors).
|
||||
|
||||
---
|
||||
|
||||
### Constructor parameters:
|
||||
|
||||
| **name** | **type** | **description** | **default** |
|
||||
|----------|----------|-----------------|-------------|
|
||||
| `data` | [`MatType`](../../matrices.md) | [Column-major](../../matrices.md#representing-data-in-mlpack) matrix to build the tree on. Pass with `std::move(data)` to avoid copying the matrix. | _(N/A)_ |
|
||||
| `maxLeafSize` | `size_t` | Maximum number of points to store in each leaf. | `20` |
|
||||
| `minLeafSize` | `size_t` | Minimum number of points to store in each leaf. | `8` |
|
||||
| `maxNumChildren` | `size_t` | Maximum number of children allowed in each non-leaf node. | `5` |
|
||||
| `minNumChildren` | `size_t` | Minimum number of children in each non-leaf node. | `2` |
|
||||
| `dimensionality` | `size_t` | Dimensionality of points to be held in the tree. | _(N/A)_ |
|
||||
| | | |
|
||||
| `x` | [`arma::vec`](../../matrices.md) | Column vector: point to insert into tree. Should have type matching the column vector type associated with `MatType`, and must have `node.Dataset().n_rows` elements. | _(N/A)_ |
|
||||
| `i` | `size_t` | Index of point in `node.Dataset()` to delete from `node`. | _(N/A)_ |
|
||||
|
||||
## Basic tree properties
|
||||
|
||||
Once an `RTree` object is constructed, various properties of the tree can be
|
||||
accessed or inspected. Many of these functions are required by the [TreeType
|
||||
API](../../../developer/trees.md#the-treetype-api).
|
||||
|
||||
### Navigating the tree
|
||||
|
||||
* `node.NumChildren()` returns the number of children in `node`. This is `0`
|
||||
if `node` is a leaf, and between the values of `node.MinNumChildren()` and
|
||||
`node.MaxNumChildren()` (inclusive) otherwise.
|
||||
|
||||
* `node.IsLeaf()` returns a `bool` indicating whether or not `node` is a leaf.
|
||||
|
||||
* `node.Child(i)` returns an `RTree&` that is the `i`th child.
|
||||
- `i` must be less than `node.NumChildren()`.
|
||||
- This function should only be called if `node.NumChildren()` is not `0`
|
||||
(e.g. if `node` is not a leaf). Note that this returns a valid `RTree&`
|
||||
that can itself be used just like the root node of the tree!
|
||||
|
||||
* `node.Parent()` will return an `RTree*` that points to the parent of `node`,
|
||||
or `NULL` if `node` is the root of the `RectangleTree`.
|
||||
|
||||
---
|
||||
|
||||
### Accessing members of a tree
|
||||
|
||||
* `node.Bound()` will return an
|
||||
[`HRectBound<DistanceType, ElemType>&`](binary_space_tree.md#hrectbound)
|
||||
object that represents the hyperrectangle bounding box of `node`.
|
||||
- `ElemType` is the element type of `MatType`; so, if default template
|
||||
parameters are used, `ElemType` is `double`.
|
||||
- `bound` is a hyperrectangle that encloses all the descendant points of
|
||||
`node`. It may be somewhat loose (e.g. points may not be very near the
|
||||
edges).
|
||||
|
||||
* `node.Stat()` will return a `StatisticType&` holding the statistics of the
|
||||
node that were computed during tree construction.
|
||||
|
||||
* `node.Distance()` will return a `EuclideanDistance&`. Since
|
||||
`EuclideanDistance` has no members, this function is not likely to be useful,
|
||||
but it is required by the TreeType API.
|
||||
|
||||
* `node.MinNumChildren()` returns the minimum number of children that the
|
||||
node is required to have as a `size_t`. If points are deleted such that the
|
||||
number of children falls below this limit, then `node` will become a leaf and
|
||||
the tree will be rebalanced.
|
||||
|
||||
* `node.MaxNumChildren()` returns the maximum number of children that the
|
||||
node is required to have as a `size_t`. If points are inserted such that the
|
||||
number of children goes above this limit, new nodes will be added and the
|
||||
tree will be rebalanced.
|
||||
|
||||
* `node.MaxLeafSize()` returns the maximum number of points that the node is
|
||||
allowed to hold as a `size_t`. If the number of points held by `node`
|
||||
exceeds this limit during insertion, then `node` will be split and the tree
|
||||
will be rebalanced.
|
||||
|
||||
* `node.MinLeafSize()` returns the minimum number of points that the node is
|
||||
allowed to hold as a `size_t`. If the number of points held by `node` goes
|
||||
under this limit during deletion, then `node` will be deleted (if possible)
|
||||
and the tree will be rebalanced.
|
||||
|
||||
See also the
|
||||
[developer documentation](../../../developer/trees.md#basic-tree-functionality)
|
||||
for basic tree functionality in mlpack.
|
||||
|
||||
---
|
||||
|
||||
### Accessing data held in a tree
|
||||
|
||||
* `node.Dataset()` will return a `const MatType&` that is an internally-held
|
||||
representation of the dataset the tree was built on.
|
||||
|
||||
* `node.NumPoints()` returns a `size_t` indicating the number of points held
|
||||
directly in `node`.
|
||||
- If `node` is not a leaf, this will return `0`, as `RTree` only holds points
|
||||
directly in its leaves.
|
||||
- If `node` is a leaf, then this will return values between
|
||||
`node.MinLeafSize()` and `node.MaxLeafSize()` (inclusive).
|
||||
- If the tree has fewer than `node.MinLeafSize()` points total, then
|
||||
`node.NumPoints()` will return a value less than `node.MinLeafSize()`.
|
||||
|
||||
* `node.Point(i)` returns a `size_t` indicating the index of the `i`'th point
|
||||
in `node.Dataset()`.
|
||||
- `i` must be in the range `[0, node.NumPoints() - 1]` (inclusive).
|
||||
- `node` must be a leaf (as non-leaves do not hold any points).
|
||||
- The `i`'th point in `node` can then be accessed as
|
||||
`node.Dataset().col(node.Point(i))`.
|
||||
- Accessing the actual `i`'th point itself can be done with, e.g.,
|
||||
`node.Dataset().col(node.Point(i))`.
|
||||
- Point indices are not necessarily contiguous for `RTree`s; that is,
|
||||
`node.Point(i) + 1` is not necessarily `node.Point(i + 1)`.
|
||||
|
||||
* `node.NumDescendants()` returns a `size_t` indicating the number of points
|
||||
held in all descendant leaves of `node`.
|
||||
- If `node` is the root of the tree, then `node.NumDescendants()` will be
|
||||
equal to `node.Dataset().n_cols`.
|
||||
|
||||
* `node.Descendant(i)` returns a `size_t` indicating the index of the `i`'th
|
||||
descendant point in `node.Dataset()`.
|
||||
- `i` must be in the range `[0, node.NumDescendants() - 1]` (inclusive).
|
||||
- `node` does not need to be a leaf.
|
||||
- The `i`'th descendant point in `node` can then be accessed as
|
||||
`node.Dataset().col(node.Descendant(i))`.
|
||||
- Accessing the actual `i`'th descendant itself can be done with, e.g.,
|
||||
`node.Dataset().col(node.Descendant(i))`.
|
||||
- Descendant point indices are not necessarily contiguous for
|
||||
`RTree`s; that is, `node.Descendant(i) + 1` is not necessarily
|
||||
`node.Descendant(i + 1)`.
|
||||
|
||||
---
|
||||
|
||||
### Accessing computed bound quantities of a tree
|
||||
|
||||
The following quantities are cached for each node in a `RTree`, and so accessing
|
||||
them does not require any computation. In the documentation below, `ElemType`
|
||||
is the element type of the given `MatType`; e.g., if `MatType` is `arma::mat`,
|
||||
then `ElemType` is `double`.
|
||||
|
||||
* `node.FurthestPointDistance()` returns an `ElemType` representing the
|
||||
distance between the center of the bound of `node` and the furthest point
|
||||
held by `node`.
|
||||
- If `node` is not a leaf, this returns 0 (because `node` does not hold any
|
||||
points).
|
||||
|
||||
* `node.FurthestDescendantDistance()` returns an `ElemType` representing the
|
||||
distance between the center of the bound of `node` and the furthest
|
||||
descendant point held by `node`.
|
||||
|
||||
* `node.MinimumBoundDistance()` returns an `ElemType` representing the minimum
|
||||
possible distance from the center of the node to any edge of its bound.
|
||||
|
||||
* `node.ParentDistance()` returns an `ElemType` representing the distance
|
||||
between the center of the bound of `node` and the center of the bound of its
|
||||
parent.
|
||||
- If `node` is the root of the tree, `0` is returned.
|
||||
|
||||
***Note:*** for more details on each bound quantity, see the [developer
|
||||
documentation](../../../developer/trees.md#complex-tree-functionality-and-bounds)
|
||||
on bound quantities for trees.
|
||||
|
||||
---
|
||||
|
||||
### Other functionality
|
||||
|
||||
* `node.Center(center)` computes the center of the hyperrectangle bounding box
|
||||
of `node` and stores it in `center`.
|
||||
- `center` should be of type `arma::Col<ElemType>&`, where `ElemType` is the
|
||||
element type of the specified `MatType`.
|
||||
- `center` will be set to have size equivalent to the dimensionality of the
|
||||
dataset held by `node`.
|
||||
- This is equivalent to calling `node.Bound().Center(center)`.
|
||||
|
||||
* An `RTree` can be serialized with
|
||||
[`data::Save()` and `data::Load()`](../../load_save.md#mlpack-objects).
|
||||
|
||||
## Bounding distances with the tree
|
||||
|
||||
The primary use of trees in mlpack is bounding distances to points or other tree
|
||||
nodes. The following functions can be used for these tasks.
|
||||
|
||||
* `node.GetNearestChild(point)`
|
||||
* `node.GetFurthestChild(point)`
|
||||
- Return a `size_t` indicating the index of the child that is closest to (or
|
||||
furthest from) `point`, with respect to the `MinDistance()` (or
|
||||
`MaxDistance()`) function.
|
||||
- If there is a tie, the node with the lowest index is returned.
|
||||
- If `node` is a leaf, `0` is returned.
|
||||
- `point` should be a column vector type of the same type as `MatType`.
|
||||
(e.g., if `MatType` is `arma::mat`, then `point` should be an `arma::vec`.)
|
||||
|
||||
* `node.GetNearestChild(other)`
|
||||
* `node.GetFurthestChild(other)`
|
||||
- Return a `size_t` indicating the index of the child that is closest to (or
|
||||
furthest from) the `RTree` node `other`, with respect to the
|
||||
`MinDistance()` (or `MaxDistance()`) function.
|
||||
- If there is a tie, the node with the lowest index is returned.
|
||||
- If `node` is a leaf, `0` is returned.
|
||||
|
||||
---
|
||||
|
||||
* `node.MinDistance(point)`
|
||||
* `node.MinDistance(other)`
|
||||
- Return a `double` indicating the minimum possible distance between `node`
|
||||
and `point`, or the `RTree` node `other`.
|
||||
- This is equivalent to the minimum possible distance between any point
|
||||
contained in the bounding hyperrectangle of `node` and `point`, or between
|
||||
any point contained in the bounding hyperrectangle of `node` and any point
|
||||
contained in the bounding hyperrectangle of `other`.
|
||||
- `point` should be a column vector type of the same type as `MatType`.
|
||||
(e.g., if `MatType` is `arma::mat`, then `point` should be an `arma::vec`.)
|
||||
|
||||
* `node.MaxDistance(point)`
|
||||
* `node.MaxDistance(other)`
|
||||
- Return a `double` indicating the maximum possible distance between `node`
|
||||
and `point`, or the `RTree` node `other`.
|
||||
- This is equivalent to the maximum possible distance between any point
|
||||
contained in the bounding hyperrectangle of `node` and `point`, or between
|
||||
any point contained in the bounding hyperrectangle of `node` and any point
|
||||
contained in the bounding hyperrectangle of `other`.
|
||||
- `point` should be a column vector type of the same type as `MatType`.
|
||||
(e.g., if `MatType` is `arma::mat`, then `point` should be an `arma::vec`.)
|
||||
|
||||
* `node.RangeDistance(point)`
|
||||
* `node.RangeDistance(other)`
|
||||
- Return a [`RangeType<ElemType>`](../math.md#range) whose lower bound is
|
||||
`node.MinDistance(point)` or `node.MinDistance(other)`, and whose upper
|
||||
bound is `node.MaxDistance(point)` or `node.MaxDistance(other)`.
|
||||
- `ElemType` is the element type of `MatType`.
|
||||
- `point` should be a column vector type of the same type as `MatType`.
|
||||
(e.g., if `MatType` is `arma::mat`, then `point` should be an `arma::vec`.)
|
||||
|
||||
## Tree traversals
|
||||
|
||||
Like every mlpack tree, the `RTree` class provides a [single-tree and
|
||||
dual-tree traversal](../../../developer/trees.md#traversals) that can be paired
|
||||
with a [`RuleType` class](../../../developer/trees.md#rules) to implement a
|
||||
single-tree or dual-tree algorithm.
|
||||
|
||||
* `RTree::SingleTreeTraverser`
|
||||
- Implements a depth-first single-tree traverser.
|
||||
|
||||
* `RTree::DualTreeTraverser`
|
||||
- Implements a dual-depth-first dual-tree traverser.
|
||||
|
||||
## Example usage
|
||||
|
||||
Build an `RTree` on the `cloud` dataset and print basic statistics about the
|
||||
tree.
|
||||
|
||||
```c++
|
||||
// See https://datasets.mlpack.org/cloud.csv.
|
||||
arma::mat dataset;
|
||||
mlpack::data::Load("cloud.csv", dataset, true);
|
||||
|
||||
// Build the rectangle tree with a leaf size of 10. (This means that leaf nodes
|
||||
// cannot contain more than 10 points.)
|
||||
//
|
||||
// The std::move() means that `dataset` will be empty after this call, and no
|
||||
// data will be copied during tree building.
|
||||
//
|
||||
// Note that the '<>' is not necessary if C++20 is being used (e.g.
|
||||
// `mlpack::RTree tree(...)` will work fine in C++20 or newer).
|
||||
mlpack::RTree<> tree(std::move(dataset));
|
||||
|
||||
// Print the bounding box of the root node.
|
||||
std::cout << "Bounding box of root node:" << std::endl;
|
||||
for (size_t i = 0; i < tree.Bound().Dim(); ++i)
|
||||
{
|
||||
std::cout << " - Dimension " << i << ": [" << tree.Bound()[i].Lo() << ", "
|
||||
<< tree.Bound()[i].Hi() << "]." << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
// Print the number of children in the root, and the allowable range.
|
||||
std::cout << "Number of children of root: " << tree.NumChildren()
|
||||
<< "; allowable range: [" << tree.MinNumChildren() << ", "
|
||||
<< tree.MaxNumChildren() << "]." << std::endl;
|
||||
|
||||
// Print the number of descendant points of the root, and of each of its
|
||||
// children.
|
||||
std::cout << "Descendant points of root: "
|
||||
<< tree.NumDescendants() << "." << std::endl;
|
||||
for (size_t i = 0; i < tree.NumChildren(); ++i)
|
||||
{
|
||||
std::cout << "Descendant points of child " << i << ": "
|
||||
<< tree.Child(i).NumDescendants() << "." << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
// Compute the center of the RTree.
|
||||
arma::vec center;
|
||||
tree.Center(center);
|
||||
std::cout << "Center of tree: " << center.t();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Build two `RTree`s on subsets of the corel dataset and compute minimum and
|
||||
maximum distances between different nodes in the tree.
|
||||
|
||||
```c++
|
||||
// See https://datasets.mlpack.org/corel-histogram.csv.
|
||||
arma::mat dataset;
|
||||
mlpack::data::Load("corel-histogram.csv", dataset, true);
|
||||
|
||||
// Build trees on the first half and the second half of points.
|
||||
mlpack::RTree<> tree1(dataset.cols(0, dataset.n_cols / 2));
|
||||
mlpack::RTree<> tree2(dataset.cols(dataset.n_cols / 2 + 1, dataset.n_cols - 1));
|
||||
|
||||
// Compute the maximum distance between the trees.
|
||||
std::cout << "Maximum distance between tree root nodes: "
|
||||
<< tree1.MaxDistance(tree2) << "." << std::endl;
|
||||
|
||||
// Get the leftmost grandchild of the first tree's root---if it exists.
|
||||
if (!tree1.IsLeaf() && !tree1.Child(0).IsLeaf())
|
||||
{
|
||||
mlpack::RTree<>& node1 = tree1.Child(0).Child(0);
|
||||
|
||||
// Get the leftmost grandchild of the second tree's root---if it exists.
|
||||
if (!tree2.IsLeaf() && !tree2.Child(0).IsLeaf())
|
||||
{
|
||||
mlpack::RTree<>& node2 = tree2.Child(0).Child(0);
|
||||
|
||||
// Print the minimum and maximum distance between the nodes.
|
||||
mlpack::Range dists = node1.RangeDistance(node2);
|
||||
std::cout << "Possible distances between two grandchild nodes: ["
|
||||
<< dists.Lo() << ", " << dists.Hi() << "]." << std::endl;
|
||||
|
||||
// Print the minimum distance between the first node and the first
|
||||
// descendant point of the second node.
|
||||
const size_t descendantIndex = node2.Descendant(0);
|
||||
const double descendantMinDist =
|
||||
node1.MinDistance(node2.Dataset().col(descendantIndex));
|
||||
std::cout << "Minimum distance between grandchild node and descendant "
|
||||
<< "point: " << descendantMinDist << "." << std::endl;
|
||||
|
||||
// Which child of node2 is closer to node1?
|
||||
const size_t closestIndex = node2.GetNearestChild(node1);
|
||||
std::cout << "Child " << closestIndex << " is closest to node1."
|
||||
<< std::endl;
|
||||
|
||||
// And which child of node1 is further from node2?
|
||||
const size_t furthestIndex = node1.GetFurthestChild(node2);
|
||||
std::cout << "Child " << furthestIndex << " is furthest from node2."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Build an `RTree` on 32-bit floating point data and save it to disk.
|
||||
|
||||
```c++
|
||||
// See https://datasets.mlpack.org/corel-histogram.csv.
|
||||
arma::fmat dataset;
|
||||
mlpack::data::Load("corel-histogram.csv", dataset);
|
||||
|
||||
// Build the RTree using 32-bit floating point data as the matrix type. We will
|
||||
// still use the default EmptyStatistic and EuclideanDistance parameters. A
|
||||
// leaf size of 100 is used here.
|
||||
mlpack::RTree<mlpack::EuclideanDistance,
|
||||
mlpack::EmptyStatistic,
|
||||
arma::fmat> tree(std::move(dataset), 100);
|
||||
|
||||
// Save the tree to disk with the name 'tree'.
|
||||
mlpack::data::Save("tree.bin", "tree", tree);
|
||||
|
||||
std::cout << "Saved tree with " << tree.Dataset().n_cols << " points to "
|
||||
<< "'tree.bin'." << std::endl;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Load a 32-bit floating point `RTree` from disk, then traverse it manually and
|
||||
find the number of leaf nodes with less than 10 points.
|
||||
|
||||
```c++
|
||||
// This assumes the tree has already been saved to 'tree.bin' (as in the example
|
||||
// above).
|
||||
|
||||
// This convenient typedef saves us a long type name!
|
||||
using TreeType = mlpack::RTree<mlpack::EuclideanDistance,
|
||||
mlpack::EmptyStatistic,
|
||||
arma::fmat>;
|
||||
|
||||
TreeType tree;
|
||||
mlpack::data::Load("tree.bin", "tree", tree);
|
||||
std::cout << "Tree loaded with " << tree.NumDescendants() << " points."
|
||||
<< std::endl;
|
||||
|
||||
// Recurse in a depth-first manner. Count both the total number of leaves, and
|
||||
// the number of leaves with less than 10 points.
|
||||
size_t leafCount = 0;
|
||||
size_t totalLeafCount = 0;
|
||||
std::stack<TreeType*> stack;
|
||||
stack.push(&tree);
|
||||
while (!stack.empty())
|
||||
{
|
||||
TreeType* node = stack.top();
|
||||
stack.pop();
|
||||
|
||||
if (node->NumPoints() < 10)
|
||||
++leafCount;
|
||||
++totalLeafCount;
|
||||
|
||||
for (size_t i = 0; i < node->NumChildren(); ++i)
|
||||
stack.push(&node->Child(i));
|
||||
}
|
||||
|
||||
// Note that it would be possible to use TreeType::SingleTreeTraverser to
|
||||
// perform the recursion above, but that is more well-suited for more complex
|
||||
// tasks that require pruning and other non-trivial behavior; so using a simple
|
||||
// stack is the better option here.
|
||||
|
||||
// Print the results.
|
||||
std::cout << leafCount << " out of " << totalLeafCount << " leaves have fewer "
|
||||
<< "than 10 points." << std::endl;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Build an `RTree` by iteratively inserting points from the corel dataset, print
|
||||
some information, and then remove a few randomly chosen points.
|
||||
|
||||
```c++
|
||||
// See https://datasets.mlpack.org/corel-histogram.csv.
|
||||
arma::mat dataset;
|
||||
mlpack::data::Load("corel-histogram.csv", dataset, true);
|
||||
|
||||
// Create an empty tree of the right dimensionality.
|
||||
mlpack::RTree<> t(dataset.n_rows);
|
||||
|
||||
// Insert points one by one for the first half of the dataset.
|
||||
for (size_t i = 0; i < dataset.n_cols / 2; ++i)
|
||||
t.Insert(dataset.col(i));
|
||||
|
||||
std::cout << "After inserting half the points, the root node has "
|
||||
<< t.NumDescendants() << " descendant points and "
|
||||
<< t.NumChildren() << " child nodes." << std::endl;
|
||||
|
||||
// For the second half, insert the points backwards.
|
||||
for (size_t i = dataset.n_cols - 1; i >= dataset.n_cols / 2; --i)
|
||||
t.Insert(dataset.col(i));
|
||||
|
||||
std::cout << "After inserting all the points, the root node has "
|
||||
<< t.NumDescendants() << " descendant points and "
|
||||
<< t.NumChildren() << " child nodes." << std::endl;
|
||||
|
||||
// Remove three random points.
|
||||
t.Delete(mlpack::math::RandInt(0, t.NumDescendants()));
|
||||
std::cout << "After removing 1 point, the root node has " << t.NumDescendants()
|
||||
<< " descendant points." << std::endl;
|
||||
t.Delete(mlpack::math::RandInt(0, t.NumDescendants()));
|
||||
std::cout << "After removing 2 points, the root node has " << t.NumDescendants()
|
||||
<< " descendant points." << std::endl;
|
||||
t.Delete(mlpack::math::RandInt(0, t.NumDescendants()));
|
||||
std::cout << "After removing 3 points, the root node has " << t.NumDescendants()
|
||||
<< " descendant points." << std::endl;
|
||||
```
|
||||
@@ -0,0 +1,934 @@
|
||||
# `RectangleTree`
|
||||
|
||||
The `RectangleTree` class represents a generic multidimensional space
|
||||
partitioning tree. It is heavily templatized to control splitting behavior and
|
||||
other behaviors, and is the actual class underlying trees such as the
|
||||
[`RTree`](r_tree.md). In general, the `RectangleTree` class is not meant to
|
||||
be used directly, and instead one of the numerous variants should be used
|
||||
instead:
|
||||
|
||||
* [`RTree`](r_tree.md)
|
||||
|
||||
The `RectangleTree` and its variants are capable of inserting points and
|
||||
deleting them. This is different from [`BinarySpaceTree`](binary_space_tree.md)
|
||||
and other mlpack tree types, where the tree is built entirely in batch at
|
||||
construction time. However, this capability comes with a runtime cost, and so
|
||||
in general the use of `RectangleTree` with mlpack algorithms will be slower than
|
||||
the batch-construction trees---but, if insert/delete functionality is required,
|
||||
`RectangleTree` is the only choice.
|
||||
|
||||
---
|
||||
|
||||
For users who want to use `RectangleTree` directly or with custom behavior,
|
||||
the full class is still detailed in the subsections below. `RectangleTree`
|
||||
supports the [TreeType API](../../../developer/trees.md#the-treetype-api) and
|
||||
can be used with mlpack's tree-based algorithms, although using custom behavior
|
||||
may require a template typedef.
|
||||
|
||||
* [Template parameters](#template-parameters)
|
||||
* [Constructors](#constructors)
|
||||
* [Basic tree properties](#basic-tree-properties)
|
||||
* [Bounding distances with the tree](#bounding-distances-with-the-tree)
|
||||
* [`StatisticType`](#statistictype) template parameter
|
||||
* [`SplitType`](#splittype) template parameter
|
||||
* [`DescentType`](#descenttype) template parameter
|
||||
* [`AuxiliaryInformationType`](#auxiliaryinformationtype) template parameter
|
||||
* [Tree traversals](#tree-traversals)
|
||||
* [Example usage](#example-usage)
|
||||
|
||||
## See also
|
||||
|
||||
<!-- TODO: add links to all distance-based algorithms and other trees? -->
|
||||
|
||||
* [`RTree`](r_tree.md)
|
||||
* [R-Tree on Wikipedia](https://en.wikipedia.org/wiki/R-tree)
|
||||
* [R-Trees: A Dynamic Index Structure for Spatial Searching (pdf)](http://www-db.deis.unibo.it/courses/SI-LS/papers/Gut84.pdf)
|
||||
* [Tree-Independent Dual-Tree Algorithms (pdf)](https://www.ratml.org/pub/pdf/2013tree.pdf)
|
||||
|
||||
## Template parameters
|
||||
|
||||
The `RectangleTree` class takes six template parameters. The first three of
|
||||
these are required by the
|
||||
[TreeType API](../../../developer/trees.md#template-parameters-required-by-the-treetype-policy)
|
||||
(see also
|
||||
[this more detailed section](../../../developer/trees.md#template-parameters)). The
|
||||
full signature of the class is:
|
||||
|
||||
```
|
||||
template<typename DistanceType,
|
||||
typename StatisticType,
|
||||
typename MatType,
|
||||
typename SplitType,
|
||||
typename DescentType,
|
||||
template<typename> class AuxiliaryInformationType>
|
||||
class RectangleTree;
|
||||
```
|
||||
|
||||
* `DistanceType`: the [distance metric](../distances.md) to use for distance
|
||||
computations. `RectangleTree` requires that this is
|
||||
[`EuclideanDistance`](../distances.md#lmetric), and a compilation error will
|
||||
be thrown if any other `DistanceType` is specified.
|
||||
|
||||
* `StatisticType`: this holds auxiliary information in each tree node. By
|
||||
default, [`EmptyStatistic`](#emptystatistic) is used, which holds no
|
||||
information.
|
||||
- See the [`StatisticType`](#statistictype) section for more details.
|
||||
|
||||
* `MatType`: the type of matrix used to represent points. Must be a type
|
||||
matching the [Armadillo API](../../matrices.md). By default, `arma::mat` is
|
||||
used, but other types such as `arma::fmat` or similar will work just fine.
|
||||
|
||||
* `SplitType`: the class defining how an individual `RectangleTree` node
|
||||
should be split. By default, [`RTreeSplit`](#rtreesplit) is used.
|
||||
- See the [`SplitType`](#splittype) section for more details.
|
||||
|
||||
* `DescentType`: the class defining how a child node is chosen for point
|
||||
insertion. By default, [`RTreeDescentHeuristic`](#rtreedescentheuristic) is
|
||||
used.
|
||||
- See the [`DescentType`](#descenttype) section for more details.
|
||||
|
||||
* `AuxiliaryInformationType`: holds information specific to the variant of the
|
||||
`RectangleTree`. By default, [`NoAuxiliaryInformation`] is used.
|
||||
|
||||
Note that the TreeType API requires trees to have only three template
|
||||
parameters. In order to use a `RectangleTree` with its six template parameters
|
||||
with an mlpack algorithm that needs a TreeType, it is easiest to define a
|
||||
template typedef:
|
||||
|
||||
```
|
||||
template<typename DistanceType, typename StatisticType, typename MatType>
|
||||
using CustomTree = Rectangle<DistanceType, StatisticType, MatType,
|
||||
CustomSplitType, CustomDescentType, CustomAuxiliaryInformationType>
|
||||
```
|
||||
|
||||
Here, `CustomSplitType`, `CustomDescentType`, and
|
||||
`CustomAuxiliaryInformationType` are the desired splitting and descent
|
||||
strategies and auxiliary information type. This is the way that all
|
||||
`RectangleTree` variants (such as [`RTree`](r_tree.md)) are defined.
|
||||
|
||||
## Constructors
|
||||
|
||||
`RectangleTree`s are constructed by inserting points in a dataset sequentially.
|
||||
The dataset is not permuted during the construction process.
|
||||
|
||||
---
|
||||
|
||||
* `node = RectangleTree(data)`
|
||||
* `node = RectangleTree(data, maxLeafSize=20, minLeafSize=8)`
|
||||
* `node = RectangleTree(data, maxLeafSize=20, minLeafSize=8, maxNumChildren=5, minNumChildren=2)`
|
||||
- Construct a `RectangleTree` on the given `data` with the given construction
|
||||
parameters.
|
||||
- Default template parameters are used, meaning that this tree will be a
|
||||
[`RTree`](r_tree.md).
|
||||
- By default, `data` is copied. Avoid a copy by using `std::move()` (e.g.
|
||||
`std::move(data)`); when doing this, `data` will be set to an empty matrix.
|
||||
|
||||
---
|
||||
|
||||
* `node = RectangleTree<DistanceType, StatisticType, MatType, SplitType, DescentType, AuxiliaryInformationType>(data)`
|
||||
* `node = RectangleTree<DistanceType, StatisticType, MatType, SplitType, DescentType, AuxiliaryInformationType>(data, maxLeafSize=20, minLeafSize=8)`
|
||||
* `node = RectangleTree<DistanceType, StatisticType, MatType, SplitType, DescentType, AuxiliaryInformationType>(data, maxLeafSize=20, minLeafSize=8, maxNumChildren=5, minNumChildren=2)`
|
||||
- Construct a `RectangleTree` on the given `data`, using custom template
|
||||
parameters to control the behavior of the tree and the given construction
|
||||
parameters.
|
||||
- By default, `data` is copied. Avoid a copy by using `std::move()` (e.g.
|
||||
`std::move(data)`); when doing this, `data` will be set to an empty matrix.
|
||||
|
||||
---
|
||||
|
||||
* `node = RectangleTree(dimensionality)`
|
||||
- Construct an empty `RectangleTree` with no children, no points, and default
|
||||
template parameters.
|
||||
- Use `node.Insert()` to insert points into the tree. All points must have
|
||||
dimensionality `dimensionality`.
|
||||
|
||||
---
|
||||
|
||||
* `node.Insert(x)`
|
||||
- Insert the point `x` into the tree.
|
||||
- `x` should have vector type compatible with the chosen `MatType`; so, for
|
||||
default `MatType`, `arma::vec` is the expected type.
|
||||
- If a custom `MatType` is specified (e.g. `arma::fmat`), then `x` should
|
||||
have type equivalent to the corresponding column vector type (e.g.
|
||||
`arma::fvec`).
|
||||
- Due to tree rebalancing, this may change the internal structure of the
|
||||
tree; so references and pointers to children of `node` may become invalid.
|
||||
- ***Warning:*** This will throw an exception if `node` is not the root of
|
||||
the tree!
|
||||
|
||||
* `node.Delete(i)
|
||||
- Delete the point with index `i` from the tree.
|
||||
- The point to be deleted from the tree will be `node.Dataset().col(i)`;
|
||||
after deleting, the column will be removed from `node.Dataset()` and all
|
||||
indexes held in all tree nodes will be updated. (Thus, this operation can
|
||||
be expensive!)
|
||||
- Due to tree rebalancing, this may change the internal structure of the
|
||||
tree; so references and pointers to children of `node` may become invalid.
|
||||
- ***Warning:*** This will throw an exception if `node` is not the root of
|
||||
the tree!
|
||||
|
||||
---
|
||||
|
||||
***Notes:***
|
||||
|
||||
- The name `node` is used here for `RectangleTree` objects instead of `tree`,
|
||||
because each `RectangleTree` object is a single node in the tree. The
|
||||
constructor returns the node that is the root of the tree.
|
||||
|
||||
- See also the
|
||||
[developer documentation on tree constructors](../../../developer/trees.md#constructors-and-destructors).
|
||||
|
||||
<!-- TODO: add links to RectangleTree above when it is documented -->
|
||||
|
||||
---
|
||||
|
||||
### Constructor parameters:
|
||||
|
||||
| **name** | **type** | **description** | **default** |
|
||||
|----------|----------|-----------------|-------------|
|
||||
| `data` | [`MatType`](../../matrices.md) | [Column-major](../../matrices.md#representing-data-in-mlpack) matrix to build the tree on. Pass with `std::move(data)` to avoid copying the matrix. | _(N/A)_ |
|
||||
| `maxLeafSize` | `size_t` | Maximum number of points to store in each leaf. | `20` |
|
||||
| `minLeafSize` | `size_t` | Minimum number of points to store in each leaf. | `8` |
|
||||
| `maxNumChildren` | `size_t` | Maximum number of children allowed in each non-leaf node. | `5` |
|
||||
| `minNumChildren` | `size_t` | Minimum number of children in each non-leaf node. | `2` |
|
||||
| `dimensionality` | `size_t` | Dimensionality of points to be held in the tree. | _(N/A)_ |
|
||||
| | | |
|
||||
| `x` | [`arma::vec`](../../matrices.md) | Column vector: point to insert into tree. Should have type matching the column vector type associated with `MatType`, and must have `node.Dataset().n_rows` elements. | _(N/A)_ |
|
||||
| `i` | `size_t` | Index of point in `node.Dataset()` to delete from `node`. | _(N/A)_ |
|
||||
|
||||
## Basic tree properties
|
||||
|
||||
Once a `RectangleTree` object is constructed, various properties of the tree
|
||||
can be accessed or inspected. Many of these functions are required by the
|
||||
[TreeType API](../../../developer/trees.md#the-treetype-api).
|
||||
|
||||
### Navigating the tree
|
||||
|
||||
* `node.NumChildren()` returns the number of children in `node`. This is `0`
|
||||
if `node` is a leaf, and between the values of `node.MinNumChildren()` and
|
||||
`node.MaxNumChildren()` (inclusive) otherwise.
|
||||
|
||||
* `node.IsLeaf()` returns a `bool` indicating whether or not `node` is a leaf.
|
||||
|
||||
* `node.Child(i)` returns a `RectangleTree&` that is the `i`th child.
|
||||
- `i` must be less than `node.NumChildren()`.
|
||||
- This function should only be called if `node.NumChildren()` is not `0`
|
||||
(e.g. if `node` is not a leaf). Note that this returns a valid
|
||||
`RectangleTree&` that can itself be used just like the root node of the
|
||||
tree!
|
||||
|
||||
* `node.Parent()` will return a `RectangleTree*` that points to the parent of
|
||||
`node`, or `NULL` if `node` is the root of the `RectangleTree`.
|
||||
|
||||
---
|
||||
|
||||
### Accessing members of a tree
|
||||
|
||||
* `node.Bound()` will return an
|
||||
[`HRectBound<DistanceType, ElemType>&`](binary_space_tree.md#hrectbound)
|
||||
object that represents the hyperrectangle bounding box of `node`.
|
||||
- `ElemType` is the element type of `MatType`; so, if default template
|
||||
parameters are used, `ElemType` is `double`.
|
||||
- `bound` is a hyperrectangle that encloses all the descendant points of
|
||||
`node`. It may be somewhat loose (e.g. points may not be very near the
|
||||
edges).
|
||||
|
||||
* `node.Stat()` will return a `StatisticType&` holding the statistics of the
|
||||
node that were computed during tree construction.
|
||||
|
||||
* `node.Distance()` will return a `EuclideanDistance&`. Since
|
||||
`EuclideanDistance` has no members, this function is not likely to be useful,
|
||||
but it is required by the TreeType API.
|
||||
|
||||
* `node.AuxiliaryInfo()` returns an `AuxiliaryInformationType&` that holds any
|
||||
auxiliary information required by the node.
|
||||
|
||||
* `node.MinNumChildren()` returns the minimum number of children that the
|
||||
node is required to have as a `size_t`. If points are deleted such that the
|
||||
number of children falls below this limit, then `node` will become a leaf and
|
||||
the tree will be rebalanced.
|
||||
|
||||
* `node.MaxNumChildren()` returns the maximum number of children that the
|
||||
node is required to have as a `size_t`. If points are inserted such that the
|
||||
number of children goes above this limit, new nodes will be added and the
|
||||
tree will be rebalanced.
|
||||
|
||||
* `node.MaxLeafSize()` returns the maximum number of points that the node is
|
||||
allowed to hold as a `size_t`. If the number of points held by `node`
|
||||
exceeds this limit during insertion, then `node` will be split and the tree
|
||||
will be rebalanced.
|
||||
|
||||
* `node.MinLeafSize()` returns the minimum number of points that the node is
|
||||
allowed to hold as a `size_t`. If the number of points held by `node` goes
|
||||
under this limit during deletion, then `node` will be deleted (if possible)
|
||||
and the tree will be rebalanced.
|
||||
|
||||
See also the
|
||||
[developer documentation](../../../developer/trees.md#basic-tree-functionality)
|
||||
for basic tree functionality in mlpack.
|
||||
|
||||
---
|
||||
|
||||
### Accessing data held in a tree
|
||||
|
||||
* `node.Dataset()` will return a `const MatType&` that is an internally-held
|
||||
representation of the dataset the tree was built on.
|
||||
|
||||
* `node.NumPoints()` returns a `size_t` indicating the number of points held
|
||||
directly in `node`.
|
||||
- If `node` is not a leaf, this will return `0`, as `RectangleTree` only
|
||||
holds points directly in its leaves.
|
||||
- If `node` is a leaf, then this will return values between
|
||||
`node.MinLeafSize()` and `node.MaxLeafSize()` (inclusive).
|
||||
- If the tree has fewer than `node.MinLeafSize()` points total, then
|
||||
`node.NumPoints()` will return a value less than `node.MinLeafSize()`.
|
||||
|
||||
* `node.Point(i)` returns a `size_t` indicating the index of the `i`'th point
|
||||
in `node.Dataset()`.
|
||||
- `i` must be in the range `[0, node.NumPoints() - 1]` (inclusive).
|
||||
- `node` must be a leaf (as non-leaves do not hold any points).
|
||||
- The `i`'th point in `node` can then be accessed as
|
||||
`node.Dataset().col(node.Point(i))`.
|
||||
- Accessing the actual `i`'th point itself can be done with, e.g.,
|
||||
`node.Dataset().col(node.Point(i))`.
|
||||
- Point indices are not necessarily contiguous for `RectangleTree`s; that is,
|
||||
`node.Point(i) + 1` is not necessarily `node.Point(i + 1)`.
|
||||
|
||||
* `node.NumDescendants()` returns a `size_t` indicating the number of points
|
||||
held in all descendant leaves of `node`.
|
||||
- If `node` is the root of the tree, then `node.NumDescendants()` will be
|
||||
equal to `node.Dataset().n_cols`.
|
||||
|
||||
* `node.Descendant(i)` returns a `size_t` indicating the index of the `i`'th
|
||||
descendant point in `node.Dataset()`.
|
||||
- `i` must be in the range `[0, node.NumDescendants() - 1]` (inclusive).
|
||||
- `node` does not need to be a leaf.
|
||||
- The `i`'th descendant point in `node` can then be accessed as
|
||||
`node.Dataset().col(node.Descendant(i))`.
|
||||
- Accessing the actual `i`'th descendant itself can be done with, e.g.,
|
||||
`node.Dataset().col(node.Descendant(i))`.
|
||||
- Descendant point indices are not necessarily contiguous for
|
||||
`RectangleTree`s; that is, `node.Descendant(i) + 1` is not necessarily
|
||||
`node.Descendant(i + 1)`.
|
||||
|
||||
---
|
||||
|
||||
### Accessing computed bound quantities of a tree
|
||||
|
||||
The following quantities are cached for each node in a `RectangleTree`, and so
|
||||
accessing them does not require any computation. In the documentation below,
|
||||
`ElemType` is the element type of the given `MatType`; e.g., if `MatType` is
|
||||
`arma::mat`, then `ElemType` is `double`.
|
||||
|
||||
* `node.FurthestPointDistance()` returns an `ElemType` representing the
|
||||
distance between the center of the bound of `node` and the furthest point
|
||||
held by `node`.
|
||||
- If `node` is not a leaf, this returns 0 (because `node` does not hold any
|
||||
points).
|
||||
|
||||
* `node.FurthestDescendantDistance()` returns an `ElemType` representing the
|
||||
distance between the center of the bound of `node` and the furthest
|
||||
descendant point held by `node`.
|
||||
|
||||
* `node.MinimumBoundDistance()` returns an `ElemType` representing the minimum
|
||||
possible distance from the center of the node to any edge of its bound.
|
||||
|
||||
* `node.ParentDistance()` returns an `ElemType` representing the distance
|
||||
between the center of the bound of `node` and the center of the bound of its
|
||||
parent.
|
||||
- If `node` is the root of the tree, `0` is returned.
|
||||
|
||||
***Note:*** for more details on each bound quantity, see the [developer
|
||||
documentation](../../../developer/trees.md#complex-tree-functionality-and-bounds)
|
||||
on bound quantities for trees.
|
||||
|
||||
---
|
||||
|
||||
### Other functionality
|
||||
|
||||
* `node.Center(center)` computes the center of the hyperrectangle bounding box
|
||||
of `node` and stores it in `center`.
|
||||
- `center` should be of type `arma::Col<ElemType>&`, where `ElemType` is the
|
||||
element type of the specified `MatType`.
|
||||
- `center` will be set to have size equivalent to the dimensionality of the
|
||||
dataset held by `node`.
|
||||
- This is equivalent to calling `node.Bound().Center(center)`.
|
||||
|
||||
* A `RectangleTree` can be serialized with
|
||||
[`data::Save()` and `data::Load()`](../../load_save.md#mlpack-objects).
|
||||
|
||||
## Bounding distances with the tree
|
||||
|
||||
The primary use of trees in mlpack is bounding distances to points or other tree
|
||||
nodes. The following functions can be used for these tasks.
|
||||
|
||||
* `node.GetNearestChild(point)`
|
||||
* `node.GetFurthestChild(point)`
|
||||
- Return a `size_t` indicating the index of the child that is closest to (or
|
||||
furthest from) `point`, with respect to the `MinDistance()` (or
|
||||
`MaxDistance()`) function.
|
||||
- If there is a tie, the node with the lowest index is returned.
|
||||
- If `node` is a leaf, `0` is returned.
|
||||
- `point` should be a column vector type of the same type as `MatType`.
|
||||
(e.g., if `MatType` is `arma::mat`, then `point` should be an `arma::vec`.)
|
||||
|
||||
* `node.GetNearestChild(other)`
|
||||
* `node.GetFurthestChild(other)`
|
||||
- Return a `size_t` indicating the index of the child that is closest to (or
|
||||
furthest from) the `RectangleTree` node `other`, with respect to the
|
||||
`MinDistance()` (or `MaxDistance()`) function.
|
||||
- If there is a tie, the node with the lowest index is returned.
|
||||
- If `node` is a leaf, `0` is returned.
|
||||
|
||||
---
|
||||
|
||||
* `node.MinDistance(point)`
|
||||
* `node.MinDistance(other)`
|
||||
- Return a `double` indicating the minimum possible distance between `node`
|
||||
and `point`, or the `RectangleTree` node `other`.
|
||||
- This is equivalent to the minimum possible distance between any point
|
||||
contained in the bounding hyperrectangle of `node` and `point`, or between
|
||||
any point contained in the bounding hyperrectangle of `node` and any point
|
||||
contained in the bounding hyperrectangle of `other`.
|
||||
- `point` should be a column vector type of the same type as `MatType`.
|
||||
(e.g., if `MatType` is `arma::mat`, then `point` should be an `arma::vec`.)
|
||||
|
||||
* `node.MaxDistance(point)`
|
||||
* `node.MaxDistance(other)`
|
||||
- Return a `double` indicating the maximum possible distance between `node`
|
||||
and `point`, or the `RectangleTree` node `other`.
|
||||
- This is equivalent to the maximum possible distance between any point
|
||||
contained in the bounding hyperrectangle of `node` and `point`, or between
|
||||
any point contained in the bounding hyperrectangle of `node` and any point
|
||||
contained in the bounding hyperrectangle of `other`.
|
||||
- `point` should be a column vector type of the same type as `MatType`.
|
||||
(e.g., if `MatType` is `arma::mat`, then `point` should be an `arma::vec`.)
|
||||
|
||||
* `node.RangeDistance(point)`
|
||||
* `node.RangeDistance(other)`
|
||||
- Return a [`RangeType<ElemType>`](../math.md#range) whose lower bound is
|
||||
`node.MinDistance(point)` or `node.MinDistance(other)`, and whose upper
|
||||
bound is `node.MaxDistance(point)` or `node.MaxDistance(other)`.
|
||||
- `ElemType` is the element type of `MatType`.
|
||||
- `point` should be a column vector type of the same type as `MatType`.
|
||||
(e.g., if `MatType` is `arma::mat`, then `point` should be an `arma::vec`.)
|
||||
|
||||
## Tree traversals
|
||||
|
||||
Like every mlpack tree, the `RectangleTree` class provides a [single-tree and
|
||||
dual-tree traversal](../../../developer/trees.md#traversals) that can be paired
|
||||
with a [`RuleType` class](../../../developer/trees.md#rules) to implement a
|
||||
single-tree or dual-tree algorithm.
|
||||
|
||||
* `RectangleTree::SingleTreeTraverser`
|
||||
- Implements a depth-first single-tree traverser.
|
||||
|
||||
* `RectangleTree::DualTreeTraverser`
|
||||
- Implements a dual-depth-first dual-tree traverser.
|
||||
|
||||
## `StatisticType`
|
||||
|
||||
Each node in a `RectangleTree` holds an instance of the `StatisticType` class.
|
||||
This class can be used to store additional bounding information or other cached
|
||||
quantities that a `RectangleTree` does not already compute.
|
||||
|
||||
mlpack provides a few existing `StatisticType` classes, and a custom
|
||||
`StatisticType` can also be easily implemented:
|
||||
|
||||
* [`EmptyStatistic`](#emptystatistic): an empty statistic class that does not
|
||||
hold any information
|
||||
* [Custom `StatisticType`s](#custom-statistictypes): implement a fully custom
|
||||
`StatisticType`
|
||||
|
||||
*Note:* this section is still under construction---not all statistic types are
|
||||
documented yet.
|
||||
|
||||
### `EmptyStatistic`
|
||||
|
||||
The `EmptyStatistic` class is an empty placeholder class that is used as the
|
||||
default `StatisticType` template parameter for mlpack trees.
|
||||
|
||||
The class ***does not hold any members and provides no functionality***.
|
||||
[See the implementation.](/src/mlpack/core/tree/statistic.hpp)
|
||||
|
||||
### Custom `StatisticType`s
|
||||
|
||||
A custom `StatisticType` is trivial to implement. Only a default constructor
|
||||
and a constructor taking a `RectangleTree` is necessary.
|
||||
|
||||
```
|
||||
class CustomStatistic
|
||||
{
|
||||
public:
|
||||
// Default constructor required by the StatisticType policy.
|
||||
CustomStatistic();
|
||||
|
||||
// Construct a CustomStatistic for the given fully-constructed
|
||||
// `RectangleTree` node. Here we have templatized the tree type to make it
|
||||
// easy to handle any type of `RectangleTree`.
|
||||
template<typename TreeType>
|
||||
StatisticType(TreeType& node);
|
||||
|
||||
//
|
||||
// Adding any additional precomputed bound quantities can be done; these
|
||||
// quantities should be computed in the constructor. They can then be
|
||||
// accessed from the tree with `node.Stat()`.
|
||||
//
|
||||
};
|
||||
```
|
||||
|
||||
*Example*: suppose we wanted to know, for each node, the exact time at which it
|
||||
was created. A `StatisticType` could be created that has a
|
||||
[`std::time_t`](https://en.cppreference.com/w/cpp/chrono/c/time_t) member,
|
||||
whose value is computed in the constructor.
|
||||
|
||||
## `SplitType`
|
||||
|
||||
The `SplitType` template parameter controls the algorithm used to split each
|
||||
node of a `RectangleTree` while building. The splitting strategy used can be
|
||||
entirely arbitrary---the `SplitType` simply needs to split a leaf node and a
|
||||
non-leaf node into children.
|
||||
|
||||
mlpack provides several drop-in choices for `SplitType`, and it is also possible
|
||||
to write a fully custom split:
|
||||
|
||||
* [`RTreeSplit`](#rtreesplit): splits according to a simple binary heuristic
|
||||
* [Custom `SplitType`s](#custom-splittypes): implement a fully custom
|
||||
`SplitType` class
|
||||
|
||||
*Note:* this section is still under construction---not all split types are
|
||||
documented yet.
|
||||
|
||||
### `RTreeSplit`
|
||||
|
||||
The `RTreeSplit` class implements the original R-tree splitting strategy and can
|
||||
be used with the [`RectangleTree`](#rectangletree) class. This is the splitting
|
||||
strategy used for the [`RTree`](r_tree.md) class, and is the same strategy
|
||||
proposed in the [original paper
|
||||
(pdf)](http://www-db.deis.unibo.it/courses/SI-LS/papers/Gut84.pdf). The
|
||||
strategy works as follows:
|
||||
|
||||
* Find the two furthest-apart points (or children if the node is not a leaf).
|
||||
* Create two children with each point (or child) as the only point (or child).
|
||||
* Iteratively add each remaining point (or child) to the new child whose
|
||||
hyperrectangle bound volume increases the least.
|
||||
|
||||
For implementation details, see
|
||||
[the source code](/src/mlpack/core/tree/rectangle_tree/r_tree_split_impl.hpp).
|
||||
|
||||
### Custom `SplitType`s
|
||||
|
||||
Custom split strategies for a `RectangleTree` can be implemented via the
|
||||
`SplitType` template parameter. By default, the [`RTreeSplit`](#rtreesplit)
|
||||
splitting strategy is used, but it is also possible to implement and use a
|
||||
custom `SplitType`. Any custom `SplitType` class must implement the following
|
||||
signature:
|
||||
|
||||
```c++
|
||||
class SplitType
|
||||
{
|
||||
public:
|
||||
// Given the leaf node `tree`, split into multiple nodes. `TreeType` will be
|
||||
// the relevant `RectangleTree` type. `tree` should be modified directly.
|
||||
//
|
||||
// `relevels` is an auxiliary array used by some splitting strategies to
|
||||
// indicate whether a node needs to be reinserted into the tree.
|
||||
template<typename TreeType>
|
||||
static void SplitLeafNode(TreeType* tree, std::vector<bool>& relevels);
|
||||
|
||||
// Given the non-leaf node `tree`, split into multiple nodes. `TreeType` will
|
||||
// be the relevant `RectangleTree` type. `tree` should be modified directly.
|
||||
//
|
||||
// `relevels` is an auxiliary array used by some splitting strategies to
|
||||
// indicate whether a node needs to be reinserted into the tree.
|
||||
template<typename TreeType>
|
||||
static void SplitNonLeafNode(TreeType* tree, std::vector<bool>& relevels);
|
||||
};
|
||||
```
|
||||
|
||||
## `DescentType`
|
||||
|
||||
The `DescentType` template parameter controls the algorithm used to assign child
|
||||
points and child nodes to nodes in a `RectangleTree`. The strategy used can be
|
||||
arbitrary: the `DescentType` simply needs to return an index of a child to
|
||||
insert a point or node into.
|
||||
|
||||
mlpack provides several drop-in choices for `DescentType`, and it is also
|
||||
possible to write a fully custom split:
|
||||
|
||||
* [`RTreeDescentHeuristic`](#rtreedescentheuristic): selects the closest child,
|
||||
which is the child whose volume will increase the least
|
||||
* [Custom `SplitType`s](#custom-splittypes): implement a fully custom
|
||||
`SplitType` class
|
||||
|
||||
*Note:* this section is still under construction---not all split types are
|
||||
documented yet.
|
||||
|
||||
### `RTreeDescentHeuristic`
|
||||
|
||||
The `RTreeDescentHeuristic` is the default descent strategy for the
|
||||
`RectangleTree` and is used by the [`RTree`](r_tree.md). The strategy is
|
||||
simple: the child node whose volume will increase the least is chosen as the
|
||||
child to insert a point or other node into.
|
||||
|
||||
For implementation details, see [the source
|
||||
code](/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic.hpp).
|
||||
|
||||
### Custom `DescentType`s
|
||||
|
||||
Custom descent strategies for a `RectangleTree` can be implemented via the
|
||||
`DescentType` template parameter. By default, the
|
||||
[`RTreeDescentHeuristic`](#rtreedescentheuristic) descent strategy is used,
|
||||
but it is also possible to implement and use a custom `DescentType`. Any custom
|
||||
`DescentType` class must implement the following signature:
|
||||
|
||||
```c++
|
||||
class DescentType
|
||||
{
|
||||
public:
|
||||
// Return a `size_t` indicating which child of `node` should be chosen to
|
||||
// insert `point` in.
|
||||
//
|
||||
// `TreeType` will be the relevant `RectangleTree` type.
|
||||
template<typename TreeType>
|
||||
static size_t ChooseDescentNode(const TreeType* node, const size_t point);
|
||||
|
||||
// Return a `size_t` indicating which child of `node` should be chosen to
|
||||
// insert `insertedNode` in.
|
||||
//
|
||||
// `TreeType` will be the relevant `RectangleTree` type.
|
||||
template<typename TreeType>
|
||||
static size_t ChooseDescentNode(const TreeType* node,
|
||||
const TreeType* insertedNode);
|
||||
};
|
||||
```
|
||||
|
||||
## `AuxiliaryInformationType`
|
||||
|
||||
The `AuxiliaryInformationType` template parameter holds any auxiliary
|
||||
information required by the `SplitType` or `DescentType` strategies. By
|
||||
default, the `NoAuxiliaryInformation` class is used, which holds nothing.
|
||||
|
||||
### Custom `AuxiliaryInformationType`s
|
||||
|
||||
Custom `AuxiliaryInformationType`s can be implemented and used with the
|
||||
`AuxiliaryInformationType` template parameter. Any custom
|
||||
`AuxiliaryInformationType` class must implement the following signature:
|
||||
|
||||
```c++
|
||||
// TreeType will be the type of RectangleTree that the auxiliary information
|
||||
// type is being used in.
|
||||
template<typename TreeType>
|
||||
class CustomAuxiliaryInformationType
|
||||
{
|
||||
public:
|
||||
// Default constructor is required.
|
||||
CustomAuxiliaryInformationType();
|
||||
// Construct the object with a tree node that may not yet be constructed.
|
||||
CustomAuxiliaryInformationType(TreeType* node);
|
||||
// Construct the object with another object and another tree node, optionally
|
||||
// making a 'deep copy' instead of just copying pointers where relevant.
|
||||
CustomAuxiliaryInformationType(const CustomAuxiliaryInformationType& other,
|
||||
TreeType* node,
|
||||
const bool deepCopy = true);
|
||||
|
||||
// Just before a point is inserted into a node, this is called.
|
||||
// `node` is the node that will have `node.Dataset().col(point)` inserted into
|
||||
// it.
|
||||
//
|
||||
// Optionally, this method can manipulate `node`. If so, `true` should be
|
||||
// returned to indicate that `node` was changed. Otherwise, return `false`
|
||||
// and the RectangleTree will perform its default behavior.
|
||||
bool HandlePointInsertion(TreeType* node, const size_t point);
|
||||
|
||||
// Just before a child node is inserted into a node, this is called.
|
||||
// `node` is the node that will have `nodeToInsert` inserted into it as a
|
||||
// child.
|
||||
//
|
||||
// Optionally, this method can manipulate `node`. If so, `true` should be
|
||||
// returned to indicate that `node` was changed. Otherwise, return `false`
|
||||
// and the RectangleTree will perform its default behavior.
|
||||
bool HandleNodeInsertion(TreeType* node,
|
||||
TreeType* nodeToInsert,
|
||||
const bool atMaxDepth);
|
||||
|
||||
// Just before a point is deleted from a node, this is called.
|
||||
// `node` is the node that will have `node.Dataset().col(point)` deleted from
|
||||
// it.
|
||||
//
|
||||
// Optionally, this method can manipulate `node`. If so, `true` should be
|
||||
// returned to indicate that `node` was changed. Otherwise, return `false`
|
||||
// and the RectangleTree will perform its default behavior.
|
||||
bool HandlePointDeletion(TreeType* node, const size_t point);
|
||||
|
||||
// Just before a child node is deleted from a node, this is called.
|
||||
// `node` is the node that will have `node.Child(nodeIndex)` deleted from it.
|
||||
//
|
||||
// Optionally, this method can manipulate `node`. If so, `true` should be
|
||||
// returned to indicate that `node` was changed. Otherwise, return `false`
|
||||
// and the RectangleTree will perform its default behavior.
|
||||
bool HandleNodeRemoval(TreeType* node, const size_t nodeIndex);
|
||||
|
||||
// When `node` is changed, this is called so that the auxiliary information
|
||||
// can be updated. If information needs to be propagated upward, return
|
||||
// `true` and then `UpdateAuxiliaryInfo(node->Parent())` will be called.
|
||||
bool UpdateAuxiliaryInfo(TreeType* node);
|
||||
};
|
||||
```
|
||||
|
||||
## Example usage
|
||||
|
||||
The `RectangleTree` class is only really necessary when a custom split type or
|
||||
custom descent strategy is intended to be used. For simpler use cases, one of
|
||||
the typedefs of `RectangleTree` (such as [`RTree`](r_tree.md)) will suffice.
|
||||
|
||||
For this reason, all of the examples below explicitly specify all six template
|
||||
parameters of `RectangleTree`.
|
||||
[Writing a custom splitting strategy](#custom-splittypes),
|
||||
[writing a custom descent strategy](#custom-descenttypes),
|
||||
and [writing a custom auxiliary information
|
||||
type](#custom-auxiliaryinformationtypes) are discussed in the previous sections.
|
||||
Each of the parameters in the examples below can be trivially changed for
|
||||
different behavior.
|
||||
|
||||
---
|
||||
|
||||
Build a `RectangleTree` on the `cloud` dataset and print basic statistics
|
||||
about the tree.
|
||||
|
||||
```c++
|
||||
// See https://datasets.mlpack.org/cloud.csv.
|
||||
arma::mat dataset;
|
||||
mlpack::data::Load("cloud.csv", dataset, true);
|
||||
|
||||
// Build the rectangle tree with a leaf size of 10. (This means that leaf nodes
|
||||
// cannot contain more than 10 points.)
|
||||
//
|
||||
// The std::move() means that `dataset` will be empty after this call, and no
|
||||
// data will be copied during tree building.
|
||||
mlpack::RectangleTree<mlpack::EuclideanDistance,
|
||||
mlpack::EmptyStatistic,
|
||||
arma::mat,
|
||||
mlpack::RTreeSplit,
|
||||
mlpack::RTreeDescentHeuristic,
|
||||
mlpack::NoAuxiliaryInformation> tree(std::move(dataset));
|
||||
|
||||
// Print the bounding box of the root node.
|
||||
std::cout << "Bounding box of root node:" << std::endl;
|
||||
for (size_t i = 0; i < tree.Bound().Dim(); ++i)
|
||||
{
|
||||
std::cout << " - Dimension " << i << ": [" << tree.Bound()[i].Lo() << ", "
|
||||
<< tree.Bound()[i].Hi() << "]." << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
// Print the number of children in the root, and the allowable range.
|
||||
std::cout << "Number of children of root: " << tree.NumChildren()
|
||||
<< "; allowable range: [" << tree.MinNumChildren() << ", "
|
||||
<< tree.MaxNumChildren() << "]." << std::endl;
|
||||
|
||||
// Print the number of descendant points of the root, and of each of its
|
||||
// children.
|
||||
std::cout << "Descendant points of root: "
|
||||
<< tree.NumDescendants() << "." << std::endl;
|
||||
for (size_t i = 0; i < tree.NumChildren(); ++i)
|
||||
{
|
||||
std::cout << "Descendant points of child " << i << ": "
|
||||
<< tree.Child(i).NumDescendants() << "." << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
// Compute the center of the RectangleTree.
|
||||
arma::vec center;
|
||||
tree.Center(center);
|
||||
std::cout << "Center of tree: " << center.t();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Build two `RectangleTree`s on subsets of the corel dataset and compute minimum
|
||||
and maximum distances between different nodes in the tree.
|
||||
|
||||
```c++
|
||||
// See https://datasets.mlpack.org/corel-histogram.csv.
|
||||
arma::mat dataset;
|
||||
mlpack::data::Load("corel-histogram.csv", dataset, true);
|
||||
|
||||
// Convenience typedef for the tree type.
|
||||
using TreeType = mlpack::RectangleTree<mlpack::EuclideanDistance,
|
||||
mlpack::EmptyStatistic,
|
||||
arma::mat,
|
||||
mlpack::RTreeSplit,
|
||||
mlpack::RTreeDescentHeuristic,
|
||||
mlpack::NoAuxiliaryInformation>;
|
||||
|
||||
// Build trees on the first half and the second half of points.
|
||||
TreeType tree1(dataset.cols(0, dataset.n_cols / 2));
|
||||
TreeType tree2(dataset.cols(dataset.n_cols / 2 + 1, dataset.n_cols - 1));
|
||||
|
||||
// Compute the maximum distance between the trees.
|
||||
std::cout << "Maximum distance between tree root nodes: "
|
||||
<< tree1.MaxDistance(tree2) << "." << std::endl;
|
||||
|
||||
// Get the leftmost grandchild of the first tree's root---if it exists.
|
||||
if (!tree1.IsLeaf() && !tree1.Child(0).IsLeaf())
|
||||
{
|
||||
TreeType& node1 = tree1.Child(0).Child(0);
|
||||
|
||||
// Get the leftmost grandchild of the second tree's root---if it exists.
|
||||
if (!tree2.IsLeaf() && !tree2.Child(0).IsLeaf())
|
||||
{
|
||||
TreeType& node2 = tree2.Child(0).Child(0);
|
||||
|
||||
// Print the minimum and maximum distance between the nodes.
|
||||
mlpack::Range dists = node1.RangeDistance(node2);
|
||||
std::cout << "Possible distances between two grandchild nodes: ["
|
||||
<< dists.Lo() << ", " << dists.Hi() << "]." << std::endl;
|
||||
|
||||
// Print the minimum distance between the first node and the first
|
||||
// descendant point of the second node.
|
||||
const size_t descendantIndex = node2.Descendant(0);
|
||||
const double descendantMinDist =
|
||||
node1.MinDistance(node2.Dataset().col(descendantIndex));
|
||||
std::cout << "Minimum distance between grandchild node and descendant "
|
||||
<< "point: " << descendantMinDist << "." << std::endl;
|
||||
|
||||
// Which child of node2 is closer to node1?
|
||||
const size_t closestIndex = node2.GetNearestChild(node1);
|
||||
std::cout << "Child " << closestIndex << " is closest to node1."
|
||||
<< std::endl;
|
||||
|
||||
// And which child of node1 is further from node2?
|
||||
const size_t furthestIndex = node1.GetFurthestChild(node2);
|
||||
std::cout << "Child " << furthestIndex << " is furthest from node2."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Build a `RectangleTree` on 32-bit floating point data and save it to disk.
|
||||
|
||||
```c++
|
||||
// See https://datasets.mlpack.org/corel-histogram.csv.
|
||||
arma::fmat dataset;
|
||||
mlpack::data::Load("corel-histogram.csv", dataset);
|
||||
|
||||
// Build the RectangleTree using 32-bit floating point data as the matrix
|
||||
// type. We will still use the default EmptyStatistic and EuclideanDistance
|
||||
// parameters. A leaf size of 100 is used here.
|
||||
mlpack::RectangleTree<mlpack::EuclideanDistance,
|
||||
mlpack::EmptyStatistic,
|
||||
arma::fmat,
|
||||
mlpack::RTreeSplit,
|
||||
mlpack::RTreeDescentHeuristic,
|
||||
mlpack::NoAuxiliaryInformation> tree(
|
||||
std::move(dataset), 100);
|
||||
|
||||
// Save the tree to disk with the name 'tree'.
|
||||
mlpack::data::Save("tree.bin", "tree", tree);
|
||||
|
||||
std::cout << "Saved tree with " << tree.Dataset().n_cols << " points to "
|
||||
<< "'tree.bin'." << std::endl;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Load a 32-bit floating point `RectangleTree` from disk, then traverse it
|
||||
manually and find the number of leaf nodes with less than 10 points.
|
||||
|
||||
```c++
|
||||
// This assumes the tree has already been saved to 'tree.bin' (as in the example
|
||||
// above).
|
||||
|
||||
// This convenient typedef saves us a long type name!
|
||||
using TreeType = mlpack::RectangleTree<mlpack::EuclideanDistance,
|
||||
mlpack::EmptyStatistic,
|
||||
arma::fmat,
|
||||
mlpack::RTreeSplit,
|
||||
mlpack::RTreeDescentHeuristic,
|
||||
mlpack::NoAuxiliaryInformation>;
|
||||
|
||||
TreeType tree;
|
||||
mlpack::data::Load("tree.bin", "tree", tree);
|
||||
std::cout << "Tree loaded with " << tree.NumDescendants() << " points."
|
||||
<< std::endl;
|
||||
|
||||
// Recurse in a depth-first manner. Count both the total number of leaves, and
|
||||
// the number of leaves with less than 10 points.
|
||||
size_t leafCount = 0;
|
||||
size_t totalLeafCount = 0;
|
||||
std::stack<TreeType*> stack;
|
||||
stack.push(&tree);
|
||||
while (!stack.empty())
|
||||
{
|
||||
TreeType* node = stack.top();
|
||||
stack.pop();
|
||||
|
||||
if (node->NumPoints() < 10)
|
||||
++leafCount;
|
||||
++totalLeafCount;
|
||||
|
||||
for (size_t i = 0; i < node->NumChildren(); ++i)
|
||||
stack.push(&node->Child(i));
|
||||
}
|
||||
|
||||
// Note that it would be possible to use TreeType::SingleTreeTraverser to
|
||||
// perform the recursion above, but that is more well-suited for more complex
|
||||
// tasks that require pruning and other non-trivial behavior; so using a simple
|
||||
// stack is the better option here.
|
||||
|
||||
// Print the results.
|
||||
std::cout << leafCount << " out of " << totalLeafCount << " leaves have fewer "
|
||||
<< "than 10 points." << std::endl;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Build a `RectangleTree` by iteratively inserting points from the corel dataset,
|
||||
print some information, and then remove a few randomly chosen points.
|
||||
|
||||
```c++
|
||||
// See https://datasets.mlpack.org/corel-histogram.csv.
|
||||
arma::mat dataset;
|
||||
mlpack::data::Load("corel-histogram.csv", dataset, true);
|
||||
|
||||
// This convenient typedef saves us a long type name!
|
||||
using TreeType = mlpack::RectangleTree<mlpack::EuclideanDistance,
|
||||
mlpack::EmptyStatistic,
|
||||
arma::mat,
|
||||
mlpack::RTreeSplit,
|
||||
mlpack::RTreeDescentHeuristic,
|
||||
mlpack::NoAuxiliaryInformation>;
|
||||
|
||||
// Create an empty tree of the right dimensionality.
|
||||
TreeType t(dataset.n_rows);
|
||||
|
||||
// Insert points one by one for the first half of the dataset.
|
||||
for (size_t i = 0; i < dataset.n_cols / 2; ++i)
|
||||
t.Insert(dataset.col(i));
|
||||
|
||||
std::cout << "After inserting half the points, the root node has "
|
||||
<< t.NumDescendants() << " descendant points and "
|
||||
<< t.NumChildren() << " child nodes." << std::endl;
|
||||
|
||||
// For the second half, insert the points backwards.
|
||||
for (size_t i = dataset.n_cols - 1; i >= dataset.n_cols / 2; --i)
|
||||
t.Insert(dataset.col(i));
|
||||
|
||||
std::cout << "After inserting all the points, the root node has "
|
||||
<< t.NumDescendants() << " descendant points and "
|
||||
<< t.NumChildren() << " child nodes." << std::endl;
|
||||
|
||||
// Remove three random points.
|
||||
t.Delete(mlpack::math::RandInt(0, t.NumDescendants()));
|
||||
std::cout << "After removing 1 point, the root node has " << t.NumDescendants()
|
||||
<< " descendant points." << std::endl;
|
||||
t.Delete(mlpack::math::RandInt(0, t.NumDescendants()));
|
||||
std::cout << "After removing 2 points, the root node has " << t.NumDescendants()
|
||||
<< " descendant points." << std::endl;
|
||||
t.Delete(mlpack::math::RandInt(0, t.NumDescendants()));
|
||||
std::cout << "After removing 3 points, the root node has " << t.NumDescendants()
|
||||
<< " descendant points." << std::endl;
|
||||
```
|
||||
@@ -136,8 +136,8 @@ different.
|
||||
not supported, because this generally results in a random projection tree
|
||||
with very loose bounding boxes. It is better to simply build a new `RPTree`
|
||||
on the modified dataset. For trees that support individual insertion and
|
||||
deletions, see the `RectangleTree` class and all its variants (e.g. `RTree`,
|
||||
`RStarTree`, etc.).
|
||||
deletions, see the [`RectangleTree`](rectangle_tree.md) class and all its
|
||||
variants (e.g. [`RTree`](r_tree.md), `RStarTree`, etc.).
|
||||
|
||||
- See also the
|
||||
[developer documentation on tree constructors](../../../developer/trees.md#constructors-and-destructors).
|
||||
@@ -390,7 +390,7 @@ nodes. The following functions can be used for these tasks.
|
||||
`arma::fmat`, and the returned type is
|
||||
[`RangeType<float>`](../math.md#range)).
|
||||
|
||||
### Tree traversals
|
||||
## Tree traversals
|
||||
|
||||
Like every mlpack tree, the `RPTree` class provides a [single-tree and dual-tree
|
||||
traversal](../../../developer/trees.md#traversals) that can be paired with a
|
||||
|
||||
@@ -132,7 +132,8 @@ different.
|
||||
not supported, because this generally results in a UB-tree with very loose
|
||||
bounding boxes. It is better to simply build a new `UBTree` on the modified
|
||||
dataset. For trees that support individual insertion and deletions, see the
|
||||
`RectangleTree` class and all its variants (e.g. `RTree`, `RStarTree`, etc.).
|
||||
[`RectangleTree`](rectangle_tree.md) class and all its variants (e.g.
|
||||
[`RTree`](r_tree.md), `RStarTree`, etc.).
|
||||
|
||||
- See also the
|
||||
[developer documentation on tree constructors](../../../developer/trees.md#constructors-and-destructors).
|
||||
@@ -384,7 +385,7 @@ nodes. The following functions can be used for these tasks.
|
||||
`arma::fmat`, and the returned type is
|
||||
[`RangeType<float>`](../math.md#range)).
|
||||
|
||||
### Tree traversals
|
||||
## Tree traversals
|
||||
|
||||
Like every mlpack tree, the `UBTree` class provides a [single-tree and dual-tree
|
||||
traversal](../../../developer/trees.md#traversals) that can be paired with a
|
||||
|
||||
@@ -127,8 +127,8 @@ different.
|
||||
not supported, because this generally results in a vantage point tree with
|
||||
very loose bounding balls. It is better to simply build a new `VPTree` on
|
||||
the modified dataset. For trees that support individual insertion and
|
||||
deletions, see the `RectangleTree` class and all its variants (e.g. `RTree`,
|
||||
`RStarTree`, etc.).
|
||||
deletions, see the [`RectangleTree`](rectangle_tree.md) class and all its
|
||||
variants (e.g. [`RTree`](r_tree.md), `RStarTree`, etc.).
|
||||
|
||||
- See also the
|
||||
[developer documentation on tree constructors](../../../developer/trees.md#constructors-and-destructors).
|
||||
@@ -375,7 +375,7 @@ nodes. The following functions can be used for these tasks.
|
||||
`arma::fmat`, and the returned type is
|
||||
[`RangeType<float>`](../math.md#range)).
|
||||
|
||||
### Tree traversals
|
||||
## Tree traversals
|
||||
|
||||
Like every mlpack tree, the `VPTree` class provides a [single-tree and dual-tree
|
||||
traversal](../../../developer/trees.md#traversals) that can be paired with a
|
||||
@@ -595,7 +595,7 @@ while (!stack.empty())
|
||||
// stack is the better option here.
|
||||
|
||||
// Print the results.
|
||||
std::cout << leafCount << " out of " << totalLeafCount << " leaves have less "
|
||||
std::cout << leafCount << " out of " << totalLeafCount << " leaves have fewer "
|
||||
<< "than 10 points." << std::endl;
|
||||
```
|
||||
|
||||
|
||||
@@ -552,6 +552,13 @@ template<typename MatType>
|
||||
inline HRectBound<DistanceType, ElemType>&
|
||||
HRectBound<DistanceType, ElemType>::operator|=(const MatType& data)
|
||||
{
|
||||
if (dim == 0)
|
||||
{
|
||||
delete[] bounds;
|
||||
dim = data.n_rows;
|
||||
bounds = new RangeType<ElemType>[dim];
|
||||
}
|
||||
|
||||
Log::Assert(data.n_rows == dim);
|
||||
|
||||
arma::Col<ElemType> mins(min(data, 1));
|
||||
@@ -576,7 +583,14 @@ template<typename DistanceType, typename ElemType>
|
||||
inline HRectBound<DistanceType, ElemType>&
|
||||
HRectBound<DistanceType, ElemType>::operator|=(const HRectBound& other)
|
||||
{
|
||||
assert(other.dim == dim);
|
||||
if (dim == 0)
|
||||
{
|
||||
delete[] bounds;
|
||||
dim = other.dim;
|
||||
bounds = new RangeType<ElemType>[dim];
|
||||
}
|
||||
|
||||
Log::Assert(other.dim == dim);
|
||||
|
||||
minWidth = std::numeric_limits<ElemType>::max();
|
||||
for (size_t i = 0; i < dim; ++i)
|
||||
|
||||
@@ -74,11 +74,6 @@ class RectangleTree
|
||||
std::vector<RectangleTree*> children;
|
||||
//! The parent node (NULL if this is the root of the tree).
|
||||
RectangleTree* parent;
|
||||
//! The index of the first point in the dataset contained in this node (and
|
||||
//! its children). THIS IS ALWAYS 0 AT THE MOMENT. IT EXISTS MERELY IN CASE
|
||||
//! I THINK OF A WAY TO CHANGE THAT. IN OTHER WORDS, IT WILL PROBABLY BE
|
||||
//! REMOVED.
|
||||
size_t begin;
|
||||
//! The number of points in the dataset contained in this node (and its
|
||||
//! children).
|
||||
size_t count;
|
||||
@@ -95,7 +90,7 @@ class RectangleTree
|
||||
//! The distance from the centroid of this node to the centroid of the parent.
|
||||
ElemType parentDistance;
|
||||
//! The dataset.
|
||||
const MatType* dataset;
|
||||
MatType* dataset;
|
||||
//! Whether or not we are responsible for deleting the dataset. This is
|
||||
//! probably not aligned well...
|
||||
bool ownsDataset;
|
||||
@@ -124,15 +119,12 @@ class RectangleTree
|
||||
* have.
|
||||
* @param minNumChildren The minimum number of child nodes a non-leaf node may
|
||||
* have.
|
||||
* @param firstDataIndex The index of the first data point. UNUSED UNLESS WE
|
||||
* ADD SUPPORT FOR HAVING A "CENTERAL" DATA MATRIX.
|
||||
*/
|
||||
RectangleTree(const MatType& data,
|
||||
const size_t maxLeafSize = 20,
|
||||
const size_t minLeafSize = 8,
|
||||
const size_t maxNumChildren = 5,
|
||||
const size_t minNumChildren = 2,
|
||||
const size_t firstDataIndex = 0);
|
||||
const size_t minNumChildren = 2);
|
||||
|
||||
/**
|
||||
* Construct this as the root node of a rectangle tree type using the given
|
||||
@@ -145,15 +137,30 @@ class RectangleTree
|
||||
* have.
|
||||
* @param minNumChildren The minimum number of child nodes a non-leaf node may
|
||||
* have.
|
||||
* @param firstDataIndex The index of the first data point. UNUSED UNLESS WE
|
||||
* ADD SUPPORT FOR HAVING A "CENTERAL" DATA MATRIX.
|
||||
*/
|
||||
RectangleTree(MatType&& data,
|
||||
const size_t maxLeafSize = 20,
|
||||
const size_t minLeafSize = 8,
|
||||
const size_t maxNumChildren = 5,
|
||||
const size_t minNumChildren = 2,
|
||||
const size_t firstDataIndex = 0);
|
||||
const size_t minNumChildren = 2);
|
||||
|
||||
/**
|
||||
* Construct an empty RectangleTree where points will have the given
|
||||
* dimensionality.
|
||||
*
|
||||
* @param data Dataset from which to create the tree.
|
||||
* @param maxLeafSize Maximum size of each leaf in the tree.
|
||||
* @param minLeafSize Minimum size of each leaf in the tree.
|
||||
* @param maxNumChildren The maximum number of child nodes a non-leaf node may
|
||||
* have.
|
||||
* @param minNumChildren The minimum number of child nodes a non-leaf node may
|
||||
* have.
|
||||
*/
|
||||
RectangleTree(const size_t dimensionality = 0,
|
||||
const size_t maxLeafSize = 20,
|
||||
const size_t minLeafSize = 8,
|
||||
const size_t maxNumChildren = 5,
|
||||
const size_t minNumChildren = 2);
|
||||
|
||||
/**
|
||||
* Construct this as an empty node with the specified parent. Copying the
|
||||
@@ -214,6 +221,24 @@ class RectangleTree
|
||||
*/
|
||||
~RectangleTree();
|
||||
|
||||
/**
|
||||
* Insert the given point into the tree and into the dataset held by the tree.
|
||||
*
|
||||
* An exception will be thrown if this is not called on the root of the tree.
|
||||
*/
|
||||
template<typename InMatType>
|
||||
void Insert(const InMatType& points);
|
||||
|
||||
/**
|
||||
* Delete the given point index from the tree. This will resize Dataset()
|
||||
* accordingly and correct the point indexes in the rest of the tree, so is
|
||||
* not a trivial operation.
|
||||
*
|
||||
* `pointIndex` must be between `0` and `numDescendants`, and an exception
|
||||
* will be thrown if this is not called on the root of the tree.
|
||||
*/
|
||||
void Delete(const size_t pointIndex);
|
||||
|
||||
/**
|
||||
* Delete this node of the tree, but leave the stuff contained in it intact.
|
||||
* This is used when splitting a node, where the data in this tree is moved to
|
||||
@@ -311,9 +336,9 @@ class RectangleTree
|
||||
RectangleTree* FindByBeginCount(size_t begin, size_t count);
|
||||
|
||||
//! Return the bound object for this node.
|
||||
const HRectBound<DistanceType>& Bound() const { return bound; }
|
||||
const HRectBound<DistanceType, ElemType>& Bound() const { return bound; }
|
||||
//! Modify the bound object for this node.
|
||||
HRectBound<DistanceType>& Bound() { return bound; }
|
||||
HRectBound<DistanceType, ElemType>& Bound() { return bound; }
|
||||
|
||||
//! Return the statistic object for this node.
|
||||
const StatisticType& Stat() const { return stat; }
|
||||
@@ -330,6 +355,11 @@ class RectangleTree
|
||||
//! Return whether or not this node is a leaf (true if it has no children).
|
||||
bool IsLeaf() const;
|
||||
|
||||
//! Return the number of points in the node.
|
||||
size_t Count() const { return count; }
|
||||
//! Modify the number of points in the node.
|
||||
size_t& Count() { return count; }
|
||||
|
||||
//! Return the maximum leaf size.
|
||||
size_t MaxLeafSize() const { return maxLeafSize; }
|
||||
//! Modify the maximum leaf size.
|
||||
@@ -368,7 +398,8 @@ class RectangleTree
|
||||
DistanceType Distance() const { return DistanceType(); }
|
||||
|
||||
//! Get the centroid of the node and store it in the given vector.
|
||||
void Center(arma::vec& center) { bound.Center(center); }
|
||||
template<typename VecType>
|
||||
void Center(VecType& center) { bound.Center(center); }
|
||||
|
||||
//! Return the number of child nodes. (One level beneath this one only.)
|
||||
size_t NumChildren() const { return numChildren; }
|
||||
@@ -542,16 +573,6 @@ class RectangleTree
|
||||
*/
|
||||
size_t TreeDepth() const;
|
||||
|
||||
//! Return the index of the beginning point of this subset.
|
||||
size_t Begin() const { return begin; }
|
||||
//! Modify the index of the beginning point of this subset.
|
||||
size_t& Begin() { return begin; }
|
||||
|
||||
//! Return the number of points in this subset.
|
||||
size_t Count() const { return count; }
|
||||
//! Modify the number of points in this subset.
|
||||
size_t& Count() { return count; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Splits the current node, recursing up the tree.
|
||||
@@ -567,15 +588,6 @@ class RectangleTree
|
||||
*/
|
||||
void BuildStatistics(RectangleTree* node);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* A default constructor. This is meant to only be used with
|
||||
* cereal, which is allowed with the friend declaration below.
|
||||
* This does not return a valid tree! This method must be protected, so that
|
||||
* the serialization shim can work with the default constructor.
|
||||
*/
|
||||
RectangleTree();
|
||||
|
||||
//! Friend access is given for the default constructor.
|
||||
friend class cereal::access;
|
||||
|
||||
@@ -633,11 +645,18 @@ class RectangleTree
|
||||
* Serialize the tree.
|
||||
*/
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar, const uint32_t /* version */);
|
||||
void serialize(Archive& ar, const uint32_t version);
|
||||
};
|
||||
|
||||
} // namespace mlpack
|
||||
|
||||
CEREAL_TEMPLATE_CLASS_VERSION(
|
||||
(typename DistanceType, typename StatisticType, typename MatType,
|
||||
typename SplitType, typename DescentType,
|
||||
template<typename> class AuxiliaryInformationType),
|
||||
(mlpack::RectangleTree<DistanceType, StatisticType, MatType, SplitType,
|
||||
DescentType, AuxiliaryInformationType>), (1));
|
||||
|
||||
// Include implementation.
|
||||
#include "rectangle_tree_impl.hpp"
|
||||
|
||||
|
||||
@@ -50,14 +50,12 @@ RectangleTree(const MatType& data,
|
||||
const size_t maxLeafSize,
|
||||
const size_t minLeafSize,
|
||||
const size_t maxNumChildren,
|
||||
const size_t minNumChildren,
|
||||
const size_t firstDataIndex) :
|
||||
const size_t minNumChildren) :
|
||||
maxNumChildren(maxNumChildren),
|
||||
minNumChildren(minNumChildren),
|
||||
numChildren(0),
|
||||
children(maxNumChildren + 1), // Add one to make splitting the node simpler.
|
||||
parent(NULL),
|
||||
begin(0),
|
||||
count(0),
|
||||
numDescendants(0),
|
||||
maxLeafSize(maxLeafSize),
|
||||
@@ -72,7 +70,7 @@ RectangleTree(const MatType& data,
|
||||
// For now, just insert the points in order.
|
||||
RectangleTree* root = this;
|
||||
|
||||
for (size_t i = firstDataIndex; i < data.n_cols; ++i)
|
||||
for (size_t i = 0; i < data.n_cols; ++i)
|
||||
root->InsertPoint(i);
|
||||
|
||||
// Initialize statistic recursively after tree construction is complete.
|
||||
@@ -91,14 +89,12 @@ RectangleTree(MatType&& data,
|
||||
const size_t maxLeafSize,
|
||||
const size_t minLeafSize,
|
||||
const size_t maxNumChildren,
|
||||
const size_t minNumChildren,
|
||||
const size_t firstDataIndex) :
|
||||
const size_t minNumChildren) :
|
||||
maxNumChildren(maxNumChildren),
|
||||
minNumChildren(minNumChildren),
|
||||
numChildren(0),
|
||||
children(maxNumChildren + 1), // Add one to make splitting the node simpler.
|
||||
parent(NULL),
|
||||
begin(0),
|
||||
count(0),
|
||||
numDescendants(0),
|
||||
maxLeafSize(maxLeafSize),
|
||||
@@ -113,7 +109,7 @@ RectangleTree(MatType&& data,
|
||||
// For now, just insert the points in order.
|
||||
RectangleTree* root = this;
|
||||
|
||||
for (size_t i = firstDataIndex; i < dataset->n_cols; ++i)
|
||||
for (size_t i = 0; i < dataset->n_cols; ++i)
|
||||
root->InsertPoint(i);
|
||||
|
||||
// Initialize statistic recursively after tree construction is complete.
|
||||
@@ -138,7 +134,6 @@ RectangleTree(
|
||||
numChildren(0),
|
||||
children(maxNumChildren + 1),
|
||||
parent(parentNode),
|
||||
begin(0),
|
||||
count(0),
|
||||
numDescendants(0),
|
||||
maxLeafSize(parentNode->MaxLeafSize()),
|
||||
@@ -175,8 +170,7 @@ RectangleTree(
|
||||
numChildren(other.NumChildren()),
|
||||
children(maxNumChildren + 1, NULL),
|
||||
parent(deepCopy ? newParent : other.Parent()),
|
||||
begin(other.Begin()),
|
||||
count(other.Count()),
|
||||
count(other.count),
|
||||
numDescendants(other.numDescendants),
|
||||
maxLeafSize(other.MaxLeafSize()),
|
||||
minLeafSize(other.MinLeafSize()),
|
||||
@@ -185,7 +179,7 @@ RectangleTree(
|
||||
parentDistance(other.ParentDistance()),
|
||||
dataset(deepCopy ?
|
||||
(parent ? parent->dataset : new MatType(*other.dataset)) :
|
||||
&other.Dataset()),
|
||||
other.dataset),
|
||||
ownsDataset(deepCopy && (!parent)),
|
||||
points(other.points),
|
||||
auxiliaryInfo(other.auxiliaryInfo, this, deepCopy)
|
||||
@@ -219,8 +213,7 @@ RectangleTree(RectangleTree&& other) :
|
||||
numChildren(other.NumChildren()),
|
||||
children(std::move(other.children)),
|
||||
parent(other.Parent()),
|
||||
begin(other.Begin()),
|
||||
count(other.Count()),
|
||||
count(other.count),
|
||||
numDescendants(other.numDescendants),
|
||||
maxLeafSize(other.MaxLeafSize()),
|
||||
minLeafSize(other.MinLeafSize()),
|
||||
@@ -251,7 +244,6 @@ RectangleTree(RectangleTree&& other) :
|
||||
other.minNumChildren = 0;
|
||||
other.numChildren = 0;
|
||||
other.parent = NULL;
|
||||
other.begin = 0;
|
||||
other.count = 0;
|
||||
other.numDescendants = 0;
|
||||
other.maxLeafSize = 0;
|
||||
@@ -292,8 +284,7 @@ operator=(const RectangleTree& other)
|
||||
numChildren = other.NumChildren();
|
||||
children.resize(maxNumChildren + 1, NULL);
|
||||
parent = NULL;
|
||||
begin = other.Begin();
|
||||
count = other.Count();
|
||||
count = other.count;
|
||||
numDescendants = other.numDescendants;
|
||||
maxLeafSize = other.MaxLeafSize();
|
||||
minLeafSize = other.MinLeafSize();
|
||||
@@ -345,8 +336,7 @@ operator=(RectangleTree&& other)
|
||||
numChildren = other.NumChildren();
|
||||
children = std::move(other.children);
|
||||
parent = other.Parent();
|
||||
begin = other.Begin();
|
||||
count = other.Count();
|
||||
count = other.count;
|
||||
numDescendants = other.numDescendants;
|
||||
maxLeafSize = other.MaxLeafSize();
|
||||
minLeafSize = other.MinLeafSize();
|
||||
@@ -364,7 +354,6 @@ operator=(RectangleTree&& other)
|
||||
other.minNumChildren = 0;
|
||||
other.numChildren = 0;
|
||||
other.parent = NULL;
|
||||
other.begin = 0;
|
||||
other.count = 0;
|
||||
other.numDescendants = 0;
|
||||
other.maxLeafSize = 0;
|
||||
@@ -376,6 +365,39 @@ operator=(RectangleTree&& other)
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Construct an empty but ready-to-use tree.
|
||||
template<typename DistanceType,
|
||||
typename StatisticType,
|
||||
typename MatType,
|
||||
typename SplitType,
|
||||
typename DescentType,
|
||||
template<typename> class AuxiliaryInformationType>
|
||||
RectangleTree<DistanceType, StatisticType, MatType, SplitType, DescentType,
|
||||
AuxiliaryInformationType>::
|
||||
RectangleTree(const size_t dimensionality,
|
||||
const size_t maxLeafSize,
|
||||
const size_t minLeafSize,
|
||||
const size_t maxNumChildren,
|
||||
const size_t minNumChildren) :
|
||||
maxNumChildren(maxNumChildren),
|
||||
minNumChildren(minNumChildren),
|
||||
numChildren(0),
|
||||
children(maxNumChildren + 1), // Add one to make splitting the node simpler.
|
||||
parent(NULL),
|
||||
count(0),
|
||||
numDescendants(0),
|
||||
maxLeafSize(maxLeafSize),
|
||||
minLeafSize(minLeafSize),
|
||||
bound(dimensionality),
|
||||
parentDistance(0.0),
|
||||
dataset(new MatType(dimensionality, 0)),
|
||||
ownsDataset(true),
|
||||
points(maxLeafSize + 1), // Add one to make splitting the node simpler.
|
||||
auxiliaryInfo(this)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the tree from a cereal archive.
|
||||
*/
|
||||
@@ -419,6 +441,58 @@ RectangleTree<DistanceType, StatisticType, MatType, SplitType, DescentType,
|
||||
delete dataset;
|
||||
}
|
||||
|
||||
// Insert a point into the dataset.
|
||||
template<typename DistanceType,
|
||||
typename StatisticType,
|
||||
typename MatType,
|
||||
typename SplitType,
|
||||
typename DescentType,
|
||||
template<typename> class AuxiliaryInformationType>
|
||||
template<typename InMatType>
|
||||
void RectangleTree<DistanceType, StatisticType, MatType, SplitType, DescentType,
|
||||
AuxiliaryInformationType>::Insert(const InMatType& points)
|
||||
{
|
||||
if (parent != NULL)
|
||||
throw std::runtime_error("RectangleTree::Insert(): cannot insert points "
|
||||
"into a node that is not the root of the tree!");
|
||||
|
||||
const size_t oldCols = dataset->n_cols;
|
||||
dataset->insert_cols(dataset->n_cols, points);
|
||||
for (size_t i = 0; i < points.n_cols; ++i)
|
||||
InsertPoint(oldCols + i);
|
||||
}
|
||||
|
||||
// Delete a point from the dataset.
|
||||
template<typename DistanceType,
|
||||
typename StatisticType,
|
||||
typename MatType,
|
||||
typename SplitType,
|
||||
typename DescentType,
|
||||
template<typename> class AuxiliaryInformationType>
|
||||
void RectangleTree<DistanceType, StatisticType, MatType, SplitType, DescentType,
|
||||
AuxiliaryInformationType>::Delete(const size_t pointIndex)
|
||||
{
|
||||
// First delete the point.
|
||||
DeletePoint(pointIndex);
|
||||
dataset->shed_col(pointIndex);
|
||||
|
||||
// Now we have to iterate over every node and update any child indices.
|
||||
std::stack<RectangleTree*> s;
|
||||
s.push(this);
|
||||
while (!s.empty())
|
||||
{
|
||||
RectangleTree* node = s.top();
|
||||
s.pop();
|
||||
|
||||
for (size_t i = 0; i < node->points.size(); ++i)
|
||||
if (node->points[i] > pointIndex)
|
||||
--node->points[i];
|
||||
|
||||
for (size_t i = 0; i < node->NumChildren(); ++i)
|
||||
s.push(&node->Child(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes this node but leaves the children untouched. Needed for when we
|
||||
* split nodes and remove nodes (inserting and deleting points).
|
||||
@@ -785,7 +859,7 @@ size_t RectangleTree<DistanceType, StatisticType, MatType, SplitType,
|
||||
for (size_t i = 0; i < NumChildren(); ++i)
|
||||
{
|
||||
ElemType distance = Child(i).MinDistance(point);
|
||||
if (distance <= bestDistance)
|
||||
if (distance < bestDistance)
|
||||
{
|
||||
bestDistance = distance;
|
||||
bestIndex = i;
|
||||
@@ -818,7 +892,7 @@ size_t RectangleTree<DistanceType, StatisticType, MatType, SplitType,
|
||||
for (size_t i = 0; i < NumChildren(); ++i)
|
||||
{
|
||||
ElemType distance = Child(i).MaxDistance(point);
|
||||
if (distance >= bestDistance)
|
||||
if (distance > bestDistance)
|
||||
{
|
||||
bestDistance = distance;
|
||||
bestIndex = i;
|
||||
@@ -849,7 +923,7 @@ size_t RectangleTree<DistanceType, StatisticType, MatType, SplitType,
|
||||
for (size_t i = 0; i < NumChildren(); ++i)
|
||||
{
|
||||
ElemType distance = Child(i).MinDistance(queryNode);
|
||||
if (distance <= bestDistance)
|
||||
if (distance < bestDistance)
|
||||
{
|
||||
bestDistance = distance;
|
||||
bestIndex = i;
|
||||
@@ -880,7 +954,7 @@ size_t RectangleTree<DistanceType, StatisticType, MatType, SplitType,
|
||||
for (size_t i = 0; i < NumChildren(); ++i)
|
||||
{
|
||||
ElemType distance = Child(i).MaxDistance(queryNode);
|
||||
if (distance >= bestDistance)
|
||||
if (distance > bestDistance)
|
||||
{
|
||||
bestDistance = distance;
|
||||
bestIndex = i;
|
||||
@@ -1037,32 +1111,6 @@ void RectangleTree<DistanceType, StatisticType, MatType, SplitType, DescentType,
|
||||
}
|
||||
}
|
||||
|
||||
//! Default constructor for cereal.
|
||||
template<typename DistanceType,
|
||||
typename StatisticType,
|
||||
typename MatType,
|
||||
typename SplitType,
|
||||
typename DescentType,
|
||||
template<typename> class AuxiliaryInformationType>
|
||||
RectangleTree<DistanceType, StatisticType, MatType, SplitType, DescentType,
|
||||
AuxiliaryInformationType>::
|
||||
RectangleTree() :
|
||||
maxNumChildren(0), // Try to give sensible defaults, but it shouldn't matter
|
||||
minNumChildren(0), // because this tree isn't valid anyway and is only used
|
||||
numChildren(0), // by cereal.
|
||||
parent(NULL),
|
||||
begin(0),
|
||||
count(0),
|
||||
numDescendants(0),
|
||||
maxLeafSize(0),
|
||||
minLeafSize(0),
|
||||
parentDistance(0.0),
|
||||
dataset(NULL),
|
||||
ownsDataset(false)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* Condense the tree. This shrinks the bounds and moves up the tree if
|
||||
* applicable. If a node goes below minimum fill, this code will deal with it.
|
||||
@@ -1220,7 +1268,7 @@ void RectangleTree<DistanceType, StatisticType, MatType, SplitType, DescentType,
|
||||
numChildren = child->NumChildren();
|
||||
child->NumChildren() = 0;
|
||||
|
||||
for (size_t i = 0; i < child->Count(); ++i)
|
||||
for (size_t i = 0; i < child->count; ++i)
|
||||
{
|
||||
// In case the tree has a height of two.
|
||||
points[i] = child->Point(i);
|
||||
@@ -1228,8 +1276,8 @@ void RectangleTree<DistanceType, StatisticType, MatType, SplitType, DescentType,
|
||||
|
||||
auxiliaryInfo = child->AuxiliaryInfo();
|
||||
|
||||
count = child->Count();
|
||||
child->Count() = 0;
|
||||
count = child->count;
|
||||
child->count = 0;
|
||||
|
||||
delete child;
|
||||
return;
|
||||
@@ -1395,7 +1443,7 @@ template<typename Archive>
|
||||
void RectangleTree<DistanceType, StatisticType, MatType, SplitType, DescentType,
|
||||
AuxiliaryInformationType>::serialize(
|
||||
Archive& ar,
|
||||
const uint32_t /* version */)
|
||||
const uint32_t version)
|
||||
{
|
||||
// Clean up memory, if necessary.
|
||||
if (cereal::is_loading<Archive>())
|
||||
@@ -1418,7 +1466,11 @@ void RectangleTree<DistanceType, StatisticType, MatType, SplitType, DescentType,
|
||||
if (cereal::is_loading<Archive>())
|
||||
children.resize(maxNumChildren + 1);
|
||||
|
||||
ar(CEREAL_NVP(begin));
|
||||
if (version == 0)
|
||||
{
|
||||
size_t begin;
|
||||
ar(CEREAL_NVP(begin));
|
||||
}
|
||||
ar(CEREAL_NVP(count));
|
||||
ar(CEREAL_NVP(numDescendants));
|
||||
ar(CEREAL_NVP(maxLeafSize));
|
||||
@@ -1466,6 +1518,7 @@ void RectangleTree<DistanceType, StatisticType, MatType, SplitType, DescentType,
|
||||
RectangleTree* node = stack.top();
|
||||
stack.pop();
|
||||
node->dataset = dataset;
|
||||
node->ownsDataset = false;
|
||||
for (size_t i = 0; i < node->numChildren; ++i)
|
||||
{
|
||||
stack.push(node->children[i]);
|
||||
|
||||
@@ -37,7 +37,9 @@ namespace mlpack {
|
||||
*
|
||||
* @see @ref trees, RStarTree
|
||||
*/
|
||||
template<typename DistanceType, typename StatisticType, typename MatType>
|
||||
template<typename DistanceType = EuclideanDistance,
|
||||
typename StatisticType = EmptyStatistic,
|
||||
typename MatType = arma::mat>
|
||||
using RTree = RectangleTree<DistanceType,
|
||||
StatisticType,
|
||||
MatType,
|
||||
@@ -65,7 +67,9 @@ using RTree = RectangleTree<DistanceType,
|
||||
*
|
||||
* @see @ref trees, RTree
|
||||
*/
|
||||
template<typename DistanceType, typename StatisticType, typename MatType>
|
||||
template<typename DistanceType = EuclideanDistance,
|
||||
typename StatisticType = EmptyStatistic,
|
||||
typename MatType = arma::mat>
|
||||
using RStarTree = RectangleTree<DistanceType,
|
||||
StatisticType,
|
||||
MatType,
|
||||
@@ -91,7 +95,9 @@ using RStarTree = RectangleTree<DistanceType,
|
||||
*
|
||||
* @see @ref trees, RTree, RStarTree
|
||||
*/
|
||||
template<typename DistanceType, typename StatisticType, typename MatType>
|
||||
template<typename DistanceType = EuclideanDistance,
|
||||
typename StatisticType = EmptyStatistic,
|
||||
typename MatType = arma::mat>
|
||||
using XTree = RectangleTree<DistanceType,
|
||||
StatisticType,
|
||||
MatType,
|
||||
@@ -126,7 +132,9 @@ template<typename TreeType>
|
||||
using DiscreteHilbertRTreeAuxiliaryInformation =
|
||||
HilbertRTreeAuxiliaryInformation<TreeType, DiscreteHilbertValue>;
|
||||
|
||||
template<typename DistanceType, typename StatisticType, typename MatType>
|
||||
template<typename DistanceType = EuclideanDistance,
|
||||
typename StatisticType = EmptyStatistic,
|
||||
typename MatType = arma::mat>
|
||||
using HilbertRTree = RectangleTree<DistanceType,
|
||||
StatisticType,
|
||||
MatType,
|
||||
@@ -157,7 +165,9 @@ using HilbertRTree = RectangleTree<DistanceType,
|
||||
*
|
||||
* @see @ref trees, RTree, RTree, RPlusTree
|
||||
*/
|
||||
template<typename DistanceType, typename StatisticType, typename MatType>
|
||||
template<typename DistanceType = EuclideanDistance,
|
||||
typename StatisticType = EmptyStatistic,
|
||||
typename MatType = arma::mat>
|
||||
using RPlusTree = RectangleTree<DistanceType,
|
||||
StatisticType,
|
||||
MatType,
|
||||
@@ -186,7 +196,9 @@ using RPlusTree = RectangleTree<DistanceType,
|
||||
*
|
||||
* @see @ref trees, RTree, RTree, RPlusTree, RPlusPlusTree
|
||||
*/
|
||||
template<typename DistanceType, typename StatisticType, typename MatType>
|
||||
template<typename DistanceType = EuclideanDistance,
|
||||
typename StatisticType = EmptyStatistic,
|
||||
typename MatType = arma::mat>
|
||||
using RPlusPlusTree = RectangleTree<DistanceType,
|
||||
StatisticType,
|
||||
MatType,
|
||||
|
||||
@@ -21,7 +21,7 @@ using namespace mlpack;
|
||||
|
||||
// Test the traits on RectangleTrees.
|
||||
|
||||
TEST_CASE("RectangleTreeTraitsTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("RectangleTreeTest", "[RectangleTreeTest]")
|
||||
{
|
||||
// Children may be overlapping.
|
||||
bool b = TreeTraits<RTree<EuclideanDistance, EmptyStatistic,
|
||||
@@ -37,7 +37,7 @@ TEST_CASE("RectangleTreeTraitsTest", "[RectangleTreeTraitsTest]")
|
||||
// Test to make sure the tree can be contains the correct number of points after
|
||||
// it is constructed.
|
||||
|
||||
TEST_CASE("RectangleTreeConstructionCountTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("RectangleTreeConstructionCountTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset;
|
||||
dataset.randu(3, 1000); // 1000 points in 3 dimensions.
|
||||
@@ -45,7 +45,7 @@ TEST_CASE("RectangleTreeConstructionCountTest", "[RectangleTreeTraitsTest]")
|
||||
using TreeType = RTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
|
||||
TreeType tree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType tree(dataset, 20, 6, 5, 2);
|
||||
TreeType tree2 = tree;
|
||||
|
||||
REQUIRE(tree.NumDescendants() == 1000);
|
||||
@@ -85,7 +85,7 @@ std::vector<arma::vec*> GetAllPointsInTree(const TreeType& tree)
|
||||
// Test to ensure that none of the points in the tree are duplicates. This,
|
||||
// combined with the above test to see how many points are in the tree, should
|
||||
// ensure that we inserted all points.
|
||||
TEST_CASE("RectangleTreeConstructionRepeatTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("RectangleTreeConstructionRepeatTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset;
|
||||
dataset.randu(8, 1000); // 1000 points in 8 dimensions.
|
||||
@@ -93,7 +93,7 @@ TEST_CASE("RectangleTreeConstructionRepeatTest", "[RectangleTreeTraitsTest]")
|
||||
using TreeType = RTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
|
||||
TreeType tree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType tree(dataset, 20, 6, 5, 2);
|
||||
|
||||
std::vector<arma::vec*> allPoints = GetAllPointsInTree(tree);
|
||||
for (size_t i = 0; i < allPoints.size(); ++i)
|
||||
@@ -213,7 +213,7 @@ void CheckHierarchy(const TreeType& tree)
|
||||
|
||||
// Test to see if the bounds of the tree are correct. (Cover all bounds and
|
||||
// points beneath this node of the tree).
|
||||
TEST_CASE("RectangleTreeContainmentTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("RectangleTreeContainmentTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset;
|
||||
dataset.randu(8, 1000); // 1000 points in 8 dimensions.
|
||||
@@ -221,7 +221,7 @@ TEST_CASE("RectangleTreeContainmentTest", "[RectangleTreeTraitsTest]")
|
||||
using TreeType = RTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
|
||||
TreeType tree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType tree(dataset, 20, 6, 5, 2);
|
||||
CheckContainment(tree);
|
||||
CheckExactContainment(tree);
|
||||
}
|
||||
@@ -258,7 +258,7 @@ void CheckFills(const TreeType& tree)
|
||||
}
|
||||
|
||||
// Test to ensure that the minimum and maximum fills are satisfied.
|
||||
TEST_CASE("CheckMinAndMaxFills", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("CheckMinAndMaxFills", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset;
|
||||
dataset.randu(8, 1000); // 1000 points in 8 dimensions.
|
||||
@@ -266,7 +266,7 @@ TEST_CASE("CheckMinAndMaxFills", "[RectangleTreeTraitsTest]")
|
||||
using TreeType = RTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
|
||||
TreeType tree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType tree(dataset, 20, 6, 5, 2);
|
||||
CheckFills(tree);
|
||||
}
|
||||
|
||||
@@ -348,7 +348,7 @@ size_t CheckNumDescendants(const TreeType& tree)
|
||||
|
||||
// A test to ensure that all leaf nodes are stored on the same level of the
|
||||
// tree.
|
||||
TEST_CASE("TreeBalance", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("TreeBalance", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset;
|
||||
dataset.randu(8, 1000); // 1000 points in 8 dimensions.
|
||||
@@ -356,7 +356,7 @@ TEST_CASE("TreeBalance", "[RectangleTreeTraitsTest]")
|
||||
using TreeType = RTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
|
||||
TreeType tree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType tree(dataset, 20, 6, 5, 2);
|
||||
|
||||
REQUIRE(GetMinLevel(tree) == GetMaxLevel(tree));
|
||||
REQUIRE((int) tree.TreeDepth() == GetMinLevel(tree));
|
||||
@@ -366,7 +366,7 @@ TEST_CASE("TreeBalance", "[RectangleTreeTraitsTest]")
|
||||
// delete numIter points and test that the query gives correct results. It is
|
||||
// remotely possible that this test will give a false negative if it should
|
||||
// happen that two points are the same distance from a third point.
|
||||
TEST_CASE("PointDeletion", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("PointDeletion", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset;
|
||||
dataset.randu(8, 1000); // 1000 points in 8 dimensions.
|
||||
@@ -378,7 +378,7 @@ TEST_CASE("PointDeletion", "[RectangleTreeTraitsTest]")
|
||||
|
||||
using TreeType = RTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
TreeType tree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType tree(dataset, 20, 6, 5, 2);
|
||||
|
||||
for (int i = 0; i < numIter; ++i)
|
||||
tree.DeletePoint(999 - i);
|
||||
@@ -443,7 +443,7 @@ TEST_CASE("PointDeletion", "[RectangleTreeTraitsTest]")
|
||||
// negative if it should happen that two points are the same distance from a
|
||||
// third point. Note that this is extremely inefficient. You should not use
|
||||
// dynamic insertion until a better solution for resizing matrices is available.
|
||||
TEST_CASE("PointDynamicAdd", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("PointDynamicAdd", "[RectangleTreeTest]")
|
||||
{
|
||||
const int numIter = 50;
|
||||
arma::mat dataset;
|
||||
@@ -451,7 +451,7 @@ TEST_CASE("PointDynamicAdd", "[RectangleTreeTraitsTest]")
|
||||
|
||||
using TreeType = RTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
TreeType tree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType tree(dataset, 20, 6, 5, 2);
|
||||
|
||||
// Add numIter new points to the dataset. The tree copies the dataset, so we
|
||||
// must modify both the original dataset and the one that the tree holds.
|
||||
@@ -520,7 +520,7 @@ TEST_CASE("PointDynamicAdd", "[RectangleTreeTraitsTest]")
|
||||
|
||||
// A test to ensure that the SingleTreeTraverser is working correctly by
|
||||
// comparing its results to the results of a naive search.
|
||||
TEST_CASE("SingleTreeTraverserTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("SingleTreeTraverserTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset;
|
||||
dataset.randu(8, 1000); // 1000 points in 8 dimensions.
|
||||
@@ -531,7 +531,7 @@ TEST_CASE("SingleTreeTraverserTest", "[RectangleTreeTraitsTest]")
|
||||
|
||||
using TreeType = RStarTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
TreeType rTree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType rTree(dataset, 20, 6, 5, 2);
|
||||
|
||||
REQUIRE(rTree.NumDescendants() == 1000);
|
||||
|
||||
@@ -560,7 +560,7 @@ TEST_CASE("SingleTreeTraverserTest", "[RectangleTreeTraitsTest]")
|
||||
|
||||
// A test to ensure that the SingleTreeTraverser is working correctly by
|
||||
// comparing its results to the results of a naive search.
|
||||
TEST_CASE("XTreeTraverserTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("XTreeTraverserTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset;
|
||||
|
||||
@@ -574,7 +574,7 @@ TEST_CASE("XTreeTraverserTest", "[RectangleTreeTraitsTest]")
|
||||
|
||||
using TreeType = XTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
TreeType xTree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType xTree(dataset, 20, 6, 5, 2);
|
||||
|
||||
REQUIRE(xTree.NumDescendants() == numP);
|
||||
|
||||
@@ -601,7 +601,7 @@ TEST_CASE("XTreeTraverserTest", "[RectangleTreeTraitsTest]")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("HilbertRTreeTraverserTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("HilbertRTreeTraverserTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset;
|
||||
|
||||
@@ -615,7 +615,7 @@ TEST_CASE("HilbertRTreeTraverserTest", "[RectangleTreeTraitsTest]")
|
||||
|
||||
using TreeType = HilbertRTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType hilbertRTree(dataset, 20, 6, 5, 2);
|
||||
|
||||
REQUIRE(hilbertRTree.NumDescendants() == numP);
|
||||
|
||||
@@ -679,14 +679,14 @@ void CheckHilbertOrdering(const TreeType& tree)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("HilbertRTreeOrderingTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("HilbertRTreeOrderingTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset;
|
||||
dataset.randu(8, 1000); // 1000 points in 8 dimensions.
|
||||
|
||||
using TreeType = HilbertRTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType hilbertRTree(dataset, 20, 6, 5, 2);
|
||||
|
||||
CheckHilbertOrdering(hilbertRTree);
|
||||
}
|
||||
@@ -719,19 +719,19 @@ void CheckDiscreteHilbertValueSync(const TreeType& tree)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("DiscreteHilbertValueSyncTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("DiscreteHilbertValueSyncTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset;
|
||||
dataset.randu(8, 1000); // 1000 points in 8 dimensions.
|
||||
|
||||
using TreeType = HilbertRTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType hilbertRTree(dataset, 20, 6, 5, 2);
|
||||
|
||||
CheckDiscreteHilbertValueSync(hilbertRTree);
|
||||
}
|
||||
|
||||
TEST_CASE("DiscreteHilbertValueTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("DiscreteHilbertValueTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::vec point01(1);
|
||||
arma::vec point02(1);
|
||||
@@ -888,7 +888,7 @@ void CheckHilbertValue(const TreeType& tree)
|
||||
CheckHilbertValue(tree.Child(i));
|
||||
}
|
||||
|
||||
TEST_CASE("HilbertRTeeCopyConstructorTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("HilbertRTreeCopyConstructorTest", "[RectangleTreeTest]")
|
||||
{
|
||||
using TreeType = HilbertRTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
@@ -896,7 +896,7 @@ TEST_CASE("HilbertRTeeCopyConstructorTest", "[RectangleTreeTraitsTest]")
|
||||
arma::mat dataset;
|
||||
dataset.randu(8, 1000); // 1000 points in 8 dimensions.
|
||||
|
||||
TreeType tree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType tree(dataset, 20, 6, 5, 2);
|
||||
TreeType copy(tree);
|
||||
|
||||
CheckHilbertValue(copy);
|
||||
@@ -908,7 +908,7 @@ TEST_CASE("HilbertRTeeCopyConstructorTest", "[RectangleTreeTraitsTest]")
|
||||
CheckNumDescendants(copy);
|
||||
}
|
||||
|
||||
TEST_CASE("HilbertRTeeMoveConstructorTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("HilbertRTeeMoveConstructorTest", "[RectangleTreeTest]")
|
||||
{
|
||||
using TreeType = HilbertRTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
@@ -916,7 +916,7 @@ TEST_CASE("HilbertRTeeMoveConstructorTest", "[RectangleTreeTraitsTest]")
|
||||
arma::mat dataset;
|
||||
dataset.randu(8, 1000); // 1000 points in 8 dimensions.
|
||||
|
||||
TreeType tree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType tree(dataset, 20, 6, 5, 2);
|
||||
TreeType copy(std::move(tree));
|
||||
|
||||
CheckHilbertValue(copy);
|
||||
@@ -958,14 +958,14 @@ void CheckOverlap(const TreeType& tree)
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("RPlusTreeOverlapTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("RPlusTreeOverlapTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset;
|
||||
dataset.randu(8, 1000); // 1000 points in 8 dimensions.
|
||||
|
||||
using TreeType = RPlusTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
TreeType rPlusTree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType rPlusTree(dataset, 20, 6, 5, 2);
|
||||
|
||||
CheckOverlap(rPlusTree);
|
||||
|
||||
@@ -979,7 +979,7 @@ TEST_CASE("RPlusTreeOverlapTest", "[RectangleTreeTraitsTest]")
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("RPlusTreeTraverserTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("RPlusTreeTraverserTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset;
|
||||
|
||||
@@ -993,7 +993,7 @@ TEST_CASE("RPlusTreeTraverserTest", "[RectangleTreeTraitsTest]")
|
||||
|
||||
using TreeType = RPlusTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
TreeType rPlusTree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType rPlusTree(dataset, 20, 6, 5, 2);
|
||||
|
||||
REQUIRE(rPlusTree.NumDescendants() == numP);
|
||||
|
||||
@@ -1074,7 +1074,7 @@ void CheckRPlusPlusTreeBound(const TreeType& tree)
|
||||
CheckRPlusPlusTreeBound(tree.Child(i));
|
||||
}
|
||||
|
||||
TEST_CASE("RPlusPlusTreeBoundTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("RPlusPlusTreeBoundTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset;
|
||||
dataset.randu(8, 1000); // 1000 points in 8 dimensions.
|
||||
@@ -1082,7 +1082,7 @@ TEST_CASE("RPlusPlusTreeBoundTest", "[RectangleTreeTraitsTest]")
|
||||
// Check the MinimalCoverageSweep.
|
||||
using TreeType = RPlusPlusTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
TreeType rPlusPlusTree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType rPlusPlusTree(dataset, 20, 6, 5, 2);
|
||||
|
||||
CheckRPlusPlusTreeBound(rPlusPlusTree);
|
||||
|
||||
@@ -1099,7 +1099,7 @@ TEST_CASE("RPlusPlusTreeBoundTest", "[RectangleTreeTraitsTest]")
|
||||
RPlusTreeSplit<RPlusPlusTreeSplitPolicy, MinimalCoverageSweep>,
|
||||
RPlusPlusTreeDescentHeuristic, RPlusPlusTreeAuxiliaryInformation>;
|
||||
|
||||
RPlusPlusTreeMinimalSplits rPlusPlusTree2(dataset, 20, 6, 5, 2, 0);
|
||||
RPlusPlusTreeMinimalSplits rPlusPlusTree2(dataset, 20, 6, 5, 2);
|
||||
|
||||
CheckRPlusPlusTreeBound(rPlusPlusTree2);
|
||||
|
||||
@@ -1107,7 +1107,7 @@ TEST_CASE("RPlusPlusTreeBoundTest", "[RectangleTreeTraitsTest]")
|
||||
REQUIRE((int) rPlusPlusTree2.TreeDepth() == GetMinLevel(rPlusPlusTree2));
|
||||
}
|
||||
|
||||
TEST_CASE("RPlusPlusTreeTraverserTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("RPlusPlusTreeTraverserTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset;
|
||||
|
||||
@@ -1121,7 +1121,7 @@ TEST_CASE("RPlusPlusTreeTraverserTest", "[RectangleTreeTraitsTest]")
|
||||
|
||||
using TreeType = RPlusPlusTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
TreeType rPlusPlusTree(dataset, 20, 6, 5, 2, 0);
|
||||
TreeType rPlusPlusTree(dataset, 20, 6, 5, 2);
|
||||
|
||||
REQUIRE(rPlusPlusTree.NumDescendants() == numP);
|
||||
|
||||
@@ -1153,7 +1153,7 @@ TEST_CASE("RPlusPlusTreeTraverserTest", "[RectangleTreeTraitsTest]")
|
||||
|
||||
// Test the tree splitting. We set MaxLeafSize and MaxNumChildren rather low
|
||||
// to allow us to test by hand without adding hundreds of points.
|
||||
TEST_CASE("RTreeSplitTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("RTreeSplitTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat data = trans(arma::mat("0.0 0.0;"
|
||||
"0.0 1.0;"
|
||||
@@ -1168,7 +1168,7 @@ TEST_CASE("RTreeSplitTest", "[RectangleTreeTraitsTest]")
|
||||
|
||||
using TreeType = RTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
TreeType rTree(data, 5, 2, 2, 1, 0);
|
||||
TreeType rTree(data, 5, 2, 2, 1);
|
||||
|
||||
// There's technically no reason they have to be in a certain order, so we
|
||||
// use firstChild etc. to arbitrarily name them.
|
||||
@@ -1248,7 +1248,7 @@ TEST_CASE("RTreeSplitTest", "[RectangleTreeTraitsTest]")
|
||||
|
||||
// Test the tree splitting. We set MaxLeafSize and MaxNumChildren rather low
|
||||
// to allow us to test by hand without adding hundreds of points.
|
||||
TEST_CASE("RStarTreeSplitTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("RStarTreeSplitTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat data = trans(arma::mat("0.0 0.0;"
|
||||
"0.0 1.0;"
|
||||
@@ -1264,7 +1264,7 @@ TEST_CASE("RStarTreeSplitTest", "[RectangleTreeTraitsTest]")
|
||||
using TreeType = RStarTree<EuclideanDistance,
|
||||
NeighborSearchStat<NearestNeighborSort>, arma::mat>;
|
||||
|
||||
TreeType rTree(data, 5, 2, 2, 1, 0);
|
||||
TreeType rTree(data, 5, 2, 2, 1);
|
||||
|
||||
// There's technically no reason they have to be in a certain order, so we
|
||||
// use firstChild etc. to arbitrarily name them.
|
||||
@@ -1339,7 +1339,7 @@ TEST_CASE("RStarTreeSplitTest", "[RectangleTreeTraitsTest]")
|
||||
Approx(0.9).epsilon(1e-17));
|
||||
}
|
||||
|
||||
TEST_CASE("RectangleTreeMoveDatasetTest", "[RectangleTreeTraitsTest]")
|
||||
TEST_CASE("RectangleTreeMoveDatasetTest", "[RectangleTreeTest]")
|
||||
{
|
||||
arma::mat dataset = arma::randu<arma::mat>(3, 1000);
|
||||
using TreeType = RTree<EuclideanDistance, EmptyStatistic, arma::mat>;
|
||||
@@ -1350,3 +1350,77 @@ TEST_CASE("RectangleTreeMoveDatasetTest", "[RectangleTreeTraitsTest]")
|
||||
REQUIRE(tree.Dataset().n_rows == 3);
|
||||
REQUIRE(tree.Dataset().n_cols == 1000);
|
||||
}
|
||||
|
||||
// Test that points can be added to the tree.
|
||||
TEST_CASE("RectangleTreeInsertVsBatchTest", "[RectangleTreeTest]")
|
||||
{
|
||||
// Make sure that manually inserting points gives the same tree as if we build
|
||||
// the tree all at once.
|
||||
arma::mat dataset = arma::randu<arma::mat>(3, 1000);
|
||||
using TreeType = RTree<EuclideanDistance, EmptyStatistic, arma::mat>;
|
||||
|
||||
TreeType tree1(dataset);
|
||||
TreeType tree2;
|
||||
for (size_t i = 0; i < dataset.n_cols; ++i)
|
||||
tree2.Insert(dataset.col(i));
|
||||
|
||||
REQUIRE(tree1.Dataset().n_cols == tree2.Dataset().n_cols);
|
||||
REQUIRE(tree1.Dataset().n_rows == tree2.Dataset().n_rows);
|
||||
|
||||
// We should have the same structure, too.
|
||||
std::stack<std::pair<TreeType*, TreeType*>> s;
|
||||
s.push(std::make_pair(&tree1, &tree2));
|
||||
while (!s.empty())
|
||||
{
|
||||
std::pair<TreeType*, TreeType*> p = s.top();
|
||||
s.pop();
|
||||
|
||||
TreeType* n1 = p.first;
|
||||
TreeType* n2 = p.second;
|
||||
|
||||
REQUIRE(n1->NumChildren() == n2->NumChildren());
|
||||
REQUIRE(n1->MinNumChildren() == n2->MinNumChildren());
|
||||
REQUIRE(n1->MaxNumChildren() == n2->MaxNumChildren());
|
||||
REQUIRE(n1->NumDescendants() == n2->NumDescendants());
|
||||
REQUIRE(n1->NumPoints() == n2->NumPoints());
|
||||
REQUIRE(n1->MinLeafSize() == n2->MinLeafSize());
|
||||
REQUIRE(n1->MaxLeafSize() == n2->MaxLeafSize());
|
||||
for (size_t p = 0; p < n1->NumPoints(); ++p)
|
||||
REQUIRE(n1->Point(p) == n2->Point(p));
|
||||
|
||||
for (size_t c = 0; c < n1->NumChildren(); ++c)
|
||||
s.push(std::make_pair(&n1->Child(c), &n2->Child(c)));
|
||||
}
|
||||
}
|
||||
|
||||
// Test that points can be removed from the tree.
|
||||
TEST_CASE("RectangleTreeDeleteTest", "[RectangleTreeTest]")
|
||||
{
|
||||
// Build a tree on a random dataset and then remove points from it one-by-one.
|
||||
// At the end, we should have an empty tree.
|
||||
arma::mat dataset = arma::randu<arma::mat>(3, 1000);
|
||||
using TreeType = RTree<EuclideanDistance, EmptyStatistic, arma::mat>;
|
||||
|
||||
TreeType tree(dataset);
|
||||
REQUIRE(tree.NumDescendants() == dataset.n_cols);
|
||||
|
||||
arma::Col<size_t> removeOrder = arma::shuffle(
|
||||
arma::linspace<arma::Col<size_t>>(0, dataset.n_cols - 1, dataset.n_cols));
|
||||
|
||||
for (size_t i = 0; i < removeOrder.n_elem; ++i)
|
||||
{
|
||||
// We will have to adjust the index of our point, since we have removed
|
||||
// other points first.
|
||||
const size_t adjust = (i == 0) ? 0 :
|
||||
arma::accu(removeOrder.subvec(0, i - 1) < removeOrder[i]);
|
||||
|
||||
REQUIRE((removeOrder[i] - adjust) < tree.Dataset().n_cols);
|
||||
tree.Delete(removeOrder[i] - adjust);
|
||||
REQUIRE(tree.NumDescendants() == (dataset.n_cols - i - 1));
|
||||
REQUIRE(tree.Dataset().n_cols == (dataset.n_cols - i - 1));
|
||||
}
|
||||
|
||||
// Now the tree should be empty.
|
||||
REQUIRE(tree.NumDescendants() == 0);
|
||||
REQUIRE(tree.NumPoints() == 0);
|
||||
}
|
||||
|
||||
@@ -441,7 +441,7 @@ TEST_CASE("CoverTreeOverwriteTest", "[SerializationTest]")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("RectangleTreeTest", "[SerializationTest]")
|
||||
TEST_CASE("RectangleTreeSerializationTest", "[SerializationTest]")
|
||||
{
|
||||
arma::mat data;
|
||||
data.randu(3, 1000);
|
||||
|
||||
Reference in New Issue
Block a user