Skip to content

Commit 5dc1cde

Browse files
authored
Merge pull request #127 from stdgraph/algorithms/cc
merge CC code into master
2 parents db94c6e + 189e8b8 commit 5dc1cde

File tree

5 files changed

+237
-0
lines changed

5 files changed

+237
-0
lines changed

data/cc_directed.csv

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
From,To,Distance
2+
A,B,1
3+
B,C,1
4+
C,D,1
5+
D,A,1
6+
B,E,1
7+
E,F,1
8+
F,G,1
9+
G,H,1
10+
H,E,1
11+
G,Q,1
12+
Q,N,1
13+
N,O,1
14+
O,P,1
15+
P,M,1
16+
M,R,1
17+
R,H,1
18+
J,R,1
19+
J,K,1
20+
K,L,1
21+
L,I,1
22+
I,J,1

data/cc_undirected.csv

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
From,To,Distance
2+
A,H,1
3+
H,A,1
4+
B,C,1
5+
C,B,1
6+
B,F,1
7+
F,B,1
8+
B,G,1
9+
G,B,1
10+
C,F,1
11+
F,C,1
12+
D,E,1
13+
E,D,1
14+
H,I,1
15+
I,H,1
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/**
2+
* @file connected_components.hpp
3+
*
4+
* @brief Single-Source Shortest paths and shortest sistances algorithms using Dijkstra &
5+
* Bellman-Ford algorithms.
6+
*
7+
* @copyright Copyright (c) 2022
8+
*
9+
* SPDX-License-Identifier: BSL-1.0
10+
*
11+
* @authors
12+
* Andrew Lumsdaine
13+
* Phil Ratzloff
14+
* Kevin Deweese
15+
*/
16+
17+
#include "graph/graph.hpp"
18+
#include "graph/views/incidence.hpp"
19+
20+
#ifndef GRAPH_CC_HPP
21+
# define GRAPH_CC_HPP
22+
23+
namespace std::graph {
24+
25+
template <adjacency_list G,
26+
adjacency_list GT,
27+
ranges::random_access_range Component>
28+
requires ranges::random_access_range<vertex_range_t<G>> && integral<vertex_id_t<G>>
29+
void kosaraju(G&& g, // graph
30+
GT&& g_t, // graph transpose
31+
Component& component // out: strongly connected component assignment
32+
33+
) {
34+
size_t N(size(vertices(g)));
35+
std::vector<bool> visited(N, false);
36+
using CT = typename std::decay<decltype(*component.begin())>::type;
37+
std::fill(component.begin(), component.end(), std::numeric_limits<CT>::max());
38+
std::vector<vertex_id_t<G>> order;
39+
40+
for (auto&& [uid, u] : views::vertexlist(g)) {
41+
if (visited[uid]) {
42+
continue;
43+
}
44+
visited[uid] = true;
45+
std::stack<vertex_id_t<G>> active;
46+
active.push(uid);
47+
auto dfs = std::graph::views::sourced_edges_depth_first_search(g, uid);
48+
for (auto&& [vid, wid, vw] : dfs) {
49+
while (vid != active.top()) {
50+
order.push_back(active.top());
51+
active.pop();
52+
}
53+
if (visited[wid]) {
54+
dfs.cancel(cancel_search::cancel_branch);
55+
}
56+
else {
57+
active.push(wid);
58+
visited[wid] = true;
59+
}
60+
}
61+
while (!active.empty()) {
62+
order.push_back(active.top());
63+
active.pop();
64+
}
65+
}
66+
67+
size_t cid = 0;
68+
std::ranges::reverse_view reverse {order};
69+
for (auto& uid : reverse) {
70+
if (component[uid] == std::numeric_limits<CT>::max()) {
71+
component[uid] = cid;
72+
vertices_depth_first_search_view<GT> dfs(g_t, uid);
73+
for (auto&& [vid, v] : dfs) {
74+
if (component[vid] != std::numeric_limits<CT>::max()) {
75+
dfs.cancel(cancel_search::cancel_branch);
76+
}
77+
else {
78+
component[vid] = cid;
79+
}
80+
}
81+
++cid;
82+
}
83+
}
84+
}
85+
86+
template <adjacency_list G,
87+
ranges::random_access_range Component>
88+
requires ranges::random_access_range<vertex_range_t<G>> && integral<vertex_id_t<G>>
89+
void connected_components(G&& g, // graph
90+
Component& component // out: connected component assignment
91+
) {
92+
size_t N(size(vertices(g)));
93+
std::vector<bool> visited(N, false);
94+
using CT = typename std::decay<decltype(*component.begin())>::type;
95+
std::fill(component.begin(), component.end(), std::numeric_limits<CT>::max());
96+
97+
CT cid = 0;
98+
for (auto&& [uid, u] : views::vertexlist(g)) {
99+
if (visited[uid]) {
100+
continue;
101+
}
102+
visited[uid] = true;
103+
component[uid] = cid;
104+
vertices_depth_first_search_view<G, void> dfs(g, uid);
105+
for (auto&& [vid, v] : dfs) {
106+
component[vid] = cid;
107+
visited[vid] = true;
108+
}
109+
++cid;
110+
}
111+
}
112+
113+
} // namespace std::graph
114+
115+
#endif //GRAPH_CC_HPP

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ set(UNITTEST_SOURCES
5454
"mis_tests.cpp"
5555
"mst_tests.cpp"
5656
"tc_tests.cpp"
57+
"cc_tests.cpp"
5758
)
5859

