Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 27 additions & 57 deletions include/graph/algorithm/connected_components.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/**
/**
* @file connected_components.hpp
*
* @brief Single-Source Shortest paths and shortest sistances algorithms using Dijkstra &
Expand All @@ -21,7 +21,6 @@
#include "graph/views/breadth_first_search.hpp"
#include <stack>
#include <random>
#include <numeric>

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

std::stack<vertex_id_t<G>> S;
CT cid = 0;
for (auto&& [uid, u] : views::vertexlist(g)) {
if (visited[uid]) {
for (vertex_id_t<G> uid = 0; uid < N; ++uid) {
if (component[uid] < std::numeric_limits<CT>::max()) {
continue;
}
visited[uid] = true;
component[uid] = cid;
if (!size(edges(g, u))) {
++cid;

if (!size(edges(g, uid))) {
component[uid] = cid++;
continue;
}
vertices_breadth_first_search_view<G, void> bfs(g, uid);
for (auto&& [vid, v] : bfs) {
component[vid] = cid;
visited[vid] = true;

component[uid] = cid;
S.push(uid);
while (!S.empty()) {
auto vid = S.top();
S.pop();
for (auto&& [wid, vw] : views::incidence(g, vid)) {
if (component[wid] == std::numeric_limits<CT>::max()) {
component[wid] = cid;
S.push(wid);
}
}
}
++cid;
}
Expand Down Expand Up @@ -156,7 +162,7 @@ template <typename vertex_id_t, random_access_range Component>
static vertex_id_t sample_frequent_element(Component& component, size_t num_samples = 1024) {
std::unordered_map<vertex_id_t, int> counts(32);
std::mt19937 gen;
std::uniform_int_distribution<vertex_id_t> distribution(0, static_cast<vertex_id_t>(component.size() - 1));
std::uniform_int_distribution<vertex_id_t> distribution(0, component.size() - 1);

for (size_t i = 0; i < num_samples; ++i) {
vertex_id_t sample = distribution(gen);
Expand All @@ -172,9 +178,9 @@ template <adjacency_list G, random_access_range Component>
requires random_access_range<vertex_range_t<G>> && integral<vertex_id_t<G>> &&
std::convertible_to<range_value_t<Component>, vertex_id_t<G>> &&
std::convertible_to<vertex_id_t<G>, range_value_t<Component>>
size_t afforest(G&& g, // graph
Component& component, // out: connected component assignment
const size_t neighbor_rounds = 2) {
void afforest(G&& g, // graph
Component& component, // out: connected component assignment
const size_t neighbor_rounds = 2) {
size_t N(size(vertices(g)));
std::iota(component.begin(), component.end(), 0);

Expand Down Expand Up @@ -205,34 +211,16 @@ size_t afforest(G&& g, // graph
}

compress(component);
vertex_id_t<G> target_id = 0;
std::map<vertex_id_t<G>, vertex_id_t<G>> reindex;
for (vertex_id_t<G> vtx = 0; vtx < N; ++vtx) {
if (!reindex.empty()) {
auto it = reindex.find(component[vtx]);
if (it != reindex.end()) {
component[vtx] = (*it).second;
}
} else if (component[vtx] == target_id) {
++target_id;
} else if (component[vtx] > target_id) {
reindex.insert(pair(component[vtx], target_id));
component[vtx] = target_id;
++target_id;
}
}

return target_id;
}

template <adjacency_list G, adjacency_list GT, random_access_range Component>
requires random_access_range<vertex_range_t<G>> && integral<vertex_id_t<G>> &&
std::convertible_to<range_value_t<Component>, vertex_id_t<G>> &&
std::convertible_to<vertex_id_t<G>, range_value_t<Component>>
size_t afforest(G&& g, // graph
GT&& g_t, // graph transpose
Component& component, // out: connected component assignment
const size_t neighbor_rounds = 2) {
void afforest(G&& g, // graph
GT&& g_t, // graph transpose
Component& component, // out: connected component assignment
const size_t neighbor_rounds = 2) {
size_t N(size(vertices(g)));
std::iota(component.begin(), component.end(), 0);

Expand Down Expand Up @@ -266,24 +254,6 @@ size_t afforest(G&& g, // graph
}

compress(component);
vertex_id_t<G> target_id = 0;
std::map<vertex_id_t<G>, vertex_id_t<G>> reindex;
for (vertex_id_t<G> vtx = 0; vtx < N; ++vtx) {
if (!reindex.empty()) {
auto it = reindex.find(component[vtx]);
if (it != reindex.end()) {
component[vtx] = (*it).second;
}
} else if (component[vtx] == target_id) {
++target_id;
} else if (component[vtx] > target_id) {
reindex.insert(pair(component[vtx], target_id));
component[vtx] = target_id;
++target_id;
}
}

return target_id;
}

} // namespace graph
Expand Down
17 changes: 13 additions & 4 deletions tests/cc_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,13 @@ TEST_CASE("afforest test", "[afforest cc]") {
gt.load_edges(reverse, edge_proj, N);

std::vector<vertex_id_t<G>> component(size(vertices(g)));
auto components = graph::afforest(g, component);
graph::afforest(g, component);
std::unordered_set<vertex_id_t<G>> componentIds;
for (vertex_id_t<G> vtx = 0; vtx < N; ++vtx) {
componentIds.insert(component[vtx]);
}
auto components = componentIds.size();
REQUIRE(components == 3);
REQUIRE(*std::ranges::max_element(component) == 2);
}

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

std::vector<vertex_id_t<G>> component(size(vertices(g)));
auto components = graph::afforest(g, gt, component);
graph::afforest(g, gt, component);
std::unordered_set<vertex_id_t<G>> componentIds;
for (vertex_id_t<G> vtx = 0; vtx < N; ++vtx) {
componentIds.insert(component[vtx]);
}
auto components = componentIds.size();

REQUIRE(components == 1);
REQUIRE(*std::ranges::max_element(component) == 0);
}
#endif

Expand Down
Loading