use std::cmp::min;
pub use petgraph;
use petgraph::{
graph::Edge, prelude::UnGraph, stable_graph::IndexType,
stable_graph::NodeIndex, visit::NodeIndexable,
};
pub trait Bcc {
type Output;
fn bcc(&self) -> Self::Output;
}
impl<N, E, Ix: IndexType> Bcc for UnGraph<N, E, Ix> {
type Output = Vec<Vec<NodeIndex<Ix>>>;
fn bcc(&self) -> Self::Output {
let mut dfs = HopcroftTarjan::new(self.node_count());
dfs.find_bcc(self)
}
}
pub trait SplitIntoBcc {
type Output;
fn split_into_bcc(self) -> Self::Output;
}
impl<N: Clone, E, Ix: IndexType> SplitIntoBcc for UnGraph<N, E, Ix> {
type Output = Vec<UnGraph<N, E, Ix>>;
fn split_into_bcc(self) -> Self::Output {
let bccs = self.bcc();
let res = bccs.iter().map(|bcc| {
let mut g = UnGraph::with_capacity(bcc.len(), 0);
for n in bcc {
g.add_node(self.node_weight(*n).unwrap().clone());
}
g
});
let mut res = Vec::from_iter(res);
let (nodes, edges) = self.into_nodes_edges();
for edge in edges {
if edge.source() == edge.target() {
if res.len() == 1
&& res[0].node_count() == 1
&& res[0].edge_count() == 0
{
res.clear();
}
let mut g = UnGraph::with_capacity(1, 1);
let weight = nodes[edge.source().index()].weight.clone();
let idx = g.add_node(weight);
g.add_edge(idx, idx, edge.weight);
res.push(g);
} else {
add_edge(&mut res, &bccs, edge);
}
}
res
}
}
fn add_edge<N: Clone, E, Ix: IndexType>(
res: &mut [UnGraph<N, E, Ix>],
bccs: &[Vec<NodeIndex<Ix>>],
edge: Edge<E, Ix>,
) {
for (res, bcc) in res.iter_mut().zip(bccs.iter()) {
let source_pos = bcc.iter().position(|&n| n == edge.source());
if let Some(from) = source_pos {
let target_pos = bcc.iter().position(|&n| n == edge.target());
if let Some(to) = target_pos {
let from = res.from_index(from);
let to = res.from_index(to);
res.add_edge(from, to, edge.weight);
return;
}
}
}
unreachable!("Found edge that is in none of the biconnected components");
}
struct HopcroftTarjan {
visited: Vec<bool>,
depth: Vec<usize>,
lowpoint: Vec<usize>,
parent: Vec<usize>,
}
impl HopcroftTarjan {
fn new(nnodes: usize) -> Self {
Self {
visited: vec![false; nnodes],
depth: vec![0; nnodes],
lowpoint: vec![0; nnodes],
parent: vec![0; nnodes],
}
}
fn find_bcc<N, E, Ix: IndexType>(
&mut self,
g: &UnGraph<N, E, Ix>,
) -> Vec<Vec<NodeIndex<Ix>>> {
if g.node_count() == 0 {
return vec![];
}
let mut res = vec![];
let mut start_node = 0;
while let Some(start) = self.next_start_pos(start_node) {
self.find_bcc_from(g, start, 0, &mut res);
start_node = start + 1;
}
if res.is_empty() {
vec![g.node_indices().collect()]
} else {
res
}
}
fn find_bcc_from<N, E, Ix: IndexType>(
&mut self,
g: &UnGraph<N, E, Ix>,
node: usize,
depth: usize,
res: &mut Vec<Vec<NodeIndex<Ix>>>,
) {
self.visited[node] = true;
self.depth[node] = depth;
self.lowpoint[node] = depth;
let mut is_cut_vx = false;
let idx = g.from_index(node);
for n_idx in g.neighbors(idx).filter(|&n_idx| n_idx != idx) {
let n = g.to_index(n_idx);
if !self.visited[n] {
let parent = node;
self.parent[n] = parent;
self.find_bcc_from(g, n, depth + 1, res);
if self.lowpoint[n] >= self.depth[parent] {
is_cut_vx = true;
}
self.lowpoint[parent] =
min(self.lowpoint[parent], self.lowpoint[n]);
} else if n != self.parent[node] {
self.lowpoint[node] = min(self.lowpoint[node], self.depth[n]);
}
}
if node > 0 && is_cut_vx {
for n_idx in g.neighbors(idx) {
let n = g.to_index(n_idx);
if self.parent[n] == node
&& self.lowpoint[n] >= self.depth[node]
{
let mut bcc = vec![idx, n_idx];
self.add_subtree(g, n, &mut bcc);
res.push(bcc);
}
}
} else if node == 0 {
for n_idx in g.neighbors(idx).filter(|&n_idx| n_idx != idx) {
let n = g.to_index(n_idx);
if self.parent[n] == node {
let mut bcc = vec![idx, n_idx];
self.add_subtree(g, n, &mut bcc);
res.push(bcc);
}
}
}
}
fn add_subtree<N, E, Ix: IndexType>(
&mut self,
g: &UnGraph<N, E, Ix>,
root: usize,
bcc: &mut Vec<NodeIndex<Ix>>,
) {
self.parent[root] = usize::MAX;
let root_idx = g.from_index(root);
for n_idx in g.neighbors(root_idx) {
let n = g.to_index(n_idx);
if self.parent[n] == root {
bcc.push(n_idx);
self.add_subtree(g, n, bcc)
}
}
}
fn next_start_pos(&self, nskip: usize) -> Option<usize> {
self.visited
.iter()
.skip(nskip)
.position(|v| !v)
.map(|p| p + nskip)
}
}
#[cfg(test)]
mod tests {
use petgraph::Graph;
use super::*;
fn bcc_indices(g: &UnGraph<(), (), u32>) -> Vec<Vec<usize>> {
let mut bcc: Vec<Vec<usize>> = g
.bcc()
.into_iter()
.map(|bcc| bcc.into_iter().map(|i| g.to_index(i)).collect())
.collect();
for bcc in &mut bcc {
bcc.sort();
}
bcc.sort();
bcc
}
#[test]
fn trivial() {
let g: UnGraph<(), (), u32> = UnGraph::default();
assert!(g.bcc().is_empty());
}
#[test]
fn single_edge() {
let g: UnGraph<(), (), u32> = Graph::from_edges([(0, 1)]);
let bcc = bcc_indices(&g);
assert_eq!(bcc, [[0, 1]]);
}
#[test]
fn star_2() {
let g: UnGraph<(), (), u32> = Graph::from_edges([(0, 1), (0, 2)]);
let bcc = bcc_indices(&g);
let bcc_ref = [[0, 1], [0, 2]];
assert_eq!(bcc, bcc_ref);
}
#[test]
fn star_2_alt() {
let g: UnGraph<(), (), u32> = Graph::from_edges([(0, 1), (1, 2)]);
let bcc = bcc_indices(&g);
let bcc_ref = [[0, 1], [1, 2]];
assert_eq!(bcc, bcc_ref);
}
#[test]
fn lacrosse_stick() {
let g: UnGraph<(), (), u32> =
Graph::from_edges([(0, 1), (1, 2), (2, 0), (2, 3)]);
let bcc = bcc_indices(&g);
let bcc_ref = [vec![0, 1, 2], vec![2, 3]];
assert_eq!(bcc, bcc_ref);
}
#[test]
fn forest() {
let g: UnGraph<(), (), u32> =
Graph::from_edges([(0, 1), (1, 2), (3, 4), (3, 5)]);
let bcc = bcc_indices(&g);
let bcc_ref = [[0, 1], [1, 2], [3, 4], [3, 5]];
assert_eq!(bcc, bcc_ref);
}
#[test]
fn wp_example() {
let g: UnGraph<(), (), u32> = Graph::from_edges([
(0, 1, ()),
(0, 9, ()),
(1, 2, ()),
(1, 6, ()),
(1, 8, ()),
(2, 3, ()),
(2, 4, ()),
(3, 4, ()),
(4, 5, ()),
(5, 6, ()),
(6, 7, ()),
(9, 10, ()),
(10, 11, ()),
(10, 13, ()),
(11, 12, ()),
(12, 13, ()),
]);
let bcc = bcc_indices(&g);
let bcc_ref = [
vec![0, 1],
vec![0, 9],
vec![1, 2, 3, 4, 5, 6],
vec![1, 8],
vec![6, 7],
vec![9, 10],
vec![10, 11, 12, 13],
];
assert_eq!(bcc, bcc_ref);
}
#[test]
fn self_loops() {
let g: UnGraph<(), (), u32> = Graph::from_edges([(0, 0, ())]);
let bcc = bcc_indices(&g);
let bcc_ref = [vec![0]];
assert_eq!(bcc, bcc_ref);
let g: UnGraph<(), (), u32> =
Graph::from_edges([(0, 0, ()), (1, 0, ()), (1, 1, ())]);
let bcc = bcc_indices(&g);
let bcc_ref = [vec![0, 1]];
assert_eq!(bcc, bcc_ref);
}
#[test]
fn split() {
let g: UnGraph<(), (), u32> = Graph::new_undirected();
let bccs = g.split_into_bcc();
assert!(bccs.is_empty());
let mut g: UnGraph<(), (), u32> = Graph::new_undirected();
g.add_node(());
let bccs = g.clone().split_into_bcc();
assert_eq!(bccs.len(), 1);
assert_eq!(bccs[0].node_count(), 1);
assert_eq!(bccs[0].edge_count(), 0);
let g: UnGraph<(), (), u32> = Graph::from_edges([(0, 0, ())]);
let bccs = g.split_into_bcc();
assert_eq!(bccs.len(), 1);
assert_eq!(bccs[0].node_count(), 1);
assert_eq!(bccs[0].edge_count(), 1);
}
}