c++kdtreeflann

Stange behaviour using nanoflann


Using the nanoflann-library for k-nearest-neighbor searches based on KDTrees I encountered a very strange behavior. My Code is a simple set of queries:

#include <vector>
#include <iostream>
#include <nanoflann.hpp>
#include <eigen3/Eigen/Dense>

using Eigen::MatrixX3d;
using Eigen::Vector3d;

using nanoflann::KNNResultSet;
using nanoflann::SearchParams;
using kdt = nanoflann::KDTreeEigenMatrixAdaptor<MatrixX3d, 3, nanoflann::metric_L2>;

int main()
{
    // Create simple matrix
    MatrixX3d matrix(10, 3);
    for(unsigned int i = 0; i < 10; i++)
    {
        double f_i = static_cast<double>(i);
        matrix.row(i) = Vector3d(f_i, 0, 0);
    }

    // Create test points
    std::vector<Vector3d> test_vecs;
    for(unsigned int i = 0; i < 10; i++)
    {
        double f_i = static_cast<double>(i);
        test_vecs.push_back(Vector3d(f_i, f_i, f_i));
    }
    
    // Result buffer
    double distance;
    size_t index;
    KNNResultSet<double> result_set(1);
    result_set.init(&index, &distance);
    SearchParams sp;
    // KDTree
    kdt matrix_index(3, std::ref(matrix), 10);
    matrix_index.index->buildIndex();

    //Query points backwards
    for(int i = 9; i >= 0; i--)
    {
        Vector3d curr_vec = test_vecs.at(i);
        matrix_index.index->findNeighbors(result_set, &curr_vec[0], sp);
        std::cout << i << std::endl;
        std::cout << index << " " << distance << std::endl << std::endl;
    }

    // Query points forwards
    for(unsigned int i = 0; i < 10; i++)
    {
        Vector3d curr_vec = test_vecs.at(i);
        matrix_index.index->findNeighbors(result_set, &curr_vec[0], sp);
        std::cout << i << std::endl;
        std::cout << index << " " << distance << std::endl << std::endl;
    }
}

The backward query (BQ) returns the expected results. However the forward query (FQ) only yields zeros (both index and distance). FQ also seems to break the KDTree altogether. If you change the order of the two queries (the last two for loops), so that FQ is performed before BQ both will now only yield zeros.

Why does that behavior occur and how to circumvent it?


Solution

  • The result set appears to be stateful - it's always showing you the nearest overall neighbor of all the points. For instance, if you loop from 5 to 10 you get 5 50 for each iteration

    Reinitialize the result set each iteration and you'll get your desired behavior:

    result_set.init(&index, &distance);
    matrix_index.index->findNeighbors(result_set, &curr_vec[0], sp);
    

    Demo: https://godbolt.org/z/s5f1jq