From ed55543280ca69a46c5af485c1130f4803030ba1 Mon Sep 17 00:00:00 2001 From: Leonard Eyer Date: Tue, 25 Nov 2025 18:38:57 +0100 Subject: [PATCH] fix: respect SearchParameters::sorted in dynamic index search --- include/nanoflann.hpp | 3 +++ tests/test_main.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/include/nanoflann.hpp b/include/nanoflann.hpp index bbeedf7..dd9f39f 100644 --- a/include/nanoflann.hpp +++ b/include/nanoflann.hpp @@ -2267,6 +2267,9 @@ class KDTreeSingleIndexDynamicAdaptor_ static_cast(0)); DistanceType dist = this->computeInitialDistances(*this, vec, dists); searchLevel(result, vec, Base::root_node_, dist, dists, epsError); + + if (searchParams.sorted) result.sort(); + return result.full(); } diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 15a6a45..6201f05 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -661,6 +661,60 @@ void L2_concurrent_build_vs_L2_test(const size_t nSamples, const size_t DIM) EXPECT_EQ(mat_index.index->vAcc_, mat_index_concurrent_build.index->vAcc_); } + +template +void L2_dynamic_sorted_test(const size_t N, const size_t num_results) +{ + PointCloud cloud; + generateRandomPointCloud(cloud, N); + + num_t query_pt[3] = {0.5, 0.5, 0.5}; + + using DynamicKDTree = KDTreeSingleIndexDynamicAdaptor< + L2_Adaptor>, + PointCloud, + 3 /* dim */ + >; + + DynamicKDTree dynamic_index(3, cloud); + + // Prepare result containers + std::vector dynamic_idx(num_results); + std::vector dynamic_dist(num_results); + KNNResultSet dynamic_knn_result(num_results); + std::vector> radius_results_vec; + RadiusResultSet dynamic_radius_result(10.0 * 10.0, radius_results_vec); + + // Prepare search params to sort result + const auto search_params = SearchParameters(0, true); + + dynamic_knn_result.init(&dynamic_idx[0], &dynamic_dist[0]); + dynamic_radius_result.init(); + + dynamic_index.findNeighbors(dynamic_knn_result, &query_pt[0], search_params); + dynamic_index.findNeighbors(dynamic_radius_result, &query_pt[0], search_params); + + // Check size + const size_t expected_size = std::min(N, num_results); + ASSERT_EQ(dynamic_knn_result.size(), expected_size); + + // Ensure knn results are sorted + num_t last_dist = -1; + for (size_t i = 0; i < expected_size; i++) + { + EXPECT_GE(dynamic_dist[i], last_dist); + last_dist = dynamic_dist[i]; + } + + // Ensure radius results are sorted + num_t last = -1; + for (const auto& r : radius_results_vec) + { + EXPECT_GE(r.second, last); + last = r.second; + } +} + TEST(kdtree, L2_vs_L2_simple) { for (int nResults = 1; nResults < 10; nResults++) @@ -908,6 +962,15 @@ TEST(kdtree, L2_concurrent_build_vs_L2) } } +TEST(kdtree, L2_static_vs_dynamic) +{ + for (int nResults = 0; nResults < 10; nResults++) + { + L2_dynamic_sorted_test(100, nResults); + L2_dynamic_sorted_test(100, nResults); + } +} + TEST(kdtree, same_points) { using num_t = double;