-
Notifications
You must be signed in to change notification settings - Fork 2
/
components.cc
209 lines (187 loc) · 6.33 KB
/
components.cc
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#include "components.h"
#include <cassert>
#include <cilk/cilk.h>
#include <emu_c_utils/emu_c_utils.h>
#include <queue>
#include <unordered_map>
#include <emu_cxx_utils/fill.h>
using namespace emu;
using namespace emu::parallel;
components::components(graph & g)
: g_(&g)
, worklist_(g.num_vertices())
, component_(g.num_vertices())
, component_size_(g.num_vertices())
, num_components_(g.num_vertices())
, changed_(false)
{
clear();
}
components::components(const components& other, emu::shallow_copy shallow)
: g_(other.g_)
, worklist_(other.worklist_, shallow)
, component_(other.component_, shallow)
, component_size_(other.component_size_, shallow)
, num_components_(other.num_components_)
, changed_(other.changed_)
{}
void
components::clear()
{
}
components::stats
components::run()
{
worklist_.clear_all();
for_each(fixed, g_->vertices_begin(), g_->vertices_end(), [this] (long v){
// Put each vertex in its own component
component_[v] = v;
// Set size of each component to zero
component_size_[v] = 0;
// Build the worklist for the first iteration
// Later, we do this during the tree-climbing step
worklist_.append(v, g_->out_edges_begin(v), g_->out_edges_end(v));
});
long num_iters;
for (num_iters = 1; ; ++num_iters) {
changed_ = false;
// For all edges that connect vertices in different components...
worklist_.process_all_edges(dynamic_policy<64>(),
[this](long src, long dst) {
long &comp_src = component_[src];
long &comp_dst = component_[dst];
if (comp_dst < comp_src) {
comp_src = comp_dst;
changed_ = true;
}
}
);
// No changes? We're done!
if (!repl_reduce(changed_, std::logical_or<>())) break;
worklist_.clear_all();
g_->for_each_vertex(fixed, [this](long v) {
// Merge connected components
while (component_[v] != component_[component_[v]]) {
component_[v] = component_[component_[v]];
}
// Add this vertex to the worklist for the next step
worklist_.append(v, g_->out_edges_begin(v), g_->out_edges_end(v));
});
}
// Count up the size of each component
for_each(fixed, component_.begin(), component_.end(),
[this](long c) { emu::remote_add(&component_size_[c], 1); }
);
stats s;
s.num_iters = num_iters;
// TODO should use parallel count_if here (can use transform_reduce)
// s.num_components = parallel::count_if(
// component_size_.begin(), component_size_.end(),
// [](long size) { return size > 0; }
// );
// Count number of components
num_components_ = 0;
for_each(component_size_.begin(), component_size_.end(),
[&](long size) {
if (size > 0) { emu::remote_add(&num_components_, 1); }
}
);
s.num_components = emu::repl_reduce(num_components_, std::plus<>());
return s;
}
void
components::dump()
{
long max_component = *std::max_element(component_.begin(), component_.end());
auto print_range = [](bool first_entry, long c, long first, long last) {
if (first_entry) {
printf("Component %li: ", c);
} else {
printf(", ");
}
if (first == last) {
printf("%li", first);
} else {
printf("%li-%li", first, last);
}
};
// For each component...
for (long c = 0; c <= max_component; ++c) {
long range_start = -1;
bool first_entry = true;
for (long v = 0; v < g_->num_vertices(); ++v) {
// Is the vertex in the component?
if (c == component_[v]) {
// Record the start of a range of vertices
if (range_start < 0) { range_start = v; }
} else {
// End of the range
if (range_start >= 0) {
// Print start of line only once, omit for empty components
// Print vertices as a range if possible
print_range(first_entry, c, range_start, v - 1);
// Reset range
range_start = -1;
first_entry = false;
}
}
}
// Print last range
if (range_start >= 0) {
print_range(first_entry, c, range_start, g_->num_vertices() - 1);
}
if (!first_entry) { printf("\n"); }
fflush(stdout);
}
}
// Do serial BFS on each component to check labels
bool
components::check()
{
// Make sure we reach each vertex at least once
std::vector<long> visited(g_->num_vertices(), 0);
// Build a map from component labels to a vertex in that component
std::unordered_map<long, long> label_to_source;
for (long v = 0; v < g_->num_vertices(); ++v) {
label_to_source[component_[v]] = v;
}
for (auto p : label_to_source) {
long my_component = p.first;
long source = p.second;
visited[source] = 1;
// Do a serial BFS
// Push source into the queue
std::queue<long> q;
q.push(source);
// For each vertex in the queue...
while (!q.empty()) {
long u = q.front(); q.pop();
// For each out-neighbor of this vertex...
auto edges_begin = g_->out_neighbors(u);
auto edges_end = edges_begin + g_->out_degree(u);
for (auto e = edges_begin; e < edges_end; ++e) {
long v = *e;
// Check that all neighbors have the same component ID
if (component_[v] != my_component) {
LOG("Connected vertices in different components: \n");
LOG("%li (component %li) -> %li (component %li)\n",
source, my_component, v, component_[v]);
return false;
}
// Add unexplored neighbors to the queue
if (!visited[v]) {
visited[v] = 1;
q.push(v);
}
}
}
}
// Make sure we visited all vertices
for (long v = 0; v < g_->num_vertices(); ++v) {
if (visited[v] == 0) {
LOG("Failed to visit %li\n", v);
return false;
}
}
return true;
}