blob: fb813570253c0dfe59b919231aa899540ba736b6 [file] [log] [blame]
//! Graph algorithms.
//!
//! It is a goal to gradually migrate the algorithms to be based on graph traits
//! so that they are generally applicable. For now, most of these use only the
//! **Graph** type.
use std::collections::BinaryHeap;
use super::{
Graph,
Directed,
Undirected,
EdgeType,
Outgoing,
Incoming,
Dfs,
MinScored,
};
use super::visit::{
Reversed,
Visitable,
VisitMap,
};
use super::unionfind::UnionFind;
use super::graph::{
NodeIndex,
IndexType,
};
pub use super::isomorphism::is_isomorphic;
pub use super::dijkstra::dijkstra;
/// Return **true** if the input graph contains a cycle.
///
/// Treat the input graph as undirected.
pub fn is_cyclic_undirected<N, E, Ty, Ix>(g: &Graph<N, E, Ty, Ix>) -> bool where
Ty: EdgeType,
Ix: IndexType,
{
let mut edge_sets = UnionFind::new(g.node_count());
for edge in g.raw_edges().iter() {
let (a, b) = (edge.source(), edge.target());
// union the two vertices of the edge
// -- if they were already the same, then we have a cycle
if !edge_sets.union(a.index(), b.index()) {
return true
}
}
false
}
/// **Deprecated: Renamed to *is_cyclic_undirected*.**
pub fn is_cyclic<N, E, Ty, Ix>(g: &Graph<N, E, Ty, Ix>) -> bool where
Ty: EdgeType,
Ix: IndexType,
{
is_cyclic_undirected(g)
}
/// Perform a topological sort of a directed graph.
///
/// Visit each node in order (if it is part of a topological order.
fn generic_toposort<N, E, Ix, F>(g: &Graph<N, E, Directed, Ix>,
mut visit: F) where
Ix: IndexType,
F: FnMut(&NodeIndex<Ix>),
{
let mut ordered = g.visit_map();
let mut tovisit = Vec::new();
// find all initial nodes
tovisit.extend(g.without_edges(Incoming));
// Take an unvisited element and find which of its neighbors are next
while let Some(nix) = tovisit.pop() {
if ordered.is_visited(&nix) {
continue;
}
visit(&nix);
ordered.visit(nix);
for neigh in g.neighbors_directed(nix, Outgoing) {
// Look at each neighbor, and those that only have incoming edges
// from the already ordered list, they are the next to visit.
if g.neighbors_directed(neigh, Incoming).all(|b| ordered.is_visited(&b)) {
tovisit.push(neigh);
}
}
}
}
/// Check if a directed graph contains cycles.
///
/// Using the topological sort algorithm.
///
/// Return **true** if the graph had a cycle.
pub fn is_cyclic_directed<N, E, Ix>(g: &Graph<N, E, Directed, Ix>) -> bool where
Ix: IndexType,
{
let mut n_ordered = 0;
generic_toposort(g, |_| n_ordered += 1);
n_ordered != g.node_count()
}
/// Perform a topological sort of a directed graph.
///
/// Return a vector of nodes in topological order: each node is ordered
/// before its successors.
///
/// If the returned vec contains less than all the nodes of the graph, then
/// the graph was cyclic.
pub fn toposort<N, E, Ix>(g: &Graph<N, E, Directed, Ix>) -> Vec<NodeIndex<Ix>> where
Ix: IndexType,
{
let mut order = Vec::with_capacity(g.node_count());
generic_toposort(g, |&ix| order.push(ix));
order
}
/// Compute *Strongly connected components* using Kosaraju's algorithm.
///
/// Return a vector where each element is an scc.
///
/// For an undirected graph, the sccs are simply the connected components.
pub fn scc<N, E, Ty, Ix>(g: &Graph<N, E, Ty, Ix>) -> Vec<Vec<NodeIndex<Ix>>> where
Ty: EdgeType,
Ix: IndexType,
{
let mut dfs = Dfs::empty(g);
// First phase, reverse dfs pass, compute finishing times.
let mut finish_order = Vec::new();
for index in (0..g.node_count()) {
if dfs.discovered.is_visited(&NodeIndex::<Ix>::new(index)) {
continue
}
// We want to order the vertices by finishing time --
// so record when we see them, then reverse all we have seen in this DFS pass.
let pass_start = finish_order.len();
dfs.move_to(NodeIndex::new(index));
while let Some(nx) = dfs.next(&Reversed(g)) {
finish_order.push(nx);
}
finish_order[pass_start..].reverse();
}
dfs.discovered.clear();
let mut sccs = Vec::new();
// Second phase
// Process in decreasing finishing time order
for &nindex in finish_order.iter().rev() {
if dfs.discovered.is_visited(&nindex) {
continue;
}
// Move to the leader node.
dfs.move_to(nindex);
//let leader = nindex;
let mut scc = Vec::new();
while let Some(nx) = dfs.next(g) {
scc.push(nx);
}
sccs.push(scc);
}
sccs
}
/// Return the number of connected components of the graph.
///
/// For a directed graph, this is the *weakly* connected components.
pub fn connected_components<N, E, Ty, Ix>(g: &Graph<N, E, Ty, Ix>) -> usize where
Ty: EdgeType,
Ix: IndexType,
{
let mut vertex_sets = UnionFind::new(g.node_count());
for edge in g.raw_edges().iter() {
let (a, b) = (edge.source(), edge.target());
// union the two vertices of the edge
vertex_sets.union(a.index(), b.index());
}
let mut labels = vertex_sets.into_labeling();
labels.sort();
labels.dedup();
labels.len()
}
/// Return a *Minimum Spanning Tree* of a graph.
///
/// Treat the input graph as undirected.
///
/// Using Kruskal's algorithm with runtime **O(|E| log |E|)**. We actually
/// return a minimum spanning forest, i.e. a minimum spanning tree for each connected
/// component of the graph.
///
/// The resulting graph has all the vertices of the input graph (with identical node indices),
/// and **|V| - c** edges, where **c** is the number of connected components in **g**.
pub fn min_spanning_tree<N, E, Ty, Ix>(g: &Graph<N, E, Ty, Ix>) -> Graph<N, E, Undirected, Ix> where
N: Clone,
E: Clone + PartialOrd,
Ty: EdgeType,
Ix: IndexType,
{
if g.node_count() == 0 {
return Graph::with_capacity(0, 0)
}
// Create a mst skeleton by copying all nodes
let mut mst = Graph::with_capacity(g.node_count(), g.node_count() - 1);
for node in g.raw_nodes().iter() {
mst.add_node(node.weight.clone());
}
// Initially each vertex is its own disjoint subgraph, track the connectedness
// of the pre-MST with a union & find datastructure.
let mut subgraphs = UnionFind::new(g.node_count());
let mut sort_edges = BinaryHeap::with_capacity(g.edge_count());
for edge in g.raw_edges().iter() {
sort_edges.push(MinScored(edge.weight.clone(), (edge.source(), edge.target())));
}
// Kruskal's algorithm.
// Algorithm is this:
//
// 1. Create a pre-MST with all the vertices and no edges.
// 2. Repeat:
//
// a. Remove the shortest edge from the original graph.
// b. If the edge connects two disjoint trees in the pre-MST,
// add the edge.
while let Some(MinScored(score, (a, b))) = sort_edges.pop() {
// check if the edge would connect two disjoint parts
if subgraphs.union(a.index(), b.index()) {
mst.add_edge(a, b, score);
}
}
debug_assert!(mst.node_count() == g.node_count());
debug_assert!(mst.edge_count() < g.node_count());
mst
}