MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ProximityGraphs.cpp
Go to the documentation of this file.
1#include "ProximityGraphs.hpp"
2
4#include <queue>
5
6namespace MayaFlux::Kinesis {
7
8namespace {
9 double distance_squared(const Eigen::VectorXd& a, const Eigen::VectorXd& b)
10 {
11 return (b - a).squaredNorm();
12 }
13
14 double distance(const Eigen::VectorXd& a, const Eigen::VectorXd& b)
15 {
16 return (b - a).norm();
17 }
18
19 struct Edge {
20 size_t a, b;
21 double weight;
22 bool operator>(const Edge& other) const { return weight > other.weight; }
23 };
24}
25
26EdgeList sequential_chain(const Eigen::MatrixXd& points)
27{
28 Eigen::Index n = points.cols();
29 if (n < 2) {
30 return {};
31 }
32
33 EdgeList edges;
34 edges.reserve(n - 1);
35
36 for (Eigen::Index i = 0; i < n - 1; ++i) {
37 edges.emplace_back(static_cast<size_t>(i), static_cast<size_t>(i + 1));
38 }
39
41 "sequential_chain: {} points, generated {} edges", n, edges.size());
42
43 return edges;
44}
45
47 const Eigen::MatrixXd& points,
48 size_t k)
49{
50 Eigen::Index n = points.cols();
51 if (n < 2) {
52 return {};
53 }
54
55 k = std::min(k, static_cast<size_t>(n - 1));
56
57 EdgeList edges;
58 edges.reserve(n * k);
59
60 for (Eigen::Index i = 0; i < n; ++i) {
61 std::vector<std::pair<double, size_t>> distances;
62 distances.reserve(n - 1);
63
64 for (Eigen::Index j = 0; j < n; ++j) {
65 if (i == j)
66 continue;
67 double dist_sq = distance_squared(points.col(i), points.col(j));
68 distances.emplace_back(dist_sq, static_cast<size_t>(j));
69 }
70
71 std::partial_sort(
72 distances.begin(),
73 distances.begin() + k,
74 distances.end());
75
76 for (size_t neighbor_idx = 0; neighbor_idx < k; ++neighbor_idx) {
77 edges.emplace_back(static_cast<size_t>(i), distances[neighbor_idx].second);
78 }
79 }
80
82 "k_nearest_neighbors: {} points, k={}, generated {} edges",
83 n, k, edges.size());
84
85 return edges;
86}
87
89 const Eigen::MatrixXd& points,
90 double radius)
91{
92 Eigen::Index n = points.cols();
93 if (n < 2) {
94 return {};
95 }
96
97 double radius_sq = radius * radius;
98 EdgeList edges;
99
100 for (Eigen::Index i = 0; i < n; ++i) {
101 for (Eigen::Index j = i + 1; j < n; ++j) {
102 double dist_sq = distance_squared(points.col(i), points.col(j));
103 if (dist_sq <= radius_sq) {
104 edges.emplace_back(static_cast<size_t>(i), static_cast<size_t>(j));
105 }
106 }
107 }
108
110 "radius_threshold_graph: {} points, radius={:.3f}, generated {} edges",
111 n, radius, edges.size());
112
113 return edges;
114}
115
116EdgeList minimum_spanning_tree(const Eigen::MatrixXd& points)
117{
118 Eigen::Index n = points.cols();
119 if (n < 2) {
120 return {};
121 }
122
123 EdgeList mst_edges;
124 mst_edges.reserve(n - 1);
125
126 std::vector<bool> in_mst(n, false);
127 std::priority_queue<Edge, std::vector<Edge>, std::greater<>> pq;
128
129 in_mst[0] = true;
130
131 for (Eigen::Index j = 1; j < n; ++j) {
132 double dist = distance(points.col(0), points.col(j));
133 pq.push({ 0, static_cast<size_t>(j), dist });
134 }
135
136 while (!pq.empty() && mst_edges.size() < static_cast<size_t>(n - 1)) {
137 Edge e = pq.top();
138 pq.pop();
139
140 if (in_mst[e.b])
141 continue;
142
143 mst_edges.emplace_back(e.a, e.b);
144 in_mst[e.b] = true;
145
146 for (Eigen::Index j = 0; j < n; ++j) {
147 if (!in_mst[j]) {
148 double dist = distance(points.col(e.b), points.col(j));
149 pq.push({ e.b, static_cast<size_t>(j), dist });
150 }
151 }
152 }
153
155 "minimum_spanning_tree: {} points, generated {} edges",
156 n, mst_edges.size());
157
158 return mst_edges;
159}
160
161EdgeList gabriel_graph(const Eigen::MatrixXd& points)
162{
163 Eigen::Index n = points.cols();
164 if (n < 2) {
165 return {};
166 }
167
168 EdgeList edges;
169
170 for (Eigen::Index i = 0; i < n; ++i) {
171 for (Eigen::Index j = i + 1; j < n; ++j) {
172 Eigen::VectorXd p = points.col(i);
173 Eigen::VectorXd q = points.col(j);
174
175 double pq_dist_sq = distance_squared(p, q);
176 bool is_gabriel_edge = true;
177
178 for (Eigen::Index k = 0; k < n; ++k) {
179 if (k == i || k == j)
180 continue;
181
182 Eigen::VectorXd r = points.col(k);
183
184 double pr_dist_sq = distance_squared(p, r);
185 double qr_dist_sq = distance_squared(q, r);
186
187 if (pr_dist_sq + qr_dist_sq < pq_dist_sq) {
188 is_gabriel_edge = false;
189 break;
190 }
191 }
192
193 if (is_gabriel_edge) {
194 edges.emplace_back(static_cast<size_t>(i), static_cast<size_t>(j));
195 }
196 }
197 }
198
200 "gabriel_graph: {} points, generated {} edges",
201 n, edges.size());
202
203 return edges;
204}
205
206EdgeList nearest_neighbor_graph(const Eigen::MatrixXd& points)
207{
208 Eigen::Index n = points.cols();
209 if (n < 2) {
210 return {};
211 }
212
213 EdgeList edges;
214 edges.reserve(n);
215
216 for (Eigen::Index i = 0; i < n; ++i) {
217 double min_dist_sq = std::numeric_limits<double>::max();
218 size_t nearest = i;
219
220 for (Eigen::Index j = 0; j < n; ++j) {
221 if (i == j)
222 continue;
223
224 double dist_sq = distance_squared(points.col(i), points.col(j));
225 if (dist_sq < min_dist_sq) {
226 min_dist_sq = dist_sq;
227 nearest = static_cast<size_t>(j);
228 }
229 }
230
231 if (nearest != static_cast<size_t>(i)) {
232 edges.emplace_back(static_cast<size_t>(i), nearest);
233 }
234 }
235
237 "nearest_neighbor_graph: {} points, generated {} edges", n, edges.size());
238
239 return edges;
240}
241
242EdgeList relative_neighborhood_graph(const Eigen::MatrixXd& points)
243{
244 Eigen::Index n = points.cols();
245 if (n < 2) {
246 return {};
247 }
248
249 EdgeList edges;
250
251 for (Eigen::Index i = 0; i < n; ++i) {
252 for (Eigen::Index j = i + 1; j < n; ++j) {
253 Eigen::VectorXd p = points.col(i);
254 Eigen::VectorXd q = points.col(j);
255
256 double pq_dist = distance(p, q);
257 bool is_rng_edge = true;
258
259 for (Eigen::Index k = 0; k < n; ++k) {
260 if (k == i || k == j)
261 continue;
262
263 Eigen::VectorXd r = points.col(k);
264
265 double pr_dist = distance(p, r);
266 double qr_dist = distance(q, r);
267
268 double max_dist = std::max(pr_dist, qr_dist);
269
270 if (max_dist < pq_dist) {
271 is_rng_edge = false;
272 break;
273 }
274 }
275
276 if (is_rng_edge) {
277 edges.emplace_back(static_cast<size_t>(i), static_cast<size_t>(j));
278 }
279 }
280 }
281
283 "relative_neighborhood_graph: {} points, generated {} edges",
284 n, edges.size());
285
286 return edges;
287}
288
290 const Eigen::MatrixXd& points,
291 const std::function<EdgeList(const Eigen::MatrixXd&)>& connection_function)
292{
293 if (!connection_function) {
295 "custom_proximity_graph: connection_function is null");
296 return {};
297 }
298
299 EdgeList edges = connection_function(points);
300
302 "custom_proximity_graph: {} points, generated {} edges",
303 points.cols(), edges.size());
304
305 return edges;
306}
307
309 const Eigen::MatrixXd& points,
310 const ProximityConfig& config)
311{
312 switch (config.mode) {
313
315 return sequential_chain(points);
316
318 return k_nearest_neighbors(points, config.k_neighbors);
319
321 return radius_threshold_graph(points, config.radius);
322
324 return minimum_spanning_tree(points);
325
327 return gabriel_graph(points);
328
330 return nearest_neighbor_graph(points);
331
333 return relative_neighborhood_graph(points);
334
336 return custom_proximity_graph(points, config.custom_function);
337
338 default:
339 return {};
340 }
341}
342
343} // namespace MayaFlux::Kinesis
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
size_t a
double weight
size_t b
double q
@ Runtime
General runtime operations (default fallback)
@ Kinesis
General mathematical and physics algorithns.
EdgeList minimum_spanning_tree(const Eigen::MatrixXd &points)
Compute minimum spanning tree (Prim's algorithm)
EdgeList nearest_neighbor_graph(const Eigen::MatrixXd &points)
Compute nearest neighbor graph.
EdgeList custom_proximity_graph(const Eigen::MatrixXd &points, const std::function< EdgeList(const Eigen::MatrixXd &)> &connection_function)
Custom proximity graph via user function.
EdgeList k_nearest_neighbors(const Eigen::MatrixXd &points, size_t k)
Compute K-nearest neighbors graph.
std::vector< std::pair< size_t, size_t > > EdgeList
EdgeList sequential_chain(const Eigen::MatrixXd &points)
Compute sequential chain graph.
EdgeList relative_neighborhood_graph(const Eigen::MatrixXd &points)
Compute relative neighborhood graph.
EdgeList generate_proximity_graph(const Eigen::MatrixXd &points, const ProximityConfig &config)
Generate proximity graph using specified mode.
EdgeList radius_threshold_graph(const Eigen::MatrixXd &points, double radius)
Compute radius threshold graph.
EdgeList gabriel_graph(const Eigen::MatrixXd &points)
Compute Gabriel graph.
std::function< EdgeList(const Eigen::MatrixXd &)> custom_function