petgraph 0.8.3

Graph data structure library. Provides graph types and graph algorithms.
Documentation
#![feature(test)]

extern crate petgraph;
extern crate test;

use test::Bencher;

use petgraph::algo;
use petgraph::matrix_graph::{node_index, MatrixGraph};
use petgraph::{Directed, EdgeType, Incoming, Outgoing};

#[bench]
fn add_100_nodes(b: &mut test::Bencher) {
    b.iter(|| {
        let mut g = MatrixGraph::<(), ()>::with_capacity(100);

        for _ in 0..100 {
            let _ = g.add_node(());
        }
    });
}

#[bench]
fn add_100_edges_to_self(b: &mut test::Bencher) {
    let mut g = MatrixGraph::<(), ()>::with_capacity(100);
    let nodes: Vec<_> = (0..100).map(|_| g.add_node(())).collect();
    let g = g;

    b.iter(|| {
        let mut g = g.clone();

        for &node in nodes.iter() {
            g.add_edge(node, node, ());
        }
    });
}

#[bench]
fn add_5_edges_for_each_of_100_nodes(b: &mut test::Bencher) {
    let mut g = MatrixGraph::<(), ()>::with_capacity(100);
    let nodes: Vec<_> = (0..100).map(|_| g.add_node(())).collect();
    let g = g;

    let edges_to_add: Vec<_> = nodes
        .iter()
        .enumerate()
        .flat_map(|(i, &node)| {
            let edges: Vec<_> = (0..5)
                .map(|j| (i + j + 1) % nodes.len())
                .map(|j| (node, nodes[j]))
                .collect();

            edges
        })
        .collect();

    b.iter(|| {
        let mut g = g.clone();

        for &(source, target) in edges_to_add.iter() {
            g.add_edge(source, target, ());
        }
    });
}

#[bench]
fn add_edges_from_root(bench: &mut test::Bencher) {
    bench.iter(|| {
        let mut gr = MatrixGraph::<_, _>::new();
        let a = gr.add_node(());

        for _ in 0..100 {
            let b = gr.add_node(());
            gr.add_edge(a, b, ());
        }
    });
}

#[bench]
fn add_adjacent_edges(bench: &mut test::Bencher) {
    bench.iter(|| {
        let mut gr = MatrixGraph::<_, _>::new();
        let mut prev = None;
        for _ in 0..100 {
            let b = gr.add_node(());

            if let Some(a) = prev {
                gr.add_edge(a, b, ());
            }

            prev = Some(b);
        }
    });
}

/// An almost full set
const FULL: &str = "
 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 0 1 1 1 0 1
 1 1 1 1 1 1 1 1 1 1
";

const BIGGER: &str = "
 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 1 0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 1 0 1 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0
 0 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0
 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 1 0 0
 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0 0 0
 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1
 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 1 0 1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1
 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 1 1 0 1 1 1 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 1
 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0
 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
 1 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 1 0 1 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 1 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0
 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0
 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0
 0 1 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 1 0 0
 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0
 0 0 0 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1
 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 1 0 0
 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0 0 0
 0 1 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1
 0 1 0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0
 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1
 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1
 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 1
 0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0
";

/// Parse a text adjacency matrix format into a directed graph
fn parse_matrix<Ty: EdgeType>(s: &str) -> MatrixGraph<(), (), std::hash::RandomState, Ty> {
    let mut gr = MatrixGraph::default();
    let s = s.trim();
    let lines = s.lines().filter(|l| !l.is_empty());
    for (row, line) in lines.enumerate() {
        for (col, word) in line.split(' ').filter(|s| !s.is_empty()).enumerate() {
            let has_edge = word.parse::<i32>().unwrap();
            assert!(has_edge == 0 || has_edge == 1);
            if has_edge == 0 {
                continue;
            }
            while col >= gr.node_count() || row >= gr.node_count() {
                gr.add_node(());
            }
            gr.add_edge(node_index(row), node_index(col), ());
        }
    }
    gr
}

#[bench]
fn full_edges_out(bench: &mut Bencher) {
    let a = parse_matrix::<Directed>(FULL);
    bench.iter(|| a.edges_directed(node_index(1), Outgoing).count())
}

#[bench]
fn full_edges_in(bench: &mut Bencher) {
    let a = parse_matrix::<Directed>(FULL);
    bench.iter(|| a.edges_directed(node_index(1), Incoming).count())
}

#[bench]
fn full_neighbors_out(bench: &mut Bencher) {
    let a = parse_matrix::<Directed>(FULL);
    bench.iter(|| a.neighbors_directed(node_index(1), Outgoing).count())
}

#[bench]
fn full_neighbors_in(bench: &mut Bencher) {
    let a = parse_matrix::<Directed>(FULL);

    bench.iter(|| a.neighbors_directed(node_index(1), Incoming).count())
}

#[bench]
fn full_kosaraju_sccs(bench: &mut Bencher) {
    let a = parse_matrix::<Directed>(FULL);
    bench.iter(|| algo::kosaraju_scc(&a));
}

#[bench]
fn full_tarjan_sccs(bench: &mut Bencher) {
    let a = parse_matrix::<Directed>(FULL);
    bench.iter(|| algo::tarjan_scc(&a));
}

#[bench]
fn bigger_edges_out(bench: &mut Bencher) {
    let a = parse_matrix::<Directed>(BIGGER);
    bench.iter(|| a.edges_directed(node_index(1), Outgoing).count())
}

#[bench]
fn bigger_edges_in(bench: &mut Bencher) {
    let a = parse_matrix::<Directed>(BIGGER);
    bench.iter(|| a.edges_directed(node_index(1), Incoming).count())
}

#[bench]
fn bigger_neighbors_out(bench: &mut Bencher) {
    let a = parse_matrix::<Directed>(BIGGER);
    bench.iter(|| a.neighbors_directed(node_index(1), Outgoing).count())
}

#[bench]
fn bigger_neighbors_in(bench: &mut Bencher) {
    let a = parse_matrix::<Directed>(BIGGER);

    bench.iter(|| a.neighbors_directed(node_index(1), Incoming).count())
}

#[bench]
fn bigger_kosaraju_sccs(bench: &mut Bencher) {
    let a = parse_matrix::<Directed>(BIGGER);
    bench.iter(|| algo::kosaraju_scc(&a));
}

#[bench]
fn bigger_tarjan_sccs(bench: &mut Bencher) {
    let a = parse_matrix::<Directed>(BIGGER);
    bench.iter(|| algo::tarjan_scc(&a));
}