3#ifdef MAYAFLUX_ARCH_X64
6#ifdef MAYAFLUX_ARCH_ARM64
28 constexpr size_t k_max_table_points = 8192;
36 [[nodiscard]]
inline double dist_sq(
const double*
a,
const double*
b,
size_t dim)
noexcept
39 for (
size_t c = 0; c < dim; ++c) {
40 const double delta =
b[c] -
a[c];
52 [[nodiscard]]
inline bool sum_below(
53 const double* row_i,
const double* row_j,
size_t n,
double threshold)
noexcept
57#ifdef MAYAFLUX_ARCH_X64
58 const __m256d limit = _mm256_set1_pd(
threshold);
59 for (;
k + 4 <= n;
k += 4) {
60 const __m256d
sum = _mm256_add_pd(
61 _mm256_loadu_pd(row_i +
k),
62 _mm256_loadu_pd(row_j +
k));
63 if (_mm256_movemask_pd(_mm256_cmp_pd(sum, limit, _CMP_LT_OS)) != 0) {
67#elif defined(MAYAFLUX_ARCH_ARM64)
68 const float64x2_t limit = vdupq_n_f64(
threshold);
69 for (;
k + 2 <= n;
k += 2) {
70 const float64x2_t
sum = vaddq_f64(vld1q_f64(row_i +
k), vld1q_f64(row_j +
k));
71 const uint64x2_t mask = vcltq_f64(sum, limit);
72 if ((vgetq_lane_u64(mask, 0) | vgetq_lane_u64(mask, 1)) != 0) {
92 [[nodiscard]]
inline bool max_below(
93 const double* row_i,
const double* row_j,
size_t n,
double threshold)
noexcept
97#ifdef MAYAFLUX_ARCH_X64
98 const __m256d limit = _mm256_set1_pd(
threshold);
99 for (;
k + 4 <= n;
k += 4) {
100 const __m256d
hi = _mm256_max_pd(
101 _mm256_loadu_pd(row_i +
k),
102 _mm256_loadu_pd(row_j +
k));
103 if (_mm256_movemask_pd(_mm256_cmp_pd(
hi, limit, _CMP_LT_OS)) != 0) {
107#elif defined(MAYAFLUX_ARCH_ARM64)
108 const float64x2_t limit = vdupq_n_f64(
threshold);
109 for (;
k + 2 <= n;
k += 2) {
110 const float64x2_t
hi = vmaxq_f64(vld1q_f64(row_i +
k), vld1q_f64(row_j +
k));
111 const uint64x2_t mask = vcltq_f64(
hi, limit);
112 if ((vgetq_lane_u64(mask, 0) | vgetq_lane_u64(mask, 1)) != 0) {
130 [[nodiscard]]
bool sum_below_direct(
131 const double* base,
size_t dim,
size_t n,
132 size_t i,
size_t j,
double threshold)
noexcept
134 const double* pi = base + i * dim;
135 const double* pj = base + j * dim;
137 for (
size_t k = 0;
k < n; ++
k) {
138 if (
k == i ||
k == j) {
141 const double* pk = base +
k * dim;
142 if (dist_sq(pi, pk, dim) + dist_sq(pj, pk, dim) <
threshold) {
153 [[nodiscard]]
bool max_below_direct(
154 const double* base,
size_t dim,
size_t n,
155 size_t i,
size_t j,
double threshold)
noexcept
157 const double* pi = base + i * dim;
158 const double* pj = base + j * dim;
160 for (
size_t k = 0;
k < n; ++
k) {
161 if (
k == i ||
k == j) {
164 const double* pk = base +
k * dim;
165 if (std::max(dist_sq(pi, pk, dim), dist_sq(pj, pk, dim)) <
threshold) {
179 [[nodiscard]] std::vector<double> build_distance_table(
const Eigen::MatrixXd&
points)
181 const auto n =
static_cast<size_t>(
points.cols());
182 if (n > k_max_table_points) {
186 const auto dim =
static_cast<size_t>(
points.rows());
187 const double* base =
points.data();
189 std::vector<double> table(n * n);
191 P::for_each(P::par_unseq,
192 std::views::iota(
size_t { 0 }, n).begin(),
193 std::views::iota(
size_t { 0 }, n).end(),
195 const double* pi = base + i * dim;
196 double* row = table.data() + i * n;
197 for (
size_t j = 0; j < n; ++j) {
198 row[j] = dist_sq(pi, base + j * dim, dim);
211 [[nodiscard]]
EdgeList flatten_bins(std::vector<EdgeList>& bins)
219 edges.reserve(total);
222 edges.insert(edges.end(), bin.begin(), bin.end());
231 bool operator>(
const Edge& other)
const {
return weight > other.weight; }
238 Eigen::Index n =
points.cols();
244 edges.reserve(n - 1);
246 for (Eigen::Index i = 0; i < n - 1; ++i) {
247 edges.emplace_back(
static_cast<size_t>(i),
static_cast<size_t>(i + 1));
251 "sequential_chain: {} points, generated {} edges", n, edges.size());
257 const Eigen::MatrixXd&
points,
260 const auto n =
static_cast<size_t>(
points.cols());
265 k = std::min(
k, n - 1);
270 const auto dim =
static_cast<size_t>(
points.rows());
271 const double* base =
points.data();
275 P::for_each(P::par_unseq,
276 std::views::iota(
size_t { 0 }, n).begin(),
277 std::views::iota(
size_t { 0 }, n).end(),
279 std::vector<std::pair<double, size_t>> distances;
280 distances.reserve(n - 1);
282 const double* pi = base + i * dim;
283 for (
size_t j = 0; j < n; ++j) {
287 distances.emplace_back(dist_sq(pi, base + j * dim, dim), j);
292 distances.begin() +
static_cast<ptrdiff_t
>(
k),
295 for (
size_t m = 0; m <
k; ++m) {
296 edges[i *
k + m] = { i, distances[m].second };
301 "k_nearest_neighbors: {} points, k={}, generated {} edges",
308 const Eigen::MatrixXd&
points,
311 const auto n =
static_cast<size_t>(
points.cols());
317 const auto dim =
static_cast<size_t>(
points.rows());
318 const double* base =
points.data();
320 std::vector<size_t> offsets(n + 1, 0);
322 P::for_each(P::par_unseq,
323 std::views::iota(
size_t { 0 }, n).begin(),
324 std::views::iota(
size_t { 0 }, n).end(),
326 const double* pi = base + i * dim;
328 for (
size_t j = i + 1; j < n; ++j) {
329 if (dist_sq(pi, base + j * dim, dim) <= radius_sq) {
333 offsets[i + 1] =
count;
336 for (
size_t i = 0; i < n; ++i) {
337 offsets[i + 1] += offsets[i];
342 P::for_each(P::par_unseq,
343 std::views::iota(
size_t { 0 }, n).begin(),
344 std::views::iota(
size_t { 0 }, n).end(),
346 const double* pi = base + i * dim;
347 size_t at = offsets[i];
348 for (
size_t j = i + 1; j < n; ++j) {
349 if (dist_sq(pi, base + j * dim, dim) <= radius_sq) {
350 edges[at++] = { i, j };
356 "radius_threshold_graph: {} points, radius={:.3f}, generated {} edges",
364 const auto n =
static_cast<size_t>(
points.cols());
369 const auto dim =
static_cast<size_t>(
points.rows());
370 const double* base =
points.data();
373 mst_edges.reserve(n - 1);
375 std::vector<bool> in_mst(n,
false);
376 std::priority_queue<Edge, std::vector<Edge>, std::greater<>> pq;
380 for (
size_t j = 1; j < n; ++j) {
381 pq.push({ .a = 0, .b = j, .weight = std::sqrt(dist_sq(base, base + j * dim, dim)) });
384 while (!pq.empty() && mst_edges.size() < n - 1) {
385 const Edge e = pq.top();
392 mst_edges.emplace_back(e.a, e.b);
395 const double* pb = base + e.b * dim;
396 for (
size_t j = 0; j < n; ++j) {
398 pq.push({ .a = e.b, .b = j, .weight = std::sqrt(dist_sq(pb, base + j * dim, dim)) });
404 "minimum_spanning_tree: {} points, generated {} edges",
405 n, mst_edges.size());
412 const auto n =
static_cast<size_t>(
points.cols());
417 const auto dim =
static_cast<size_t>(
points.rows());
418 const double* base =
points.data();
420 const std::vector<double> table = build_distance_table(
points);
421 const bool resident = !table.empty();
423 std::vector<EdgeList> bins(n);
425 P::for_each(P::par_unseq,
426 std::views::iota(
size_t { 0 }, n).begin(),
427 std::views::iota(
size_t { 0 }, n).end(),
429 const double* row_i = resident ? table.data() + i * n :
nullptr;
430 const double* pi = base + i * dim;
433 for (
size_t j = i + 1; j < n; ++j) {
434 const double pq_sq = resident
436 : dist_sq(pi, base + j * dim, dim);
438 const bool rejected = resident
439 ? sum_below(row_i, table.data() + j * n, n, pq_sq)
440 : sum_below_direct(base, dim, n, i, j, pq_sq);
443 out.emplace_back(i, j);
448 EdgeList edges = flatten_bins(bins);
451 "gabriel_graph: {} points, generated {} edges",
459 const auto n =
static_cast<size_t>(
points.cols());
464 const auto dim =
static_cast<size_t>(
points.rows());
465 const double* base =
points.data();
467 std::vector<size_t> nearest(n);
469 P::for_each(P::par_unseq,
470 std::views::iota(
size_t { 0 }, n).begin(),
471 std::views::iota(
size_t { 0 }, n).end(),
473 double min_dist_sq = std::numeric_limits<double>::max();
476 const double* pi = base + i * dim;
477 for (
size_t j = 0; j < n; ++j) {
481 const double d = dist_sq(pi, base + j * dim, dim);
482 if (d < min_dist_sq) {
494 for (
size_t i = 0; i < n; ++i) {
495 if (nearest[i] != i) {
496 edges.emplace_back(i, nearest[i]);
501 "nearest_neighbor_graph: {} points, generated {} edges", n, edges.size());
508 const auto n =
static_cast<size_t>(
points.cols());
513 const auto dim =
static_cast<size_t>(
points.rows());
514 const double* base =
points.data();
516 const std::vector<double> table = build_distance_table(
points);
517 const bool resident = !table.empty();
519 std::vector<EdgeList> bins(n);
521 P::for_each(P::par_unseq,
522 std::views::iota(
size_t { 0 }, n).begin(),
523 std::views::iota(
size_t { 0 }, n).end(),
525 const double* row_i = resident ? table.data() + i * n :
nullptr;
526 const double* pi = base + i * dim;
529 for (
size_t j = i + 1; j < n; ++j) {
530 const double pq_sq = resident
532 : dist_sq(pi, base + j * dim, dim);
534 const bool rejected = resident
535 ? max_below(row_i, table.data() + j * n, n, pq_sq)
536 : max_below_direct(base, dim, n, i, j, pq_sq);
539 out.emplace_back(i, j);
544 EdgeList edges = flatten_bins(bins);
547 "relative_neighborhood_graph: {} points, generated {} edges",
554 const Eigen::MatrixXd&
points,
555 const std::function<
EdgeList(
const Eigen::MatrixXd&)>& connection_function)
557 if (!connection_function) {
559 "custom_proximity_graph: connection_function is null");
566 "custom_proximity_graph: {} points, generated {} edges",
567 points.cols(), edges.size());
573 const Eigen::MatrixXd&
points,
576 switch (config.
mode) {
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
std::vector< glm::vec2 > * points
@ 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