5960
foreach(SOURCE IN LISTS UNITTEST_SOURCES)

tests/cc_tests.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include <catch2/catch_test_macros.hpp>
2+
#include <catch2/catch_template_test_macros.hpp>
3+
#include "csv_routes.hpp"
4+
#include "graph/graph.hpp"
5+
#include "graph/algorithm/connected_components.hpp"
6+
#include "graph/container/dynamic_graph.hpp"
7+
#include "graph/views/incidence.hpp"
8+
#ifdef _MSC_VER
9+
# include "Windows.h"
10+
#endif
11+
12+
#define TEST_OPTION_OUTPUT (1) // output tests for visual inspection
13+
#define TEST_OPTION_GEN (2) // generate unit test code to be pasted into this file
14+
#define TEST_OPTION_TEST (3) // run unit tests
15+
#define TEST_OPTION TEST_OPTION_TEST
16+
17+
using std::cout;
18+
using std::endl;
19+
20+
using std::graph::vertex_t;
21+
using std::graph::vertex_id_t;
22+
using std::graph::vertex_reference_t;
23+
using std::graph::vertex_iterator_t;
24+
using std::graph::vertex_edge_range_t;
25+
using std::graph::edge_t;
26+
27+
using std::graph::vertices;
28+
using std::graph::edges;
29+
using std::graph::vertex_value;
30+
using std::graph::target_id;
31+
using std::graph::target;
32+
using std::graph::edge_value;
33+
using std::graph::find_vertex;
34+
using std::graph::vertex_id;
35+
36+
using routes_vol_graph_traits = std::graph::container::vol_graph_traits<double, std::string, std::string>;
37+
using routes_vol_graph_type = std::graph::container::dynamic_adjacency_graph<routes_vol_graph_traits>;
38+
39+
#if 1
40+
TEST_CASE("strongly connected components test", "[strong cc]") {
41+
init_console();
42+
43+
using G = routes_vol_graph_type;
44+
auto&& g = load_ordered_graph<G>(TEST_DATA_ROOT_DIR "cc_directed.csv", name_order_policy::alphabetical);
45+
G gt;
46+
47+
std::vector<std::tuple<vertex_id_t<G>,vertex_id_t<G>,double>> reverse;
48+
vertex_id_t<G> vid = 0;
49+
for ( auto && u : vertices(g) ) {
50+
for ( auto && v : edges(g, u)) {
51+
reverse.push_back(std::make_tuple(target_id(g,v), vid, edge_value(g,v)));
52+
}
53+
++vid;
54+
}
55+
56+
57+
using value = std::ranges::range_value_t<decltype(reverse)>;
58+
59+
vertex_id_t<G> N = size(vertices(g));
60+
using edge_desc = std::graph::edge_descriptor<vertex_id_t<G>, true, void, double>;
61+
auto edge_proj = [](const value& val) -> edge_desc {
62+
return edge_desc{std::get<0>(val), std::get<1>(val), std::get<2>(val)};
63+
};
64+
65+
gt.load_edges(reverse, edge_proj, N);
66+
67+
std::vector<size_t> component(size(vertices(g)));
68+
std::graph::kosaraju(g, gt, component);
69+
70+
REQUIRE( *std::ranges::max_element( component ) == 2 );
71+
}
72+
#endif
73+
74+
TEST_CASE("connected components test", "[cc]") {
75+
init_console();
76+
77+
using G = routes_vol_graph_type;
78+
auto&& g = load_ordered_graph<G>(TEST_DATA_ROOT_DIR "cc_undirected.csv", name_order_policy::alphabetical);
79+
80+
std::vector<size_t> component(size(vertices(g)));
81+
std::graph::connected_components(g, component);
82+
83+
REQUIRE( *std::ranges::max_element( component ) == 2 );
84+
}

0 commit comments

Comments
 (0)