Lagrange
fast_edge_sort.h
1/*
2 * Copyright 2024 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12
13#pragma once
14
15#include <lagrange/utils/assert.h>
16#include <lagrange/utils/timing.h>
17
18// clang-format off
19#include <lagrange/utils/warnoff.h>
20#include <tbb/parallel_sort.h>
21#include <lagrange/utils/warnon.h>
22// clang-format on
23
24#include <algorithm>
25#include <numeric>
26
27namespace lagrange::internal {
28
29template <typename Index>
31{
32 Index v1;
33 Index v2;
34 Index id;
35
36 UnorientedEdge(Index x, Index y, Index c)
37 : v1(std::min(x, y))
38 , v2(std::max(x, y))
39 , id(c)
40 {}
41
42 auto key() const { return std::make_pair(v1, v2); }
43
44 bool operator<(const UnorientedEdge& e) const { return key() < e.key(); }
45
46 bool operator!=(const UnorientedEdge& e) const { return key() != e.key(); }
47};
48
70template <typename Index, typename Func>
71std::vector<Index> fast_edge_sort(
72 Index num_edges,
73 Index num_vertices,
74 Func get_edge,
75 span<Index> vertex_to_first_edge = {})
76{
77 std::vector<Index> local_buffer;
78 if (vertex_to_first_edge.empty()) {
79 local_buffer.assign(num_vertices + 1, 0);
80 vertex_to_first_edge = local_buffer;
81 } else {
82 std::fill(vertex_to_first_edge.begin(), vertex_to_first_edge.end(), Index(0));
83 }
84 la_runtime_assert(vertex_to_first_edge.size() == static_cast<size_t>(num_vertices) + 1);
85 // Count number of edges starting at each vertex
86 for (Index e = 0; e < num_edges; ++e) {
87 std::array<Index, 2> v = get_edge(e);
88 if (v[0] > v[1]) {
89 std::swap(v[0], v[1]);
90 }
91 vertex_to_first_edge[v[0] + 1]++;
92 }
93 // Prefix sum to compute actual offsets
94 std::partial_sum(
95 vertex_to_first_edge.begin(),
96 vertex_to_first_edge.end(),
97 vertex_to_first_edge.begin());
98 la_runtime_assert(vertex_to_first_edge.back() == num_edges);
99 // Bucket each edge id to its respective starting vertex
100 std::vector<Index> edge_ids(num_edges);
101 for (Index e = 0; e < num_edges; ++e) {
102 std::array<Index, 2> v = get_edge(e);
103 if (v[0] > v[1]) {
104 std::swap(v[0], v[1]);
105 }
106 edge_ids[vertex_to_first_edge[v[0]]++] = e;
107 }
108 // Shift back the offset buffer 'vertex_to_first_edge' (can use std::shift_right in C++20 :p)
109 std::rotate(
110 vertex_to_first_edge.rbegin(),
111 vertex_to_first_edge.rbegin() + 1,
112 vertex_to_first_edge.rend());
113 vertex_to_first_edge.front() = 0;
114 // Sort each bucket in parallel
115 tbb::parallel_for(Index(0), num_vertices, [&](Index v) {
116 tbb::parallel_sort(
117 edge_ids.begin() + vertex_to_first_edge[v],
118 edge_ids.begin() + vertex_to_first_edge[v + 1],
119 [&](Index ei, Index ej) {
120 auto vi = get_edge(ei);
121 auto vj = get_edge(ej);
122 if (vi[0] > vi[1]) std::swap(vi[0], vi[1]);
123 if (vj[0] > vj[1]) std::swap(vj[0], vj[1]);
124 return vi < vj;
125 });
126 });
127 return edge_ids;
128}
129
130} // namespace lagrange::internal
#define la_runtime_assert(...)
Runtime assertion check.
Definition: assert.h:169
constexpr void swap(function_ref< R(Args...)> &lhs, function_ref< R(Args...)> &rhs) noexcept
Swaps the referred callables of lhs and rhs.
Definition: function_ref.h:114
::nonstd::span< T, Extent > span
A bounds-safe view for sequences of objects.
Definition: span.h:27
nullptr_t, size_t, ptrdiff_t basic_ostream bad_weak_ptr extent, remove_extent, is_array,...
Definition: attribute_string_utils.h:21
std::vector< Index > fast_edge_sort(Index num_edges, Index num_vertices, Func get_edge, span< Index > vertex_to_first_edge={})
Sort an array of edges using a parallel bucket sort.
Definition: fast_edge_sort.h:71
Definition: fast_edge_sort.h:31