3#ifdef MAYAFLUX_ARCH_X64
6#ifdef MAYAFLUX_ARCH_ARM64
26 constexpr size_t k_max_table_points = 8192;
34 [[nodiscard]]
inline double dist_sq(
const double*
a,
const double*
b,
size_t dim)
noexcept
37 for (
size_t c = 0; c < dim; ++c) {
38 const double delta =
b[c] -
a[c];
50 [[nodiscard]]
inline bool sum_below(
51 const double* row_i,
const double* row_j,
size_t n,
double threshold)
noexcept
55#ifdef MAYAFLUX_ARCH_X64
56 const __m256d limit = _mm256_set1_pd(
threshold);
57 for (; k + 4 <= n; k += 4) {
58 const __m256d
sum = _mm256_add_pd(
59 _mm256_loadu_pd(row_i + k),
60 _mm256_loadu_pd(row_j + k));
61 if (_mm256_movemask_pd(_mm256_cmp_pd(sum, limit, _CMP_LT_OS)) != 0) {
65#elif defined(MAYAFLUX_ARCH_ARM64)
66 const float64x2_t limit = vdupq_n_f64(
threshold);
67 for (; k + 2 <= n; k += 2) {
68 const float64x2_t
sum = vaddq_f64(vld1q_f64(row_i + k), vld1q_f64(row_j + k));
69 const uint64x2_t mask = vcltq_f64(sum, limit);
70 if ((vgetq_lane_u64(mask, 0) | vgetq_lane_u64(mask, 1)) != 0) {
90 [[nodiscard]]
inline bool max_below(
91 const double* row_i,
const double* row_j,
size_t n,
double threshold)
noexcept
95#ifdef MAYAFLUX_ARCH_X64
96 const __m256d limit = _mm256_set1_pd(
threshold);
97 for (; k + 4 <= n; k += 4) {
98 const __m256d hi = _mm256_max_pd(
99 _mm256_loadu_pd(row_i + k),
100 _mm256_loadu_pd(row_j + k));
101 if (_mm256_movemask_pd(_mm256_cmp_pd(hi, limit, _CMP_LT_OS)) != 0) {
105#elif defined(MAYAFLUX_ARCH_ARM64)
106 const float64x2_t limit = vdupq_n_f64(
threshold);
107 for (; k + 2 <= n; k += 2) {
108 const float64x2_t hi = vmaxq_f64(vld1q_f64(row_i + k), vld1q_f64(row_j + k));
109 const uint64x2_t mask = vcltq_f64(hi, limit);
110 if ((vgetq_lane_u64(mask, 0) | vgetq_lane_u64(mask, 1)) != 0) {
117 if (std::max(row_i[k], row_j[k]) <
threshold) {
128 [[nodiscard]]
bool sum_below_direct(
129 const double* base,
size_t dim,
size_t n,
130 size_t i,
size_t j,
double threshold)
noexcept
132 const double* pi = base + i * dim;
133 const double* pj = base + j * dim;
135 for (
size_t k = 0; k < n; ++k) {
136 if (k == i || k == j) {
139 const double* pk = base + k * dim;
140 if (dist_sq(pi, pk, dim) + dist_sq(pj, pk, dim) <
threshold) {
151 [[nodiscard]]
bool max_below_direct(
152 const double* base,
size_t dim,
size_t n,
153 size_t i,
size_t j,
double threshold)
noexcept
155 const double* pi = base + i * dim;
156 const double* pj = base + j * dim;
158 for (
size_t k = 0; k < n; ++k) {
159 if (k == i || k == j) {
162 const double* pk = base + k * dim;
163 if (std::max(dist_sq(pi, pk, dim), dist_sq(pj, pk, dim)) <
threshold) {
177 [[nodiscard]] std::vector<double> build_distance_table(
const Eigen::MatrixXd&
points)
179 const auto n =
static_cast<size_t>(
points.cols());
180 if (n > k_max_table_points) {
184 const auto dim =
static_cast<size_t>(
points.rows());
185 const double* base =
points.data();
187 std::vector<double> table(n * n);
189 std::for_each(std::execution::par_unseq,
190 std::views::iota(
size_t { 0 }, n).begin(),
191 std::views::iota(
size_t { 0 }, n).end(),
193 const double* pi = base + i * dim;
194 double* row = table.data() + i * n;
195 for (
size_t j = 0; j < n; ++j) {
196 row[j] = dist_sq(pi, base + j * dim, dim);
209 [[nodiscard]]
EdgeList flatten_bins(std::vector<EdgeList>& bins)
217 edges.reserve(
total);
220 edges.insert(edges.end(), bin.begin(), bin.end());
229 bool operator>(
const Edge& other)
const {
return weight > other.weight; }
236 Eigen::Index n =
points.cols();
242 edges.reserve(n - 1);
244 for (Eigen::Index i = 0; i < n - 1; ++i) {
245 edges.emplace_back(
static_cast<size_t>(i),
static_cast<size_t>(i + 1));
249 "sequential_chain: {} points, generated {} edges", n, edges.size());
255 const Eigen::MatrixXd&
points,
258 const auto n =
static_cast<size_t>(
points.cols());
263 k = std::min(k, n - 1);
268 const auto dim =
static_cast<size_t>(
points.rows());
269 const double* base =
points.data();
273 std::for_each(std::execution::par_unseq,
274 std::views::iota(
size_t { 0 }, n).begin(),
275 std::views::iota(
size_t { 0 }, n).end(),
277 std::vector<std::pair<double, size_t>> distances;
278 distances.reserve(n - 1);
280 const double* pi = base + i * dim;
281 for (
size_t j = 0; j < n; ++j) {
285 distances.emplace_back(dist_sq(pi, base + j * dim, dim), j);
290 distances.begin() +
static_cast<ptrdiff_t
>(k),
293 for (
size_t m = 0; m < k; ++m) {
294 edges[i * k + m] = { i, distances[m].second };
299 "k_nearest_neighbors: {} points, k={}, generated {} edges",
306 const Eigen::MatrixXd&
points,
309 const auto n =
static_cast<size_t>(
points.cols());
315 const auto dim =
static_cast<size_t>(
points.rows());
316 const double* base =
points.data();
318 std::vector<size_t> offsets(n + 1, 0);
320 std::for_each(std::execution::par_unseq,
321 std::views::iota(
size_t { 0 }, n).begin(),
322 std::views::iota(
size_t { 0 }, n).end(),
324 const double* pi = base + i * dim;
326 for (
size_t j = i + 1; j < n; ++j) {
327 if (dist_sq(pi, base + j * dim, dim) <= radius_sq) {
331 offsets[i + 1] =
count;
334 for (
size_t i = 0; i < n; ++i) {
335 offsets[i + 1] += offsets[i];
340 std::for_each(std::execution::par_unseq,
341 std::views::iota(
size_t { 0 }, n).begin(),
342 std::views::iota(
size_t { 0 }, n).end(),
344 const double* pi = base + i * dim;
345 size_t at = offsets[i];
346 for (
size_t j = i + 1; j < n; ++j) {
347 if (dist_sq(pi, base + j * dim, dim) <= radius_sq) {
348 edges[at++] = { i, j };
354 "radius_threshold_graph: {} points, radius={:.3f}, generated {} edges",
362 const auto n =
static_cast<size_t>(
points.cols());
367 const auto dim =
static_cast<size_t>(
points.rows());
368 const double* base =
points.data();
371 mst_edges.reserve(n - 1);
373 std::vector<bool> in_mst(n,
false);
374 std::priority_queue<Edge, std::vector<Edge>, std::greater<>> pq;
378 for (
size_t j = 1; j < n; ++j) {
379 pq.push({ .a = 0, .b = j, .weight = std::sqrt(dist_sq(base, base + j * dim, dim)) });
382 while (!pq.empty() && mst_edges.size() < n - 1) {
383 const Edge e = pq.top();
390 mst_edges.emplace_back(e.a, e.b);
393 const double* pb = base + e.b * dim;
394 for (
size_t j = 0; j < n; ++j) {
396 pq.push({ .a = e.b, .b = j, .weight = std::sqrt(dist_sq(pb, base + j * dim, dim)) });
402 "minimum_spanning_tree: {} points, generated {} edges",
403 n, mst_edges.size());
410 const auto n =
static_cast<size_t>(
points.cols());
415 const auto dim =
static_cast<size_t>(
points.rows());
416 const double* base =
points.data();
418 const std::vector<double> table = build_distance_table(
points);
419 const bool resident = !table.empty();
421 std::vector<EdgeList> bins(n);
423 std::for_each(std::execution::par_unseq,
424 std::views::iota(
size_t { 0 }, n).begin(),
425 std::views::iota(
size_t { 0 }, n).end(),
427 const double* row_i = resident ? table.data() + i * n :
nullptr;
428 const double* pi = base + i * dim;
431 for (
size_t j = i + 1; j < n; ++j) {
432 const double pq_sq = resident
434 : dist_sq(pi, base + j * dim, dim);
436 const bool rejected = resident
437 ? sum_below(row_i, table.data() + j * n, n, pq_sq)
438 : sum_below_direct(base, dim, n, i, j, pq_sq);
441 out.emplace_back(i, j);
446 EdgeList edges = flatten_bins(bins);
449 "gabriel_graph: {} points, generated {} edges",
457 const auto n =
static_cast<size_t>(
points.cols());
462 const auto dim =
static_cast<size_t>(
points.rows());
463 const double* base =
points.data();
465 std::vector<size_t> nearest(n);
467 std::for_each(std::execution::par_unseq,
468 std::views::iota(
size_t { 0 }, n).begin(),
469 std::views::iota(
size_t { 0 }, n).end(),
471 double min_dist_sq = std::numeric_limits<double>::max();
474 const double* pi = base + i * dim;
475 for (
size_t j = 0; j < n; ++j) {
479 const double d = dist_sq(pi, base + j * dim, dim);
480 if (d < min_dist_sq) {
492 for (
size_t i = 0; i < n; ++i) {
493 if (nearest[i] != i) {
494 edges.emplace_back(i, nearest[i]);
499 "nearest_neighbor_graph: {} points, generated {} edges", n, edges.size());
506 const auto n =
static_cast<size_t>(
points.cols());
511 const auto dim =
static_cast<size_t>(
points.rows());
512 const double* base =
points.data();
514 const std::vector<double> table = build_distance_table(
points);
515 const bool resident = !table.empty();
517 std::vector<EdgeList> bins(n);
519 std::for_each(std::execution::par_unseq,
520 std::views::iota(
size_t { 0 }, n).begin(),
521 std::views::iota(
size_t { 0 }, n).end(),
523 const double* row_i = resident ? table.data() + i * n :
nullptr;
524 const double* pi = base + i * dim;
527 for (
size_t j = i + 1; j < n; ++j) {
528 const double pq_sq = resident
530 : dist_sq(pi, base + j * dim, dim);
532 const bool rejected = resident
533 ? max_below(row_i, table.data() + j * n, n, pq_sq)
534 : max_below_direct(base, dim, n, i, j, pq_sq);
537 out.emplace_back(i, j);
542 EdgeList edges = flatten_bins(bins);
545 "relative_neighborhood_graph: {} points, generated {} edges",
552 const Eigen::MatrixXd&
points,
553 const std::function<
EdgeList(
const Eigen::MatrixXd&)>& connection_function)
555 if (!connection_function) {
557 "custom_proximity_graph: connection_function is null");
564 "custom_proximity_graph: {} points, generated {} edges",
565 points.cols(), edges.size());
571 const Eigen::MatrixXd&
points,
574 switch (config.
mode) {
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
std::vector< glm::vec2 > * points
std::vector< float > * out
@ Runtime
General runtime operations (default fallback)
@ Kinesis
General mathematical and physics algorithns.
std::vector< double > sum(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Sum per window.
EdgeList sequential_chain(const Eigen::MatrixXd &points)
Compute sequential chain 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 relative_neighborhood_graph(const Eigen::MatrixXd &points)
Compute relative neighborhood graph.
EdgeList minimum_spanning_tree(const Eigen::MatrixXd &points)
Compute minimum spanning tree (Prim's algorithm)
EdgeList gabriel_graph(const Eigen::MatrixXd &points)
Compute Gabriel graph.
EdgeList k_nearest_neighbors(const Eigen::MatrixXd &points, size_t k)
Compute K-nearest neighbors graph.
@ 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.
std::vector< std::pair< size_t, size_t > > EdgeList
EdgeList nearest_neighbor_graph(const Eigen::MatrixXd &points)
Compute nearest neighbor graph.
std::function< EdgeList(const Eigen::MatrixXd &)> custom_function