Skip to content

Commit

Permalink
Merge pull request #127 from stdgraph/algorithms/cc
Browse files Browse the repository at this point in the history
merge CC code into master
  • Loading branch information
kdeweese authored Aug 24, 2024
2 parents db94c6e + 189e8b8 commit 5dc1cde
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 0 deletions.
22 changes: 22 additions & 0 deletions data/cc_directed.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
From,To,Distance
A,B,1
B,C,1
C,D,1
D,A,1
B,E,1
E,F,1
F,G,1
G,H,1
H,E,1
G,Q,1
Q,N,1
N,O,1
O,P,1
P,M,1
M,R,1
R,H,1
J,R,1
J,K,1
K,L,1
L,I,1
I,J,1
15 changes: 15 additions & 0 deletions data/cc_undirected.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
From,To,Distance
A,H,1
H,A,1
B,C,1
C,B,1
B,F,1
F,B,1
B,G,1
G,B,1
C,F,1
F,C,1
D,E,1
E,D,1
H,I,1
I,H,1
115 changes: 115 additions & 0 deletions include/graph/algorithm/connected_components.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* @file connected_components.hpp
*
* @brief Single-Source Shortest paths and shortest sistances algorithms using Dijkstra &
* Bellman-Ford algorithms.
*
* @copyright Copyright (c) 2022
*
* SPDX-License-Identifier: BSL-1.0
*
* @authors
* Andrew Lumsdaine
* Phil Ratzloff
* Kevin Deweese
*/

#include "graph/graph.hpp"
#include "graph/views/incidence.hpp"

#ifndef GRAPH_CC_HPP
# define GRAPH_CC_HPP

namespace std::graph {

template <adjacency_list G,
adjacency_list GT,
ranges::random_access_range Component>
requires ranges::random_access_range<vertex_range_t<G>> && integral<vertex_id_t<G>>
void kosaraju(G&& g, // graph
GT&& g_t, // graph transpose
Component& component // out: strongly 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::vector<vertex_id_t<G>> order;

for (auto&& [uid, u] : views::vertexlist(g)) {
if (visited[uid]) {
continue;
}
visited[uid] = true;
std::stack<vertex_id_t<G>> active;
active.push(uid);
auto dfs = std::graph::views::sourced_edges_depth_first_search(g, uid);
for (auto&& [vid, wid, vw] : dfs) {
while (vid != active.top()) {
order.push_back(active.top());
active.pop();
}
if (visited[wid]) {
dfs.cancel(cancel_search::cancel_branch);
}
else {
active.push(wid);
visited[wid] = true;
}
}
while (!active.empty()) {
order.push_back(active.top());
active.pop();
}
}

size_t cid = 0;
std::ranges::reverse_view reverse {order};
for (auto& uid : reverse) {
if (component[uid] == std::numeric_limits<CT>::max()) {
component[uid] = cid;
vertices_depth_first_search_view<GT> dfs(g_t, uid);
for (auto&& [vid, v] : dfs) {
if (component[vid] != std::numeric_limits<CT>::max()) {
dfs.cancel(cancel_search::cancel_branch);
}
else {
component[vid] = cid;
}
}
++cid;
}
}
}

template <adjacency_list G,
ranges::random_access_range Component>
requires ranges::random_access_range<vertex_range_t<G>> && integral<vertex_id_t<G>>
void 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());

CT cid = 0;
for (auto&& [uid, u] : views::vertexlist(g)) {
if (visited[uid]) {
continue;
}
visited[uid] = true;
component[uid] = cid;
vertices_depth_first_search_view<G, void> dfs(g, uid);
for (auto&& [vid, v] : dfs) {
component[vid] = cid;
visited[vid] = true;
}
++cid;
}
}

} // namespace std::graph

#endif //GRAPH_CC_HPP
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ set(UNITTEST_SOURCES
"mis_tests.cpp"
"mst_tests.cpp"
"tc_tests.cpp"
"cc_tests.cpp"
)

foreach(SOURCE IN LISTS UNITTEST_SOURCES)
Expand Down
84 changes: 84 additions & 0 deletions tests/cc_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_template_test_macros.hpp>
#include "csv_routes.hpp"
#include "graph/graph.hpp"
#include "graph/algorithm/connected_components.hpp"
#include "graph/container/dynamic_graph.hpp"
#include "graph/views/incidence.hpp"
#ifdef _MSC_VER
# include "Windows.h"
#endif

#define TEST_OPTION_OUTPUT (1) // output tests for visual inspection
#define TEST_OPTION_GEN (2) // generate unit test code to be pasted into this file
#define TEST_OPTION_TEST (3) // run unit tests
#define TEST_OPTION TEST_OPTION_TEST

using std::cout;
using std::endl;

using std::graph::vertex_t;
using std::graph::vertex_id_t;
using std::graph::vertex_reference_t;
using std::graph::vertex_iterator_t;
using std::graph::vertex_edge_range_t;
using std::graph::edge_t;

using std::graph::vertices;
using std::graph::edges;
using std::graph::vertex_value;
using std::graph::target_id;
using std::graph::target;
using std::graph::edge_value;
using std::graph::find_vertex;
using std::graph::vertex_id;

using routes_vol_graph_traits = std::graph::container::vol_graph_traits<double, std::string, std::string>;
using routes_vol_graph_type = std::graph::container::dynamic_adjacency_graph<routes_vol_graph_traits>;

#if 1
TEST_CASE("strongly connected components test", "[strong cc]") {
init_console();

using G = routes_vol_graph_type;
auto&& g = load_ordered_graph<G>(TEST_DATA_ROOT_DIR "cc_directed.csv", name_order_policy::alphabetical);
G gt;

std::vector<std::tuple<vertex_id_t<G>,vertex_id_t<G>,double>> reverse;
vertex_id_t<G> vid = 0;
for ( auto && u : vertices(g) ) {
for ( auto && v : edges(g, u)) {
reverse.push_back(std::make_tuple(target_id(g,v), vid, edge_value(g,v)));
}
++vid;
}


using value = std::ranges::range_value_t<decltype(reverse)>;

vertex_id_t<G> N = size(vertices(g));
using edge_desc = std::graph::edge_descriptor<vertex_id_t<G>, true, void, double>;
auto edge_proj = [](const value& val) -> edge_desc {
return edge_desc{std::get<0>(val), std::get<1>(val), std::get<2>(val)};
};

gt.load_edges(reverse, edge_proj, N);

std::vector<size_t> component(size(vertices(g)));
std::graph::kosaraju(g, gt, component);

REQUIRE( *std::ranges::max_element( component ) == 2 );
}
#endif

TEST_CASE("connected components test", "[cc]") {
init_console();

using G = routes_vol_graph_type;
auto&& g = load_ordered_graph<G>(TEST_DATA_ROOT_DIR "cc_undirected.csv", name_order_policy::alphabetical);

std::vector<size_t> component(size(vertices(g)));
std::graph::connected_components(g, component);

REQUIRE( *std::ranges::max_element( component ) == 2 );
}

0 comments on commit 5dc1cde

Please sign in to comment.