From 87e008f478da52c0669ddefe8a6a0372800eef65 Mon Sep 17 00:00:00 2001 From: Nicolas Mellado Date: Wed, 18 Sep 2024 21:25:31 +0200 Subject: [PATCH] [SpatialPartitioning] Add test for KdTree datastructure Aim at detecting problems with - the structure KdTreeDefaultTraits (copies, references, ...) - duplicated samples --- tests/common/kdtree_utils.h | 52 +++++++----- tests/src/CMakeLists.txt | 1 + tests/src/kdtree.cpp | 152 ++++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 20 deletions(-) create mode 100644 tests/src/kdtree.cpp diff --git a/tests/common/kdtree_utils.h b/tests/common/kdtree_utils.h index e75c9107c..0a6dcd6d0 100644 --- a/tests/common/kdtree_utils.h +++ b/tests/common/kdtree_utils.h @@ -51,9 +51,11 @@ class MyPoint { }; template -bool check_range_neighbors(const VectorContainer& points, const std::vector& sampling, int index, Scalar r, const std::vector& neighbors) +bool check_range_neighbors(const VectorContainer& points, const std::vector& sampling, int index, Scalar r, + const std::vector& neighbors, + bool allow_duplicates = false) { - if (has_duplicate(neighbors)) + if (!allow_duplicates && has_duplicate(neighbors)) { return false; } @@ -103,9 +105,11 @@ bool check_range_neighbors(const VectorContainer& points, const std::vector } template -bool check_range_neighbors(const VectorContainer& points, const std::vector& sampling, const VectorType& point, Scalar r, const std::vector& neighbors) +bool check_range_neighbors(const VectorContainer& points, const std::vector& sampling, + const VectorType& point, Scalar r, const std::vector& neighbors, + bool allow_duplicates = false) { - if (has_duplicate(neighbors)) + if (!allow_duplicates && has_duplicate(neighbors)) { return false; } @@ -147,14 +151,15 @@ bool check_range_neighbors(const VectorContainer& points, const std::vector } template -bool check_k_nearest_neighbors(const VectorContainer& points, int index, int k, const std::vector& neighbors) +bool check_k_nearest_neighbors(const VectorContainer& points, int index, int k, + const std::vector& neighbors, bool allow_duplicates = false) { if (int(points.size()) > k && int(neighbors.size()) != k) { return false; } - if (has_duplicate(neighbors)) + if (!allow_duplicates && has_duplicate(neighbors)) { return false; } @@ -190,14 +195,15 @@ bool check_k_nearest_neighbors(const VectorContainer& points, int index, int k, } template -bool check_k_nearest_neighbors(const VectorContainer& points, const std::vector& sampling, int index, int k, const std::vector& neighbors) +bool check_k_nearest_neighbors(const VectorContainer& points, const std::vector& sampling, int index, int k, + const std::vector& neighbors, bool allow_duplicates = false) { if (int(points.size()) > k && int(neighbors.size()) != k) { return false; } - if (has_duplicate(neighbors)) + if (!allow_duplicates && has_duplicate(neighbors)) { return false; } @@ -242,14 +248,16 @@ bool check_k_nearest_neighbors(const VectorContainer& points, const std::vector< } template -bool check_k_nearest_neighbors(const VectorContainer& points, const std::vector& sampling, const VectorType& point, int k, const std::vector& neighbors) +bool check_k_nearest_neighbors(const VectorContainer& points, const std::vector& sampling, + const VectorType& point, int k, const std::vector& neighbors, + bool allow_duplicates = false) { if (int(sampling.size()) >= k && int(neighbors.size()) != k) { return false; } - if (has_duplicate(neighbors)) + if (!allow_duplicates && has_duplicate(neighbors)) { return false; } @@ -287,14 +295,15 @@ bool check_k_nearest_neighbors(const VectorContainer& points, const std::vector< template -bool check_k_nearest_neighbors(const VectorContainer& points, const VectorType& point, int k, const std::vector& neighbors) +bool check_k_nearest_neighbors(const VectorContainer& points, const VectorType& point, int k, + const std::vector& neighbors, bool allow_duplicates = false) { if (int(points.size()) >= k && int(neighbors.size()) != k) { return false; } - if (has_duplicate(neighbors)) + if (!allow_duplicates && has_duplicate(neighbors)) { return false; } @@ -323,24 +332,27 @@ bool check_k_nearest_neighbors(const VectorContainer& points, const VectorType& template -bool check_nearest_neighbor(const VectorContainer& points, int index, int nearest) +bool check_nearest_neighbor(const VectorContainer& points, int index, int nearest, bool allow_duplicates = false) { - return check_k_nearest_neighbors(points, index, 1, { nearest }); + return check_k_nearest_neighbors(points, index, 1, { nearest }, allow_duplicates); } template -bool check_nearest_neighbor(const VectorContainer& points, const VectorType& point, int nearest) +bool check_nearest_neighbor(const VectorContainer& points, const VectorType& point, int nearest, + bool allow_duplicates = false) { - return check_k_nearest_neighbors(points, point, 1, { nearest }); + return check_k_nearest_neighbors(points, point, 1, { nearest }, allow_duplicates); } template -bool check_nearest_neighbor(const VectorContainer& points, const std::vector& sampling, const VectorType& point, int nearest) +bool check_nearest_neighbor(const VectorContainer& points, const std::vector& sampling, + const VectorType& point, int nearest, bool allow_duplicates = false) { - return check_k_nearest_neighbors(points, sampling, point, 1, { nearest }); + return check_k_nearest_neighbors(points, sampling, point, 1, { nearest }, allow_duplicates); } template -bool check_nearest_neighbor(const VectorContainer& points, const std::vector& sampling, int index, int nearest) +bool check_nearest_neighbor(const VectorContainer& points, const std::vector& sampling, int index, int nearest, + bool allow_duplicates = false) { - return check_k_nearest_neighbors(points, sampling, index, 1, { nearest }); + return check_k_nearest_neighbors(points, sampling, index, 1, { nearest }, allow_duplicates); } \ No newline at end of file diff --git a/tests/src/CMakeLists.txt b/tests/src/CMakeLists.txt index c35c7507b..5fb17fc76 100644 --- a/tests/src/CMakeLists.txt +++ b/tests/src/CMakeLists.txt @@ -23,3 +23,4 @@ add_multi_test(weight_kernel.cpp) add_multi_test(queries_range.cpp) add_multi_test(queries_nearest.cpp) add_multi_test(queries_knearest.cpp) +add_multi_test(kdtree.cpp) diff --git a/tests/src/kdtree.cpp b/tests/src/kdtree.cpp new file mode 100644 index 000000000..820227211 --- /dev/null +++ b/tests/src/kdtree.cpp @@ -0,0 +1,152 @@ +/* + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + + \file Test general properties of the KdTree +*/ + +#include "../common/testing.h" +#include "../common/testUtils.h" +#include "../common/has_duplicate.h" +#include "../common/kdtree_utils.h" + +#include +#include + +using namespace Ponca; + + +template +void testKdtreeWithDuplicate() +{ + using Scalar = typename DataPoint::Scalar; + using VectorContainer = typename KdTreeSparse::PointContainer; + using VectorType = typename DataPoint::VectorType; + + // Number of point samples in each KdTree leaf +#ifdef PONCA_DEBUG + const int cellSize = 6; + const int nbCells = 2; + const int N = nbCells*cellSize; +#else + const int cellSize = 64; + const int nbCells = 100; + const int N = nbCells*cellSize; +#endif + + const Scalar r = 0.001; + + auto test_tree = [r] (const auto& points, const auto&indices, const int cellSize) -> void + { + KdTreeSparse tree(points, indices, cellSize); + +#ifndef PONCA_DEBUG +#pragma omp parallel for default(none) shared(tree, points, indices, g_test_stack, r) +#endif + for (int i = 0; i < points.size(); ++i) + { + VectorType point = points[i].pos();//VectorType::Random(); // values between [-1:1] + std::vector results; + + for (int j : tree.range_neighbors(point, r)) { + results.push_back(j); + } + + bool res = check_range_neighbors(points, indices, point, r, results, true); + VERIFY(res); + } + }; + + + // Generate N random points + typename KdTreeDense::IndexContainer ids(N); + std::iota(ids.begin(), ids.end(), 0); + + auto points = VectorContainer(N); + std::generate(points.begin(), points.end(), []() {return DataPoint(VectorType::Random()); }); + + // Test on 100% random points + { + test_tree(points, ids, cellSize); + } + + // Generate a small part of duplicates by extending the index container + { + const int nbDuplicates = N; + + ids.resize(nbDuplicates+N); + std::generate(ids.begin()+N, ids.end(), [N]() {return Eigen::internal::random(0,N-1); }); + + test_tree(points, ids, cellSize); + } + + // Generate duplicated coordinates samples TODO + // { +// const int nbDuplicates = N/10; +// const int nbUniques = N; +// +// auto points = VectorContainer(nbUniques); +// std::generate(points.begin(), points.end(), []() {return DataPoint(VectorType::Random()); }); +// +// typename KdTreeDense::IndexContainer ids(nbUniques); +// std::iota(ids.begin(), ids.end(), 0); +// ids.resize(nbDuplicates*nbUniques); +// +// for (int i = 1; i < nbDuplicates; ++i) +// { +// std::copy(ids.begin(), ids.begin() + nbUniques, ids.begin()+(nbUniques*i)); +// } +// test_tree(points, ids, cellSize); +// } + +} + +template +void testKdTreeNode() { + std::vector buffer; + + buffer.resize(10); + + // simple predicate that only check if a node is a leaf or not + auto nodePredicate = [](const NodeType& n1, const NodeType& n2) -> bool { + return n1.is_leaf() == n2.is_leaf(); + }; + + auto checkProperties = [nodePredicate](std::vector& buf, bool targetLeafState) -> void{ + // Check that references works well: + for (auto& b : buf ) b.set_is_leaf(targetLeafState); + for (const auto& b : buf ) VERIFY( b.is_leaf() == targetLeafState ); + + // Check that copies are working well + std::vector other; + other.reserve(buf.size()); + other = buf; + VERIFY(std::equal(buf.cbegin(), buf.cend(), other.cbegin(), other.cend(), nodePredicate)); + + // Check that reallocation works well + other.resize(buf.size()*2); + VERIFY(std::equal(buf.cbegin(), buf.cend(), other.cbegin(), other.cbegin()+buf.size(), nodePredicate)); + }; + + checkProperties(buffer, true); + checkProperties(buffer, false); +} + +int main(int argc, char** argv) +{ + if (!init_testing(argc, argv)) + { + return EXIT_FAILURE; + } + + using PointType = TestPoint; + using KdTreeTraits = KdTreeDefaultTraits; + + cout << "Test KdTreeDefaultNode" << endl; + testKdTreeNode(); + + cout << "Test KdTreeRange with large number of duplicated points" << endl; + testKdtreeWithDuplicate(); + +}