Skip to content

Commit 35f0e43

Browse files
authored
Merge pull request #160 from stdgraph/kdeweese_fixes
changes to CC implementation arising from benchmark considerations
2 parents 3411334 + be15633 commit 35f0e43

2 files changed

Lines changed: 40 additions & 61 deletions

File tree

include/graph/algorithm/connected_components.hpp

Lines changed: 27 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/**
1+
/**
22
* @file connected_components.hpp
33
*
44
* @brief Single-Source Shortest paths and shortest sistances algorithms using Dijkstra &
@@ -21,7 +21,6 @@
2121
#include "graph/views/breadth_first_search.hpp"
2222
#include <stack>
2323
#include <random>
24-
#include <numeric>
2524

2625
#ifndef GRAPH_CC_HPP
2726
# define GRAPH_CC_HPP
@@ -94,25 +93,32 @@ size_t connected_components(G&& g, // graph
9493
Component& component // out: connected component assignment
9594
) {
9695
size_t N(size(vertices(g)));
97-
std::vector<bool> visited(N, false);
9896
using CT = typename std::decay<decltype(*component.begin())>::type;
9997
std::fill(component.begin(), component.end(), std::numeric_limits<CT>::max());
10098

99+
std::stack<vertex_id_t<G>> S;
101100
CT cid = 0;
102-
for (auto&& [uid, u] : views::vertexlist(g)) {
103-
if (visited[uid]) {
101+
for (vertex_id_t<G> uid = 0; uid < N; ++uid) {
102+
if (component[uid] < std::numeric_limits<CT>::max()) {
104103
continue;
105104
}
106-
visited[uid] = true;
107-
component[uid] = cid;
108-
if (!size(edges(g, u))) {
109-
++cid;
105+
106+
if (!size(edges(g, uid))) {
107+
component[uid] = cid++;
110108
continue;
111109
}
112-
vertices_breadth_first_search_view<G, void> bfs(g, uid);
113-
for (auto&& [vid, v] : bfs) {
114-
component[vid] = cid;
115-
visited[vid] = true;
110+
111+
component[uid] = cid;
112+
S.push(uid);
113+
while (!S.empty()) {
114+
auto vid = S.top();
115+
S.pop();
116+
for (auto&& [wid, vw] : views::incidence(g, vid)) {
117+
if (component[wid] == std::numeric_limits<CT>::max()) {
118+
component[wid] = cid;
119+
S.push(wid);
120+
}
121+
}
116122
}
117123
++cid;
118124
}
@@ -156,7 +162,7 @@ template <typename vertex_id_t, random_access_range Component>
156162
static vertex_id_t sample_frequent_element(Component& component, size_t num_samples = 1024) {
157163
std::unordered_map<vertex_id_t, int> counts(32);
158164
std::mt19937 gen;
159-
std::uniform_int_distribution<vertex_id_t> distribution(0, static_cast<vertex_id_t>(component.size() - 1));
165+
std::uniform_int_distribution<vertex_id_t> distribution(0, component.size() - 1);
160166

161167
for (size_t i = 0; i < num_samples; ++i) {
162168
vertex_id_t sample = distribution(gen);
@@ -172,9 +178,9 @@ template <adjacency_list G, random_access_range Component>
172178
requires random_access_range<vertex_range_t<G>> && integral<vertex_id_t<G>> &&
173179
std::convertible_to<range_value_t<Component>, vertex_id_t<G>> &&
174180
std::convertible_to<vertex_id_t<G>, range_value_t<Component>>
175-
size_t afforest(G&& g, // graph
176-
Component& component, // out: connected component assignment
177-
const size_t neighbor_rounds = 2) {
181+
void afforest(G&& g, // graph
182+
Component& component, // out: connected component assignment
183+
const size_t neighbor_rounds = 2) {
178184
size_t N(size(vertices(g)));
179185
std::iota(component.begin(), component.end(), 0);
180186

@@ -205,34 +211,16 @@ size_t afforest(G&& g, // graph
205211
}
206212

207213
compress(component);
208-
vertex_id_t<G> target_id = 0;
209-
std::map<vertex_id_t<G>, vertex_id_t<G>> reindex;
210-
for (vertex_id_t<G> vtx = 0; vtx < N; ++vtx) {
211-
if (!reindex.empty()) {
212-
auto it = reindex.find(component[vtx]);
213-
if (it != reindex.end()) {
214-
component[vtx] = (*it).second;
215-
}
216-
} else if (component[vtx] == target_id) {
217-
++target_id;
218-
} else if (component[vtx] > target_id) {
219-
reindex.insert(pair(component[vtx], target_id));
220-
component[vtx] = target_id;
221-
++target_id;
222-
}
223-
}
224-
225-
return target_id;
226214
}
227215

228216
template <adjacency_list G, adjacency_list GT, random_access_range Component>
229217
requires random_access_range<vertex_range_t<G>> && integral<vertex_id_t<G>> &&
230218
std::convertible_to<range_value_t<Component>, vertex_id_t<G>> &&
231219
std::convertible_to<vertex_id_t<G>, range_value_t<Component>>
232-
size_t afforest(G&& g, // graph
233-
GT&& g_t, // graph transpose
234-
Component& component, // out: connected component assignment
235-
const size_t neighbor_rounds = 2) {
220+
void afforest(G&& g, // graph
221+
GT&& g_t, // graph transpose
222+
Component& component, // out: connected component assignment
223+
const size_t neighbor_rounds = 2) {
236224
size_t N(size(vertices(g)));
237225
std::iota(component.begin(), component.end(), 0);
238226

@@ -266,24 +254,6 @@ size_t afforest(G&& g, // graph
266254
}
267255

268256
compress(component);
269-
vertex_id_t<G> target_id = 0;
270-
std::map<vertex_id_t<G>, vertex_id_t<G>> reindex;
271-
for (vertex_id_t<G> vtx = 0; vtx < N; ++vtx) {
272-
if (!reindex.empty()) {
273-
auto it = reindex.find(component[vtx]);
274-
if (it != reindex.end()) {
275-
component[vtx] = (*it).second;
276-
}
277-
} else if (component[vtx] == target_id) {
278-
++target_id;
279-
} else if (component[vtx] > target_id) {
280-
reindex.insert(pair(component[vtx], target_id));
281-
component[vtx] = target_id;
282-
++target_id;
283-
}
284-
}
285-
286-
return target_id;
287257
}
288258

289259
} // namespace graph

tests/cc_tests.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,13 @@ TEST_CASE("afforest test", "[afforest cc]") {
9898
gt.load_edges(reverse, edge_proj, N);
9999

100100
std::vector<vertex_id_t<G>> component(size(vertices(g)));
101-
auto components = graph::afforest(g, component);
101+
graph::afforest(g, component);
102+
std::unordered_set<vertex_id_t<G>> componentIds;
103+
for (vertex_id_t<G> vtx = 0; vtx < N; ++vtx) {
104+
componentIds.insert(component[vtx]);
105+
}
106+
auto components = componentIds.size();
102107
REQUIRE(components == 3);
103-
REQUIRE(*std::ranges::max_element(component) == 2);
104108
}
105109

106110
TEST_CASE("afforest test weak", "[afforest weak_cc]") {
@@ -131,9 +135,14 @@ TEST_CASE("afforest test weak", "[afforest weak_cc]") {
131135
gt.load_edges(reverse, edge_proj, N);
132136

133137
std::vector<vertex_id_t<G>> component(size(vertices(g)));
134-
auto components = graph::afforest(g, gt, component);
138+
graph::afforest(g, gt, component);
139+
std::unordered_set<vertex_id_t<G>> componentIds;
140+
for (vertex_id_t<G> vtx = 0; vtx < N; ++vtx) {
141+
componentIds.insert(component[vtx]);
142+
}
143+
auto components = componentIds.size();
144+
135145
REQUIRE(components == 1);
136-
REQUIRE(*std::ranges::max_element(component) == 0);
137146
}
138147
#endif
139148

0 commit comments

Comments
 (0)