use crate::base::{DiGraph, EdgeWeight, Graph, Node};
use petgraph::Direction;
use std::collections::{HashMap, HashSet, VecDeque};
pub type Component<N> = HashSet<N>;
#[allow(dead_code)]
pub fn connected_components<N, E, Ix>(graph: &Graph<N, E, Ix>) -> Vec<Component<N>>
where
N: Node + std::fmt::Debug,
E: EdgeWeight,
Ix: petgraph::graph::IndexType,
{
let mut components: Vec<Component<N>> = Vec::new();
let mut visited = HashSet::new();
for node_idx in graph.inner().node_indices() {
if visited.contains(&node_idx) {
continue;
}
let mut component = HashSet::new();
let mut queue = VecDeque::new();
queue.push_back(node_idx);
visited.insert(node_idx);
while let Some(current) = queue.pop_front() {
component.insert(graph.inner()[current].clone());
for neighbor in graph.inner().neighbors(current) {
if !visited.contains(&neighbor) {
visited.insert(neighbor);
queue.push_back(neighbor);
}
}
}
components.push(component);
}
components
}
#[allow(dead_code)]
pub fn strongly_connected_components<N, E, Ix>(graph: &DiGraph<N, E, Ix>) -> Vec<Component<N>>
where
N: Node + std::fmt::Debug,
E: EdgeWeight,
Ix: petgraph::graph::IndexType,
{
struct TarjanState<Ix: petgraph::graph::IndexType> {
index: usize,
stack: Vec<petgraph::graph::NodeIndex<Ix>>,
indices: HashMap<petgraph::graph::NodeIndex<Ix>, usize>,
lowlinks: HashMap<petgraph::graph::NodeIndex<Ix>, usize>,
on_stack: HashSet<petgraph::graph::NodeIndex<Ix>>,
}
fn strongconnect<N, E, Ix>(
v: petgraph::graph::NodeIndex<Ix>,
graph: &DiGraph<N, E, Ix>,
state: &mut TarjanState<Ix>,
components: &mut Vec<Component<N>>,
) where
N: Node + std::fmt::Debug,
E: EdgeWeight,
Ix: petgraph::graph::IndexType,
{
state.indices.insert(v, state.index);
state.lowlinks.insert(v, state.index);
state.index += 1;
state.stack.push(v);
state.on_stack.insert(v);
for w in graph.inner().neighbors_directed(v, Direction::Outgoing) {
if !state.indices.contains_key(&w) {
strongconnect(w, graph, state, components);
let w_lowlink = *state.lowlinks.get(&w).expect("Operation failed");
let v_lowlink = *state.lowlinks.get(&v).expect("Operation failed");
state.lowlinks.insert(v, v_lowlink.min(w_lowlink));
} else if state.on_stack.contains(&w) {
let w_index = *state.indices.get(&w).expect("Operation failed");
let v_lowlink = *state.lowlinks.get(&v).expect("Operation failed");
state.lowlinks.insert(v, v_lowlink.min(w_index));
}
}
if state.lowlinks.get(&v) == state.indices.get(&v) {
let mut component = HashSet::new();
loop {
let w = state.stack.pop().expect("Operation failed");
state.on_stack.remove(&w);
component.insert(graph.inner()[w].clone());
if w == v {
break;
}
}
if !component.is_empty() {
components.push(component);
}
}
}
let mut state = TarjanState {
index: 0,
stack: Vec::new(),
indices: HashMap::new(),
lowlinks: HashMap::new(),
on_stack: HashSet::new(),
};
let mut components = Vec::new();
for v in graph.inner().node_indices() {
if !state.indices.contains_key(&v) {
strongconnect(v, graph, &mut state, &mut components);
}
}
components
}
#[allow(dead_code)]
pub fn weakly_connected_components<N, E, Ix>(graph: &DiGraph<N, E, Ix>) -> Vec<Component<N>>
where
N: Node + std::fmt::Debug,
E: EdgeWeight,
Ix: petgraph::graph::IndexType,
{
let mut components: Vec<Component<N>> = Vec::new();
let mut visited = HashSet::new();
for node_idx in graph.inner().node_indices() {
if visited.contains(&node_idx) {
continue;
}
let mut component = HashSet::new();
let mut queue = VecDeque::new();
queue.push_back(node_idx);
visited.insert(node_idx);
while let Some(current) = queue.pop_front() {
component.insert(graph.inner()[current].clone());
for neighbor in graph.inner().neighbors_undirected(current) {
if !visited.contains(&neighbor) {
visited.insert(neighbor);
queue.push_back(neighbor);
}
}
}
components.push(component);
}
components
}
#[allow(dead_code)]
pub fn articulation_points<N, E, Ix>(graph: &Graph<N, E, Ix>) -> HashSet<N>
where
N: Node + std::fmt::Debug,
E: EdgeWeight,
Ix: petgraph::graph::IndexType,
{
struct DfsState<Ix: petgraph::graph::IndexType> {
time: usize,
disc: HashMap<petgraph::graph::NodeIndex<Ix>, usize>,
low: HashMap<petgraph::graph::NodeIndex<Ix>, usize>,
parent: HashMap<petgraph::graph::NodeIndex<Ix>, Option<petgraph::graph::NodeIndex<Ix>>>,
visited: HashSet<petgraph::graph::NodeIndex<Ix>>,
}
fn dfs<N, E, Ix>(
u: petgraph::graph::NodeIndex<Ix>,
graph: &Graph<N, E, Ix>,
state: &mut DfsState<Ix>,
articulation_points: &mut HashSet<N>,
) where
N: Node + std::fmt::Debug,
E: EdgeWeight,
Ix: petgraph::graph::IndexType,
{
state.visited.insert(u);
state.disc.insert(u, state.time);
state.low.insert(u, state.time);
state.time += 1;
let mut children = 0;
for v in graph.inner().neighbors(u) {
if !state.visited.contains(&v) {
children += 1;
state.parent.insert(v, Some(u));
dfs(v, graph, state, articulation_points);
let v_low = *state.low.get(&v).expect("Operation failed");
let u_low = *state.low.get(&u).expect("Operation failed");
state.low.insert(u, u_low.min(v_low));
let u_disc = *state.disc.get(&u).expect("Operation failed");
if (state.parent.get(&u).expect("Operation failed").is_none() && children > 1)
|| (state.parent.get(&u).expect("Operation failed").is_some()
&& v_low >= u_disc)
{
articulation_points.insert(graph.inner()[u].clone());
}
} else if state.parent.get(&u).expect("Operation failed") != &Some(v) {
let v_disc = *state.disc.get(&v).expect("Operation failed");
let u_low = *state.low.get(&u).expect("Operation failed");
state.low.insert(u, u_low.min(v_disc));
}
}
}
let mut articulation_points = HashSet::new();
let mut state = DfsState {
time: 0,
disc: HashMap::new(),
low: HashMap::new(),
parent: HashMap::new(),
visited: HashSet::new(),
};
for node in graph.inner().node_indices() {
if !state.visited.contains(&node) {
state.parent.insert(node, None);
dfs(node, graph, &mut state, &mut articulation_points);
}
}
articulation_points
}
#[derive(Debug, Clone)]
pub struct BipartiteResult<N: Node> {
pub is_bipartite: bool,
pub coloring: HashMap<N, u8>,
}
#[allow(dead_code)]
pub fn is_bipartite<N, E, Ix>(graph: &Graph<N, E, Ix>) -> BipartiteResult<N>
where
N: Node + std::fmt::Debug,
E: EdgeWeight,
Ix: petgraph::graph::IndexType,
{
let mut coloring: HashMap<petgraph::graph::NodeIndex<Ix>, u8> = HashMap::new();
let mut queue = VecDeque::new();
for start_idx in graph.inner().node_indices() {
if coloring.contains_key(&start_idx) {
continue;
}
queue.push_back(start_idx);
coloring.insert(start_idx, 0);
while let Some(node_idx) = queue.pop_front() {
let node_color = *coloring.get(&node_idx).expect("Operation failed");
let next_color = 1 - node_color;
for neighbor in graph.inner().neighbors(node_idx) {
if let Some(&neighbor_color) = coloring.get(&neighbor) {
if neighbor_color == node_color {
return BipartiteResult {
is_bipartite: false,
coloring: HashMap::new(),
};
}
} else {
coloring.insert(neighbor, next_color);
queue.push_back(neighbor);
}
}
}
}
let node_coloring: HashMap<N, u8> = coloring
.into_iter()
.map(|(idx, color)| (graph.inner()[idx].clone(), color))
.collect();
BipartiteResult {
is_bipartite: true,
coloring: node_coloring,
}
}
#[allow(dead_code)]
pub fn bridges<N, E, Ix>(graph: &Graph<N, E, Ix>) -> Vec<(N, N)>
where
N: Node + std::fmt::Debug,
E: EdgeWeight,
Ix: petgraph::graph::IndexType,
{
struct DfsState<Ix: petgraph::graph::IndexType> {
time: usize,
disc: HashMap<petgraph::graph::NodeIndex<Ix>, usize>,
low: HashMap<petgraph::graph::NodeIndex<Ix>, usize>,
parent: HashMap<petgraph::graph::NodeIndex<Ix>, Option<petgraph::graph::NodeIndex<Ix>>>,
visited: HashSet<petgraph::graph::NodeIndex<Ix>>,
}
fn dfs<N, E, Ix>(
u: petgraph::graph::NodeIndex<Ix>,
graph: &Graph<N, E, Ix>,
state: &mut DfsState<Ix>,
bridges: &mut Vec<(N, N)>,
) where
N: Node + std::fmt::Debug,
E: EdgeWeight,
Ix: petgraph::graph::IndexType,
{
state.visited.insert(u);
state.disc.insert(u, state.time);
state.low.insert(u, state.time);
state.time += 1;
for v in graph.inner().neighbors(u) {
if !state.visited.contains(&v) {
state.parent.insert(v, Some(u));
dfs(v, graph, state, bridges);
let v_low = *state.low.get(&v).expect("Operation failed");
let u_low = *state.low.get(&u).expect("Operation failed");
state.low.insert(u, u_low.min(v_low));
let u_disc = *state.disc.get(&u).expect("Operation failed");
if v_low > u_disc {
bridges.push((graph.inner()[u].clone(), graph.inner()[v].clone()));
}
} else if state.parent.get(&u).expect("Operation failed") != &Some(v) {
let v_disc = *state.disc.get(&v).expect("Operation failed");
let u_low = *state.low.get(&u).expect("Operation failed");
state.low.insert(u, u_low.min(v_disc));
}
}
}
let mut bridges = Vec::new();
let mut state = DfsState {
time: 0,
disc: HashMap::new(),
low: HashMap::new(),
parent: HashMap::new(),
visited: HashSet::new(),
};
for node in graph.inner().node_indices() {
if !state.visited.contains(&node) {
state.parent.insert(node, None);
dfs(node, graph, &mut state, &mut bridges);
}
}
bridges
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Result as GraphResult;
use crate::generators::create_graph;
#[test]
fn test_connected_components() -> GraphResult<()> {
let mut graph = create_graph::<i32, ()>();
graph.add_edge(0, 1, ())?;
graph.add_edge(1, 2, ())?;
graph.add_edge(3, 4, ())?;
let components = connected_components(&graph);
assert_eq!(components.len(), 2);
let sizes: Vec<usize> = components.iter().map(|c| c.len()).collect();
assert!(sizes.contains(&3));
assert!(sizes.contains(&2));
Ok(())
}
#[test]
fn test_strongly_connected_components() -> GraphResult<()> {
let mut graph = crate::base::DiGraph::<&str, ()>::new();
graph.add_edge("A", "B", ())?;
graph.add_edge("B", "C", ())?;
graph.add_edge("C", "A", ())?;
graph.add_edge("D", "E", ())?;
let sccs = strongly_connected_components(&graph);
assert_eq!(sccs.len(), 3);
let sizes: Vec<usize> = sccs.iter().map(|c| c.len()).collect();
assert!(sizes.contains(&3));
assert_eq!(sizes.iter().filter(|&&s| s == 1).count(), 2);
Ok(())
}
#[test]
fn test_articulation_points() -> GraphResult<()> {
let mut graph = create_graph::<i32, ()>();
graph.add_edge(0, 1, ())?;
graph.add_edge(1, 2, ())?;
graph.add_edge(1, 3, ())?;
let aps = articulation_points(&graph);
assert_eq!(aps.len(), 1);
assert!(aps.contains(&1));
Ok(())
}
#[test]
fn test_is_bipartite() -> GraphResult<()> {
let mut bipartite = create_graph::<i32, ()>();
bipartite.add_edge(0, 1, ())?;
bipartite.add_edge(1, 2, ())?;
bipartite.add_edge(2, 3, ())?;
bipartite.add_edge(3, 0, ())?;
let result = is_bipartite(&bipartite);
assert!(result.is_bipartite);
assert_eq!(result.coloring.len(), 4);
let mut non_bipartite = create_graph::<i32, ()>();
non_bipartite.add_edge(0, 1, ())?;
non_bipartite.add_edge(1, 2, ())?;
non_bipartite.add_edge(2, 0, ())?;
let result = is_bipartite(&non_bipartite);
assert!(!result.is_bipartite);
Ok(())
}
#[test]
fn test_weakly_connected_components() -> GraphResult<()> {
let mut graph = crate::base::DiGraph::<&str, ()>::new();
graph.add_edge("A", "B", ())?;
graph.add_edge("B", "C", ())?;
graph.add_edge("D", "E", ())?;
graph.add_edge("F", "E", ())?;
graph.add_edge("D", "F", ())?;
let wccs = weakly_connected_components(&graph);
assert_eq!(wccs.len(), 2);
let sizes: Vec<usize> = wccs.iter().map(|c| c.len()).collect();
assert!(sizes.contains(&3));
assert_eq!(sizes.iter().filter(|&&s| s == 3).count(), 2);
Ok(())
}
#[test]
fn test_bridges() -> GraphResult<()> {
let mut graph = create_graph::<i32, ()>();
graph.add_edge(0, 1, ())?;
graph.add_edge(1, 2, ())?;
graph.add_edge(2, 0, ())?;
graph.add_edge(2, 3, ())?;
graph.add_edge(3, 4, ())?;
graph.add_edge(4, 5, ())?;
graph.add_edge(5, 3, ())?;
let bridges_found = bridges(&graph);
assert_eq!(bridges_found.len(), 1);
let bridge = &bridges_found[0];
assert!((bridge.0 == 2 && bridge.1 == 3) || (bridge.0 == 3 && bridge.1 == 2));
Ok(())
}
}