use crate::core::types::{BaseGraph, GraphConstructor, NodeId, NodeMap, NodeSet};
use std::collections::VecDeque;
pub fn connected_components<A, W, Ty>(graph: &BaseGraph<A, W, Ty>) -> Vec<Vec<NodeId>>
where
W: Copy,
Ty: GraphConstructor<A, W>,
{
let mut visited: NodeSet = NodeSet::default();
let mut components = Vec::new();
for (start_node, _) in graph.nodes() {
if visited.contains(&start_node) {
continue;
}
let mut component = Vec::new();
let mut queue = VecDeque::new();
queue.push_back(start_node);
visited.insert(start_node);
while let Some(node) = queue.pop_front() {
component.push(node);
for neighbor in graph.neighbors(node) {
if visited.insert(neighbor) {
queue.push_back(neighbor);
}
}
}
components.push(component);
}
components
}
pub fn connected_components_map<A, W, Ty>(graph: &BaseGraph<A, W, Ty>) -> NodeMap<usize>
where
W: Copy,
Ty: GraphConstructor<A, W>,
{
let lists = connected_components(graph);
let mut map: NodeMap<usize> = NodeMap::default();
for (cid, comp) in lists.into_iter().enumerate() {
for node in comp {
map.insert(node, cid);
}
}
map
}
pub fn weakly_connected_components<A, W, Ty>(graph: &BaseGraph<A, W, Ty>) -> Vec<Vec<NodeId>>
where
W: Copy,
Ty: GraphConstructor<A, W>,
{
let mut visited: NodeSet = NodeSet::default();
let mut components = Vec::new();
for (start_node, _) in graph.nodes() {
if visited.contains(&start_node) {
continue;
}
let mut component = Vec::new();
let mut queue = VecDeque::new();
queue.push_back(start_node);
visited.insert(start_node);
while let Some(node) = queue.pop_front() {
component.push(node);
for neighbor in graph.neighbors(node).chain(graph.incoming_neighbors(node)) {
if visited.insert(neighbor) {
queue.push_back(neighbor);
}
}
}
components.push(component);
}
components
}
pub fn strongly_connected_components<A, W, Ty>(graph: &BaseGraph<A, W, Ty>) -> Vec<Vec<NodeId>>
where
W: Copy,
Ty: GraphConstructor<A, W>,
{
petgraph::algo::tarjan_scc(graph.as_petgraph())
.into_iter()
.map(|component| component.into_iter().map(NodeId::new).collect())
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::types::{Digraph, Graph};
#[test]
fn test_connected_components_simple() {
let mut g = Graph::<i32, f64>::new();
let n1 = g.add_node(1);
let n2 = g.add_node(2);
let n3 = g.add_node(3);
let n4 = g.add_node(4);
g.add_edge(n1, n2, 1.0);
g.add_edge(n3, n4, 1.0);
let components = connected_components(&g);
assert_eq!(components.len(), 2);
}
#[test]
fn test_connected_components_with_removed_nodes() {
let mut g = Graph::<i32, f64>::new();
let n1 = g.add_node(1);
let n2 = g.add_node(2);
let n3 = g.add_node(3);
let n4 = g.add_node(4);
let n5 = g.add_node(5);
g.add_edge(n1, n2, 1.0);
g.add_edge(n2, n3, 1.0);
g.add_edge(n4, n5, 1.0);
g.remove_node(n2);
let components = connected_components(&g);
assert_eq!(components.len(), 3);
}
#[test]
fn test_connected_components_empty() {
let g = Graph::<i32, f64>::new();
let components = connected_components(&g);
assert_eq!(components.len(), 0);
}
#[test]
fn test_connected_components_single_node() {
let mut g = Graph::<i32, f64>::new();
g.add_node(1);
let components = connected_components(&g);
assert_eq!(components.len(), 1);
assert_eq!(components[0].len(), 1);
}
#[test]
fn test_connected_components_map() {
let mut g = Graph::<i32, f64>::new();
let n1 = g.add_node(1);
let n2 = g.add_node(2);
let n3 = g.add_node(3);
let n4 = g.add_node(4);
g.add_edge(n1, n2, 1.0);
g.add_edge(n3, n4, 1.0);
let lists = connected_components(&g);
let map = connected_components_map(&g);
assert_eq!(lists.len(), 2);
assert_eq!(map.len(), 4);
assert_eq!(map[&n1], map[&n2]);
assert_eq!(map[&n3], map[&n4]);
assert_ne!(map[&n1], map[&n3]);
}
fn sorted_partition(components: Vec<Vec<NodeId>>) -> Vec<Vec<usize>> {
let mut parts: Vec<Vec<usize>> = components
.into_iter()
.map(|c| {
let mut v: Vec<usize> = c.iter().map(|n| n.index()).collect();
v.sort_unstable();
v
})
.collect();
parts.sort();
parts
}
#[test]
fn test_weakly_connected_components_directed() {
let mut g = Digraph::<i32, f64>::new();
let n0 = g.add_node(0);
let n1 = g.add_node(1);
let n2 = g.add_node(2);
let _n3 = g.add_node(3);
g.add_edge(n0, n1, 1.0);
g.add_edge(n1, n2, 1.0);
let wcc = weakly_connected_components(&g);
assert_eq!(
sorted_partition(wcc),
vec![vec![0, 1, 2], vec![3]],
"the path 0->1->2 is one weak component; node 3 is isolated"
);
}
#[test]
fn test_strongly_connected_components_directed() {
let mut g = Digraph::<i32, f64>::new();
let n0 = g.add_node(0);
let n1 = g.add_node(1);
let n2 = g.add_node(2);
let n3 = g.add_node(3);
g.add_edge(n0, n1, 1.0);
g.add_edge(n1, n2, 1.0);
g.add_edge(n2, n0, 1.0);
g.add_edge(n2, n3, 1.0);
let scc = strongly_connected_components(&g);
assert_eq!(
sorted_partition(scc),
vec![vec![0, 1, 2], vec![3]],
"the cycle 0->1->2->0 is one strong component; node 3 stands alone"
);
let wcc = weakly_connected_components(&g);
assert_eq!(sorted_partition(wcc), vec![vec![0, 1, 2, 3]]);
}
#[test]
fn test_weak_and_strong_match_on_undirected() {
let mut g = Graph::<i32, f64>::new();
let n1 = g.add_node(1);
let n2 = g.add_node(2);
let n3 = g.add_node(3);
let n4 = g.add_node(4);
g.add_edge(n1, n2, 1.0);
g.add_edge(n3, n4, 1.0);
let cc = sorted_partition(connected_components(&g));
assert_eq!(sorted_partition(weakly_connected_components(&g)), cc);
assert_eq!(sorted_partition(strongly_connected_components(&g)), cc);
}
}