Merge branch 'master' of https://github.com/mlpack/mlpack
This commit is contained in:
+17
-8
@@ -1,8 +1,16 @@
|
||||
clone_depth: 30
|
||||
clone_depth: 10
|
||||
|
||||
environment:
|
||||
VisualStudioVersion: 14.0
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
VSVER: Visual Studio 14 2015 Win64
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
VSVER: Visual Studio 15 2017 Win64
|
||||
|
||||
configuration: Release
|
||||
|
||||
os: Visual Studio 2015
|
||||
|
||||
install:
|
||||
- ps: nuget install boost -o "${env:APPVEYOR_BUILD_FOLDER}" -Version 1.60.0
|
||||
- ps: nuget install boost_unit_test_framework-vc140 -o "${env:APPVEYOR_BUILD_FOLDER}" -Version 1.60.0
|
||||
@@ -11,6 +19,7 @@ install:
|
||||
- ps: nuget install boost_serialization-vc140 -o "${env:APPVEYOR_BUILD_FOLDER}" -Version 1.60.0
|
||||
- ps: nuget install boost_math_c99-vc140 -o "${env:APPVEYOR_BUILD_FOLDER}" -Version 1.60.0
|
||||
- ps: nuget install OpenBLAS -o "${env:APPVEYOR_BUILD_FOLDER}"
|
||||
|
||||
build_script:
|
||||
- mkdir boost_libs
|
||||
- ps: cp C:\projects\mlpack\boost_program_options-vc140.1.60.0.0\lib\native\address-model-64\lib\*.* C:\projects\mlpack\boost_libs\
|
||||
@@ -18,13 +27,13 @@ build_script:
|
||||
- ps: cp C:\projects\mlpack\boost_random-vc140.1.60.0.0\lib\native\address-model-64\lib\*.* C:\projects\mlpack\boost_libs\
|
||||
- ps: cp C:\projects\mlpack\boost_serialization-vc140.1.60.0.0\lib\native\address-model-64\lib\*.* C:\projects\mlpack\boost_libs\
|
||||
- ps: cp C:\projects\mlpack\boost_unit_test_framework-vc140.1.60.0.0\lib\native\address-model-64\lib\*.* C:\projects\mlpack\boost_libs\
|
||||
- if not exist armadillo.tar.gz appveyor DownloadFile "http://sourceforge.net/projects/arma/files/armadillo-6.500.5.tar.gz" -FileName armadillo.tar.gz
|
||||
- 7z x armadillo.tar.gz -so | 7z x -si -ttar > nul
|
||||
- cd armadillo-6.500.5 && mkdir build && cd build
|
||||
- if not exist armadillo.tar.xz appveyor DownloadFile "http://sourceforge.net/projects/arma/files/armadillo-7.800.2.tar.xz" -FileName armadillo.tar.xz
|
||||
- 7z x armadillo.tar.xz -so | 7z x -si -ttar > nul
|
||||
- cd armadillo-7.800.2 && mkdir build && cd build
|
||||
- cmake -G "Visual Studio 14 2015 Win64" -DBLAS_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DLAPACK_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DCMAKE_PREFIX:FILEPATH="%APPVEYOR_BUILD_FOLDER%/armadillo" -DBUILD_SHARED_LIBS=OFF ..
|
||||
- '"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" "C:\projects\mlpack\armadillo-6.500.5\build\armadillo.sln" /m /verbosity:quiet /p:Configuration=Release;Platform=x64'
|
||||
- '"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" "C:\projects\mlpack\armadillo-7.800.2\build\armadillo.sln" /m /verbosity:quiet /p:Configuration=Release;Platform=x64'
|
||||
- cd C:\projects\mlpack && mkdir build && cd build
|
||||
- cmake -G "Visual Studio 14 2015 Win64" -DBLAS_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DLAPACK_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DARMADILLO_INCLUDE_DIR="C:/projects/mlpack/armadillo-6.500.5/include" -DARMADILLO_LIBRARY:FILEPATH="C:\projects\mlpack\armadillo-6.500.5\build\Debug\armadillo.lib" -DBOOST_INCLUDEDIR:PATH="C:\projects\mlpack\boost.1.60.0.0\lib\native\include" -DBOOST_LIBRARYDIR:PATH="C:\projects\mlpack\boost_libs" -DDEBUG=ON -DPROFILE=ON ..
|
||||
- cmake -G "Visual Studio 14 2015 Win64" -DBLAS_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DLAPACK_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DARMADILLO_INCLUDE_DIR="C:/projects/mlpack/armadillo-7.800.2/include" -DARMADILLO_LIBRARY:FILEPATH="C:\projects\mlpack\armadillo-7.800.2\build\Debug\armadillo.lib" -DBOOST_INCLUDEDIR:PATH="C:\projects\mlpack\boost.1.60.0.0\lib\native\include" -DBOOST_LIBRARYDIR:PATH="C:\projects\mlpack\boost_libs" -DDEBUG=ON -DPROFILE=ON ..
|
||||
- '"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" "C:\projects\mlpack\build\mlpack.sln" /m /verbosity:minimal /nologo /p:BuildInParallel=true /p:Configuration=Release;Platform=x64'
|
||||
- 7z a mlpack-windows-no-libs.zip "%APPVEYOR_BUILD_FOLDER%\build\Release\*.exe"
|
||||
- 7z a mlpack-windows.zip "%APPVEYOR_BUILD_FOLDER%\build\Release\*.*" "%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/*.*"
|
||||
@@ -42,7 +51,7 @@ notifications:
|
||||
|
||||
cache:
|
||||
- packages -> **\packages.config
|
||||
- armadillo.tar.gz -> appveyor.yaml
|
||||
- armadillo.tar.xz -> appveyor.yaml
|
||||
|
||||
# All plans have maximum build job execution time of 60 minutes. But right, now
|
||||
# the machine takes 30 minutes to build the code and at least 50 minutes to run
|
||||
|
||||
@@ -41,6 +41,19 @@ class RStarTreeSplit
|
||||
template <typename TreeType>
|
||||
static bool SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels);
|
||||
|
||||
/**
|
||||
* Reinsert any points into the tree, if needed. This returns the number of
|
||||
* points reinserted.
|
||||
*/
|
||||
template<typename TreeType>
|
||||
static size_t ReinsertPoints(TreeType* tree, std::vector<bool>& relevels);
|
||||
|
||||
/**
|
||||
* Given a node, return the best dimension and the best index to split on.
|
||||
*/
|
||||
template<typename TreeType>
|
||||
static void PickLeafSplit(TreeType* tree, size_t& bestAxis, size_t& bestIndex);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Insert a node into another node.
|
||||
@@ -52,9 +65,9 @@ class RStarTreeSplit
|
||||
* Comparator for sorting with std::pair. This comparator works a little bit
|
||||
* faster then the default comparator.
|
||||
*/
|
||||
template<typename ElemType>
|
||||
static bool PairComp(const std::pair<ElemType, size_t>& p1,
|
||||
const std::pair<ElemType, size_t>& p2)
|
||||
template<typename ElemType, typename TreeType>
|
||||
static bool PairComp(const std::pair<ElemType, TreeType>& p1,
|
||||
const std::pair<ElemType, TreeType>& p2)
|
||||
{
|
||||
return p1.first < p2.first;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,153 @@
|
||||
namespace mlpack {
|
||||
namespace tree {
|
||||
|
||||
/**
|
||||
* Reinsert any points into the tree, if needed. This returns the number of
|
||||
* points reinserted.
|
||||
*/
|
||||
template<typename TreeType>
|
||||
size_t RStarTreeSplit::ReinsertPoints(TreeType* tree,
|
||||
std::vector<bool>& relevels)
|
||||
{
|
||||
// Convenience typedef.
|
||||
typedef typename TreeType::ElemType ElemType;
|
||||
|
||||
// Check if we need to reinsert.
|
||||
if (relevels[tree->TreeDepth() - 1])
|
||||
{
|
||||
relevels[tree->TreeDepth() - 1] = false;
|
||||
|
||||
// We sort the points by decreasing distance to the centroid of the bound.
|
||||
// We then remove the first p entries and reinsert them at the root.
|
||||
TreeType* root = tree;
|
||||
while (root->Parent() != NULL)
|
||||
root = root->Parent();
|
||||
size_t p = tree->MaxLeafSize() * 0.3; // The paper says this works the best.
|
||||
if (p > 0)
|
||||
{
|
||||
// We'll only do reinsertions if p > 0. p is the number of points we will
|
||||
// reinsert. If p == 0, then no reinsertion is necessary and we continue
|
||||
// with the splitting procedure.
|
||||
std::vector<std::pair<ElemType, size_t>> sorted(tree->Count());
|
||||
arma::Col<ElemType> center;
|
||||
tree->Bound().Center(center);
|
||||
for (size_t i = 0; i < sorted.size(); i++)
|
||||
{
|
||||
sorted[i].first = tree->Metric().Evaluate(center,
|
||||
tree->Dataset().col(tree->Point(i)));
|
||||
sorted[i].second = tree->Point(i);
|
||||
}
|
||||
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType, size_t>);
|
||||
|
||||
// Remove the points furthest from the center of the node.
|
||||
for (size_t i = 0; i < p; i++)
|
||||
root->DeletePoint(sorted[sorted.size() - 1 - i].second, relevels);
|
||||
|
||||
// Now reinsert the points, but reverse the order---insert the closest to
|
||||
// the center first.
|
||||
for (size_t i = p; i > 0; --i)
|
||||
root->InsertPoint(sorted[sorted.size() - i].second, relevels);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a node, return the best dimension and the best index to split on.
|
||||
*/
|
||||
template<typename TreeType>
|
||||
void RStarTreeSplit::PickLeafSplit(TreeType* tree,
|
||||
size_t& bestAxis,
|
||||
size_t& bestIndex)
|
||||
{
|
||||
// Convenience typedef.
|
||||
typedef typename TreeType::ElemType ElemType;
|
||||
typedef bound::HRectBound<metric::EuclideanDistance, ElemType> BoundType;
|
||||
|
||||
bestAxis = 0;
|
||||
bestIndex = 0;
|
||||
ElemType bestScore = std::numeric_limits<ElemType>::max();
|
||||
|
||||
/**
|
||||
* Check each dimension, to find which dimension is best to split on.
|
||||
*/
|
||||
for (size_t j = 0; j < tree->Bound().Dim(); j++)
|
||||
{
|
||||
ElemType axisScore = 0.0;
|
||||
|
||||
// Sort in increasing values of the selected dimension j.
|
||||
arma::Col<ElemType> dimValues(tree->Count());
|
||||
for (size_t i = 0; i < tree->Count(); ++i)
|
||||
dimValues[i] = tree->Dataset().col(tree->Point(i))[j];
|
||||
arma::uvec sortedIndices = arma::sort_index(dimValues);
|
||||
|
||||
// We'll store each of the three scores for each distribution.
|
||||
const size_t numPossibleSplits = tree->MaxLeafSize() -
|
||||
2 * tree->MinLeafSize() + 2;
|
||||
arma::Col<ElemType> areas(numPossibleSplits, arma::fill::zeros);
|
||||
arma::Col<ElemType> margins(numPossibleSplits, arma::fill::zeros);
|
||||
arma::Col<ElemType> overlaps(numPossibleSplits, arma::fill::zeros);
|
||||
|
||||
for (size_t i = 0; i < numPossibleSplits; i++)
|
||||
{
|
||||
// The ith arrangement is obtained by placing the first
|
||||
// tree->MinLeafSize() + i points in one rectangle and the rest in
|
||||
// another. Then we calculate the three scores for that distribution.
|
||||
size_t splitIndex = tree->MinLeafSize() + i;
|
||||
|
||||
BoundType bound1(tree->Bound().Dim());
|
||||
BoundType bound2(tree->Bound().Dim());
|
||||
|
||||
for (size_t l = 0; l < splitIndex; l++)
|
||||
bound1 |= tree->Dataset().col(tree->Point(sortedIndices[l]));
|
||||
|
||||
for (size_t l = splitIndex; l < tree->Count(); l++)
|
||||
bound2 |= tree->Dataset().col(tree->Point(sortedIndices[l]));
|
||||
|
||||
areas[i] = bound1.Volume() + bound2.Volume();
|
||||
overlaps[i] = bound1.Overlap(bound2);
|
||||
|
||||
for (size_t k = 0; k < bound1.Dim(); k++)
|
||||
margins[i] += bound1[k].Width() + bound2[k].Width();
|
||||
|
||||
axisScore += margins[i];
|
||||
}
|
||||
|
||||
// Is this dimension a new best score? We want the lowest possible score.
|
||||
if (axisScore < bestScore)
|
||||
{
|
||||
bestScore = axisScore;
|
||||
bestAxis = j;
|
||||
size_t overlapIndex = 0;
|
||||
size_t areaIndex = 0;
|
||||
bool tiedOnOverlap = false;
|
||||
|
||||
for (size_t i = 1; i < areas.n_elem; i++)
|
||||
{
|
||||
if (overlaps[i] < overlaps[overlapIndex])
|
||||
{
|
||||
tiedOnOverlap = false;
|
||||
overlapIndex = i;
|
||||
areaIndex = i;
|
||||
}
|
||||
else if (overlaps[i] == overlaps[overlapIndex])
|
||||
{
|
||||
tiedOnOverlap = true;
|
||||
if (areas[i] < areas[areaIndex])
|
||||
areaIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Select the best index for splitting.
|
||||
bestIndex = (tiedOnOverlap ? areaIndex : overlapIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We call GetPointSeeds to get the two points which will be the initial points
|
||||
* in the new nodes We then call AssignPointDestNode to assign the remaining
|
||||
@@ -31,220 +178,86 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector<bool>& relevels)
|
||||
{
|
||||
// Convenience typedef.
|
||||
typedef typename TreeType::ElemType ElemType;
|
||||
typedef bound::HRectBound<metric::EuclideanDistance, ElemType> BoundType;
|
||||
|
||||
// If there's no need to split, don't.
|
||||
if (tree->Count() <= tree->MaxLeafSize())
|
||||
return;
|
||||
|
||||
// If we are splitting the root node, we need will do things differently so
|
||||
// that the constructor and other methods don't confuse the end user by giving
|
||||
// an address of another node.
|
||||
if (tree->Parent() == NULL)
|
||||
{
|
||||
// We actually want to copy this way. Pointers and everything.
|
||||
TreeType* copy = new TreeType(*tree, false);
|
||||
copy->Parent() = tree;
|
||||
tree->Count() = 0;
|
||||
tree->NullifyData();
|
||||
// Because this was a leaf node, numChildren must be 0.
|
||||
tree->children[(tree->NumChildren())++] = copy;
|
||||
assert(tree->NumChildren() == 1);
|
||||
|
||||
RStarTreeSplit::SplitLeafNode(copy,relevels);
|
||||
// If we haven't yet checked if we need to reinsert on this level, we try
|
||||
// doing so now.
|
||||
if (ReinsertPoints(tree, relevels) > 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// If we haven't yet reinserted on this level, we try doing so now.
|
||||
if (relevels[tree->TreeDepth()])
|
||||
{
|
||||
relevels[tree->TreeDepth()] = false;
|
||||
|
||||
// We sort the points by decreasing distance to the centroid of the bound.
|
||||
// We then remove the first p entries and reinsert them at the root.
|
||||
TreeType* root = tree;
|
||||
while (root->Parent() != NULL)
|
||||
root = root->Parent();
|
||||
size_t p = tree->MaxLeafSize() * 0.3; // The paper says this works the best.
|
||||
if (p == 0)
|
||||
{
|
||||
RStarTreeSplit::SplitLeafNode(tree,relevels);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::pair<ElemType, size_t>> sorted(tree->Count());
|
||||
arma::Col<ElemType> center;
|
||||
tree->Bound().Center(center); // Modifies centroid.
|
||||
for (size_t i = 0; i < sorted.size(); i++)
|
||||
{
|
||||
sorted[i].first = tree->Metric().Evaluate(center,
|
||||
tree->Dataset().col(tree->Point(i)));
|
||||
sorted[i].second = i;
|
||||
}
|
||||
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType>);
|
||||
std::vector<size_t> pointIndices(p);
|
||||
|
||||
for (size_t i = 0; i < p; i++)
|
||||
{
|
||||
// We start from the end of sorted.
|
||||
pointIndices[i] = tree->Point(sorted[sorted.size() - 1 - i].second);
|
||||
|
||||
root->DeletePoint(tree->Point(sorted[sorted.size() - 1 - i].second),
|
||||
relevels);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < p; i++)
|
||||
{
|
||||
// We reverse the order again to reinsert the closest points first.
|
||||
root->InsertPoint(pointIndices[p - 1 - i], relevels);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int bestOverlapIndexOnBestAxis = 0;
|
||||
int bestAreaIndexOnBestAxis = 0;
|
||||
bool tiedOnOverlap = false;
|
||||
int bestAxis = 0;
|
||||
ElemType bestAxisScore = std::numeric_limits<ElemType>::max();
|
||||
|
||||
for (size_t j = 0; j < tree->Bound().Dim(); j++)
|
||||
{
|
||||
ElemType axisScore = 0.0;
|
||||
// Since we only have points in the leaf nodes, we only need to sort once.
|
||||
std::vector<std::pair<ElemType, size_t>> sorted(tree->Count());
|
||||
for (size_t i = 0; i < sorted.size(); i++)
|
||||
{
|
||||
sorted[i].first = tree->Dataset().col(tree->Point(i))[j];
|
||||
sorted[i].second = i;
|
||||
}
|
||||
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType>);
|
||||
|
||||
// We'll store each of the three scores for each distribution.
|
||||
std::vector<ElemType> areas(tree->MaxLeafSize() - 2 * tree->MinLeafSize() +
|
||||
2);
|
||||
std::vector<ElemType> margins(tree->MaxLeafSize() - 2 * tree->MinLeafSize() +
|
||||
2);
|
||||
std::vector<ElemType> overlapedAreas(tree->MaxLeafSize() -
|
||||
2 * tree->MinLeafSize() + 2);
|
||||
|
||||
for (size_t i = 0; i < areas.size(); i++)
|
||||
{
|
||||
areas[i] = 0.0;
|
||||
margins[i] = 0.0;
|
||||
overlapedAreas[i] = 0.0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < areas.size(); i++)
|
||||
{
|
||||
// The ith arrangement is obtained by placing the first
|
||||
// tree->MinLeafSize() + i points in one rectangle and the rest in
|
||||
// another. Then we calculate the three scores for that distribution.
|
||||
|
||||
size_t cutOff = tree->MinLeafSize() + i;
|
||||
|
||||
BoundType bound1(tree->Bound().Dim());
|
||||
BoundType bound2(tree->Bound().Dim());
|
||||
|
||||
for (size_t l = 0; l < cutOff; l++)
|
||||
bound1 |= tree->Dataset().col(tree->Point(sorted[l].second));
|
||||
|
||||
for (size_t l = cutOff; l < tree->Count(); l++)
|
||||
bound2 |= tree->Dataset().col(tree->Point(sorted[l].second));
|
||||
|
||||
ElemType area1 = bound1.Volume();
|
||||
ElemType area2 = bound2.Volume();
|
||||
ElemType oArea = bound1.Overlap(bound2);
|
||||
|
||||
for (size_t k = 0; k < bound1.Dim(); k++)
|
||||
margins[i] += bound1[k].Width() + bound2[k].Width();
|
||||
|
||||
areas[i] += area1 + area2;
|
||||
overlapedAreas[i] += oArea;
|
||||
axisScore += margins[i];
|
||||
}
|
||||
|
||||
if (axisScore < bestAxisScore)
|
||||
{
|
||||
bestAxisScore = axisScore;
|
||||
bestAxis = j;
|
||||
bestOverlapIndexOnBestAxis = 0;
|
||||
bestAreaIndexOnBestAxis = 0;
|
||||
|
||||
for (size_t i = 1; i < areas.size(); i++)
|
||||
{
|
||||
if (overlapedAreas[i] < overlapedAreas[bestOverlapIndexOnBestAxis])
|
||||
{
|
||||
tiedOnOverlap = false;
|
||||
bestAreaIndexOnBestAxis = i;
|
||||
bestOverlapIndexOnBestAxis = i;
|
||||
}
|
||||
else if (overlapedAreas[i] ==
|
||||
overlapedAreas[bestOverlapIndexOnBestAxis])
|
||||
{
|
||||
tiedOnOverlap = true;
|
||||
if (areas[i] < areas[bestAreaIndexOnBestAxis])
|
||||
bestAreaIndexOnBestAxis = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// We don't need to reinsert. Instead, we need to split the node.
|
||||
size_t bestAxis;
|
||||
size_t bestIndex;
|
||||
PickLeafSplit(tree, bestAxis, bestIndex);
|
||||
|
||||
/**
|
||||
* Now that we have found the best dimension to split on, re-sort in that
|
||||
* dimension to prepare for reinsertion of points into the new nodes.
|
||||
*/
|
||||
std::vector<std::pair<ElemType, size_t>> sorted(tree->Count());
|
||||
for (size_t i = 0; i < sorted.size(); i++)
|
||||
{
|
||||
sorted[i].first = tree->Dataset().col(tree->Point(i))[bestAxis];
|
||||
sorted[i].second = i;
|
||||
sorted[i].second = tree->Point(i);
|
||||
}
|
||||
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType>);
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType, size_t>);
|
||||
|
||||
TreeType* treeOne = new TreeType(tree->Parent());
|
||||
TreeType* treeTwo = new TreeType(tree->Parent());
|
||||
/**
|
||||
* If 'tree' is the root of the tree (i.e. if it has no parent), then we must
|
||||
* create two new child nodes, distribute the points from the original node
|
||||
* among them, and insert those. If 'tree' is not the root of the tree, then
|
||||
* we may create only one new child node, redistribute the points from the
|
||||
* original node between 'tree' and the new node, then insert those nodes into
|
||||
* the parent.
|
||||
*
|
||||
* Here we simply set treeOne and treeTwo to the right values to avoid code
|
||||
* duplication.
|
||||
*/
|
||||
TreeType* par = tree->Parent();
|
||||
TreeType* treeOne = (par) ? tree : new TreeType(tree);
|
||||
TreeType* treeTwo = (par) ? new TreeType(par) : new TreeType(tree);
|
||||
|
||||
if (tiedOnOverlap)
|
||||
// Now clean the node, and we will re-use this.
|
||||
const size_t numPoints = tree->Count();
|
||||
|
||||
// Reset the original node's values, regardless of whether it will become
|
||||
// the new parent or not.
|
||||
tree->numChildren = 0;
|
||||
tree->numDescendants = 0;
|
||||
tree->count = 0;
|
||||
tree->bound.Clear();
|
||||
|
||||
// Insert the points into the appropriate tree.
|
||||
for (size_t i = 0; i < numPoints; i++)
|
||||
{
|
||||
for (size_t i = 0; i < tree->Count(); i++)
|
||||
{
|
||||
if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize())
|
||||
treeOne->InsertPoint(tree->Point(sorted[i].second));
|
||||
else
|
||||
treeTwo->InsertPoint(tree->Point(sorted[i].second));
|
||||
}
|
||||
if (i < bestIndex + tree->MinLeafSize())
|
||||
treeOne->InsertPoint(sorted[i].second);
|
||||
else
|
||||
treeTwo->InsertPoint(sorted[i].second);
|
||||
}
|
||||
|
||||
// Insert the new tree node(s).
|
||||
if (par)
|
||||
{
|
||||
// Just insert the new node into the parent.
|
||||
par->children[par->NumChildren()++] = treeTwo;
|
||||
|
||||
// If we have overflowed the parent's children, then we need to split that
|
||||
// node also.
|
||||
if (par->NumChildren() == par->MaxNumChildren() + 1)
|
||||
RStarTreeSplit::SplitNonLeafNode(par, relevels);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < tree->Count(); i++)
|
||||
{
|
||||
if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize())
|
||||
treeOne->InsertPoint(tree->Point(sorted[i].second));
|
||||
else
|
||||
treeTwo->InsertPoint(tree->Point(sorted[i].second));
|
||||
}
|
||||
// Now insert the two nodes into 'tree', which is now a higher-level root
|
||||
// node in the tree.
|
||||
InsertNodeIntoTree(tree, treeOne);
|
||||
InsertNodeIntoTree(tree, treeTwo);
|
||||
}
|
||||
|
||||
// Remove this node and insert treeOne and treeTwo.
|
||||
TreeType* par = tree->Parent();
|
||||
size_t index = 0;
|
||||
while (par->children[index] != tree) { index++; }
|
||||
|
||||
assert(index != par->NumChildren());
|
||||
par->children[index] = treeOne;
|
||||
par->children[par->NumChildren()++] = treeTwo;
|
||||
|
||||
// We only add one at a time, so we should only need to test for equality
|
||||
// just in case, we use an assert.
|
||||
assert(par->NumChildren() <= par->MaxNumChildren() + 1);
|
||||
if (par->NumChildren() == par->MaxNumChildren() + 1)
|
||||
RStarTreeSplit::SplitNonLeafNode(par,relevels);
|
||||
|
||||
assert(treeOne->Parent()->NumChildren() <= treeOne->MaxNumChildren());
|
||||
assert(treeOne->Parent()->NumChildren() >= treeOne->MinNumChildren());
|
||||
assert(treeTwo->Parent()->NumChildren() <= treeTwo->MaxNumChildren());
|
||||
assert(treeTwo->Parent()->NumChildren() >= treeTwo->MinNumChildren());
|
||||
|
||||
tree->SoftDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,334 +274,200 @@ bool RStarTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels
|
||||
typedef typename TreeType::ElemType ElemType;
|
||||
typedef bound::HRectBound<metric::EuclideanDistance, ElemType> BoundType;
|
||||
|
||||
// If we are splitting the root node, we need will do things differently so
|
||||
// that the constructor and other methods don't confuse the end user by giving
|
||||
// an address of another node.
|
||||
if (tree->Parent() == NULL)
|
||||
{
|
||||
// We actually want to copy this way. Pointers and everything.
|
||||
TreeType* copy = new TreeType(*tree, false);
|
||||
// Reinsertion isn't done for non-leaf nodes; the paper doesn't seem to make
|
||||
// it clear how to reinsert an entire node without reinserting each of the
|
||||
// points, so we will avoid that here. This is a possible area for
|
||||
// improvement of this code.
|
||||
|
||||
copy->Parent() = tree;
|
||||
tree->NumChildren() = 0;
|
||||
tree->NullifyData();
|
||||
tree->children[(tree->NumChildren())++] = copy;
|
||||
size_t bestAxis = 0;
|
||||
size_t bestIndex = 0;
|
||||
ElemType bestScore = std::numeric_limits<ElemType>::max();
|
||||
bool lowIsBetter = true;
|
||||
|
||||
RStarTreeSplit::SplitNonLeafNode(copy,relevels);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
// If we haven't yet reinserted on this level, we try doing so now.
|
||||
if (relevels[tree->TreeDepth()]) {
|
||||
relevels[tree->TreeDepth()] = false;
|
||||
// We sort the points by decreasing centroid to centroid distance.
|
||||
// We then remove the first p entries and reinsert them at the root.
|
||||
RectangleTree<RStarTreeSplit, DescentType, StatisticType, MatType>* root = tree;
|
||||
while(root->Parent() != NULL)
|
||||
root = root->Parent();
|
||||
size_t p = tree->MaxNumChildren() * 0.3; // The paper says this works the best.
|
||||
if (p == 0) {
|
||||
SplitNonLeafNode(tree, relevels);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<sortStruct> sorted(tree->NumChildren());
|
||||
arma::vec c1;
|
||||
tree->Bound().Center(c1); // Modifies c1.
|
||||
for(size_t i = 0; i < sorted.size(); i++) {
|
||||
arma::vec c2;
|
||||
tree->Children()[i]->Bound().Center(c2); // Modifies c2.
|
||||
sorted[i].d = tree->Bound().Metric().Evaluate(c1,c2);
|
||||
sorted[i].n = i;
|
||||
}
|
||||
std::sort(sorted.begin(), sorted.end(), structComp);
|
||||
|
||||
//bool startBelowMinFill = tree->NumChildren() < tree->MinNumChildren();
|
||||
|
||||
std::vector<RectangleTree<RStarTreeSplit, DescentType, StatisticType, MatType>*> removedNodes(p);
|
||||
for(size_t i =0; i < p; i++) {
|
||||
removedNodes[i] = tree->Children()[sorted[sorted.size()-1-i].n]; // We start from the end of sorted.
|
||||
root->RemoveNode(tree->Children()[sorted[sorted.size()-1-i].n], relevels);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < p; i++) {
|
||||
root->InsertNode(removedNodes[p-1-i], tree->TreeDepth(), relevels); // We reverse the order again to reinsert the closest nodes first.
|
||||
}
|
||||
|
||||
// If we went below min fill, delete this node and reinsert all children.
|
||||
//SOMETHING IS WRONG. SHOULD NOT GO BELOW MIN FILL.
|
||||
// if (!startBelowMinFill && tree->NumChildren() < tree->MinNumChildren())
|
||||
// std::cout<<"MINFILLERROR "<< p << ", " << tree->NumChildren() << "; " << tree->MaxNumChildren()<<std::endl;
|
||||
|
||||
// if (tree->NumChildren() < tree->MinNumChildren()) {
|
||||
// std::vector<RectangleTree<RStarTreeSplit, DescentType, StatisticType, MatType>*> rmNodes(tree->NumChildren());
|
||||
// for(size_t i = 0; i < rmNodes.size(); i++) {
|
||||
// rmNodes[i] = tree->Children()[i];
|
||||
// }
|
||||
// root->RemoveNode(tree, relevels);
|
||||
// for(size_t i = 0; i < rmNodes.size(); i++) {
|
||||
// root->InsertNode(rmNodes[i], tree->TreeDepth(), relevels);
|
||||
// }
|
||||
// // tree->SoftDelete();
|
||||
// }
|
||||
// assert(tree->NumChildren() >= tree->MinNumChildren());
|
||||
// assert(tree->NumChildren() <= tree->MaxNumChildren());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
int bestOverlapIndexOnBestAxis = 0;
|
||||
int bestAreaIndexOnBestAxis = 0;
|
||||
bool tiedOnOverlap = false;
|
||||
bool lowIsBest = true;
|
||||
int bestAxis = 0;
|
||||
ElemType bestAxisScore = std::numeric_limits<ElemType>::max();
|
||||
/**
|
||||
* Check over each dimension to see which is best to use for splitting.
|
||||
*/
|
||||
for (size_t j = 0; j < tree->Bound().Dim(); j++)
|
||||
{
|
||||
ElemType axisScore = 0.0;
|
||||
ElemType axisLoScore = 0.0;
|
||||
ElemType axisHiScore = 0.0;
|
||||
|
||||
// We'll do Bound().Lo() now and use Bound().Hi() later.
|
||||
std::vector<std::pair<ElemType, size_t>> sorted(tree->NumChildren());
|
||||
for (size_t i = 0; i < sorted.size(); i++)
|
||||
// We have to calculate values for both the lower and higher parts of the
|
||||
// bound.
|
||||
arma::Col<ElemType> loDimValues(tree->NumChildren());
|
||||
arma::Col<ElemType> hiDimValues(tree->NumChildren());
|
||||
for (size_t i = 0; i < tree->NumChildren(); i++)
|
||||
{
|
||||
sorted[i].first = tree->Child(i).Bound()[j].Lo();
|
||||
sorted[i].second = i;
|
||||
loDimValues[i] = tree->Child(i).Bound()[j].Lo();
|
||||
hiDimValues[i] = tree->Child(i).Bound()[j].Hi();
|
||||
}
|
||||
arma::uvec sortedLoDimIndices = arma::sort_index(loDimValues);
|
||||
arma::uvec sortedHiDimIndices = arma::sort_index(hiDimValues);
|
||||
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType>);
|
||||
// We'll store each of the three scores for each distribution. Remember
|
||||
// that these are the sums calculated over both the low and high bounds of
|
||||
// each rectangle.
|
||||
const size_t numPossibleSplits = tree->MaxNumChildren() -
|
||||
2 * tree->MinNumChildren() + 2;
|
||||
arma::Col<ElemType> areas(2 * numPossibleSplits, arma::fill::zeros);
|
||||
arma::Col<ElemType> margins(2 * numPossibleSplits, arma::fill::zeros);
|
||||
arma::Col<ElemType> overlaps(2 * numPossibleSplits, arma::fill::zeros);
|
||||
|
||||
// We'll store each of the three scores for each distribution.
|
||||
std::vector<ElemType> areas(tree->MaxNumChildren() -
|
||||
2 * tree->MinNumChildren() + 2);
|
||||
std::vector<ElemType> margins(tree->MaxNumChildren() -
|
||||
2 * tree->MinNumChildren() + 2);
|
||||
std::vector<ElemType> overlapedAreas(tree->MaxNumChildren() -
|
||||
2 * tree->MinNumChildren() + 2);
|
||||
|
||||
for (size_t i = 0; i < areas.size(); i++)
|
||||
{
|
||||
areas[i] = 0.0;
|
||||
margins[i] = 0.0;
|
||||
overlapedAreas[i] = 0.0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < areas.size(); i++)
|
||||
for (size_t i = 0; i < numPossibleSplits; ++i)
|
||||
{
|
||||
// The ith arrangement is obtained by placing the first
|
||||
// tree->MinNumChildren() + i points in one rectangle and the rest in
|
||||
// another. Then we calculate the three scores for that distribution.
|
||||
const size_t splitIndex = tree->MinNumChildren() + i;
|
||||
|
||||
size_t cutOff = tree->MinNumChildren() + i;
|
||||
BoundType lb1(tree->Bound().Dim());
|
||||
BoundType lb2(tree->Bound().Dim());
|
||||
BoundType hb1(tree->Bound().Dim());
|
||||
BoundType hb2(tree->Bound().Dim());
|
||||
|
||||
BoundType bound1(tree->Bound().Dim());
|
||||
BoundType bound2(tree->Bound().Dim());
|
||||
for (size_t l = 0; l < splitIndex; ++l)
|
||||
{
|
||||
lb1 |= tree->Child(sortedLoDimIndices[l]).Bound();
|
||||
hb1 |= tree->Child(sortedHiDimIndices[l]).Bound();
|
||||
}
|
||||
for (size_t l = splitIndex; l < tree->NumChildren(); ++l)
|
||||
{
|
||||
lb2 |= tree->Child(sortedLoDimIndices[l]).Bound();
|
||||
hb2 |= tree->Child(sortedHiDimIndices[l]).Bound();
|
||||
}
|
||||
|
||||
for (size_t l = 0; l < cutOff; l++)
|
||||
bound1 |= tree->Child(sorted[l].second).Bound();
|
||||
// Calculate low bound distributions.
|
||||
areas[2 * i] = lb1.Volume() + lb2.Volume();
|
||||
overlaps[2 * i] = lb1.Overlap(lb2);
|
||||
|
||||
for (size_t l = cutOff; l < tree->NumChildren(); l++)
|
||||
bound2 |= tree->Child(sorted[l].second).Bound();
|
||||
// Calculate high bound distributions.
|
||||
areas[2 * i + 1] = hb1.Volume() + hb2.Volume();
|
||||
overlaps[2 * i + 1] = hb1.Overlap(hb2);
|
||||
|
||||
ElemType area1 = bound1.Volume();
|
||||
ElemType area2 = bound2.Volume();
|
||||
ElemType oArea = bound1.Overlap(bound2);
|
||||
// Now calculate margins for each.
|
||||
for (size_t k = 0; k < lb1.Dim(); k++)
|
||||
{
|
||||
margins[2 * i] += lb1[k].Width() + lb2[k].Width();
|
||||
margins[2 * i + 1] += hb1[k].Width() + hb2[k].Width();
|
||||
}
|
||||
|
||||
for (size_t k = 0; k < bound1.Dim(); k++)
|
||||
margins[i] += bound1[k].Width() + bound2[k].Width();
|
||||
|
||||
areas[i] += area1 + area2;
|
||||
overlapedAreas[i] += oArea;
|
||||
axisScore += margins[i];
|
||||
// The score we use is the sum of all scores.
|
||||
axisLoScore += margins[2 * i];
|
||||
axisHiScore += margins[2 * i + 1];
|
||||
}
|
||||
|
||||
if (axisScore < bestAxisScore)
|
||||
// If this dimension's score (for lower or higher bound scores) is a new
|
||||
// best, then extract the necessary split information.
|
||||
if (std::min(axisLoScore, axisHiScore) < bestScore)
|
||||
{
|
||||
bestAxisScore = axisScore;
|
||||
bestScore = std::min(axisLoScore, axisHiScore);
|
||||
if (axisLoScore < axisHiScore)
|
||||
lowIsBetter = true;
|
||||
else
|
||||
lowIsBetter = false;
|
||||
|
||||
bestAxis = j;
|
||||
bestOverlapIndexOnBestAxis = 0;
|
||||
bestAreaIndexOnBestAxis = 0;
|
||||
for (size_t i = 1; i < areas.size(); i++)
|
||||
|
||||
// This will get us either the lower or higher bound depending on which we
|
||||
// want. Remember that we selected *either* the lower or higher bounds to
|
||||
// split on, so we want to only check those.
|
||||
const size_t indexOffset = lowIsBetter ? 0 : 1;
|
||||
|
||||
size_t overlapIndex = indexOffset;
|
||||
size_t areaIndex = indexOffset;
|
||||
bool tiedOnOverlap = false;
|
||||
|
||||
// Find the best possible split (and whether it is on the low values or
|
||||
// high values of the bounds).
|
||||
for (size_t i = 1; i < numPossibleSplits; i++)
|
||||
{
|
||||
if (overlapedAreas[i] < overlapedAreas[bestOverlapIndexOnBestAxis])
|
||||
// Check bounds.
|
||||
if (overlaps[2 * i + indexOffset] < overlaps[overlapIndex])
|
||||
{
|
||||
tiedOnOverlap = false;
|
||||
bestAreaIndexOnBestAxis = i;
|
||||
bestOverlapIndexOnBestAxis = i;
|
||||
areaIndex = 2 * i + indexOffset;
|
||||
overlapIndex = 2 * i + indexOffset;
|
||||
}
|
||||
else if (overlapedAreas[i] ==
|
||||
overlapedAreas[bestOverlapIndexOnBestAxis])
|
||||
else if (overlaps[i] == overlaps[overlapIndex])
|
||||
{
|
||||
tiedOnOverlap = true;
|
||||
if (areas[i] < areas[bestAreaIndexOnBestAxis])
|
||||
bestAreaIndexOnBestAxis = i;
|
||||
if (areas[2 * i + indexOffset] < areas[areaIndex])
|
||||
areaIndex = 2 * i + indexOffset;
|
||||
}
|
||||
}
|
||||
|
||||
bestIndex = ((tiedOnOverlap ? areaIndex : overlapIndex) - indexOffset) / 2
|
||||
+ tree->MinNumChildren();
|
||||
}
|
||||
}
|
||||
|
||||
// Now we do the same thing using Bound().Hi() and choose the best of the two.
|
||||
for (size_t j = 0; j < tree->Bound().Dim(); j++)
|
||||
{
|
||||
ElemType axisScore = 0.0;
|
||||
// Get a list of the old children.
|
||||
std::vector<TreeType*> oldChildren(tree->NumChildren());
|
||||
for (size_t i = 0; i < oldChildren.size(); ++i)
|
||||
oldChildren[i] = &tree->Child(i);
|
||||
|
||||
std::vector<std::pair<ElemType, size_t>> sorted(tree->NumChildren());
|
||||
for (size_t i = 0; i < sorted.size(); i++)
|
||||
{
|
||||
sorted[i].first = tree->Child(i).Bound()[j].Hi();
|
||||
sorted[i].second = i;
|
||||
}
|
||||
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType>);
|
||||
|
||||
// We'll store each of the three scores for each distribution.
|
||||
std::vector<ElemType> areas(tree->MaxNumChildren() -
|
||||
2 * tree->MinNumChildren() + 2);
|
||||
std::vector<ElemType> margins(tree->MaxNumChildren() -
|
||||
2 * tree->MinNumChildren() + 2);
|
||||
std::vector<ElemType> overlapedAreas(tree->MaxNumChildren() -
|
||||
2 * tree->MinNumChildren() + 2);
|
||||
|
||||
for (size_t i = 0; i < areas.size(); i++)
|
||||
{
|
||||
areas[i] = 0.0;
|
||||
margins[i] = 0.0;
|
||||
overlapedAreas[i] = 0.0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < areas.size(); i++)
|
||||
{
|
||||
// The ith arrangement is obtained by placing the first
|
||||
// tree->MinNumChildren() + i points in one rectangle and the rest in
|
||||
// another. Then we calculate the three scores for that distribution.
|
||||
|
||||
size_t cutOff = tree->MinNumChildren() + i;
|
||||
|
||||
BoundType bound1(tree->Bound().Dim());
|
||||
BoundType bound2(tree->Bound().Dim());
|
||||
|
||||
for (size_t l = 0; l < cutOff; l++)
|
||||
bound1 |= tree->Child(sorted[l].second).Bound();
|
||||
|
||||
for (size_t l = cutOff; l < tree->NumChildren(); l++)
|
||||
bound2 |= tree->Child(sorted[l].second).Bound();
|
||||
|
||||
ElemType area1 = bound1.Volume();
|
||||
ElemType area2 = bound2.Volume();
|
||||
ElemType oArea = bound1.Overlap(bound2);
|
||||
|
||||
for (size_t k = 0; k < bound1.Dim(); k++)
|
||||
margins[i] += bound1[k].Width() + bound2[k].Width();
|
||||
|
||||
areas[i] += area1 + area2;
|
||||
overlapedAreas[i] += oArea;
|
||||
axisScore += margins[i];
|
||||
}
|
||||
|
||||
if (axisScore < bestAxisScore)
|
||||
{
|
||||
bestAxisScore = axisScore;
|
||||
bestAxis = j;
|
||||
lowIsBest = false;
|
||||
bestOverlapIndexOnBestAxis = 0;
|
||||
bestAreaIndexOnBestAxis = 0;
|
||||
|
||||
for (size_t i = 1; i < areas.size(); i++)
|
||||
{
|
||||
if (overlapedAreas[i] < overlapedAreas[bestOverlapIndexOnBestAxis])
|
||||
{
|
||||
tiedOnOverlap = false;
|
||||
bestAreaIndexOnBestAxis = i;
|
||||
bestOverlapIndexOnBestAxis = i;
|
||||
}
|
||||
else if (overlapedAreas[i] ==
|
||||
overlapedAreas[bestOverlapIndexOnBestAxis])
|
||||
{
|
||||
tiedOnOverlap = true;
|
||||
if (areas[i] < areas[bestAreaIndexOnBestAxis])
|
||||
bestAreaIndexOnBestAxis = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<ElemType, size_t>> sorted(tree->NumChildren());
|
||||
if (lowIsBest)
|
||||
{
|
||||
for (size_t i = 0; i < sorted.size(); i++)
|
||||
{
|
||||
sorted[i].first = tree->Child(i).Bound()[bestAxis].Lo();
|
||||
sorted[i].second = i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < sorted.size(); i++)
|
||||
{
|
||||
sorted[i].first = tree->Child(i).Bound()[bestAxis].Hi();
|
||||
sorted[i].second = i;
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType>);
|
||||
|
||||
TreeType* treeOne = new TreeType(tree->Parent());
|
||||
TreeType* treeTwo = new TreeType(tree->Parent());
|
||||
|
||||
if (tiedOnOverlap)
|
||||
{
|
||||
for (size_t i = 0; i < tree->NumChildren(); i++)
|
||||
{
|
||||
if (i < bestAreaIndexOnBestAxis + tree->MinNumChildren())
|
||||
InsertNodeIntoTree(treeOne, &(tree->Child(sorted[i].second)));
|
||||
else
|
||||
InsertNodeIntoTree(treeTwo, &(tree->Child(sorted[i].second)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < tree->NumChildren(); i++)
|
||||
{
|
||||
if (i < bestOverlapIndexOnBestAxis + tree->MinNumChildren())
|
||||
InsertNodeIntoTree(treeOne, &(tree->Child(sorted[i].second)));
|
||||
else
|
||||
InsertNodeIntoTree(treeTwo, &(tree->Child(sorted[i].second)));
|
||||
}
|
||||
}
|
||||
|
||||
// Remove this node and insert treeOne and treeTwo
|
||||
/**
|
||||
* If 'tree' is the root of the tree (i.e. if it has no parent), then we must
|
||||
* create two new child nodes, distribute the children from the original node
|
||||
* among them, and insert those. If 'tree' is not the root of the tree, then
|
||||
* we may create only one new child node, redistribute the children from the
|
||||
* original node between 'tree' and the new node, then insert those nodes into
|
||||
* the parent.
|
||||
*
|
||||
* Here, we simply set treeOne and treeTwo to the right values to avoid code
|
||||
* duplication.
|
||||
*/
|
||||
TreeType* par = tree->Parent();
|
||||
size_t index = 0;
|
||||
while (par->children[index] != tree) { index++; }
|
||||
TreeType* treeOne = par ? tree : new TreeType(tree);
|
||||
TreeType* treeTwo = par ? new TreeType(par) : new TreeType(tree);
|
||||
|
||||
par->children[index] = treeOne;
|
||||
par->children[par->NumChildren()++] = treeTwo;
|
||||
// Now clean the node.
|
||||
tree->numChildren = 0;
|
||||
tree->numDescendants = 0;
|
||||
tree->count = 0;
|
||||
tree->bound.Clear();
|
||||
|
||||
// We only add one at a time, so we should only need to test for equality
|
||||
// just in case, we use an assert.
|
||||
assert(par->NumChildren() <= par->MaxNumChildren() + 1);
|
||||
if (par->NumChildren() == par->MaxNumChildren() + 1)
|
||||
RStarTreeSplit::SplitNonLeafNode(par,relevels);
|
||||
// Assemble vector of values.
|
||||
arma::Col<ElemType> values(oldChildren.size());
|
||||
for (size_t i = 0; i < oldChildren.size(); ++i)
|
||||
{
|
||||
values[i] = (lowIsBetter ? oldChildren[i]->Bound()[bestAxis].Lo()
|
||||
: oldChildren[i]->Bound()[bestAxis].Hi());
|
||||
}
|
||||
arma::uvec indices = arma::sort_index(values);
|
||||
|
||||
// We have to update the children of each of these new nodes so that they
|
||||
// record the correct parent.
|
||||
for (size_t i = 0; i < treeOne->NumChildren(); i++)
|
||||
treeOne->children[i]->Parent() = treeOne;
|
||||
for (size_t i = 0; i < bestIndex; ++i)
|
||||
InsertNodeIntoTree(treeOne, oldChildren[indices[i]]);
|
||||
for (size_t i = bestIndex; i < oldChildren.size(); ++i)
|
||||
InsertNodeIntoTree(treeTwo, oldChildren[indices[i]]);
|
||||
|
||||
// Insert the new tree node(s).
|
||||
if (par)
|
||||
{
|
||||
// Insert the new node into the parent. The number of descendants does not
|
||||
// need to be updated.
|
||||
par->children[par->NumChildren()++] = treeTwo;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Insert both nodes into 'tree', which is now a higher-level root node.
|
||||
InsertNodeIntoTree(tree, treeOne);
|
||||
InsertNodeIntoTree(tree, treeTwo);
|
||||
|
||||
// We have to update the children of treeOne so that they record the correct
|
||||
// parent.
|
||||
for (size_t i = 0; i < treeOne->NumChildren(); i++)
|
||||
treeOne->children[i]->Parent() = treeOne;
|
||||
}
|
||||
|
||||
// Update the children of treeTwo to have the correct parent.
|
||||
for (size_t i = 0; i < treeTwo->NumChildren(); i++)
|
||||
treeTwo->children[i]->Parent() = treeTwo;
|
||||
|
||||
assert(treeOne->Parent()->NumChildren() <= treeOne->MaxNumChildren());
|
||||
assert(treeOne->Parent()->NumChildren() >= treeOne->MinNumChildren());
|
||||
assert(treeTwo->Parent()->NumChildren() <= treeTwo->MaxNumChildren());
|
||||
assert(treeTwo->Parent()->NumChildren() >= treeTwo->MinNumChildren());
|
||||
|
||||
assert(treeOne->MaxNumChildren() < 7);
|
||||
assert(treeTwo->MaxNumChildren() < 7);
|
||||
|
||||
tree->SoftDelete();
|
||||
// If we have overflowed hte parent's children, then we need to split that
|
||||
// node also.
|
||||
if (par && par->NumChildren() >= par->MaxNumChildren() + 1)
|
||||
RStarTreeSplit::SplitNonLeafNode(par, relevels);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -275,7 +275,6 @@ RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
|
||||
|
||||
if (ownsDataset)
|
||||
delete dataset;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,9 +335,7 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
|
||||
|
||||
numDescendants++;
|
||||
|
||||
std::vector<bool> lvls(TreeDepth());
|
||||
for (size_t i = 0; i < lvls.size(); i++)
|
||||
lvls[i] = true;
|
||||
std::vector<bool> lvls(TreeDepth(), true);
|
||||
|
||||
// If this is a leaf node, we stop here and add the point.
|
||||
if (numChildren == 0)
|
||||
@@ -452,9 +449,7 @@ bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
|
||||
while (root->Parent() != NULL)
|
||||
root = root->Parent();
|
||||
|
||||
std::vector<bool> lvls(root->TreeDepth());
|
||||
for (size_t i = 0; i < lvls.size(); i++)
|
||||
lvls[i] = true;
|
||||
std::vector<bool> lvls(root->TreeDepth(), true);
|
||||
|
||||
if (numChildren == 0)
|
||||
{
|
||||
@@ -1067,15 +1062,17 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
|
||||
if (child->NumChildren() > maxNumChildren)
|
||||
{
|
||||
maxNumChildren = child->MaxNumChildren();
|
||||
children.resize(maxNumChildren+1);
|
||||
children.resize(maxNumChildren + 1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < child->NumChildren(); i++) {
|
||||
children[i] = child->children[i];
|
||||
children[i]->Parent() = this;
|
||||
child->children[i] = NULL;
|
||||
}
|
||||
|
||||
numChildren = child->NumChildren();
|
||||
child->NumChildren() = 0;
|
||||
|
||||
for (size_t i = 0; i < child->Count(); i++)
|
||||
{
|
||||
@@ -1086,7 +1083,9 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
|
||||
auxiliaryInfo = child->AuxiliaryInfo();
|
||||
|
||||
count = child->Count();
|
||||
child->SoftDelete();
|
||||
child->Count() = 0;
|
||||
|
||||
delete child;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,9 +62,9 @@ class XTreeSplit
|
||||
* Comparator for sorting with std::pair. This comparator works a little bit
|
||||
* faster then the default comparator.
|
||||
*/
|
||||
template<typename ElemType>
|
||||
static bool PairComp(const std::pair<ElemType, size_t>& p1,
|
||||
const std::pair<ElemType, size_t>& p2)
|
||||
template<typename ElemType, typename SecondType>
|
||||
static bool PairComp(const std::pair<ElemType, SecondType>& p1,
|
||||
const std::pair<ElemType, SecondType>& p2)
|
||||
{
|
||||
return p1.first < p2.first;
|
||||
}
|
||||
|
||||
@@ -30,249 +30,87 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector<bool>& relevels)
|
||||
{
|
||||
// Convenience typedef.
|
||||
typedef typename TreeType::ElemType ElemType;
|
||||
typedef bound::HRectBound<metric::EuclideanDistance, ElemType> BoundType;
|
||||
|
||||
if (tree->Count() <= tree->MaxLeafSize())
|
||||
return;
|
||||
|
||||
// If we are splitting the root node, we need will do things differently so
|
||||
// that the constructor and other methods don't confuse the end user by giving
|
||||
// an address of another node.
|
||||
if (tree->Parent() == NULL)
|
||||
{
|
||||
// We actually want to copy this way. Pointers and everything.
|
||||
TreeType* copy = new TreeType(*tree, false);
|
||||
copy->Parent() = tree;
|
||||
tree->Count() = 0;
|
||||
tree->NullifyData();
|
||||
// Because this was a leaf node, numChildren must be 0.
|
||||
tree->children[(tree->NumChildren())++] = copy;
|
||||
assert(tree->NumChildren() == 1);
|
||||
XTreeSplit::SplitLeafNode(copy,relevels);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we haven't yet reinserted on this level, we try doing so now.
|
||||
if (relevels[tree->TreeDepth()])
|
||||
{
|
||||
relevels[tree->TreeDepth()] = false;
|
||||
// We sort the points by decreasing distance to the centroid of the bound.
|
||||
// We then remove the first p entries and reinsert them at the root.
|
||||
TreeType* root = tree;
|
||||
while (root->Parent() != NULL)
|
||||
root = root->Parent();
|
||||
|
||||
// The R*-tree paper says this works the best.
|
||||
size_t p = tree->MaxLeafSize() * 0.3;
|
||||
if (p == 0)
|
||||
{
|
||||
XTreeSplit::SplitLeafNode(tree,relevels);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::pair<ElemType, size_t>> sorted(tree->Count());
|
||||
arma::Col<ElemType> center;
|
||||
tree->Bound().Center(center); // Modifies centroid.
|
||||
for (size_t i = 0; i < sorted.size(); i++)
|
||||
{
|
||||
sorted[i].first = tree->Metric().Evaluate(center,
|
||||
tree->Dataset().col(tree->Point(i)));
|
||||
sorted[i].second = i;
|
||||
}
|
||||
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType>);
|
||||
std::vector<size_t> pointIndices(p);
|
||||
|
||||
for (size_t i = 0; i < p; i++)
|
||||
{
|
||||
// We start from the end of sorted.
|
||||
pointIndices[i] = tree->Point(sorted[sorted.size() - 1 - i].second);
|
||||
|
||||
root->DeletePoint(tree->Point(sorted[sorted.size() - 1 - i].second),
|
||||
relevels);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < p; i++)
|
||||
{
|
||||
// We reverse the order again to reinsert the closest points first.
|
||||
root->InsertPoint(pointIndices[p - 1 - i], relevels);
|
||||
}
|
||||
|
||||
// // If we went below min fill, delete this node and reinsert all points.
|
||||
// if (tree->Count() < tree->MinLeafSize()) {
|
||||
// std::vector<int> pointIndices(tree->Count());
|
||||
// for(size_t i = 0; i < tree->Count(); i++) {
|
||||
// pointIndices[i] = tree->Points()[i];
|
||||
// }
|
||||
// root->RemoveNode(tree, relevels);
|
||||
// for(size_t i = 0; i < pointIndices.size(); i++) {
|
||||
// root->InsertPoint(pointIndices[i], relevels);
|
||||
// }
|
||||
// //tree->SoftDelete();
|
||||
// }
|
||||
if (RStarTreeSplit::ReinsertPoints(tree, relevels) > 0)
|
||||
return;
|
||||
}
|
||||
|
||||
int bestOverlapIndexOnBestAxis = 0;
|
||||
int bestAreaIndexOnBestAxis = 0;
|
||||
bool tiedOnOverlap = false;
|
||||
int bestAxis = 0;
|
||||
ElemType bestAxisScore = std::numeric_limits<ElemType>::max();
|
||||
for (size_t j = 0; j < tree->Bound().Dim(); j++)
|
||||
{
|
||||
ElemType axisScore = 0.0;
|
||||
// Since we only have points in the leaf nodes, we only need to sort once.
|
||||
std::vector<std::pair<ElemType, size_t>> sorted(tree->Count());
|
||||
for (size_t i = 0; i < sorted.size(); i++)
|
||||
{
|
||||
sorted[i].first = tree->Dataset().col(tree->Point(i))[j];
|
||||
sorted[i].second = i;
|
||||
}
|
||||
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType>);
|
||||
|
||||
// We'll store each of the three scores for each distribution.
|
||||
std::vector<ElemType> areas(tree->MaxLeafSize() -
|
||||
2 * tree->MinLeafSize() + 2);
|
||||
std::vector<ElemType> margins(tree->MaxLeafSize() -
|
||||
2 * tree->MinLeafSize() + 2);
|
||||
std::vector<ElemType> overlapedAreas(tree->MaxLeafSize() -
|
||||
2 * tree->MinLeafSize() + 2);
|
||||
for (size_t i = 0; i < areas.size(); i++)
|
||||
{
|
||||
areas[i] = 0.0;
|
||||
margins[i] = 0.0;
|
||||
overlapedAreas[i] = 0.0;
|
||||
}
|
||||
for (size_t i = 0; i < areas.size(); i++)
|
||||
{
|
||||
// The ith arrangement is obtained by placing the first
|
||||
// tree->MinLeafSize() + i points in one rectangle and the rest in
|
||||
// another. Then we calculate the three scores for that distribution.
|
||||
|
||||
size_t cutOff = tree->MinLeafSize() + i;
|
||||
|
||||
BoundType bound1(tree->Bound().Dim());
|
||||
BoundType bound2(tree->Bound().Dim());
|
||||
|
||||
for (size_t l = 0; l < cutOff; l++)
|
||||
bound1 |= tree->Dataset().col(tree->Point(sorted[l].second));
|
||||
|
||||
for (size_t l = cutOff; l < tree->Count(); l++)
|
||||
bound2 |= tree->Dataset().col(tree->Point(sorted[l].second));
|
||||
|
||||
ElemType area1 = bound1.Volume();
|
||||
ElemType area2 = bound2.Volume();
|
||||
ElemType oArea = bound1.Overlap(bound2);
|
||||
|
||||
for (size_t k = 0; k < bound1.Dim(); k++)
|
||||
margins[i] += bound1[k].Width() + bound2[k].Width();
|
||||
|
||||
areas[i] += area1 + area2;
|
||||
overlapedAreas[i] += oArea;
|
||||
axisScore += margins[i];
|
||||
}
|
||||
|
||||
if (axisScore < bestAxisScore)
|
||||
{
|
||||
bestAxisScore = axisScore;
|
||||
bestAxis = j;
|
||||
bestOverlapIndexOnBestAxis = 0;
|
||||
bestAreaIndexOnBestAxis = 0;
|
||||
for (size_t i = 1; i < areas.size(); i++)
|
||||
{
|
||||
if (overlapedAreas[i] < overlapedAreas[bestOverlapIndexOnBestAxis])
|
||||
{
|
||||
tiedOnOverlap = false;
|
||||
bestAreaIndexOnBestAxis = i;
|
||||
bestOverlapIndexOnBestAxis = i;
|
||||
}
|
||||
else if (overlapedAreas[i] ==
|
||||
overlapedAreas[bestOverlapIndexOnBestAxis])
|
||||
{
|
||||
tiedOnOverlap = true;
|
||||
if (areas[i] < areas[bestAreaIndexOnBestAxis])
|
||||
bestAreaIndexOnBestAxis = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// The procedure of splitting a leaf node is virtually identical to the R*
|
||||
// tree procedure, so we can reuse code.
|
||||
size_t bestAxis;
|
||||
size_t bestIndex;
|
||||
RStarTreeSplit::PickLeafSplit(tree, bestAxis, bestIndex);
|
||||
|
||||
/**
|
||||
* Now that we have found the best dimension to split on, re-sort in that
|
||||
* dimension to prepare for reinsertion of points into the new nodes.
|
||||
*/
|
||||
std::vector<std::pair<ElemType, size_t>> sorted(tree->Count());
|
||||
for (size_t i = 0; i < sorted.size(); i++)
|
||||
{
|
||||
sorted[i].first = tree->Dataset().col(tree->Point(i))[bestAxis];
|
||||
sorted[i].second = i;
|
||||
sorted[i].second = tree->Point(i);
|
||||
}
|
||||
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType>);
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType, size_t>);
|
||||
|
||||
TreeType* treeOne = new TreeType(tree->Parent(),
|
||||
tree->AuxiliaryInfo().NormalNodeMaxNumChildren());
|
||||
TreeType* treeTwo = new TreeType(tree->Parent(),
|
||||
tree->AuxiliaryInfo().NormalNodeMaxNumChildren());
|
||||
/**
|
||||
* If 'tree' is the root of the tree (i.e. if it has no parent), then we must
|
||||
* create two new child nodes, distribute the points from the original node
|
||||
* among them, and insert those. If 'tree' is not the root of the tree, then
|
||||
* we may create only one new child node, redistribute the points from the
|
||||
* original node between 'tree' and the new node, then insert those nodes into
|
||||
* the parent.
|
||||
*
|
||||
* Here we simply set treeOne and treeTwo to the right values to avoid code
|
||||
* duplication.
|
||||
*/
|
||||
TreeType* par = tree->Parent();
|
||||
TreeType* treeOne = (par) ? tree : new TreeType(tree);
|
||||
TreeType* treeTwo = (par) ? new TreeType(par) : new TreeType(tree);
|
||||
|
||||
// The leaf nodes should never have any overlap introduced by the above method
|
||||
// since a split axis is chosen and then points are assigned based on their
|
||||
// value along that axis.
|
||||
if (tiedOnOverlap)
|
||||
// Now clean the node, and we will re-use this.
|
||||
const size_t numPoints = tree->Count();
|
||||
|
||||
// Reset the original node's values, regardless of whether it will become
|
||||
// the new parent or not.
|
||||
tree->numChildren = 0;
|
||||
tree->numDescendants = 0;
|
||||
tree->count = 0;
|
||||
tree->bound.Clear();
|
||||
|
||||
// Insert the points into the appropriate tree.
|
||||
for (size_t i = 0; i < numPoints; i++)
|
||||
{
|
||||
for (size_t i = 0; i < tree->Count(); i++)
|
||||
{
|
||||
if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize())
|
||||
treeOne->InsertPoint(tree->Point(sorted[i].second));
|
||||
else
|
||||
treeTwo->InsertPoint(tree->Point(sorted[i].second));
|
||||
}
|
||||
if (i < bestIndex + tree->MinLeafSize())
|
||||
treeOne->InsertPoint(sorted[i].second);
|
||||
else
|
||||
treeTwo->InsertPoint(sorted[i].second);
|
||||
}
|
||||
|
||||
// Insert the new tree node(s).
|
||||
if (par)
|
||||
{
|
||||
par->children[par->NumChildren()++] = treeTwo;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < tree->Count(); i++)
|
||||
{
|
||||
if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize())
|
||||
treeOne->InsertPoint(tree->Point(sorted[i].second));
|
||||
else
|
||||
treeTwo->InsertPoint(tree->Point(sorted[i].second));
|
||||
}
|
||||
InsertNodeIntoTree(tree, treeOne);
|
||||
InsertNodeIntoTree(tree, treeTwo);
|
||||
}
|
||||
|
||||
// Remove this node and insert treeOne and treeTwo.
|
||||
TreeType* par = tree->Parent();
|
||||
size_t index = par->NumChildren();
|
||||
for (size_t i = 0; i < par->NumChildren(); i++)
|
||||
{
|
||||
if (par->children[i] == tree)
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(index != par->NumChildren());
|
||||
par->children[index] = treeOne;
|
||||
par->children[par->NumChildren()++] = treeTwo;
|
||||
|
||||
// We now update the split history of each new node.
|
||||
treeOne->AuxiliaryInfo().SplitHistory().history[bestAxis] = true;
|
||||
treeOne->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis;
|
||||
treeTwo->AuxiliaryInfo().SplitHistory().history[bestAxis] = true;
|
||||
treeTwo->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis;
|
||||
|
||||
// We only add one at a time, so we should only need to test for equality just
|
||||
// in case, we use an assert.
|
||||
assert(par->NumChildren() <= par->MaxNumChildren() + 1);
|
||||
if (par->NumChildren() == par->MaxNumChildren() + 1)
|
||||
// If we overflowed the parent, split it.
|
||||
if (par && par->NumChildren() == par->MaxNumChildren() + 1)
|
||||
XTreeSplit::SplitNonLeafNode(par,relevels);
|
||||
|
||||
assert(treeOne->Parent()->NumChildren() <=
|
||||
treeOne->Parent()->MaxNumChildren());
|
||||
assert(treeOne->Parent()->NumChildren() >=
|
||||
treeOne->Parent()->MinNumChildren());
|
||||
assert(treeTwo->Parent()->NumChildren() <=
|
||||
treeTwo->Parent()->MaxNumChildren());
|
||||
assert(treeTwo->Parent()->NumChildren() >=
|
||||
treeTwo->Parent()->MinNumChildren());
|
||||
|
||||
tree->SoftDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -289,22 +127,6 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels)
|
||||
typedef typename TreeType::ElemType ElemType;
|
||||
typedef bound::HRectBound<metric::EuclideanDistance, ElemType> BoundType;
|
||||
|
||||
// If we are splitting the root node, we need will do things differently so
|
||||
// that the constructor and other methods don't confuse the end user by giving
|
||||
// an address of another node.
|
||||
if (tree->Parent() == NULL)
|
||||
{
|
||||
// We actually want to copy this way. Pointers and everything.
|
||||
TreeType* copy = new TreeType(*tree, false);
|
||||
|
||||
copy->Parent() = tree;
|
||||
tree->NumChildren() = 0;
|
||||
tree->NullifyData();
|
||||
tree->children[(tree->NumChildren())++] = copy;
|
||||
XTreeSplit::SplitNonLeafNode(copy,relevels);
|
||||
return true;
|
||||
}
|
||||
|
||||
// The X tree paper doesn't explain how to handle the split history when
|
||||
// reinserting nodes and reinserting nodes seems to hurt the performance, so
|
||||
// we don't do it.
|
||||
@@ -313,29 +135,29 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels)
|
||||
// to save CPU time.
|
||||
|
||||
// Find the next split axis.
|
||||
std::vector<bool> axes(tree->Bound().Dim());
|
||||
std::vector<int> dimensionsLastUsed(tree->NumChildren());
|
||||
std::vector<bool> axes(tree->Bound().Dim(), true);
|
||||
std::vector<size_t> dimensionsLastUsed(tree->NumChildren());
|
||||
for (size_t i = 0; i < tree->NumChildren(); i++)
|
||||
dimensionsLastUsed[i] =
|
||||
tree->Child(i).AuxiliaryInfo().SplitHistory().lastDimension;
|
||||
std::sort(dimensionsLastUsed.begin(), dimensionsLastUsed.end());
|
||||
|
||||
size_t lastDim = dimensionsLastUsed[dimensionsLastUsed.size()/2];
|
||||
size_t lastDim = dimensionsLastUsed[dimensionsLastUsed.size() / 2];
|
||||
size_t minOverlapSplitDimension = tree->Bound().Dim();
|
||||
|
||||
// See if we can use a new dimension.
|
||||
for (size_t i = lastDim + 1; i < axes.size(); i++)
|
||||
{
|
||||
axes[i] = true;
|
||||
for (size_t j = 0; j < tree->NumChildren(); j++)
|
||||
axes[i] = axes[i] &
|
||||
tree->Child(j).AuxiliaryInfo().SplitHistory().history[i];
|
||||
tree->Child(j).AuxiliaryInfo().SplitHistory().history[i];
|
||||
if (axes[i] == true)
|
||||
{
|
||||
minOverlapSplitDimension = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (minOverlapSplitDimension == tree->Bound().Dim())
|
||||
{
|
||||
for (size_t i = 0; i < lastDim + 1; i++)
|
||||
@@ -373,14 +195,14 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels)
|
||||
ElemType axisScore = 0.0;
|
||||
|
||||
// We'll do Bound().Lo() now and use Bound().Hi() later.
|
||||
std::vector<std::pair<ElemType, size_t>> sorted(tree->NumChildren());
|
||||
std::vector<std::pair<ElemType, TreeType*>> sorted(tree->NumChildren());
|
||||
for (size_t i = 0; i < sorted.size(); i++)
|
||||
{
|
||||
sorted[i].first = tree->Child(i).Bound()[j].Lo();
|
||||
sorted[i].second = i;
|
||||
sorted[i].second = &tree->Child(i);
|
||||
}
|
||||
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType>);
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType, TreeType*>);
|
||||
|
||||
// We'll store each of the three scores for each distribution.
|
||||
std::vector<ElemType> areas(tree->MaxNumChildren() -
|
||||
@@ -408,10 +230,10 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels)
|
||||
BoundType bound2(tree->Bound().Dim());
|
||||
|
||||
for (size_t l = 0; l < cutOff; l++)
|
||||
bound1 |= tree->Child(sorted[l].second).Bound();
|
||||
bound1 |= sorted[l].second->Bound();
|
||||
|
||||
for (size_t l = cutOff; l < tree->NumChildren(); l++)
|
||||
bound2 |= tree->Child(sorted[l].second).Bound();
|
||||
bound2 |= sorted[l].second->Bound();
|
||||
|
||||
ElemType area1 = bound1.Volume();
|
||||
ElemType area2 = bound2.Volume();
|
||||
@@ -478,14 +300,14 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels)
|
||||
{
|
||||
ElemType axisScore = 0.0;
|
||||
|
||||
std::vector<std::pair<ElemType, size_t>> sorted(tree->NumChildren());
|
||||
std::vector<std::pair<ElemType, TreeType*>> sorted(tree->NumChildren());
|
||||
for (size_t i = 0; i < sorted.size(); i++)
|
||||
{
|
||||
sorted[i].first = tree->Child(i).Bound()[j].Hi();
|
||||
sorted[i].second = i;
|
||||
sorted[i].second = &tree->Child(i);
|
||||
}
|
||||
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType>);
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType, TreeType*>);
|
||||
|
||||
// We'll store each of the three scores for each distribution.
|
||||
std::vector<ElemType> areas(tree->MaxNumChildren() -
|
||||
@@ -513,10 +335,10 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels)
|
||||
BoundType bound2(tree->Bound().Dim());
|
||||
|
||||
for (size_t l = 0; l < cutOff; l++)
|
||||
bound1 |= tree->Child(sorted[l].second).Bound();
|
||||
bound1 |= sorted[l].second->Bound();
|
||||
|
||||
for (size_t l = cutOff; l < tree->NumChildren(); l++)
|
||||
bound2 |= tree->Child(sorted[l].second).Bound();
|
||||
bound2 |= sorted[l].second->Bound();
|
||||
|
||||
ElemType area1 = bound1.Volume();
|
||||
ElemType area2 = bound2.Volume();
|
||||
@@ -581,13 +403,13 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels)
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<ElemType, size_t>> sorted(tree->NumChildren());
|
||||
std::vector<std::pair<ElemType, TreeType*>> sorted(tree->NumChildren());
|
||||
if (lowIsBest)
|
||||
{
|
||||
for (size_t i = 0; i < sorted.size(); i++)
|
||||
{
|
||||
sorted[i].first = tree->Child(i).Bound()[bestAxis].Lo();
|
||||
sorted[i].second = i;
|
||||
sorted[i].second = &tree->Child(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -595,181 +417,288 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels)
|
||||
for (size_t i = 0; i < sorted.size(); i++)
|
||||
{
|
||||
sorted[i].first = tree->Child(i).Bound()[bestAxis].Hi();
|
||||
sorted[i].second = i;
|
||||
sorted[i].second = &tree->Child(i);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType>);
|
||||
std::sort(sorted.begin(), sorted.end(), PairComp<ElemType, TreeType*>);
|
||||
|
||||
TreeType* treeOne = new TreeType(tree->Parent(), tree->MaxNumChildren());
|
||||
TreeType* treeTwo = new TreeType(tree->Parent(), tree->MaxNumChildren());
|
||||
if (tree->Parent() != NULL)
|
||||
{
|
||||
// Reuse tree as the new child.
|
||||
TreeType* treeTwo = new TreeType(tree->Parent(), tree->MaxNumChildren());
|
||||
const size_t numChildren = tree->NumChildren();
|
||||
tree->numChildren = 0;
|
||||
tree->count = 0;
|
||||
|
||||
// Now as per the X-tree paper, we ensure that this split was good enough.
|
||||
bool useMinOverlapSplit = false;
|
||||
if (tiedOnOverlap)
|
||||
{
|
||||
if (overlapBestAreaAxis/areaBestAreaAxis < MAX_OVERLAP)
|
||||
// Now as per the X-tree paper, we ensure that this split was good enough.
|
||||
bool useMinOverlapSplit = false;
|
||||
if (tiedOnOverlap)
|
||||
{
|
||||
for (size_t i = 0; i < tree->NumChildren(); i++)
|
||||
if (overlapBestAreaAxis/areaBestAreaAxis < MAX_OVERLAP)
|
||||
{
|
||||
if (i < bestAreaIndexOnBestAxis + tree->MinNumChildren())
|
||||
InsertNodeIntoTree(treeOne, tree->children[sorted[i].second]);
|
||||
else
|
||||
InsertNodeIntoTree(treeTwo, tree->children[sorted[i].second]);
|
||||
}
|
||||
}
|
||||
else
|
||||
useMinOverlapSplit = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (overlapBestOverlapAxis/areaBestOverlapAxis < MAX_OVERLAP)
|
||||
{
|
||||
for (size_t i = 0; i < tree->NumChildren(); i++)
|
||||
{
|
||||
if (i < bestOverlapIndexOnBestAxis + tree->MinNumChildren())
|
||||
InsertNodeIntoTree(treeOne, tree->children[sorted[i].second]);
|
||||
else
|
||||
InsertNodeIntoTree(treeTwo, tree->children[sorted[i].second]);
|
||||
}
|
||||
}
|
||||
else
|
||||
useMinOverlapSplit = true;
|
||||
}
|
||||
|
||||
// If the split was not good enough, then we try the minimal overlap split.
|
||||
// If that fails, we create a "super node" (more accurately we resize this one
|
||||
// to make it a super node).
|
||||
if (useMinOverlapSplit)
|
||||
{
|
||||
// If there is a dimension that might work, try that.
|
||||
if ((minOverlapSplitDimension != tree->Bound().Dim()) &&
|
||||
(bestScoreMinOverlapSplit / areaOfBestMinOverlapSplit < MAX_OVERLAP))
|
||||
{
|
||||
std::vector<std::pair<ElemType, size_t>> sorted2(tree->NumChildren());
|
||||
if (minOverlapSplitUsesHi)
|
||||
{
|
||||
for (size_t i = 0; i < sorted2.size(); i++)
|
||||
for (size_t i = 0; i < numChildren; i++)
|
||||
{
|
||||
sorted2[i].first = tree->Child(i).Bound()[bestAxis].Hi();
|
||||
sorted2[i].second = i;
|
||||
if (i < bestAreaIndexOnBestAxis + tree->MinNumChildren())
|
||||
InsertNodeIntoTree(tree, sorted[i].second);
|
||||
else
|
||||
InsertNodeIntoTree(treeTwo, sorted[i].second);
|
||||
}
|
||||
}
|
||||
else
|
||||
useMinOverlapSplit = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (overlapBestOverlapAxis/areaBestOverlapAxis < MAX_OVERLAP)
|
||||
{
|
||||
tree->numDescendants = 0;
|
||||
tree->bound.Clear();
|
||||
for (size_t i = 0; i < numChildren; i++)
|
||||
{
|
||||
if (i < bestOverlapIndexOnBestAxis + tree->MinNumChildren())
|
||||
InsertNodeIntoTree(tree, sorted[i].second);
|
||||
else
|
||||
InsertNodeIntoTree(treeTwo, sorted[i].second);
|
||||
}
|
||||
}
|
||||
else
|
||||
useMinOverlapSplit = true;
|
||||
}
|
||||
|
||||
// If the split was not good enough, then we try the minimal overlap split.
|
||||
// If that fails, we create a "super node" (more accurately we resize this
|
||||
// one to make it a super node).
|
||||
if (useMinOverlapSplit)
|
||||
{
|
||||
// If there is a dimension that might work, try that.
|
||||
if ((minOverlapSplitDimension != tree->Bound().Dim()) &&
|
||||
(bestScoreMinOverlapSplit / areaOfBestMinOverlapSplit < MAX_OVERLAP))
|
||||
{
|
||||
std::vector<std::pair<ElemType, TreeType*>> sorted2(numChildren);
|
||||
if (minOverlapSplitUsesHi)
|
||||
{
|
||||
for (size_t i = 0; i < sorted2.size(); i++)
|
||||
{
|
||||
sorted2[i].first = sorted[i].second->Bound()[bestAxis].Hi();
|
||||
sorted2[i].second = sorted[i].second;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < sorted2.size(); i++)
|
||||
{
|
||||
sorted2[i].first = sorted[i].second->Bound()[bestAxis].Lo();
|
||||
sorted2[i].second = sorted[i].second;
|
||||
}
|
||||
}
|
||||
std::sort(sorted2.begin(), sorted2.end(), PairComp<ElemType, TreeType*>);
|
||||
|
||||
tree->numDescendants = 0;
|
||||
tree->bound.Clear();
|
||||
for (size_t i = 0; i < numChildren; i++)
|
||||
{
|
||||
if (i < bestIndexMinOverlapSplit + tree->MinNumChildren())
|
||||
InsertNodeIntoTree(tree, sorted2[i].second);
|
||||
else
|
||||
InsertNodeIntoTree(treeTwo, sorted2[i].second);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < sorted2.size(); i++)
|
||||
// We don't create a supernode that would be the only child of the root.
|
||||
// (Note that if you did try to do so you would need to update the
|
||||
// parent field on each child of this new node as creating a supernode
|
||||
// causes the function to return before that is done.
|
||||
|
||||
// I thought commenting out the bellow would make the tree less
|
||||
// efficient but would still work. It doesn't. I should look into that
|
||||
// to see if there is another bug.
|
||||
|
||||
if ((tree->Parent()->Parent() == NULL) &&
|
||||
(tree->Parent()->NumChildren() == 1))
|
||||
{
|
||||
sorted2[i].first = tree->Child(i).Bound()[bestAxis].Lo();
|
||||
sorted2[i].second = i;
|
||||
// We make the root a supernode instead.
|
||||
tree->Parent()->MaxNumChildren() = tree->MaxNumChildren() +
|
||||
tree->AuxiliaryInfo().NormalNodeMaxNumChildren();
|
||||
tree->Parent()->children.resize(tree->Parent()->MaxNumChildren() + 1);
|
||||
tree->Parent()->NumChildren() = tree->NumChildren();
|
||||
for (size_t i = 0; i < numChildren; ++i)
|
||||
{
|
||||
tree->Parent()->children[i] = sorted[i].second;
|
||||
tree->Parent()->children[i]->Parent() = tree->Parent();
|
||||
tree->children[i] = NULL;
|
||||
}
|
||||
|
||||
delete tree;
|
||||
delete treeTwo;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we don't have to worry about the root, we just enlarge this node.
|
||||
tree->MaxNumChildren() +=
|
||||
tree->AuxiliaryInfo().NormalNodeMaxNumChildren();
|
||||
tree->children.resize(tree->MaxNumChildren() + 1);
|
||||
tree->numChildren = numChildren;
|
||||
for (size_t i = 0; i < numChildren; i++)
|
||||
tree->Child(i).Parent() = tree;
|
||||
|
||||
delete treeTwo;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the split history of each child.
|
||||
tree->AuxiliaryInfo().SplitHistory().history[bestAxis] = true;
|
||||
tree->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis;
|
||||
treeTwo->AuxiliaryInfo().SplitHistory().history[bestAxis] = true;
|
||||
treeTwo->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis;
|
||||
|
||||
// Remove this node and insert treeOne and treeTwo
|
||||
TreeType* par = tree->Parent();
|
||||
par->children[par->NumChildren()++] = treeTwo;
|
||||
|
||||
// We only add one at a time, so we should only need to test for equality
|
||||
// just in case, we use an assert.
|
||||
if (!(par->NumChildren() <= par->MaxNumChildren() + 1))
|
||||
Log::Debug << "error " << par->NumChildren() << ", "
|
||||
<< par->MaxNumChildren() + 1 << std::endl;
|
||||
assert(par->NumChildren() <= par->MaxNumChildren() + 1);
|
||||
|
||||
if (par->NumChildren() == par->MaxNumChildren() + 1)
|
||||
XTreeSplit::SplitNonLeafNode(par,relevels);
|
||||
|
||||
// We have to update the children of each of these new nodes so that they
|
||||
// record the correct parent.
|
||||
for (size_t i = 0; i < treeTwo->NumChildren(); i++)
|
||||
treeTwo->Child(i).Parent() = treeTwo;
|
||||
|
||||
assert(tree->Parent()->NumChildren() <=
|
||||
tree->Parent()->MaxNumChildren());
|
||||
assert(tree->Parent()->NumChildren() >=
|
||||
tree->Parent()->MinNumChildren());
|
||||
assert(treeTwo->Parent()->NumChildren() <=
|
||||
treeTwo->Parent()->MaxNumChildren());
|
||||
assert(treeTwo->Parent()->NumChildren() >=
|
||||
treeTwo->Parent()->MinNumChildren());
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are the root of the tree, so we need to create two children to add.
|
||||
TreeType* treeOne = new TreeType(tree, tree->MaxNumChildren());
|
||||
TreeType* treeTwo = new TreeType(tree, tree->MaxNumChildren());
|
||||
const size_t numChildren = tree->NumChildren();
|
||||
tree->numChildren = 0;
|
||||
|
||||
// Now as per the X-tree paper, we ensure that this split was good enough.
|
||||
bool useMinOverlapSplit = false;
|
||||
if (tiedOnOverlap)
|
||||
{
|
||||
if (overlapBestAreaAxis/areaBestAreaAxis < MAX_OVERLAP)
|
||||
{
|
||||
for (size_t i = 0; i < numChildren; i++)
|
||||
{
|
||||
if (i < bestAreaIndexOnBestAxis + tree->MinNumChildren())
|
||||
InsertNodeIntoTree(treeOne, sorted[i].second);
|
||||
else
|
||||
InsertNodeIntoTree(treeTwo, sorted[i].second);
|
||||
}
|
||||
}
|
||||
std::sort(sorted2.begin(), sorted2.end(), PairComp<ElemType>);
|
||||
|
||||
for (size_t i = 0; i < tree->NumChildren(); i++)
|
||||
{
|
||||
if (i < bestIndexMinOverlapSplit + tree->MinNumChildren())
|
||||
InsertNodeIntoTree(treeOne, tree->children[sorted2[i].second]);
|
||||
else
|
||||
InsertNodeIntoTree(treeTwo, tree->children[sorted2[i].second]);
|
||||
}
|
||||
else
|
||||
useMinOverlapSplit = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't create a supernode that would be the only child of the root.
|
||||
// (Note that if you did try to do so you would need to update the parent
|
||||
// field on each child of this new node as creating a supernode causes the
|
||||
// function to return before that is done.
|
||||
|
||||
// I thought commenting out the bellow would make the tree less efficient
|
||||
// but would still work. It doesn't. I should look into that to see if
|
||||
// there is another bug.
|
||||
|
||||
if ((tree->Parent()->Parent() == NULL) &&
|
||||
(tree->Parent()->NumChildren() == 1))
|
||||
if (overlapBestOverlapAxis/areaBestOverlapAxis < MAX_OVERLAP)
|
||||
{
|
||||
// We make the root a supernode instead.
|
||||
tree->Parent()->MaxNumChildren() = tree->MaxNumChildren() +
|
||||
tree->AuxiliaryInfo().NormalNodeMaxNumChildren();
|
||||
tree->Parent()->children.resize(tree->Parent()->MaxNumChildren() + 1);
|
||||
tree->Parent()->NumChildren() = tree->NumChildren();
|
||||
for (size_t i = 0; i < tree->NumChildren(); i++)
|
||||
for (size_t i = 0; i < numChildren; i++)
|
||||
{
|
||||
tree->Parent()->children[i] = tree->children[i];
|
||||
tree->Child(i).Parent() = tree->Parent();
|
||||
if (i < bestOverlapIndexOnBestAxis + tree->MinNumChildren())
|
||||
InsertNodeIntoTree(treeOne, sorted[i].second);
|
||||
else
|
||||
InsertNodeIntoTree(treeTwo, sorted[i].second);
|
||||
}
|
||||
}
|
||||
else
|
||||
useMinOverlapSplit = true;
|
||||
}
|
||||
|
||||
// If the split was not good enough, then we try the minimal overlap split.
|
||||
// If that fails, we create a "super node" (more accurately we resize this one
|
||||
// to make it a super node).
|
||||
if (useMinOverlapSplit)
|
||||
{
|
||||
// If there is a dimension that might work, try that.
|
||||
if ((minOverlapSplitDimension != tree->Bound().Dim()) &&
|
||||
(bestScoreMinOverlapSplit / areaOfBestMinOverlapSplit < MAX_OVERLAP))
|
||||
{
|
||||
std::vector<std::pair<ElemType, TreeType*>> sorted2(numChildren);
|
||||
if (minOverlapSplitUsesHi)
|
||||
{
|
||||
for (size_t i = 0; i < sorted2.size(); i++)
|
||||
{
|
||||
sorted2[i].first = sorted[i].second->Bound()[bestAxis].Hi();
|
||||
sorted2[i].second = sorted[i].second;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < sorted2.size(); i++)
|
||||
{
|
||||
sorted2[i].first = sorted[i].second->Bound()[bestAxis].Lo();
|
||||
sorted2[i].second = sorted[i].second;
|
||||
}
|
||||
}
|
||||
std::sort(sorted2.begin(), sorted2.end(), PairComp<ElemType, TreeType*>);
|
||||
|
||||
for (size_t i = 0; i < numChildren; i++)
|
||||
{
|
||||
if (i < bestIndexMinOverlapSplit + tree->MinNumChildren())
|
||||
InsertNodeIntoTree(treeOne, sorted2[i].second);
|
||||
else
|
||||
InsertNodeIntoTree(treeTwo, sorted2[i].second);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make this node a supernode.
|
||||
tree->MaxNumChildren() +=
|
||||
tree->AuxiliaryInfo().NormalNodeMaxNumChildren();
|
||||
tree->children.resize(tree->MaxNumChildren() + 1);
|
||||
tree->numChildren = numChildren;
|
||||
for (size_t i = 0; i < numChildren; i++)
|
||||
tree->Child(i).Parent() = tree;
|
||||
|
||||
delete treeOne;
|
||||
delete treeTwo;
|
||||
tree->NullifyData();
|
||||
tree->SoftDelete();
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we don't have to worry about the root, we just enlarge this node.
|
||||
tree->MaxNumChildren() +=
|
||||
tree->AuxiliaryInfo().NormalNodeMaxNumChildren();
|
||||
tree->children.resize(tree->MaxNumChildren() + 1);
|
||||
for (size_t i = 0; i < tree->NumChildren(); i++)
|
||||
tree->Child(i).Parent() = tree;
|
||||
|
||||
delete treeOne;
|
||||
delete treeTwo;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the split history of each child.
|
||||
treeOne->AuxiliaryInfo().SplitHistory().history[bestAxis] = true;
|
||||
treeOne->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis;
|
||||
treeTwo->AuxiliaryInfo().SplitHistory().history[bestAxis] = true;
|
||||
treeTwo->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis;
|
||||
|
||||
// Remove this node and insert treeOne and treeTwo
|
||||
tree->children[0] = treeOne;
|
||||
tree->children[1] = treeTwo;
|
||||
tree->numChildren = 2;
|
||||
tree->numDescendants = treeOne->numDescendants + treeTwo->numDescendants;
|
||||
|
||||
// We have to update the children of each of these new nodes so that they
|
||||
// record the correct parent.
|
||||
for (size_t i = 0; i < treeOne->NumChildren(); ++i)
|
||||
treeOne->Child(i).Parent() = treeOne;
|
||||
for (size_t i = 0; i < treeTwo->NumChildren(); i++)
|
||||
treeTwo->Child(i).Parent() = treeTwo;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the split history of each child.
|
||||
treeOne->AuxiliaryInfo().SplitHistory().history[bestAxis] = true;
|
||||
treeOne->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis;
|
||||
treeTwo->AuxiliaryInfo().SplitHistory().history[bestAxis] = true;
|
||||
treeTwo->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis;
|
||||
|
||||
// Remove this node and insert treeOne and treeTwo
|
||||
TreeType* par = tree->Parent();
|
||||
size_t index = 0;
|
||||
for (size_t i = 0; i < par->NumChildren(); i++)
|
||||
{
|
||||
if (par->children[i] == tree)
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
par->children[index] = treeOne;
|
||||
par->children[par->NumChildren()++] = treeTwo;
|
||||
|
||||
// we only add one at a time, so we should only need to test for equality
|
||||
// just in case, we use an assert.
|
||||
|
||||
if (!(par->NumChildren() <= par->MaxNumChildren() + 1))
|
||||
Log::Debug << "error " << par->NumChildren() << ", "
|
||||
<< par->MaxNumChildren() + 1 << std::endl;
|
||||
assert(par->NumChildren() <= par->MaxNumChildren() + 1);
|
||||
|
||||
if (par->NumChildren() == par->MaxNumChildren() + 1)
|
||||
XTreeSplit::SplitNonLeafNode(par,relevels);
|
||||
|
||||
// We have to update the children of each of these new nodes so that they
|
||||
// record the correct parent.
|
||||
for (size_t i = 0; i < treeOne->NumChildren(); i++)
|
||||
treeOne->Child(i).Parent() = treeOne;
|
||||
for (size_t i = 0; i < treeTwo->NumChildren(); i++)
|
||||
treeTwo->Child(i).Parent() = treeTwo;
|
||||
|
||||
assert(treeOne->Parent()->NumChildren() <=
|
||||
treeOne->Parent()->MaxNumChildren());
|
||||
assert(treeOne->Parent()->NumChildren() >=
|
||||
treeOne->Parent()->MinNumChildren());
|
||||
assert(treeTwo->Parent()->NumChildren() <=
|
||||
treeTwo->Parent()->MaxNumChildren());
|
||||
assert(treeTwo->Parent()->NumChildren() >=
|
||||
treeTwo->Parent()->MinNumChildren());
|
||||
|
||||
tree->SoftDelete();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -781,8 +710,7 @@ void XTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode)
|
||||
{
|
||||
destTree->Bound() |= srcNode->Bound();
|
||||
destTree->numDescendants += srcNode->numDescendants;
|
||||
destTree->children[destTree->NumChildren()] = srcNode;
|
||||
destTree->NumChildren()++;
|
||||
destTree->children[destTree->NumChildren()++] = srcNode;
|
||||
}
|
||||
|
||||
} // namespace tree
|
||||
|
||||
Reference in New Issue
Block a user