test_main.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  1. /***********************************************************************
  2. * Software License Agreement (BSD License)
  3. *
  4. * Copyright 2011-2024 Jose Luis Blanco (joseluisblancoc@gmail.com).
  5. * All rights reserved.
  6. *
  7. * THE BSD LICENSE
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions
  11. * are met:
  12. *
  13. * 1. Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in the
  17. * documentation and/or other materials provided with the distribution.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  20. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  21. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  22. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  23. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  24. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  28. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. *************************************************************************/
  30. #include <gtest/gtest.h>
  31. #include <cmath> // for abs()
  32. #include <cstdlib>
  33. #include <iostream>
  34. #include <map>
  35. #include <nanoflann.hpp>
  36. #include "../examples/utils.h"
  37. using namespace std;
  38. using namespace nanoflann;
  39. int main(int argc, char** argv)
  40. {
  41. testing::InitGoogleTest(&argc, argv);
  42. return RUN_ALL_TESTS();
  43. }
  44. template <typename num_t>
  45. void L2_vs_L2_simple_test(const size_t N, const size_t num_results)
  46. {
  47. PointCloud<num_t> cloud;
  48. // Generate points:
  49. generateRandomPointCloud(cloud, N);
  50. num_t query_pt[3] = {0.5, 0.5, 0.5};
  51. // construct a kd-tree index:
  52. typedef KDTreeSingleIndexAdaptor<
  53. L2_Simple_Adaptor<num_t, PointCloud<num_t>>, PointCloud<num_t>,
  54. 3 /* dim */
  55. >
  56. my_kd_tree_simple_t;
  57. typedef KDTreeSingleIndexAdaptor<
  58. L2_Adaptor<num_t, PointCloud<num_t>>, PointCloud<num_t>, 3 /* dim */
  59. >
  60. my_kd_tree_t;
  61. my_kd_tree_simple_t index1(
  62. 3 /*dim*/, cloud, KDTreeSingleIndexAdaptorParams(10 /* max leaf */));
  63. my_kd_tree_t index2(
  64. 3 /*dim*/, cloud, KDTreeSingleIndexAdaptorParams(10 /* max leaf */));
  65. // do a knn search
  66. std::vector<size_t> ret_index(num_results);
  67. std::vector<num_t> out_dist_sqr(num_results);
  68. nanoflann::KNNResultSet<num_t> resultSet(num_results);
  69. resultSet.init(&ret_index[0], &out_dist_sqr[0]);
  70. index1.findNeighbors(resultSet, &query_pt[0]);
  71. std::vector<size_t> ret_index1 = ret_index;
  72. std::vector<num_t> out_dist_sqr1 = out_dist_sqr;
  73. resultSet.init(&ret_index[0], &out_dist_sqr[0]);
  74. index2.findNeighbors(resultSet, &query_pt[0]);
  75. for (size_t i = 0; i < num_results; i++)
  76. {
  77. EXPECT_EQ(ret_index1[i], ret_index[i]);
  78. EXPECT_DOUBLE_EQ(out_dist_sqr1[i], out_dist_sqr[i]);
  79. }
  80. }
  81. using namespace nanoflann;
  82. #include "../examples/KDTreeVectorOfVectorsAdaptor.h"
  83. template <typename NUM>
  84. void generateRandomPointCloud(
  85. std::vector<std::vector<NUM>>& samples, const size_t N, const size_t dim,
  86. const NUM max_range)
  87. {
  88. samples.resize(N);
  89. for (size_t i = 0; i < N; i++)
  90. {
  91. samples[i].resize(dim);
  92. for (size_t d = 0; d < dim; d++)
  93. samples[i][d] = max_range * (rand() % 1000) / NUM(1000.0);
  94. }
  95. }
  96. template <typename NUM>
  97. void L2_vs_bruteforce_test(
  98. const size_t nSamples, const size_t DIM, const size_t numToSearch)
  99. {
  100. std::vector<std::vector<NUM>> samples;
  101. const NUM max_range = NUM(20.0);
  102. // Generate points:
  103. generateRandomPointCloud(samples, nSamples, DIM, max_range);
  104. // Query point:
  105. std::vector<NUM> query_pt(DIM);
  106. for (size_t d = 0; d < DIM; d++)
  107. query_pt[d] = static_cast<NUM>(max_range * (rand() % 1000) / (1000.0));
  108. // construct a kd-tree index:
  109. // Dimensionality set at run-time (default: L2)
  110. // ------------------------------------------------------------
  111. typedef KDTreeVectorOfVectorsAdaptor<std::vector<std::vector<NUM>>, NUM>
  112. my_kd_tree_t;
  113. my_kd_tree_t mat_index(DIM /*dim*/, samples, 10 /* max leaf */);
  114. // do a knn search
  115. const size_t num_results = numToSearch;
  116. std::vector<size_t> ret_indexes(num_results);
  117. std::vector<NUM> out_dists_sqr(num_results);
  118. nanoflann::KNNResultSet<NUM> resultSet(num_results);
  119. resultSet.init(&ret_indexes[0], &out_dists_sqr[0]);
  120. mat_index.index->findNeighbors(resultSet, &query_pt[0]);
  121. const auto nFound = resultSet.size();
  122. // Brute force neighbors:
  123. std::multimap<NUM /*dist*/, size_t /*idx*/> bf_nn;
  124. {
  125. for (size_t i = 0; i < nSamples; i++)
  126. {
  127. double dist = 0.0;
  128. for (size_t d = 0; d < DIM; d++)
  129. dist += (query_pt[d] - samples[i][d]) *
  130. (query_pt[d] - samples[i][d]);
  131. bf_nn.emplace(dist, i);
  132. }
  133. }
  134. // Keep bruteforce solutions indexed by idx instead of distances,
  135. // to handle correctly almost or exactly coindicing distances for >=2 NN:
  136. std::map<size_t, NUM> bf_idx2dist;
  137. for (const auto& kv : bf_nn) bf_idx2dist[kv.second] = kv.first;
  138. // Compare:
  139. if (!bf_nn.empty())
  140. {
  141. auto it = bf_nn.begin();
  142. for (size_t i = 0; i < nFound; ++i, ++it)
  143. {
  144. // Distances must be in exact order:
  145. EXPECT_NEAR(it->first, out_dists_sqr[i], 1e-3);
  146. // indices may be not in the (rare) case of a tie:
  147. EXPECT_NEAR(bf_idx2dist.at(ret_indexes[i]), out_dists_sqr[i], 1e-3)
  148. << "For: numToSearch=" << numToSearch
  149. << " out_dists_sqr[i]=" << out_dists_sqr[i] << "\n";
  150. }
  151. }
  152. }
  153. template <typename NUM>
  154. void rknn_L2_vs_bruteforce_test(
  155. const size_t nSamples, const size_t DIM, const size_t numToSearch,
  156. const NUM maxRadiusSqr)
  157. {
  158. std::vector<std::vector<NUM>> samples;
  159. const NUM max_range = NUM(20.0);
  160. // Generate points:
  161. generateRandomPointCloud(samples, nSamples, DIM, max_range);
  162. // Query point:
  163. std::vector<NUM> query_pt(DIM);
  164. for (size_t d = 0; d < DIM; d++)
  165. query_pt[d] = static_cast<NUM>(max_range * (rand() % 1000) / (1000.0));
  166. // construct a kd-tree index:
  167. // Dimensionality set at run-time (default: L2)
  168. // ------------------------------------------------------------
  169. typedef KDTreeVectorOfVectorsAdaptor<std::vector<std::vector<NUM>>, NUM>
  170. my_kd_tree_t;
  171. my_kd_tree_t mat_index(DIM /*dim*/, samples, 10 /* max leaf */);
  172. // do a knn search
  173. const size_t num_results = numToSearch;
  174. std::vector<size_t> ret_indexes(num_results);
  175. std::vector<NUM> out_dists_sqr(num_results);
  176. nanoflann::RKNNResultSet<NUM> resultSet(num_results, maxRadiusSqr);
  177. resultSet.init(&ret_indexes[0], &out_dists_sqr[0]);
  178. mat_index.index->findNeighbors(resultSet, &query_pt[0]);
  179. const auto nFound = resultSet.size();
  180. // Brute force neighbors:
  181. std::multimap<NUM /*dist*/, size_t /*idx*/> bf_nn;
  182. {
  183. for (size_t i = 0; i < nSamples; i++)
  184. {
  185. double dist = 0.0;
  186. for (size_t d = 0; d < DIM; d++)
  187. dist += (query_pt[d] - samples[i][d]) *
  188. (query_pt[d] - samples[i][d]);
  189. if (dist <= maxRadiusSqr) bf_nn.emplace(dist, i);
  190. }
  191. }
  192. // Keep bruteforce solutions indexed by idx instead of distances,
  193. // to handle correctly almost or exactly coindicing distances for >=2 NN:
  194. std::map<size_t, NUM> bf_idx2dist;
  195. for (const auto& kv : bf_nn) bf_idx2dist[kv.second] = kv.first;
  196. // Compare:
  197. if (!bf_nn.empty())
  198. {
  199. auto it = bf_nn.begin();
  200. for (size_t i = 0; i < nFound; ++i, ++it)
  201. {
  202. // Distances must be in exact order:
  203. EXPECT_NEAR(it->first, out_dists_sqr[i], 1e-3)
  204. << "For: numToSearch=" << numToSearch
  205. << " out_dists_sqr[i]=" << out_dists_sqr[i] << "\n";
  206. // indices may be not in the (rare) case of a tie:
  207. EXPECT_NEAR(bf_idx2dist.at(ret_indexes[i]), out_dists_sqr[i], 1e-3)
  208. << "For: numToSearch=" << numToSearch
  209. << " out_dists_sqr[i]=" << out_dists_sqr[i] << "\n";
  210. }
  211. }
  212. }
  213. template <typename NUM>
  214. void SO3_vs_bruteforce_test(const size_t nSamples)
  215. {
  216. PointCloud_Quat<NUM> cloud;
  217. // Generate points:
  218. generateRandomPointCloud_Quat(cloud, nSamples);
  219. NUM query_pt[4] = {0.5, 0.5, 0.5, 0.5};
  220. // construct a kd-tree index:
  221. typedef KDTreeSingleIndexAdaptor<
  222. SO3_Adaptor<NUM, PointCloud_Quat<NUM>>, PointCloud_Quat<NUM>,
  223. 4 /* dim */
  224. >
  225. my_kd_tree_t;
  226. my_kd_tree_t index(
  227. 4 /*dim*/, cloud, KDTreeSingleIndexAdaptorParams(10 /* max leaf */));
  228. // do a knn search
  229. const size_t num_results = 1;
  230. std::vector<size_t> ret_indexes(num_results);
  231. std::vector<NUM> out_dists_sqr(num_results);
  232. nanoflann::KNNResultSet<NUM> resultSet(num_results);
  233. resultSet.init(&ret_indexes[0], &out_dists_sqr[0]);
  234. index.findNeighbors(resultSet, &query_pt[0]);
  235. // Brute force:
  236. double min_dist_L2 = std::numeric_limits<double>::max();
  237. size_t min_idx = std::numeric_limits<size_t>::max();
  238. {
  239. for (size_t i = 0; i < nSamples; i++)
  240. {
  241. double dist = 0.0;
  242. for (int d = 0; d < 4; d++)
  243. dist += (query_pt[d] - cloud.kdtree_get_pt(i, d)) *
  244. (query_pt[d] - cloud.kdtree_get_pt(i, d));
  245. if (dist < min_dist_L2)
  246. {
  247. min_dist_L2 = dist;
  248. min_idx = i;
  249. }
  250. }
  251. ASSERT_TRUE(min_idx != std::numeric_limits<size_t>::max());
  252. }
  253. // Compare:
  254. EXPECT_EQ(min_idx, ret_indexes[0]);
  255. EXPECT_NEAR(min_dist_L2, out_dists_sqr[0], 1e-3);
  256. }
  257. template <typename NUM>
  258. void SO2_vs_bruteforce_test(const size_t nSamples)
  259. {
  260. PointCloud_Orient<NUM> cloud;
  261. // Generate points:
  262. generateRandomPointCloud_Orient(cloud, nSamples);
  263. NUM query_pt[1] = {0.5};
  264. // construct a kd-tree index:
  265. typedef KDTreeSingleIndexAdaptor<
  266. SO2_Adaptor<NUM, PointCloud_Orient<NUM>>, PointCloud_Orient<NUM>,
  267. 1 /* dim */
  268. >
  269. my_kd_tree_t;
  270. my_kd_tree_t index(
  271. 1 /*dim*/, cloud, KDTreeSingleIndexAdaptorParams(10 /* max leaf */));
  272. // do a knn search
  273. const size_t num_results = 1;
  274. std::vector<size_t> ret_indexes(num_results);
  275. std::vector<NUM> out_dists_sqr(num_results);
  276. nanoflann::KNNResultSet<NUM> resultSet(num_results);
  277. resultSet.init(&ret_indexes[0], &out_dists_sqr[0]);
  278. index.findNeighbors(resultSet, &query_pt[0]);
  279. // Brute force:
  280. double min_dist_SO2 = std::numeric_limits<double>::max();
  281. size_t min_idx = std::numeric_limits<size_t>::max();
  282. {
  283. for (size_t i = 0; i < nSamples; i++)
  284. {
  285. double dist = 0.0;
  286. dist = cloud.kdtree_get_pt(i, 0) - query_pt[0];
  287. if (dist > nanoflann::pi_const<double>())
  288. dist -= 2 * nanoflann::pi_const<double>();
  289. else if (dist < -nanoflann::pi_const<double>())
  290. dist += 2 * nanoflann::pi_const<double>();
  291. if (dist < min_dist_SO2)
  292. {
  293. min_dist_SO2 = dist;
  294. min_idx = i;
  295. }
  296. }
  297. ASSERT_TRUE(min_idx != std::numeric_limits<size_t>::max());
  298. }
  299. // Compare:
  300. EXPECT_EQ(min_idx, ret_indexes[0]);
  301. EXPECT_NEAR(min_dist_SO2, out_dists_sqr[0], 1e-3);
  302. }
  303. // First add nSamples/2 points, find the closest point
  304. // Then add remaining points and find closest point
  305. // Compare both with closest point using brute force approach
  306. template <typename NUM>
  307. void L2_dynamic_vs_bruteforce_test(const size_t nSamples)
  308. {
  309. PointCloud<NUM> cloud;
  310. const NUM max_range = NUM(20.0);
  311. // construct a kd-tree index:
  312. typedef KDTreeSingleIndexDynamicAdaptor<
  313. L2_Simple_Adaptor<NUM, PointCloud<NUM>>, PointCloud<NUM>, 3 /* dim */
  314. >
  315. my_kd_tree_t;
  316. my_kd_tree_t index(
  317. 3 /*dim*/, cloud, KDTreeSingleIndexAdaptorParams(10 /* max leaf */));
  318. // Generate points:
  319. generateRandomPointCloud(cloud, nSamples, max_range);
  320. NUM query_pt[3] = {0.5, 0.5, 0.5};
  321. // add points in chunks at a time
  322. size_t chunk_size = 100;
  323. size_t end = 0;
  324. for (size_t i = 0; i < nSamples / 2; i = i + chunk_size)
  325. {
  326. end = min(size_t(i + chunk_size), nSamples / 2 - 1);
  327. index.addPoints(i, end);
  328. }
  329. {
  330. // do a knn search
  331. const size_t num_results = 1;
  332. std::vector<size_t> ret_indexes(num_results);
  333. std::vector<NUM> out_dists_sqr(num_results);
  334. nanoflann::KNNResultSet<NUM> resultSet(num_results);
  335. resultSet.init(&ret_indexes[0], &out_dists_sqr[0]);
  336. index.findNeighbors(resultSet, &query_pt[0]);
  337. // Brute force:
  338. double min_dist_L2 = std::numeric_limits<double>::max();
  339. size_t min_idx = std::numeric_limits<size_t>::max();
  340. {
  341. for (size_t i = 0; i < nSamples / 2; i++)
  342. {
  343. double dist = 0.0;
  344. for (int d = 0; d < 3; d++)
  345. dist += (query_pt[d] - cloud.kdtree_get_pt(i, d)) *
  346. (query_pt[d] - cloud.kdtree_get_pt(i, d));
  347. if (dist < min_dist_L2)
  348. {
  349. min_dist_L2 = dist;
  350. min_idx = i;
  351. }
  352. }
  353. ASSERT_TRUE(min_idx != std::numeric_limits<size_t>::max());
  354. }
  355. // Compare:
  356. EXPECT_EQ(min_idx, ret_indexes[0]);
  357. EXPECT_NEAR(min_dist_L2, out_dists_sqr[0], 1e-3);
  358. }
  359. for (size_t i = end + 1; i < nSamples; i = i + chunk_size)
  360. {
  361. end = min(size_t(i + chunk_size), nSamples - 1);
  362. index.addPoints(i, end);
  363. }
  364. {
  365. // do a knn search
  366. const size_t num_results = 1;
  367. std::vector<size_t> ret_indexes(num_results);
  368. std::vector<NUM> out_dists_sqr(num_results);
  369. nanoflann::KNNResultSet<NUM> resultSet(num_results);
  370. resultSet.init(&ret_indexes[0], &out_dists_sqr[0]);
  371. index.findNeighbors(resultSet, &query_pt[0]);
  372. // Brute force:
  373. double min_dist_L2 = std::numeric_limits<double>::max();
  374. size_t min_idx = std::numeric_limits<size_t>::max();
  375. {
  376. for (size_t i = 0; i < nSamples; i++)
  377. {
  378. double dist = 0.0;
  379. for (int d = 0; d < 3; d++)
  380. dist += (query_pt[d] - cloud.kdtree_get_pt(i, d)) *
  381. (query_pt[d] - cloud.kdtree_get_pt(i, d));
  382. if (dist < min_dist_L2)
  383. {
  384. min_dist_L2 = dist;
  385. min_idx = i;
  386. }
  387. }
  388. ASSERT_TRUE(min_idx != std::numeric_limits<size_t>::max());
  389. }
  390. // Compare:
  391. EXPECT_EQ(min_idx, ret_indexes[0]);
  392. EXPECT_NEAR(min_dist_L2, out_dists_sqr[0], 1e-3);
  393. }
  394. }
  395. template <typename NUM>
  396. void L2_concurrent_build_vs_bruteforce_test(
  397. const size_t nSamples, const size_t DIM)
  398. {
  399. std::vector<std::vector<NUM>> samples;
  400. const NUM max_range = NUM(20.0);
  401. // Generate points:
  402. generateRandomPointCloud(samples, nSamples, DIM, max_range);
  403. // Query point:
  404. std::vector<NUM> query_pt(DIM);
  405. for (size_t d = 0; d < DIM; d++)
  406. query_pt[d] = static_cast<NUM>(max_range * (rand() % 1000) / (1000.0));
  407. // construct a kd-tree index:
  408. // Dimensionality set at run-time (default: L2)
  409. // ------------------------------------------------------------
  410. typedef KDTreeVectorOfVectorsAdaptor<std::vector<std::vector<NUM>>, NUM>
  411. my_kd_tree_t;
  412. my_kd_tree_t mat_index(
  413. DIM /*dim*/, samples, 10 /* max leaf */, 0 /* concurrent build */);
  414. // do a knn search
  415. const size_t num_results = 1;
  416. std::vector<size_t> ret_indexes(num_results);
  417. std::vector<NUM> out_dists_sqr(num_results);
  418. nanoflann::KNNResultSet<NUM> resultSet(num_results);
  419. resultSet.init(&ret_indexes[0], &out_dists_sqr[0]);
  420. mat_index.index->findNeighbors(resultSet, &query_pt[0]);
  421. // Brute force:
  422. double min_dist_L2 = std::numeric_limits<double>::max();
  423. size_t min_idx = std::numeric_limits<size_t>::max();
  424. {
  425. for (size_t i = 0; i < nSamples; i++)
  426. {
  427. double dist = 0.0;
  428. for (size_t d = 0; d < DIM; d++)
  429. dist += (query_pt[d] - samples[i][d]) *
  430. (query_pt[d] - samples[i][d]);
  431. if (dist < min_dist_L2)
  432. {
  433. min_dist_L2 = dist;
  434. min_idx = i;
  435. }
  436. }
  437. ASSERT_TRUE(min_idx != std::numeric_limits<size_t>::max());
  438. }
  439. // Compare:
  440. EXPECT_EQ(min_idx, ret_indexes[0]);
  441. EXPECT_NEAR(min_dist_L2, out_dists_sqr[0], 1e-3);
  442. }
  443. template <typename NUM>
  444. void L2_concurrent_build_vs_L2_test(const size_t nSamples, const size_t DIM)
  445. {
  446. std::vector<std::vector<NUM>> samples;
  447. const NUM max_range = NUM(20.0);
  448. // Generate points:
  449. generateRandomPointCloud(samples, nSamples, DIM, max_range);
  450. // Query point:
  451. std::vector<NUM> query_pt(DIM);
  452. for (size_t d = 0; d < DIM; d++)
  453. query_pt[d] = static_cast<NUM>(max_range * (rand() % 1000) / (1000.0));
  454. // construct a kd-tree index:
  455. // Dimensionality set at run-time (default: L2)
  456. // ------------------------------------------------------------
  457. typedef KDTreeVectorOfVectorsAdaptor<std::vector<std::vector<NUM>>, NUM>
  458. my_kd_tree_t;
  459. my_kd_tree_t mat_index_concurrent_build(
  460. DIM /*dim*/, samples, 10 /* max leaf */, 0 /* concurrent build */);
  461. my_kd_tree_t mat_index(DIM /*dim*/, samples, 10 /* max leaf */);
  462. // Compare:
  463. EXPECT_EQ(mat_index.index->vAcc_, mat_index_concurrent_build.index->vAcc_);
  464. }
  465. TEST(kdtree, L2_vs_L2_simple)
  466. {
  467. for (int nResults = 1; nResults < 10; nResults++)
  468. {
  469. L2_vs_L2_simple_test<float>(100, nResults);
  470. L2_vs_L2_simple_test<double>(100, nResults);
  471. }
  472. }
  473. TEST(kdtree, robust_empty_tree)
  474. {
  475. // Try to build a tree with 0 data points, to test
  476. // robustness against this situation:
  477. PointCloud<double> cloud;
  478. double query_pt[3] = {0.5, 0.5, 0.5};
  479. // construct a kd-tree index:
  480. typedef KDTreeSingleIndexAdaptor<
  481. L2_Simple_Adaptor<double, PointCloud<double>>, PointCloud<double>,
  482. 3 /* dim */
  483. >
  484. my_kd_tree_simple_t;
  485. my_kd_tree_simple_t index1(
  486. 3 /*dim*/, cloud, KDTreeSingleIndexAdaptorParams(10 /* max leaf */));
  487. // Now we will try to search in the tree, and WE EXPECT a result of
  488. // no neighbors found if the error detection works fine:
  489. const size_t num_results = 1;
  490. std::vector<size_t> ret_index(num_results);
  491. std::vector<double> out_dist_sqr(num_results);
  492. nanoflann::KNNResultSet<double> resultSet(num_results);
  493. resultSet.init(&ret_index[0], &out_dist_sqr[0]);
  494. bool result = index1.findNeighbors(resultSet, &query_pt[0]);
  495. EXPECT_EQ(result, false);
  496. }
  497. TEST(kdtree, L2_vs_bruteforce)
  498. {
  499. srand(static_cast<unsigned int>(time(nullptr)));
  500. for (int knn = 1; knn < 20; knn += 3)
  501. {
  502. for (int i = 0; i < 500; i++)
  503. {
  504. L2_vs_bruteforce_test<float>(100, 2, knn);
  505. L2_vs_bruteforce_test<float>(100, 3, knn);
  506. L2_vs_bruteforce_test<float>(100, 7, knn);
  507. L2_vs_bruteforce_test<double>(100, 2, knn);
  508. L2_vs_bruteforce_test<double>(100, 3, knn);
  509. L2_vs_bruteforce_test<double>(100, 7, knn);
  510. }
  511. }
  512. }
  513. TEST(kdtree, L2_vs_bruteforce_rknn)
  514. {
  515. srand(static_cast<unsigned int>(time(nullptr)));
  516. for (int knn = 1; knn < 20; knn += 3)
  517. {
  518. for (int r = 1; r < 5; r++)
  519. {
  520. for (int i = 0; i < 100; i++)
  521. {
  522. rknn_L2_vs_bruteforce_test<float>(100, 2, knn, 9.0f * r * r);
  523. rknn_L2_vs_bruteforce_test<float>(100, 3, knn, 9.0f * r * r);
  524. rknn_L2_vs_bruteforce_test<float>(100, 7, knn, 9.0f * r * r);
  525. rknn_L2_vs_bruteforce_test<double>(100, 2, knn, 9.0 * r * r);
  526. rknn_L2_vs_bruteforce_test<double>(100, 3, knn, 9.0 * r * r);
  527. rknn_L2_vs_bruteforce_test<double>(100, 7, knn, 9.0 * r * r);
  528. }
  529. }
  530. }
  531. }
  532. TEST(kdtree, SO3_vs_bruteforce)
  533. {
  534. srand(static_cast<unsigned int>(time(nullptr)));
  535. for (int i = 0; i < 10; i++)
  536. {
  537. SO3_vs_bruteforce_test<float>(100);
  538. SO3_vs_bruteforce_test<float>(100);
  539. SO3_vs_bruteforce_test<float>(100);
  540. SO3_vs_bruteforce_test<double>(100);
  541. SO3_vs_bruteforce_test<double>(100);
  542. SO3_vs_bruteforce_test<double>(100);
  543. }
  544. }
  545. TEST(kdtree, SO2_vs_bruteforce)
  546. {
  547. srand(static_cast<unsigned int>(time(nullptr)));
  548. for (int i = 0; i < 10; i++)
  549. {
  550. SO2_vs_bruteforce_test<float>(100);
  551. SO2_vs_bruteforce_test<float>(100);
  552. SO2_vs_bruteforce_test<float>(100);
  553. SO2_vs_bruteforce_test<double>(100);
  554. SO2_vs_bruteforce_test<double>(100);
  555. SO2_vs_bruteforce_test<double>(100);
  556. }
  557. }
  558. TEST(kdtree, L2_dynamic_vs_bruteforce)
  559. {
  560. srand(static_cast<unsigned int>(time(nullptr)));
  561. for (int i = 0; i < 10; i++)
  562. {
  563. L2_dynamic_vs_bruteforce_test<float>(100);
  564. L2_dynamic_vs_bruteforce_test<float>(100);
  565. L2_dynamic_vs_bruteforce_test<float>(100);
  566. L2_dynamic_vs_bruteforce_test<double>(100);
  567. L2_dynamic_vs_bruteforce_test<double>(100);
  568. L2_dynamic_vs_bruteforce_test<double>(100);
  569. }
  570. }
  571. TEST(kdtree, robust_nonempty_tree)
  572. {
  573. // Try to build a dynamic tree with some initial points
  574. PointCloud<double> cloud;
  575. const size_t max_point_count = 1000;
  576. generateRandomPointCloud(cloud, max_point_count);
  577. const double query_pt[3] = {0.5, 0.5, 0.5};
  578. // construct a kd-tree index:
  579. typedef KDTreeSingleIndexDynamicAdaptor<
  580. L2_Simple_Adaptor<double, PointCloud<double>>, PointCloud<double>, 3>
  581. my_kd_tree_simple_t;
  582. my_kd_tree_simple_t index1(
  583. 3 /*dim*/, cloud, KDTreeSingleIndexAdaptorParams(10 /* max leaf */),
  584. max_point_count);
  585. // Try a search and expect a neighbor to exist because the dynamic tree was
  586. // passed a non-empty cloud
  587. const size_t num_results = 1;
  588. std::vector<size_t> ret_index(num_results);
  589. std::vector<double> out_dist_sqr(num_results);
  590. nanoflann::KNNResultSet<double> resultSet(num_results);
  591. resultSet.init(&ret_index[0], &out_dist_sqr[0]);
  592. bool result = index1.findNeighbors(resultSet, &query_pt[0]);
  593. EXPECT_EQ(result, true);
  594. }
  595. TEST(kdtree, add_and_remove_points)
  596. {
  597. PointCloud<double> cloud;
  598. cloud.pts = {{0.0, 0.0, 0.0}, {0.5, 0.5, 0.5}, {0.7, 0.7, 0.7}};
  599. typedef KDTreeSingleIndexDynamicAdaptor<
  600. L2_Simple_Adaptor<double, PointCloud<double>>, PointCloud<double>, 3>
  601. my_kd_tree_simple_t;
  602. my_kd_tree_simple_t index(
  603. 3 /*dim*/, cloud, KDTreeSingleIndexAdaptorParams(10 /* max leaf */));
  604. const auto query = [&index]() -> size_t {
  605. const double query_pt[3] = {0.5, 0.5, 0.5};
  606. const size_t num_results = 1;
  607. std::vector<size_t> ret_index(num_results);
  608. std::vector<double> out_dist_sqr(num_results);
  609. nanoflann::KNNResultSet<double> resultSet(num_results);
  610. resultSet.init(&ret_index[0], &out_dist_sqr[0]);
  611. index.findNeighbors(resultSet, &query_pt[0]);
  612. return ret_index[0];
  613. };
  614. auto actual = query();
  615. EXPECT_EQ(actual, static_cast<size_t>(1));
  616. index.removePoint(1);
  617. actual = query();
  618. EXPECT_EQ(actual, static_cast<size_t>(2));
  619. index.addPoints(1, 1);
  620. actual = query();
  621. EXPECT_EQ(actual, static_cast<size_t>(1));
  622. index.removePoint(1);
  623. index.removePoint(2);
  624. actual = query();
  625. EXPECT_EQ(actual, static_cast<size_t>(0));
  626. }
  627. TEST(kdtree, L2_concurrent_build_vs_bruteforce)
  628. {
  629. srand(static_cast<unsigned int>(time(nullptr)));
  630. for (int i = 0; i < 10; i++)
  631. {
  632. L2_concurrent_build_vs_bruteforce_test<float>(100, 2);
  633. L2_concurrent_build_vs_bruteforce_test<float>(100, 3);
  634. L2_concurrent_build_vs_bruteforce_test<float>(100, 7);
  635. L2_concurrent_build_vs_bruteforce_test<double>(100, 2);
  636. L2_concurrent_build_vs_bruteforce_test<double>(100, 3);
  637. L2_concurrent_build_vs_bruteforce_test<double>(100, 7);
  638. }
  639. }
  640. TEST(kdtree, L2_concurrent_build_vs_L2)
  641. {
  642. srand(static_cast<unsigned int>(time(nullptr)));
  643. for (int i = 0; i < 10; i++)
  644. {
  645. L2_concurrent_build_vs_L2_test<float>(100, 2);
  646. L2_concurrent_build_vs_L2_test<float>(100, 3);
  647. L2_concurrent_build_vs_L2_test<float>(100, 7);
  648. L2_concurrent_build_vs_L2_test<double>(100, 2);
  649. L2_concurrent_build_vs_L2_test<double>(100, 3);
  650. L2_concurrent_build_vs_L2_test<double>(100, 7);
  651. }
  652. }