-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathkd_tree_custom_metric.cpp
More file actions
132 lines (108 loc) · 4.31 KB
/
Copy pathkd_tree_custom_metric.cpp
File metadata and controls
132 lines (108 loc) · 4.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <pico_toolshed/point.hpp>
#include <pico_tree/kd_tree.hpp>
#include <pico_tree/vector_traits.hpp>
// This example shows how to create a custom metric for the kd_tree. The kd_tree
// does not support metrics that "normalize" the distance between two points for
// performance reasons. This means that, for example, the L2 metric is not
// supported, but that there is support for the L1 or L2^2 metric.
//
// There are two different categories of metrics: Euclidean and topological. A
// Euclidean metric supports any R^n space where all axes are orthogonal with
// respect to each other. A topological space is the same, but it allows
// wrapping of coordinate values along any of the axes. An implementation of a
// custom metric is provided for both below.
// The LP^P metric is a generalization of the L2^2 metric.
template <std::size_t P_>
struct metric_lp_p {
static_assert(P_ > 0, "P_CANNOT_BE_ZERO");
// Indicate that this metric is a Euclidean one.
using space_category = pico_tree::euclidean_space_tag;
// Calculates the distance between two points.
template <typename InputIterator_>
auto operator()(
InputIterator_ begin1, InputIterator_ end1, InputIterator_ begin2) const {
using scalar_type =
typename std::iterator_traits<InputIterator_>::value_type;
scalar_type d{};
for (; begin1 != end1; ++begin1, ++begin2) {
d += operator()(*begin1 - *begin2);
}
return d;
}
// Returns the absolute value of x to the power of p.
template <typename Scalar_>
Scalar_ operator()(Scalar_ x) const {
return std::pow(std::abs(x), static_cast<Scalar_>(P_));
}
};
// This metric measures distances on the two dimensional ring torus T2. The
// torus is the Cartesian product of two circles S1 x S1. The values of each of
// the point coordinates should be within the range of [0...1].
struct metric_t2_squared {
// Indicate that this metric is defined on a topological space. The
// topological_space_tag is required because the torus wraps around in both
// dimensions.
using space_category = pico_tree::topological_space_tag;
// Calculates the distance between two points.
template <typename InputIterator_>
auto operator()(
InputIterator_ begin1, InputIterator_ end1, InputIterator_ begin2) const {
using scalar_type =
typename std::iterator_traits<InputIterator_>::value_type;
scalar_type d{};
for (; begin1 != end1; ++begin1, ++begin2) {
d += pico_tree::squared_s1_distance(*begin1, *begin2);
}
return d;
}
// Distances are squared values.
template <typename Scalar_>
Scalar_ operator()(Scalar_ x) const {
return x * x;
}
template <typename UnaryPredicate_>
void apply_dim_space([[maybe_unused]] int dim, UnaryPredicate_ p) const {
p(pico_tree::one_space_s1{});
}
};
void search_lp3_3() {
using point = pico_tree::point_2f;
using scalar = typename point::scalar_type;
pico_tree::max_leaf_size_t max_leaf_size = 12;
std::size_t point_count = 1024 * 1024;
scalar area_size = 10;
using kd_tree = pico_tree::kd_tree<std::vector<point>, metric_lp_p<3>>;
using neighbor = typename kd_tree::neighbor_type;
kd_tree tree(
pico_tree::generate_random_n<point>(point_count, area_size),
max_leaf_size);
neighbor nn;
tree.search_nn(point{area_size / scalar(2), area_size / scalar(2)}, nn);
std::cout << "Index closest point: " << nn.index << std::endl;
}
void search_t2() {
using point = pico_tree::point_2f;
using scalar = typename point::scalar_type;
pico_tree::max_leaf_size_t max_leaf_size = 12;
std::size_t point_count = 1024 * 1024;
scalar area_size = 1;
using kd_tree = pico_tree::kd_tree<std::vector<point>, metric_t2_squared>;
using neighbor = typename kd_tree::neighbor_type;
kd_tree tree(
pico_tree::generate_random_n<point>(point_count, area_size),
max_leaf_size);
std::array<neighbor, 8> knn;
tree.search_knn(point{area_size, area_size}, knn.begin(), knn.end());
// These prints show that wrapping near values 0 ~ 1 is supported.
std::cout << "Closest points (index, distance, point): " << std::endl;
for (auto const& nn : knn) {
std::cout << " " << nn.index << ", " << nn.distance << ", ["
<< tree.space()[static_cast<std::size_t>(nn.index)] << "]"
<< std::endl;
}
}
int main() {
search_lp3_3();
search_t2();
return 0;
}