diff --git a/.appveyor.yml b/.appveyor.yml index 802f096076..d8fe306e40 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -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 diff --git a/src/mlpack/core/tree/rectangle_tree/r_star_tree_split.hpp b/src/mlpack/core/tree/rectangle_tree/r_star_tree_split.hpp index 6370120dec..f0c5f5845f 100644 --- a/src/mlpack/core/tree/rectangle_tree/r_star_tree_split.hpp +++ b/src/mlpack/core/tree/rectangle_tree/r_star_tree_split.hpp @@ -41,6 +41,19 @@ class RStarTreeSplit template static bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); + /** + * Reinsert any points into the tree, if needed. This returns the number of + * points reinserted. + */ + template + static size_t ReinsertPoints(TreeType* tree, std::vector& relevels); + + /** + * Given a node, return the best dimension and the best index to split on. + */ + template + 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 - static bool PairComp(const std::pair& p1, - const std::pair& p2) + template + static bool PairComp(const std::pair& p1, + const std::pair& p2) { return p1.first < p2.first; } diff --git a/src/mlpack/core/tree/rectangle_tree/r_star_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/r_star_tree_split_impl.hpp index d5669a6a24..6fe28a6407 100644 --- a/src/mlpack/core/tree/rectangle_tree/r_star_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/r_star_tree_split_impl.hpp @@ -20,6 +20,153 @@ namespace mlpack { namespace tree { +/** + * Reinsert any points into the tree, if needed. This returns the number of + * points reinserted. + */ +template +size_t RStarTreeSplit::ReinsertPoints(TreeType* tree, + std::vector& 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> sorted(tree->Count()); + arma::Col 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); + + // 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 +void RStarTreeSplit::PickLeafSplit(TreeType* tree, + size_t& bestAxis, + size_t& bestIndex) +{ + // Convenience typedef. + typedef typename TreeType::ElemType ElemType; + typedef bound::HRectBound BoundType; + + bestAxis = 0; + bestIndex = 0; + ElemType bestScore = std::numeric_limits::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 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 areas(numPossibleSplits, arma::fill::zeros); + arma::Col margins(numPossibleSplits, arma::fill::zeros); + arma::Col 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& relevels) { // Convenience typedef. typedef typename TreeType::ElemType ElemType; - typedef bound::HRectBound 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> sorted(tree->Count()); - arma::Col 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); - std::vector 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::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> 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); - - // We'll store each of the three scores for each distribution. - std::vector areas(tree->MaxLeafSize() - 2 * tree->MinLeafSize() + - 2); - std::vector margins(tree->MaxLeafSize() - 2 * tree->MinLeafSize() + - 2); - std::vector 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> 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); + std::sort(sorted.begin(), sorted.end(), PairComp); - 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& relevels typedef typename TreeType::ElemType ElemType; typedef bound::HRectBound 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::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* 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 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*> 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()<NumChildren() < tree->MinNumChildren()) { -// std::vector*> 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::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> 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 loDimValues(tree->NumChildren()); + arma::Col 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); + // 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 areas(2 * numPossibleSplits, arma::fill::zeros); + arma::Col margins(2 * numPossibleSplits, arma::fill::zeros); + arma::Col overlaps(2 * numPossibleSplits, arma::fill::zeros); - // We'll store each of the three scores for each distribution. - std::vector areas(tree->MaxNumChildren() - - 2 * tree->MinNumChildren() + 2); - std::vector margins(tree->MaxNumChildren() - - 2 * tree->MinNumChildren() + 2); - std::vector 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 oldChildren(tree->NumChildren()); + for (size_t i = 0; i < oldChildren.size(); ++i) + oldChildren[i] = &tree->Child(i); - std::vector> 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); - - // We'll store each of the three scores for each distribution. - std::vector areas(tree->MaxNumChildren() - - 2 * tree->MinNumChildren() + 2); - std::vector margins(tree->MaxNumChildren() - - 2 * tree->MinNumChildren() + 2); - std::vector 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> 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); - - 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 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; } diff --git a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp index d88452499a..3a31c76778 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp @@ -275,7 +275,6 @@ RectangleTree lvls(TreeDepth()); - for (size_t i = 0; i < lvls.size(); i++) - lvls[i] = true; + std::vector lvls(TreeDepth(), true); // If this is a leaf node, we stop here and add the point. if (numChildren == 0) @@ -452,9 +449,7 @@ bool RectangleTreeParent() != NULL) root = root->Parent(); - std::vector lvls(root->TreeDepth()); - for (size_t i = 0; i < lvls.size(); i++) - lvls[i] = true; + std::vector lvls(root->TreeDepth(), true); if (numChildren == 0) { @@ -1067,15 +1062,17 @@ void RectangleTreeNumChildren() > 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 RectangleTreeAuxiliaryInfo(); count = child->Count(); - child->SoftDelete(); + child->Count() = 0; + + delete child; return; } } diff --git a/src/mlpack/core/tree/rectangle_tree/x_tree_split.hpp b/src/mlpack/core/tree/rectangle_tree/x_tree_split.hpp index a3d6cefb7b..2c6ce57252 100644 --- a/src/mlpack/core/tree/rectangle_tree/x_tree_split.hpp +++ b/src/mlpack/core/tree/rectangle_tree/x_tree_split.hpp @@ -62,9 +62,9 @@ class XTreeSplit * Comparator for sorting with std::pair. This comparator works a little bit * faster then the default comparator. */ - template - static bool PairComp(const std::pair& p1, - const std::pair& p2) + template + static bool PairComp(const std::pair& p1, + const std::pair& p2) { return p1.first < p2.first; } diff --git a/src/mlpack/core/tree/rectangle_tree/x_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/x_tree_split_impl.hpp index b8482c41f7..0457d0a570 100644 --- a/src/mlpack/core/tree/rectangle_tree/x_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/x_tree_split_impl.hpp @@ -30,249 +30,87 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) { // Convenience typedef. typedef typename TreeType::ElemType ElemType; - typedef bound::HRectBound 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> sorted(tree->Count()); - arma::Col 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); - std::vector 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 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::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> 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); - - // We'll store each of the three scores for each distribution. - std::vector areas(tree->MaxLeafSize() - - 2 * tree->MinLeafSize() + 2); - std::vector margins(tree->MaxLeafSize() - - 2 * tree->MinLeafSize() + 2); - std::vector 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> 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); + std::sort(sorted.begin(), sorted.end(), PairComp); - 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& relevels) typedef typename TreeType::ElemType ElemType; typedef bound::HRectBound 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& relevels) // to save CPU time. // Find the next split axis. - std::vector axes(tree->Bound().Dim()); - std::vector dimensionsLastUsed(tree->NumChildren()); + std::vector axes(tree->Bound().Dim(), true); + std::vector 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& relevels) ElemType axisScore = 0.0; // We'll do Bound().Lo() now and use Bound().Hi() later. - std::vector> sorted(tree->NumChildren()); + std::vector> 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); + std::sort(sorted.begin(), sorted.end(), PairComp); // We'll store each of the three scores for each distribution. std::vector areas(tree->MaxNumChildren() - @@ -408,10 +230,10 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& 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& relevels) { ElemType axisScore = 0.0; - std::vector> sorted(tree->NumChildren()); + std::vector> 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); + std::sort(sorted.begin(), sorted.end(), PairComp); // We'll store each of the three scores for each distribution. std::vector areas(tree->MaxNumChildren() - @@ -513,10 +335,10 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& 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& relevels) } } - std::vector> sorted(tree->NumChildren()); + std::vector> 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& 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); + std::sort(sorted.begin(), sorted.end(), PairComp); - 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> 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> 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); + + 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); - - 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> 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); + + 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