From 922570e05c9ba0e005a706355b168ea036c02387 Mon Sep 17 00:00:00 2001 From: Marcus Edel Date: Fri, 28 Apr 2017 15:47:53 +0200 Subject: [PATCH 1/8] Build against Visual Studio 2015 and Visual Studio 2017. --- .appveyor.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 802f096076..e8015121bc 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\ From a3ecea1271d764c128683a51fe891f929c25dbc6 Mon Sep 17 00:00:00 2001 From: Marcus Edel Date: Fri, 28 Apr 2017 16:14:25 +0200 Subject: [PATCH 2/8] Update armadillo to version 7.800.2. --- .appveyor.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index e8015121bc..f58cab35bf 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -27,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.gz appveyor DownloadFile "http://sourceforge.net/projects/arma/files/armadillo-7.800.2.tar.xz" -FileName armadillo.tar.gz + - 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/*.*" From 31cfb4f17ee0857c0bc7d53580a01f2ad8e52b05 Mon Sep 17 00:00:00 2001 From: Marcus Edel Date: Fri, 28 Apr 2017 16:19:36 +0200 Subject: [PATCH 3/8] Use the correct/updated file ending tar.xz instead of tar.gz. --- .appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index f58cab35bf..d8fe306e40 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -27,7 +27,7 @@ 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-7.800.2.tar.xz" -FileName armadillo.tar.gz + - 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 .. @@ -51,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 From 1165dcc75a41da24d9c978b10edfc80cf5d4ef94 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 2 May 2017 15:00:50 -0400 Subject: [PATCH 4/8] Potential fix for OS X RectangleTree bug. --- .../tree/rectangle_tree/r_star_tree_split.hpp | 6 +- .../rectangle_tree/r_star_tree_split_impl.hpp | 420 ++++++++---- .../rectangle_tree/rectangle_tree_impl.hpp | 19 +- .../core/tree/rectangle_tree/x_tree_split.hpp | 6 +- .../tree/rectangle_tree/x_tree_split_impl.hpp | 612 +++++++++++------- 5 files changed, 690 insertions(+), 373 deletions(-) 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..7f1c036315 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 @@ -52,9 +52,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..51ddfeaf3f 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 @@ -35,29 +35,31 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) if (tree->Count() <= tree->MaxLeafSize()) return; +// std::cout << "split leaf node " << tree << " with parent " << tree->Parent() +//<< "\n"; // 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) - { +// 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(); +// 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); +// tree->children[(tree->NumChildren())++] = copy; +// assert(tree->NumChildren() == 1); - RStarTreeSplit::SplitLeafNode(copy,relevels); - return; - } +// RStarTreeSplit::SplitLeafNode(copy,relevels); +// return; +// } // If we haven't yet reinserted on this level, we try doing so now. - if (relevels[tree->TreeDepth()]) + if (relevels[tree->TreeDepth() - 1]) { - relevels[tree->TreeDepth()] = false; + 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. @@ -81,8 +83,8 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) sorted[i].second = i; } - std::sort(sorted.begin(), sorted.end(), PairComp); - std::vector pointIndices(p); + std::sort(sorted.begin(), sorted.end(), PairComp); + std::vector pointIndices(sorted.size()); for (size_t i = 0; i < p; i++) { @@ -119,7 +121,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) sorted[i].second = 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->MaxLeafSize() - 2 * tree->MinLeafSize() + @@ -195,56 +197,134 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) 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 (tiedOnOverlap) + if (tree->Parent()) { - for (size_t i = 0; i < tree->Count(); i++) +// std::cout << "first check parent " << tree->Parent() << " numDescendants " +//<< tree->Parent()->NumDescendants() << +//" with " << tree->Parent()->NumChildren() << " children\n"; +// size_t manualCount = 0; +// for (size_t i = 0; i < tree->Parent()->NumChildren(); ++i) +// { +// std::cout << "(" << &(tree->Parent()->Child(i)) << ") "; +// manualCount += tree->Parent()->Child(i).NumDescendants(); +// std::cout << tree->Parent()->Child(i).NumDescendants() << " "; +// } +// std::cout << "\n"; + // TreeType* treeOne = new TreeType(tree->Parent()); + // Now clean the node, and we will re-use this. + const size_t oldDescendants = tree->numDescendants; + const size_t numPoints = tree->count; + tree->numChildren = 0; + tree->numDescendants = 0; + tree->bound.Clear(); + tree->count = 0; + tree->begin = 0; + + TreeType* treeTwo = new TreeType(tree->Parent()); + + if (tiedOnOverlap) { - if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(tree->Point(sorted[i].second)); - else - treeTwo->InsertPoint(tree->Point(sorted[i].second)); + for (size_t i = 0; i < numPoints; i++) + { + if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) + tree->InsertPoint(sorted[i].second); + else + treeTwo->InsertPoint(sorted[i].second); + } } + else + { + for (size_t i = 0; i < numPoints; i++) + { + if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) + tree->InsertPoint(sorted[i].second); + else + treeTwo->InsertPoint(sorted[i].second); + } + } + + // Insert the new tree node. + 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. +// std::cout << "x: " << oldDescendants << " == " << tree->NumDescendants() << +//" + " << treeTwo->NumDescendants() << " (" << tree->NumDescendants() + +//treeTwo->NumDescendants() << ")\n"; + assert(oldDescendants == tree->NumDescendants() + treeTwo->NumDescendants()); +// std::cout << "check parent " << par << " numDescendants " << par->NumDescendants() << +//" with " << par->NumChildren() << " children\n"; +// manualCount = 0; +// for (size_t i = 0; i < par->NumChildren(); ++i) +// { +// std::cout << "(" << &(par->Child(i)) << ") "; +// manualCount += par->Child(i).NumDescendants(); +// std::cout << par->Child(i).NumDescendants() << " "; +// } +// std::cout << "\n"; +// assert(par->NumDescendants() == manualCount); + assert(par->NumChildren() <= par->MaxNumChildren() + 1); + if (par->NumChildren() == par->MaxNumChildren() + 1) + RStarTreeSplit::SplitNonLeafNode(par, relevels); + + assert(tree->Parent()->NumChildren() <= tree->MaxNumChildren()); + assert(tree->Parent()->NumChildren() >= tree->MinNumChildren()); + assert(treeTwo->Parent()->NumChildren() <= treeTwo->MaxNumChildren()); + assert(treeTwo->Parent()->NumChildren() >= treeTwo->MinNumChildren()); } else { - for (size_t i = 0; i < tree->Count(); i++) + TreeType* treeOne = new TreeType(tree); + TreeType* treeTwo = new TreeType(tree); + + const size_t oldNumDescendants = tree->NumDescendants(); + const size_t numPoints = tree->Count(); + tree->numChildren = 0; + tree->bound.Clear(); + tree->count = 0; + tree->begin = 0; + tree->numDescendants = 0; + + if (tiedOnOverlap) { - if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(tree->Point(sorted[i].second)); - else - treeTwo->InsertPoint(tree->Point(sorted[i].second)); + for (size_t i = 0; i < numPoints; ++i) + { + if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) + treeOne->InsertPoint(sorted[i].second); + else + treeTwo->InsertPoint(sorted[i].second); + } } + else + { + for (size_t i = 0; i < numPoints; ++i) + { + if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) + treeOne->InsertPoint(sorted[i].second); + else + treeTwo->InsertPoint(sorted[i].second); + } + } + + InsertNodeIntoTree(tree, treeOne); + InsertNodeIntoTree(tree, treeTwo); + +// std::cout << "y: " << oldNumDescendants << ": " << treeOne->NumDescendants() +//<< " + " << treeTwo->NumDescendants() << " (" << tree->NumDescendants() << +//")\n"; +// std::cout << "tree left " << treeOne->NumDescendants() << ", right " << +//treeTwo->NumDescendants() << ", parent " << tree->NumDescendants() << "\n"; +// for (size_t i = 0; i < tree->NumChildren(); ++i) +// std::cout << tree->Child(i).NumDescendants() << " "; +// std::cout << " (that's the children of the parent)\n"; + assert(oldNumDescendants == tree->NumDescendants()); } - - // 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(); } /** @@ -257,6 +337,8 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) template bool RStarTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) { +// std::cout << "split nonleaf node " << tree << " with parent " << +//tree->Parent() << "\n"; // Convenience typedef. typedef typename TreeType::ElemType ElemType; typedef bound::HRectBound BoundType; @@ -264,19 +346,19 @@ bool RStarTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels // 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) - { +// if (tree->Parent() == NULL) +// { // We actually want to copy this way. Pointers and everything. - TreeType* copy = new TreeType(*tree, false); +// TreeType* copy = new TreeType(*tree, false); - copy->Parent() = tree; - tree->NumChildren() = 0; - tree->NullifyData(); - tree->children[(tree->NumChildren())++] = copy; +// copy->Parent() = tree; +// tree->NumChildren() = 0; +// tree->NullifyData(); +// tree->children[(tree->NumChildren())++] = copy; - RStarTreeSplit::SplitNonLeafNode(copy,relevels); - return true; - } +// RStarTreeSplit::SplitNonLeafNode(copy,relevels); +// return true; +// } /* // If we haven't yet reinserted on this level, we try doing so now. @@ -358,7 +440,7 @@ bool RStarTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels sorted[i].second = 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() - @@ -441,7 +523,7 @@ bool RStarTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels sorted[i].second = 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() - @@ -514,13 +596,13 @@ bool RStarTreeSplit::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 @@ -528,68 +610,162 @@ bool RStarTreeSplit::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()); - TreeType* treeTwo = new TreeType(tree->Parent()); - - if (tiedOnOverlap) + if (tree->Parent() != NULL) { - for (size_t i = 0; i < tree->NumChildren(); i++) + const size_t oldNumDescendants = tree->NumDescendants(); +// for (size_t i = 0; i < tree->NumChildren(); ++i) +// std::cout << tree->Child(i).NumDescendants() << " "; +// std::cout << " (total " << tree->NumDescendants() << ", count " << +//tree->count << "\n"; + const size_t oldNumChildren = tree->NumChildren(); + tree->numChildren = 0; + tree->bound.Clear(); + tree->count = 0; + tree->begin = 0; + tree->numDescendants = 0; + TreeType* treeTwo = new TreeType(tree->Parent()); + + if (tiedOnOverlap) { - if (i < bestAreaIndexOnBestAxis + tree->MinNumChildren()) - InsertNodeIntoTree(treeOne, &(tree->Child(sorted[i].second))); - else - InsertNodeIntoTree(treeTwo, &(tree->Child(sorted[i].second))); + for (size_t i = 0; i < oldNumChildren; i++) + { + if (i < bestAreaIndexOnBestAxis + tree->MinNumChildren()) + { +// std::cout << "insert " << sorted[i].second->NumDescendants() << " into tree" +// << "\n"; + InsertNodeIntoTree(tree, sorted[i].second); + } + else + { +// std::cout << "insert " << sorted[i].second->NumDescendants() << " into treeTwo\n"; + InsertNodeIntoTree(treeTwo, sorted[i].second); + } + } } + else + { + for (size_t i = 0; i < oldNumChildren; i++) + { + if (i < bestOverlapIndexOnBestAxis + tree->MinNumChildren()) + { +// std::cout << "insert " << sorted[i].second->NumDescendants() << " into tree\n"; + InsertNodeIntoTree(tree, sorted[i].second); + } + else + { +// std::cout << "insert " << sorted[i].second->NumDescendants() << " into treeTwo\n"; + InsertNodeIntoTree(treeTwo, sorted[i].second); + } + } + } + + // Insert the new node into the tree. + 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. + assert(par->NumChildren() <= par->MaxNumChildren() + 1); + if (par->NumChildren() == par->MaxNumChildren() + 1) + RStarTreeSplit::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 < tree->NumChildren(); i++) + tree->children[i]->Parent() = tree; + + for (size_t i = 0; i < treeTwo->NumChildren(); i++) + treeTwo->children[i]->Parent() = treeTwo; + +// std::cout << "tree left " << tree->NumDescendants() << ", right " << +//treeTwo->NumDescendants() << ", parent " << par->NumDescendants() << "\n"; +// for (size_t i = 0; i < par->NumChildren(); ++i) +// std::cout << par->Child(i).NumDescendants() << " "; +// std::cout << " (that's the children of the parent)\n"; + assert(oldNumDescendants == (tree->NumDescendants() + +treeTwo->NumDescendants())); + + + assert(tree->Parent()->NumChildren() <= tree->MaxNumChildren()); + assert(tree->Parent()->NumChildren() >= tree->MinNumChildren()); + assert(treeTwo->Parent()->NumChildren() <= treeTwo->MaxNumChildren()); + assert(treeTwo->Parent()->NumChildren() >= treeTwo->MinNumChildren()); + + assert(tree->MaxNumChildren() < 7); + assert(treeTwo->MaxNumChildren() < 7); } else { - for (size_t i = 0; i < tree->NumChildren(); i++) + const size_t oldDescendants = tree->NumDescendants(); +// for (size_t i = 0; i < tree->NumChildren(); ++i) +// std::cout << tree->Child(i).NumDescendants() << " "; +// std::cout << " (total " << tree->NumDescendants() << ", count " << +//tree->count << "\n"; + TreeType* treeOne = new TreeType(tree); + TreeType* treeTwo = new TreeType(tree); + + const size_t oldNumChildren = tree->NumChildren(); + tree->count = 0; + tree->numChildren = 0; + tree->bound.Clear(); + tree->numDescendants = 0; + + if (tiedOnOverlap) { - if (i < bestOverlapIndexOnBestAxis + tree->MinNumChildren()) - InsertNodeIntoTree(treeOne, &(tree->Child(sorted[i].second))); - else - InsertNodeIntoTree(treeTwo, &(tree->Child(sorted[i].second))); + for (size_t i = 0; i < oldNumChildren; i++) + { + if (i < bestAreaIndexOnBestAxis + tree->MinNumChildren()) + { +// std::cout << "insert " << sorted[i].second->NumDescendants() << " into treeOne\n"; + InsertNodeIntoTree(treeOne, sorted[i].second); + } + else + { +// std::cout << "insert " << sorted[i].second->NumDescendants() << " into treeTwo\n"; + InsertNodeIntoTree(treeTwo, sorted[i].second); + } + } } + else + { + for (size_t i = 0; i < oldNumChildren; i++) + { + if (i < bestOverlapIndexOnBestAxis + tree->MinNumChildren()) + { +// std::cout << "insert " << sorted[i].second->NumDescendants() << " into treeOne\n"; + InsertNodeIntoTree(treeOne, sorted[i].second); + } + else + { +// std::cout << "insert " << sorted[i].second->NumDescendants() << " into treeTwo\n"; + InsertNodeIntoTree(treeTwo, sorted[i].second); + } + } + } + + InsertNodeIntoTree(tree, treeOne); + InsertNodeIntoTree(tree, treeTwo); + +// std::cout << oldDescendants << "; " << treeOne->numDescendants << ", " << +//treeTwo->numDescendants << " --> " << tree->numDescendants << "\n"; + assert(oldDescendants == (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->children[i]->Parent() = treeOne; + + for (size_t i = 0; i < treeTwo->NumChildren(); i++) + treeTwo->children[i]->Parent() = treeTwo; } - // Remove this node and insert treeOne and treeTwo - TreeType* par = tree->Parent(); - size_t index = 0; - while (par->children[index] != tree) { index++; } - - 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); - - // 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 < 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(); - return false; } @@ -600,9 +776,21 @@ bool RStarTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels template void RStarTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode) { +// std::cout << "insert " << srcNode << " into " << destTree << "\n"; + destTree->Bound() |= srcNode->Bound(); destTree->numDescendants += srcNode->numDescendants; destTree->children[destTree->NumChildren()++] = srcNode; + +// std::cout << "dest now has " << destTree->NumDescendants() << "\n"; +// size_t manualCount = 0; +// for (size_t i = 0; i < destTree->NumChildren(); ++i) +// { +// manualCount += destTree->Child(i).NumDescendants(); +// std::cout << destTree->Child(i).NumDescendants() << " "; +// } +// std::cout << "\n"; +// assert(manualCount == destTree->NumDescendants()); } } // namespace tree 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..6a0e4cfdd3 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) { @@ -991,6 +986,7 @@ void RectangleTreeCondenseTree(point, relevels, usePoint); // Now it should be safe to delete this node. + std::cout << "soft delete " << this << "\n"; SoftDelete(); return; @@ -1052,6 +1048,7 @@ void RectangleTreeCondenseTree(point, relevels, usePoint); // Now it should be safe to delete this node. + std::cout << "soft delete " << this << " 2\n"; SoftDelete(); return; @@ -1067,15 +1064,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 +1085,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..214cdae87f 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 @@ -38,24 +38,24 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) // 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) - { + //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(); + // 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; - } + // 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()]) + if (relevels[tree->TreeDepth() - 1]) { - relevels[tree->TreeDepth()] = false; + 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; @@ -77,10 +77,10 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) { sorted[i].first = tree->Metric().Evaluate(center, tree->Dataset().col(tree->Point(i))); - sorted[i].second = i; + sorted[i].second = i; } - std::sort(sorted.begin(), sorted.end(), PairComp); + std::sort(sorted.begin(), sorted.end(), PairComp); std::vector pointIndices(p); for (size_t i = 0; i < p; i++) @@ -129,7 +129,7 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) sorted[i].second = 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->MaxLeafSize() - @@ -202,77 +202,104 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) 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()); - - // 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) + if (tree->Parent() != NULL) { - for (size_t i = 0; i < tree->Count(); i++) + // We can reuse 'tree' as one of the two children. + //TreeType* treeOne = new TreeType(tree->Parent(), + // tree->AuxiliaryInfo().NormalNodeMaxNumChildren()); + TreeType* treeTwo = new TreeType(tree->Parent(), + tree->AuxiliaryInfo().NormalNodeMaxNumChildren()); + + const size_t oldCount = tree->Count(); + tree->Count() = 0; + + // 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) { - if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(tree->Point(sorted[i].second)); - else - treeTwo->InsertPoint(tree->Point(sorted[i].second)); + for (size_t i = 0; i < oldCount; i++) + { + if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) + tree->InsertPoint(sorted[i].second); + else + treeTwo->InsertPoint(sorted[i].second); + } } + else + { + for (size_t i = 0; i < oldCount; i++) + { + if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) + tree->InsertPoint(tree->Point(sorted[i].second)); + else + treeTwo->InsertPoint(tree->Point(sorted[i].second)); + } + } + + // Remove this node and insert treeOne and treeTwo. + TreeType* par = tree->Parent(); + par->children[par->NumChildren()++] = treeTwo; + + // We now update the split history of each new node. + tree->AuxiliaryInfo().SplitHistory().history[bestAxis] = true; + tree->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) + XTreeSplit::SplitNonLeafNode(par,relevels); } else { - for (size_t i = 0; i < tree->Count(); i++) + // We have to insert two nodes, and this node moves "up", since it is the + // root. + TreeType* treeOne = new TreeType(tree, + tree->AuxiliaryInfo().NormalNodeMaxNumChildren()); + TreeType* treeTwo = new TreeType(tree, + tree->AuxiliaryInfo().NormalNodeMaxNumChildren()); + + const size_t oldCount = tree->Count(); + tree->Count() = 0; + + if (tiedOnOverlap) { - if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(tree->Point(sorted[i].second)); - else - treeTwo->InsertPoint(tree->Point(sorted[i].second)); + for (size_t i = 0; i < oldCount; i++) + { + if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) + treeOne->InsertPoint(sorted[i].second); + else + treeTwo->InsertPoint(sorted[i].second); + } } - } - - // 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) + else { - index = i; - break; + for (size_t i = 0; i < oldCount; i++) + { + if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) + treeOne->InsertPoint(sorted[i].second); + else + treeTwo->InsertPoint(sorted[i].second); + } } + + tree->children[0] = treeOne; + tree->children[1] = treeTwo; + tree->numChildren = 2; + + treeOne->AuxiliaryInfo().SplitHistory().history[bestAxis] = true; + treeOne->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis; + treeTwo->AuxiliaryInfo().SplitHistory().history[bestAxis] = true; + treeTwo->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis; } - 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) - 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(); } /** @@ -292,18 +319,18 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) // 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) - { +// if (tree->Parent() == NULL) +// { // We actually want to copy this way. Pointers and everything. - TreeType* copy = new TreeType(*tree, false); +// 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; - } +// 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 @@ -373,14 +400,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 +435,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 +505,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 +540,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 +608,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 +622,282 @@ 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; - // 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) + { + 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); + + 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; + + // 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; } /** From c5074bf9c6661a5ce787ced34f467d68470f3f76 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Wed, 3 May 2017 17:19:00 -0400 Subject: [PATCH 5/8] Remove comments. --- src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp | 2 -- 1 file changed, 2 deletions(-) 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 6a0e4cfdd3..3a31c76778 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp @@ -986,7 +986,6 @@ void RectangleTreeCondenseTree(point, relevels, usePoint); // Now it should be safe to delete this node. - std::cout << "soft delete " << this << "\n"; SoftDelete(); return; @@ -1048,7 +1047,6 @@ void RectangleTreeCondenseTree(point, relevels, usePoint); // Now it should be safe to delete this node. - std::cout << "soft delete " << this << " 2\n"; SoftDelete(); return; From e23cdbdfd82dd55721060a9196c7438039878e3b Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Wed, 3 May 2017 17:19:16 -0400 Subject: [PATCH 6/8] Significant refactoring for clarity. --- .../rectangle_tree/r_star_tree_split_impl.hpp | 916 ++++++------------ 1 file changed, 287 insertions(+), 629 deletions(-) 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 51ddfeaf3f..3813936e55 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 @@ -33,30 +33,12 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) typedef typename TreeType::ElemType ElemType; typedef bound::HRectBound BoundType; + // If there's no need to split, don't. if (tree->Count() <= tree->MaxLeafSize()) return; -// std::cout << "split leaf node " << tree << " with parent " << tree->Parent() -//<< "\n"; - // 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); -// return; -// } - - // If we haven't yet reinserted on this level, we try doing so now. + // If we haven't yet checked if we need to reinsert on this level, we try + // doing so now. if (relevels[tree->TreeDepth() - 1]) { relevels[tree->TreeDepth() - 1] = false; @@ -67,132 +49,121 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) while (root->Parent() != NULL) root = root->Parent(); size_t p = tree->MaxLeafSize() * 0.3; // The paper says this works the best. - if (p == 0) + if (p > 0) { - RStarTreeSplit::SplitLeafNode(tree,relevels); + // 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); + + // Any reinsertions take care of splitting. 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(sorted.size()); - - 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(); + // We don't need to reinsert. Instead, we need to split the node. + size_t bestAxis = 0; + size_t bestSplitIndex = 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; - // 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); + // 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. - 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); + 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 < 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->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; + size_t splitIndex = 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 = 0; l < splitIndex; l++) + bound1 |= tree->Dataset().col(tree->Point(sortedIndices[l])); - for (size_t l = cutOff; l < tree->Count(); l++) - bound2 |= tree->Dataset().col(tree->Point(sorted[l].second)); + for (size_t l = splitIndex; l < tree->Count(); l++) + bound2 |= tree->Dataset().col(tree->Point(sortedIndices[l])); - ElemType area1 = bound1.Volume(); - ElemType area2 = bound2.Volume(); - ElemType oArea = bound1.Overlap(bound2); + 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(); - areas[i] += area1 + area2; - overlapedAreas[i] += oArea; axisScore += margins[i]; } - if (axisScore < bestAxisScore) + // Is this dimension a new best score? We want the lowest possible score. + if (axisScore < bestScore) { - bestAxisScore = axisScore; + bestScore = axisScore; bestAxis = j; - bestOverlapIndexOnBestAxis = 0; - bestAreaIndexOnBestAxis = 0; + size_t overlapIndex = 0; + size_t areaIndex = 0; + bool tiedOnOverlap = false; - for (size_t i = 1; i < areas.size(); i++) + for (size_t i = 1; i < areas.n_elem; i++) { - if (overlapedAreas[i] < overlapedAreas[bestOverlapIndexOnBestAxis]) + if (overlaps[i] < overlaps[overlapIndex]) { tiedOnOverlap = false; - bestAreaIndexOnBestAxis = i; - bestOverlapIndexOnBestAxis = i; + overlapIndex = i; + areaIndex = i; } - else if (overlapedAreas[i] == - overlapedAreas[bestOverlapIndexOnBestAxis]) + else if (overlaps[i] == overlaps[overlapIndex]) { tiedOnOverlap = true; - if (areas[i] < areas[bestAreaIndexOnBestAxis]) - bestAreaIndexOnBestAxis = i; + if (areas[i] < areas[areaIndex]) + areaIndex = i; } } + + // Select the best index for splitting. + bestSplitIndex = (tiedOnOverlap ? areaIndex : overlapIndex); } } + /** + * 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++) { @@ -202,128 +173,57 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) std::sort(sorted.begin(), sorted.end(), PairComp); - if (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); + + // 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++) { -// std::cout << "first check parent " << tree->Parent() << " numDescendants " -//<< tree->Parent()->NumDescendants() << -//" with " << tree->Parent()->NumChildren() << " children\n"; -// size_t manualCount = 0; -// for (size_t i = 0; i < tree->Parent()->NumChildren(); ++i) -// { -// std::cout << "(" << &(tree->Parent()->Child(i)) << ") "; -// manualCount += tree->Parent()->Child(i).NumDescendants(); -// std::cout << tree->Parent()->Child(i).NumDescendants() << " "; -// } -// std::cout << "\n"; - // TreeType* treeOne = new TreeType(tree->Parent()); - // Now clean the node, and we will re-use this. - const size_t oldDescendants = tree->numDescendants; - const size_t numPoints = tree->count; - tree->numChildren = 0; - tree->numDescendants = 0; - tree->bound.Clear(); - tree->count = 0; - tree->begin = 0; - - TreeType* treeTwo = new TreeType(tree->Parent()); - - if (tiedOnOverlap) - { - for (size_t i = 0; i < numPoints; i++) - { - if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) - tree->InsertPoint(sorted[i].second); - else - treeTwo->InsertPoint(sorted[i].second); - } - } + if (i < bestSplitIndex + tree->MinLeafSize()) + treeOne->InsertPoint(sorted[i].second); else - { - for (size_t i = 0; i < numPoints; i++) - { - if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) - tree->InsertPoint(sorted[i].second); - else - treeTwo->InsertPoint(sorted[i].second); - } - } + treeTwo->InsertPoint(sorted[i].second); + } - // Insert the new tree node. - TreeType* par = tree->Parent(); + // Insert the new tree node(s). + if (par) + { + // Just insert the new node into the 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. -// std::cout << "x: " << oldDescendants << " == " << tree->NumDescendants() << -//" + " << treeTwo->NumDescendants() << " (" << tree->NumDescendants() + -//treeTwo->NumDescendants() << ")\n"; - assert(oldDescendants == tree->NumDescendants() + treeTwo->NumDescendants()); -// std::cout << "check parent " << par << " numDescendants " << par->NumDescendants() << -//" with " << par->NumChildren() << " children\n"; -// manualCount = 0; -// for (size_t i = 0; i < par->NumChildren(); ++i) -// { -// std::cout << "(" << &(par->Child(i)) << ") "; -// manualCount += par->Child(i).NumDescendants(); -// std::cout << par->Child(i).NumDescendants() << " "; -// } -// std::cout << "\n"; -// assert(par->NumDescendants() == manualCount); - assert(par->NumChildren() <= par->MaxNumChildren() + 1); + // 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); - - assert(tree->Parent()->NumChildren() <= tree->MaxNumChildren()); - assert(tree->Parent()->NumChildren() >= tree->MinNumChildren()); - assert(treeTwo->Parent()->NumChildren() <= treeTwo->MaxNumChildren()); - assert(treeTwo->Parent()->NumChildren() >= treeTwo->MinNumChildren()); } else { - TreeType* treeOne = new TreeType(tree); - TreeType* treeTwo = new TreeType(tree); - - const size_t oldNumDescendants = tree->NumDescendants(); - const size_t numPoints = tree->Count(); - tree->numChildren = 0; - tree->bound.Clear(); - tree->count = 0; - tree->begin = 0; - tree->numDescendants = 0; - - if (tiedOnOverlap) - { - for (size_t i = 0; i < numPoints; ++i) - { - if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(sorted[i].second); - else - treeTwo->InsertPoint(sorted[i].second); - } - } - else - { - for (size_t i = 0; i < numPoints; ++i) - { - if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(sorted[i].second); - else - treeTwo->InsertPoint(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); - -// std::cout << "y: " << oldNumDescendants << ": " << treeOne->NumDescendants() -//<< " + " << treeTwo->NumDescendants() << " (" << tree->NumDescendants() << -//")\n"; -// std::cout << "tree left " << treeOne->NumDescendants() << ", right " << -//treeTwo->NumDescendants() << ", parent " << tree->NumDescendants() << "\n"; -// for (size_t i = 0; i < tree->NumChildren(); ++i) -// std::cout << tree->Child(i).NumDescendants() << " "; -// std::cout << " (that's the children of the parent)\n"; - assert(oldNumDescendants == tree->NumDescendants()); } } @@ -337,435 +237,205 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) template bool RStarTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) { -// std::cout << "split nonleaf node " << tree << " with parent " << -//tree->Parent() << "\n"; // Convenience typedef. 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++) - { - sorted[i].first = tree->Child(i).Bound()[j].Lo(); - 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; - 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; - } - } - } - } - - // 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; - - 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 = &tree->Child(i); - } - } - else - { - for (size_t i = 0; i < sorted.size(); i++) - { - sorted[i].first = tree->Child(i).Bound()[bestAxis].Hi(); - sorted[i].second = &tree->Child(i); - } - } - - std::sort(sorted.begin(), sorted.end(), PairComp); - - if (tree->Parent() != NULL) - { - const size_t oldNumDescendants = tree->NumDescendants(); -// for (size_t i = 0; i < tree->NumChildren(); ++i) -// std::cout << tree->Child(i).NumDescendants() << " "; -// std::cout << " (total " << tree->NumDescendants() << ", count " << -//tree->count << "\n"; - const size_t oldNumChildren = tree->NumChildren(); - tree->numChildren = 0; - tree->bound.Clear(); - tree->count = 0; - tree->begin = 0; - tree->numDescendants = 0; - TreeType* treeTwo = new TreeType(tree->Parent()); - - if (tiedOnOverlap) - { - for (size_t i = 0; i < oldNumChildren; i++) - { - if (i < bestAreaIndexOnBestAxis + tree->MinNumChildren()) - { -// std::cout << "insert " << sorted[i].second->NumDescendants() << " into tree" -// << "\n"; - InsertNodeIntoTree(tree, sorted[i].second); - } - else - { -// std::cout << "insert " << sorted[i].second->NumDescendants() << " into treeTwo\n"; - InsertNodeIntoTree(treeTwo, sorted[i].second); - } - } - } - else - { - for (size_t i = 0; i < oldNumChildren; i++) - { - if (i < bestOverlapIndexOnBestAxis + tree->MinNumChildren()) - { -// std::cout << "insert " << sorted[i].second->NumDescendants() << " into tree\n"; - InsertNodeIntoTree(tree, sorted[i].second); - } - else - { -// std::cout << "insert " << sorted[i].second->NumDescendants() << " into treeTwo\n"; - InsertNodeIntoTree(treeTwo, sorted[i].second); - } - } - } - - // Insert the new node into the tree. - 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. - assert(par->NumChildren() <= par->MaxNumChildren() + 1); - if (par->NumChildren() == par->MaxNumChildren() + 1) - RStarTreeSplit::SplitNonLeafNode(par,relevels); - - // We have to update the children of each of these new nodes so that they - // record the correct parent. + // 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++) - tree->children[i]->Parent() = tree; + { + 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); - for (size_t i = 0; i < treeTwo->NumChildren(); i++) - treeTwo->children[i]->Parent() = treeTwo; + // 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); -// std::cout << "tree left " << tree->NumDescendants() << ", right " << -//treeTwo->NumDescendants() << ", parent " << par->NumDescendants() << "\n"; -// for (size_t i = 0; i < par->NumChildren(); ++i) -// std::cout << par->Child(i).NumDescendants() << " "; -// std::cout << " (that's the children of the parent)\n"; - assert(oldNumDescendants == (tree->NumDescendants() + -treeTwo->NumDescendants())); + 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; + BoundType lb1(tree->Bound().Dim()); + BoundType lb2(tree->Bound().Dim()); + BoundType hb1(tree->Bound().Dim()); + BoundType hb2(tree->Bound().Dim()); - assert(tree->Parent()->NumChildren() <= tree->MaxNumChildren()); - assert(tree->Parent()->NumChildren() >= tree->MinNumChildren()); - assert(treeTwo->Parent()->NumChildren() <= treeTwo->MaxNumChildren()); - assert(treeTwo->Parent()->NumChildren() >= treeTwo->MinNumChildren()); + 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(); + } - assert(tree->MaxNumChildren() < 7); - assert(treeTwo->MaxNumChildren() < 7); + // Calculate low bound distributions. + areas[2 * i] = lb1.Volume() + lb2.Volume(); + overlaps[2 * i] = lb1.Overlap(lb2); + + // Calculate high bound distributions. + areas[2 * i + 1] = hb1.Volume() + hb2.Volume(); + overlaps[2 * i + 1] = hb1.Overlap(hb2); + + // 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(); + } + + // The score we use is the sum of all scores. + axisLoScore += margins[2 * i]; + axisHiScore += margins[2 * i + 1]; + } + + // 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) + { + bestScore = std::min(axisLoScore, axisHiScore); + if (axisLoScore < axisHiScore) + lowIsBetter = true; + else + lowIsBetter = false; + + bestAxis = j; + + // 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++) + { + // Check bounds. + if (overlaps[2 * i + indexOffset] < overlaps[overlapIndex]) + { + tiedOnOverlap = false; + areaIndex = 2 * i + indexOffset; + overlapIndex = 2 * i + indexOffset; + } + else if (overlaps[i] == overlaps[overlapIndex]) + { + tiedOnOverlap = true; + if (areas[2 * i + indexOffset] < areas[areaIndex]) + areaIndex = 2 * i + indexOffset; + } + } + + bestIndex = ((tiedOnOverlap ? areaIndex : overlapIndex) - indexOffset) / 2 + + tree->MinNumChildren(); + } + } + + // 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); + + /** + * 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(); + TreeType* treeOne = par ? tree : new TreeType(tree); + TreeType* treeTwo = par ? new TreeType(par) : new TreeType(tree); + + // Now clean the node. + tree->numChildren = 0; + tree->numDescendants = 0; + tree->count = 0; + tree->bound.Clear(); + + // 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); + + 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 { - const size_t oldDescendants = tree->NumDescendants(); -// for (size_t i = 0; i < tree->NumChildren(); ++i) -// std::cout << tree->Child(i).NumDescendants() << " "; -// std::cout << " (total " << tree->NumDescendants() << ", count " << -//tree->count << "\n"; - TreeType* treeOne = new TreeType(tree); - TreeType* treeTwo = new TreeType(tree); - - const size_t oldNumChildren = tree->NumChildren(); - tree->count = 0; - tree->numChildren = 0; - tree->bound.Clear(); - tree->numDescendants = 0; - - if (tiedOnOverlap) - { - for (size_t i = 0; i < oldNumChildren; i++) - { - if (i < bestAreaIndexOnBestAxis + tree->MinNumChildren()) - { -// std::cout << "insert " << sorted[i].second->NumDescendants() << " into treeOne\n"; - InsertNodeIntoTree(treeOne, sorted[i].second); - } - else - { -// std::cout << "insert " << sorted[i].second->NumDescendants() << " into treeTwo\n"; - InsertNodeIntoTree(treeTwo, sorted[i].second); - } - } - } - else - { - for (size_t i = 0; i < oldNumChildren; i++) - { - if (i < bestOverlapIndexOnBestAxis + tree->MinNumChildren()) - { -// std::cout << "insert " << sorted[i].second->NumDescendants() << " into treeOne\n"; - InsertNodeIntoTree(treeOne, sorted[i].second); - } - else - { -// std::cout << "insert " << sorted[i].second->NumDescendants() << " into treeTwo\n"; - InsertNodeIntoTree(treeTwo, sorted[i].second); - } - } - } - + // Insert both nodes into 'tree', which is now a higher-level root node. InsertNodeIntoTree(tree, treeOne); InsertNodeIntoTree(tree, treeTwo); -// std::cout << oldDescendants << "; " << treeOne->numDescendants << ", " << -//treeTwo->numDescendants << " --> " << tree->numDescendants << "\n"; - assert(oldDescendants == (treeOne->numDescendants + - treeTwo->numDescendants)); - - // We have to update the children of each of these new nodes so that they - // record the correct parent. + // 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; - - for (size_t i = 0; i < treeTwo->NumChildren(); i++) - treeTwo->children[i]->Parent() = treeTwo; } + // Update the children of treeTwo to have the correct parent. + for (size_t i = 0; i < treeTwo->NumChildren(); i++) + treeTwo->children[i]->Parent() = treeTwo; + + // 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; } @@ -776,21 +446,9 @@ treeTwo->NumDescendants())); template void RStarTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode) { -// std::cout << "insert " << srcNode << " into " << destTree << "\n"; - destTree->Bound() |= srcNode->Bound(); destTree->numDescendants += srcNode->numDescendants; destTree->children[destTree->NumChildren()++] = srcNode; - -// std::cout << "dest now has " << destTree->NumDescendants() << "\n"; -// size_t manualCount = 0; -// for (size_t i = 0; i < destTree->NumChildren(); ++i) -// { -// manualCount += destTree->Child(i).NumDescendants(); -// std::cout << destTree->Child(i).NumDescendants() << " "; -// } -// std::cout << "\n"; -// assert(manualCount == destTree->NumDescendants()); } } // namespace tree From 95e6c15f7e187f2fbe94674d213473021014da79 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Thu, 4 May 2017 16:36:25 -0400 Subject: [PATCH 7/8] Refactoring and cleanup. The X tree split code is not truly refactored, but I think it needs further inspection. At the very least, it seems to work as-is. --- .../tree/rectangle_tree/r_star_tree_split.hpp | 13 + .../rectangle_tree/r_star_tree_split_impl.hpp | 75 ++-- .../tree/rectangle_tree/x_tree_split_impl.hpp | 337 ++++-------------- 3 files changed, 135 insertions(+), 290 deletions(-) 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 7f1c036315..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. 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 3813936e55..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 @@ -21,24 +21,17 @@ namespace mlpack { namespace tree { /** - * 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 - * points to the two new nodes. Finally, we delete the old node and insert the - * new nodes into the tree, spliting the parent if necessary. + * Reinsert any points into the tree, if needed. This returns the number of + * points reinserted. */ template -void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) +size_t RStarTreeSplit::ReinsertPoints(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 haven't yet checked if we need to reinsert on this level, we try - // doing so now. + // Check if we need to reinsert. if (relevels[tree->TreeDepth() - 1]) { relevels[tree->TreeDepth() - 1] = false; @@ -60,7 +53,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < sorted.size(); i++) { sorted[i].first = tree->Metric().Evaluate(center, - tree->dataset->col(tree->Point(i))); + tree->Dataset().col(tree->Point(i))); sorted[i].second = tree->Point(i); } @@ -74,15 +67,28 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) // the center first. for (size_t i = p; i > 0; --i) root->InsertPoint(sorted[sorted.size() - i].second, relevels); - - // Any reinsertions take care of splitting. - return; } + + return p; } - // We don't need to reinsert. Instead, we need to split the node. - size_t bestAxis = 0; - size_t bestSplitIndex = 0; + 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(); /** @@ -156,9 +162,36 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) } // Select the best index for splitting. - bestSplitIndex = (tiedOnOverlap ? areaIndex : overlapIndex); + 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 + * points to the two new nodes. Finally, we delete the old node and insert the + * new nodes into the tree, spliting the parent if necessary. + */ +template +void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) +{ + // Convenience typedef. + typedef typename TreeType::ElemType ElemType; + + // If there's no need to split, don't. + if (tree->Count() <= tree->MaxLeafSize()) + return; + + // 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; + + // 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 @@ -201,7 +234,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) // Insert the points into the appropriate tree. for (size_t i = 0; i < numPoints; i++) { - if (i < bestSplitIndex + tree->MinLeafSize()) + if (i < bestIndex + tree->MinLeafSize()) treeOne->InsertPoint(sorted[i].second); else treeTwo->InsertPoint(sorted[i].second); 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 214cdae87f..0f0ff79334 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,174 +30,24 @@ 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() - 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(); - - // 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++) { @@ -207,99 +57,60 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) std::sort(sorted.begin(), sorted.end(), PairComp); - if (tree->Parent() != NULL) + /** + * 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); + + // 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++) { - // We can reuse 'tree' as one of the two children. - //TreeType* treeOne = new TreeType(tree->Parent(), - // tree->AuxiliaryInfo().NormalNodeMaxNumChildren()); - TreeType* treeTwo = new TreeType(tree->Parent(), - tree->AuxiliaryInfo().NormalNodeMaxNumChildren()); - - const size_t oldCount = tree->Count(); - tree->Count() = 0; - - // 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) - { - for (size_t i = 0; i < oldCount; i++) - { - if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) - tree->InsertPoint(sorted[i].second); - else - treeTwo->InsertPoint(sorted[i].second); - } - } + if (i < bestIndex + tree->MinLeafSize()) + treeOne->InsertPoint(sorted[i].second); else - { - for (size_t i = 0; i < oldCount; i++) - { - if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) - tree->InsertPoint(tree->Point(sorted[i].second)); - else - treeTwo->InsertPoint(tree->Point(sorted[i].second)); - } - } + treeTwo->InsertPoint(sorted[i].second); + } - // Remove this node and insert treeOne and treeTwo. - TreeType* par = tree->Parent(); + // Insert the new tree node(s). + if (par) + { par->children[par->NumChildren()++] = treeTwo; - - // We now update the split history of each new node. - tree->AuxiliaryInfo().SplitHistory().history[bestAxis] = true; - tree->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) - XTreeSplit::SplitNonLeafNode(par,relevels); } else { - // We have to insert two nodes, and this node moves "up", since it is the - // root. - TreeType* treeOne = new TreeType(tree, - tree->AuxiliaryInfo().NormalNodeMaxNumChildren()); - TreeType* treeTwo = new TreeType(tree, - tree->AuxiliaryInfo().NormalNodeMaxNumChildren()); - - const size_t oldCount = tree->Count(); - tree->Count() = 0; - - if (tiedOnOverlap) - { - for (size_t i = 0; i < oldCount; i++) - { - if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(sorted[i].second); - else - treeTwo->InsertPoint(sorted[i].second); - } - } - else - { - for (size_t i = 0; i < oldCount; i++) - { - if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(sorted[i].second); - else - treeTwo->InsertPoint(sorted[i].second); - } - } - - tree->children[0] = treeOne; - tree->children[1] = treeTwo; - tree->numChildren = 2; - - treeOne->AuxiliaryInfo().SplitHistory().history[bestAxis] = true; - treeOne->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis; - treeTwo->AuxiliaryInfo().SplitHistory().history[bestAxis] = true; - treeTwo->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis; + InsertNodeIntoTree(tree, treeOne); + InsertNodeIntoTree(tree, 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; + + // If we overflowed the parent, split it. + if (par && par->NumChildren() == par->MaxNumChildren() + 1) + XTreeSplit::SplitNonLeafNode(par,relevels); } /** @@ -316,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. @@ -340,29 +135,28 @@ 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]; + axes[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++) @@ -634,6 +428,7 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) 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; @@ -656,6 +451,8 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) { if (overlapBestOverlapAxis/areaBestOverlapAxis < MAX_OVERLAP) { + tree->numDescendants = 0; + tree->bound.Clear(); for (size_t i = 0; i < numChildren; i++) { if (i < bestOverlapIndexOnBestAxis + tree->MinNumChildren()) @@ -696,6 +493,8 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) } 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()) @@ -888,6 +687,7 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) 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. @@ -909,8 +709,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 From c614984305f6e5627a3c0d4ef706e5330c6db7c7 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Thu, 4 May 2017 16:38:09 -0400 Subject: [PATCH 8/8] operator&=() doesn't exist, and some line width fixes. --- .../tree/rectangle_tree/x_tree_split_impl.hpp | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) 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 0f0ff79334..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 @@ -149,7 +149,8 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = lastDim + 1; i < axes.size(); i++) { for (size_t j = 0; j < tree->NumChildren(); j++) - axes[i] &= tree->Child(j).AuxiliaryInfo().SplitHistory().history[i]; + axes[i] = axes[i] & + tree->Child(j).AuxiliaryInfo().SplitHistory().history[i]; if (axes[i] == true) { minOverlapSplitDimension = i; @@ -466,8 +467,8 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) } // 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 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. @@ -506,13 +507,13 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) 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. + // (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. + // 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)) @@ -558,7 +559,7 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) 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 + // 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() << ", "