2487 lines
68 KiB
Rust
2487 lines
68 KiB
Rust
extern crate petgraph;
|
|
|
|
use std::collections::HashSet;
|
|
use std::hash::Hash;
|
|
|
|
use petgraph::prelude::*;
|
|
use petgraph::EdgeType;
|
|
|
|
use petgraph as pg;
|
|
|
|
use petgraph::algo::{
|
|
dominators, has_path_connecting, is_bipartite_undirected, is_cyclic_undirected,
|
|
is_isomorphic_matching, min_spanning_tree,
|
|
};
|
|
|
|
use petgraph::graph::node_index as n;
|
|
use petgraph::graph::IndexType;
|
|
|
|
use petgraph::algo::{astar, dijkstra, DfsSpace};
|
|
use petgraph::visit::{
|
|
IntoEdges, IntoEdgesDirected, IntoNeighbors, IntoNodeIdentifiers, NodeFiltered, Reversed, Topo,
|
|
VisitMap, Walker,
|
|
};
|
|
|
|
use petgraph::dot::Dot;
|
|
|
|
fn set<I>(iter: I) -> HashSet<I::Item>
|
|
where
|
|
I: IntoIterator,
|
|
I::Item: Hash + Eq,
|
|
{
|
|
iter.into_iter().collect()
|
|
}
|
|
|
|
#[test]
|
|
fn undirected() {
|
|
let mut og = Graph::new_undirected();
|
|
let a = og.add_node(0);
|
|
let b = og.add_node(1);
|
|
let c = og.add_node(2);
|
|
let d = og.add_node(3);
|
|
let _ = og.add_edge(a, b, 0);
|
|
let _ = og.add_edge(a, c, 1);
|
|
og.add_edge(c, a, 2);
|
|
og.add_edge(a, a, 3);
|
|
og.add_edge(b, c, 4);
|
|
og.add_edge(b, a, 5);
|
|
og.add_edge(a, d, 6);
|
|
assert_eq!(og.node_count(), 4);
|
|
assert_eq!(og.edge_count(), 7);
|
|
|
|
assert!(og.find_edge(a, b).is_some());
|
|
assert!(og.find_edge(d, a).is_some());
|
|
assert!(og.find_edge(a, a).is_some());
|
|
|
|
for edge in og.raw_edges() {
|
|
assert!(og.find_edge(edge.source(), edge.target()).is_some());
|
|
assert!(og.find_edge(edge.target(), edge.source()).is_some());
|
|
}
|
|
|
|
assert_eq!(og.neighbors(b).collect::<Vec<_>>(), vec![a, c, a]);
|
|
|
|
og.remove_node(a);
|
|
assert_eq!(og.neighbors(b).collect::<Vec<_>>(), vec![c]);
|
|
assert_eq!(og.node_count(), 3);
|
|
assert_eq!(og.edge_count(), 1);
|
|
assert!(og.find_edge(a, b).is_none());
|
|
assert!(og.find_edge(d, a).is_none());
|
|
assert!(og.find_edge(a, a).is_none());
|
|
assert!(og.find_edge(b, c).is_some());
|
|
assert_graph_consistent(&og);
|
|
}
|
|
|
|
#[test]
|
|
fn dfs() {
|
|
let mut gr = Graph::new();
|
|
let h = gr.add_node("H");
|
|
let i = gr.add_node("I");
|
|
let j = gr.add_node("J");
|
|
let k = gr.add_node("K");
|
|
// Z is disconnected.
|
|
let _ = gr.add_node("Z");
|
|
gr.add_edge(h, i, 1.);
|
|
gr.add_edge(h, j, 3.);
|
|
gr.add_edge(i, j, 1.);
|
|
gr.add_edge(i, k, 2.);
|
|
|
|
println!("{}", Dot::new(&gr));
|
|
|
|
assert_eq!(Dfs::new(&gr, h).iter(&gr).count(), 4);
|
|
assert_eq!(Dfs::new(&gr, h).iter(&gr).clone().count(), 4);
|
|
|
|
assert_eq!(Dfs::new(&gr, h).iter(Reversed(&gr)).count(), 1);
|
|
|
|
assert_eq!(Dfs::new(&gr, k).iter(Reversed(&gr)).count(), 3);
|
|
|
|
assert_eq!(Dfs::new(&gr, i).iter(&gr).count(), 3);
|
|
}
|
|
|
|
#[test]
|
|
fn dfs_order() {
|
|
let mut gr = Graph::new();
|
|
let h = gr.add_node("H");
|
|
let i = gr.add_node("I");
|
|
let j = gr.add_node("J");
|
|
let k = gr.add_node("K");
|
|
gr.add_edge(h, i, ());
|
|
gr.add_edge(h, j, ());
|
|
gr.add_edge(h, k, ());
|
|
gr.add_edge(i, k, ());
|
|
gr.add_edge(k, i, ());
|
|
|
|
// H
|
|
// / | \
|
|
// I J K
|
|
// \___/
|
|
//
|
|
// J cannot be visited between I and K in a depth-first search from H
|
|
|
|
let mut seen_i = false;
|
|
let mut seen_k = false;
|
|
for node in Dfs::new(&gr, h).iter(&gr) {
|
|
seen_i |= i == node;
|
|
seen_k |= k == node;
|
|
assert!(!(j == node && (seen_i ^ seen_k)), "Invalid DFS order");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn bfs() {
|
|
let mut gr = Graph::new();
|
|
let h = gr.add_node("H");
|
|
let i = gr.add_node("I");
|
|
let j = gr.add_node("J");
|
|
let k = gr.add_node("K");
|
|
// Z is disconnected.
|
|
let _ = gr.add_node("Z");
|
|
gr.add_edge(h, i, 1.);
|
|
gr.add_edge(h, j, 3.);
|
|
gr.add_edge(i, j, 1.);
|
|
gr.add_edge(i, k, 2.);
|
|
|
|
assert_eq!(Bfs::new(&gr, h).iter(&gr).count(), 4);
|
|
assert_eq!(Bfs::new(&gr, h).iter(&gr).clone().count(), 4);
|
|
|
|
assert_eq!(Bfs::new(&gr, h).iter(Reversed(&gr)).count(), 1);
|
|
|
|
assert_eq!(Bfs::new(&gr, k).iter(Reversed(&gr)).count(), 3);
|
|
|
|
assert_eq!(Bfs::new(&gr, i).iter(&gr).count(), 3);
|
|
|
|
let mut bfs = Bfs::new(&gr, h);
|
|
let nx = bfs.next(&gr);
|
|
assert_eq!(nx, Some(h));
|
|
|
|
let nx1 = bfs.next(&gr);
|
|
assert!(nx1 == Some(i) || nx1 == Some(j));
|
|
|
|
let nx2 = bfs.next(&gr);
|
|
assert!(nx2 == Some(i) || nx2 == Some(j));
|
|
assert!(nx1 != nx2);
|
|
|
|
let nx = bfs.next(&gr);
|
|
assert_eq!(nx, Some(k));
|
|
assert_eq!(bfs.next(&gr), None);
|
|
}
|
|
|
|
#[test]
|
|
fn mst() {
|
|
use petgraph::data::FromElements;
|
|
|
|
let mut gr = Graph::<_, _>::new();
|
|
let a = gr.add_node("A");
|
|
let b = gr.add_node("B");
|
|
let c = gr.add_node("C");
|
|
let d = gr.add_node("D");
|
|
let e = gr.add_node("E");
|
|
let f = gr.add_node("F");
|
|
let g = gr.add_node("G");
|
|
gr.add_edge(a, b, 7.);
|
|
gr.add_edge(a, d, 5.);
|
|
gr.add_edge(d, b, 9.);
|
|
gr.add_edge(b, c, 8.);
|
|
gr.add_edge(b, e, 7.);
|
|
gr.add_edge(c, e, 5.);
|
|
gr.add_edge(d, e, 15.);
|
|
gr.add_edge(d, f, 6.);
|
|
gr.add_edge(f, e, 8.);
|
|
gr.add_edge(f, g, 11.);
|
|
gr.add_edge(e, g, 9.);
|
|
|
|
// add a disjoint part
|
|
let h = gr.add_node("H");
|
|
let i = gr.add_node("I");
|
|
let j = gr.add_node("J");
|
|
gr.add_edge(h, i, 1.);
|
|
gr.add_edge(h, j, 3.);
|
|
gr.add_edge(i, j, 1.);
|
|
|
|
println!("{}", Dot::new(&gr));
|
|
|
|
let mst = UnGraph::from_elements(min_spanning_tree(&gr));
|
|
|
|
println!("{}", Dot::new(&mst));
|
|
println!("{:?}", Dot::new(&mst));
|
|
println!("MST is:\n{:#?}", mst);
|
|
assert!(mst.node_count() == gr.node_count());
|
|
// |E| = |N| - 2 because there are two disconnected components.
|
|
assert!(mst.edge_count() == gr.node_count() - 2);
|
|
|
|
// check the exact edges are there
|
|
assert!(mst.find_edge(a, b).is_some());
|
|
assert!(mst.find_edge(a, d).is_some());
|
|
assert!(mst.find_edge(b, e).is_some());
|
|
assert!(mst.find_edge(e, c).is_some());
|
|
assert!(mst.find_edge(e, g).is_some());
|
|
assert!(mst.find_edge(d, f).is_some());
|
|
|
|
assert!(mst.find_edge(h, i).is_some());
|
|
assert!(mst.find_edge(i, j).is_some());
|
|
|
|
assert!(mst.find_edge(d, b).is_none());
|
|
assert!(mst.find_edge(b, c).is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn selfloop() {
|
|
let mut gr = Graph::new();
|
|
let a = gr.add_node("A");
|
|
let b = gr.add_node("B");
|
|
let c = gr.add_node("C");
|
|
gr.add_edge(a, b, 7.);
|
|
gr.add_edge(c, a, 6.);
|
|
let sed = gr.add_edge(a, a, 2.);
|
|
|
|
assert!(gr.find_edge(a, b).is_some());
|
|
assert!(gr.find_edge(b, a).is_none());
|
|
assert!(gr.find_edge_undirected(b, a).is_some());
|
|
assert!(gr.find_edge(a, a).is_some());
|
|
println!("{:?}", gr);
|
|
|
|
gr.remove_edge(sed);
|
|
assert!(gr.find_edge(a, a).is_none());
|
|
println!("{:?}", gr);
|
|
}
|
|
|
|
#[test]
|
|
fn cyclic() {
|
|
let mut gr = Graph::new();
|
|
let a = gr.add_node("A");
|
|
let b = gr.add_node("B");
|
|
let c = gr.add_node("C");
|
|
|
|
assert!(!is_cyclic_undirected(&gr));
|
|
gr.add_edge(a, b, 7.);
|
|
gr.add_edge(c, a, 6.);
|
|
assert!(!is_cyclic_undirected(&gr));
|
|
{
|
|
let e = gr.add_edge(a, a, 0.);
|
|
assert!(is_cyclic_undirected(&gr));
|
|
gr.remove_edge(e);
|
|
assert!(!is_cyclic_undirected(&gr));
|
|
}
|
|
|
|
{
|
|
let e = gr.add_edge(b, c, 0.);
|
|
assert!(is_cyclic_undirected(&gr));
|
|
gr.remove_edge(e);
|
|
assert!(!is_cyclic_undirected(&gr));
|
|
}
|
|
|
|
let d = gr.add_node("D");
|
|
let e = gr.add_node("E");
|
|
gr.add_edge(b, d, 0.);
|
|
gr.add_edge(d, e, 0.);
|
|
assert!(!is_cyclic_undirected(&gr));
|
|
gr.add_edge(c, e, 0.);
|
|
assert!(is_cyclic_undirected(&gr));
|
|
assert_graph_consistent(&gr);
|
|
}
|
|
|
|
#[test]
|
|
fn bipartite() {
|
|
{
|
|
let mut gr = Graph::new_undirected();
|
|
let a = gr.add_node("A");
|
|
let b = gr.add_node("B");
|
|
let c = gr.add_node("C");
|
|
|
|
let d = gr.add_node("D");
|
|
let e = gr.add_node("E");
|
|
let f = gr.add_node("F");
|
|
|
|
gr.add_edge(a, d, 7.);
|
|
gr.add_edge(b, d, 6.);
|
|
|
|
assert!(is_bipartite_undirected(&gr, a));
|
|
|
|
let e_index = gr.add_edge(a, b, 6.);
|
|
|
|
assert!(!is_bipartite_undirected(&gr, a));
|
|
|
|
gr.remove_edge(e_index);
|
|
|
|
assert!(is_bipartite_undirected(&gr, a));
|
|
|
|
gr.add_edge(b, e, 7.);
|
|
gr.add_edge(b, f, 6.);
|
|
gr.add_edge(c, e, 6.);
|
|
|
|
assert!(is_bipartite_undirected(&gr, a));
|
|
}
|
|
{
|
|
let mut gr = Graph::new_undirected();
|
|
let a = gr.add_node("A");
|
|
let b = gr.add_node("B");
|
|
let c = gr.add_node("C");
|
|
let d = gr.add_node("D");
|
|
let e = gr.add_node("E");
|
|
|
|
let f = gr.add_node("F");
|
|
let g = gr.add_node("G");
|
|
let h = gr.add_node("H");
|
|
|
|
gr.add_edge(a, f, 7.);
|
|
gr.add_edge(a, g, 7.);
|
|
gr.add_edge(a, h, 7.);
|
|
|
|
gr.add_edge(b, f, 6.);
|
|
gr.add_edge(b, g, 6.);
|
|
gr.add_edge(b, h, 6.);
|
|
|
|
gr.add_edge(c, f, 6.);
|
|
gr.add_edge(c, g, 6.);
|
|
gr.add_edge(c, h, 6.);
|
|
|
|
gr.add_edge(d, f, 6.);
|
|
gr.add_edge(d, g, 6.);
|
|
gr.add_edge(d, h, 6.);
|
|
|
|
gr.add_edge(e, f, 6.);
|
|
gr.add_edge(e, g, 6.);
|
|
gr.add_edge(e, h, 6.);
|
|
|
|
assert!(is_bipartite_undirected(&gr, a));
|
|
|
|
gr.add_edge(a, b, 6.);
|
|
|
|
assert!(!is_bipartite_undirected(&gr, a));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn multi() {
|
|
let mut gr = Graph::new();
|
|
let a = gr.add_node("a");
|
|
let b = gr.add_node("b");
|
|
gr.add_edge(a, b, ());
|
|
gr.add_edge(a, b, ());
|
|
assert_eq!(gr.edge_count(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn iter_multi_edges() {
|
|
let mut gr = Graph::new();
|
|
let a = gr.add_node("a");
|
|
let b = gr.add_node("b");
|
|
let c = gr.add_node("c");
|
|
|
|
let mut connecting_edges = HashSet::new();
|
|
|
|
gr.add_edge(a, a, ());
|
|
connecting_edges.insert(gr.add_edge(a, b, ()));
|
|
gr.add_edge(a, c, ());
|
|
gr.add_edge(c, b, ());
|
|
connecting_edges.insert(gr.add_edge(a, b, ()));
|
|
gr.add_edge(b, a, ());
|
|
|
|
let mut iter = gr.edges_connecting(a, b);
|
|
|
|
let edge_id = iter.next().unwrap().id();
|
|
assert!(connecting_edges.contains(&edge_id));
|
|
connecting_edges.remove(&edge_id);
|
|
|
|
let edge_id = iter.next().unwrap().id();
|
|
assert!(connecting_edges.contains(&edge_id));
|
|
connecting_edges.remove(&edge_id);
|
|
|
|
assert_eq!(None, iter.next());
|
|
assert!(connecting_edges.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn iter_multi_undirected_edges() {
|
|
let mut gr = Graph::new_undirected();
|
|
let a = gr.add_node("a");
|
|
let b = gr.add_node("b");
|
|
let c = gr.add_node("c");
|
|
|
|
let mut connecting_edges = HashSet::new();
|
|
|
|
gr.add_edge(a, a, ());
|
|
connecting_edges.insert(gr.add_edge(a, b, ()));
|
|
gr.add_edge(a, c, ());
|
|
gr.add_edge(c, b, ());
|
|
connecting_edges.insert(gr.add_edge(a, b, ()));
|
|
connecting_edges.insert(gr.add_edge(b, a, ()));
|
|
|
|
let mut iter = gr.edges_connecting(a, b);
|
|
|
|
let edge_id = iter.next().unwrap().id();
|
|
assert!(connecting_edges.contains(&edge_id));
|
|
connecting_edges.remove(&edge_id);
|
|
|
|
let edge_id = iter.next().unwrap().id();
|
|
assert!(connecting_edges.contains(&edge_id));
|
|
connecting_edges.remove(&edge_id);
|
|
|
|
let edge_id = iter.next().unwrap().id();
|
|
assert!(connecting_edges.contains(&edge_id));
|
|
connecting_edges.remove(&edge_id);
|
|
|
|
assert_eq!(None, iter.next());
|
|
assert!(connecting_edges.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn update_edge() {
|
|
{
|
|
let mut gr = Graph::new();
|
|
let a = gr.add_node("a");
|
|
let b = gr.add_node("b");
|
|
let e = gr.update_edge(a, b, 1);
|
|
let f = gr.update_edge(a, b, 2);
|
|
let _ = gr.update_edge(b, a, 3);
|
|
assert_eq!(gr.edge_count(), 2);
|
|
assert_eq!(e, f);
|
|
assert_eq!(*gr.edge_weight(f).unwrap(), 2);
|
|
}
|
|
|
|
{
|
|
let mut gr = Graph::new_undirected();
|
|
let a = gr.add_node("a");
|
|
let b = gr.add_node("b");
|
|
let e = gr.update_edge(a, b, 1);
|
|
let f = gr.update_edge(b, a, 2);
|
|
assert_eq!(gr.edge_count(), 1);
|
|
assert_eq!(e, f);
|
|
assert_eq!(*gr.edge_weight(f).unwrap(), 2);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn dijk() {
|
|
let mut g = Graph::new_undirected();
|
|
let a = g.add_node("A");
|
|
let b = g.add_node("B");
|
|
let c = g.add_node("C");
|
|
let d = g.add_node("D");
|
|
let e = g.add_node("E");
|
|
let f = g.add_node("F");
|
|
g.add_edge(a, b, 7);
|
|
g.add_edge(c, a, 9);
|
|
g.add_edge(a, d, 14);
|
|
g.add_edge(b, c, 10);
|
|
g.add_edge(d, c, 2);
|
|
g.add_edge(d, e, 9);
|
|
g.add_edge(b, f, 15);
|
|
g.add_edge(c, f, 11);
|
|
g.add_edge(e, f, 6);
|
|
println!("{:?}", g);
|
|
for no in Bfs::new(&g, a).iter(&g) {
|
|
println!("Visit {:?} = {:?}", no, g.node_weight(no));
|
|
}
|
|
|
|
let scores = dijkstra(&g, a, None, |e| *e.weight());
|
|
let mut scores: Vec<_> = scores.into_iter().map(|(n, s)| (g[n], s)).collect();
|
|
scores.sort();
|
|
assert_eq!(
|
|
scores,
|
|
vec![
|
|
("A", 0),
|
|
("B", 7),
|
|
("C", 9),
|
|
("D", 11),
|
|
("E", 20),
|
|
("F", 20)
|
|
]
|
|
);
|
|
|
|
let scores = dijkstra(&g, a, Some(c), |e| *e.weight());
|
|
assert_eq!(scores[&c], 9);
|
|
}
|
|
|
|
#[test]
|
|
fn test_astar_null_heuristic() {
|
|
let mut g = Graph::new();
|
|
let a = g.add_node("A");
|
|
let b = g.add_node("B");
|
|
let c = g.add_node("C");
|
|
let d = g.add_node("D");
|
|
let e = g.add_node("E");
|
|
let f = g.add_node("F");
|
|
g.add_edge(a, b, 7);
|
|
g.add_edge(c, a, 9);
|
|
g.add_edge(a, d, 14);
|
|
g.add_edge(b, c, 10);
|
|
g.add_edge(d, c, 2);
|
|
g.add_edge(d, e, 9);
|
|
g.add_edge(b, f, 15);
|
|
g.add_edge(c, f, 11);
|
|
g.add_edge(e, f, 6);
|
|
|
|
let path = astar(&g, a, |finish| finish == e, |e| *e.weight(), |_| 0);
|
|
assert_eq!(path, Some((23, vec![a, d, e])));
|
|
|
|
// check against dijkstra
|
|
let dijkstra_run = dijkstra(&g, a, Some(e), |e| *e.weight());
|
|
assert_eq!(dijkstra_run[&e], 23);
|
|
|
|
let path = astar(&g, e, |finish| finish == b, |e| *e.weight(), |_| 0);
|
|
assert_eq!(path, None);
|
|
}
|
|
|
|
#[test]
|
|
fn test_astar_manhattan_heuristic() {
|
|
let mut g = Graph::new();
|
|
let a = g.add_node((0., 0.));
|
|
let b = g.add_node((2., 0.));
|
|
let c = g.add_node((1., 1.));
|
|
let d = g.add_node((0., 2.));
|
|
let e = g.add_node((3., 3.));
|
|
let f = g.add_node((4., 2.));
|
|
let _ = g.add_node((5., 5.)); // no path to node
|
|
g.add_edge(a, b, 2.);
|
|
g.add_edge(a, d, 4.);
|
|
g.add_edge(b, c, 1.);
|
|
g.add_edge(b, f, 7.);
|
|
g.add_edge(c, e, 5.);
|
|
g.add_edge(e, f, 1.);
|
|
g.add_edge(d, e, 1.);
|
|
|
|
let heuristic_for = |f: NodeIndex| {
|
|
let g = &g;
|
|
move |node: NodeIndex| -> f32 {
|
|
let (x1, y1): (f32, f32) = g[node];
|
|
let (x2, y2): (f32, f32) = g[f];
|
|
|
|
(x2 - x1).abs() + (y2 - y1).abs()
|
|
}
|
|
};
|
|
let path = astar(
|
|
&g,
|
|
a,
|
|
|finish| finish == f,
|
|
|e| *e.weight(),
|
|
heuristic_for(f),
|
|
);
|
|
|
|
assert_eq!(path, Some((6., vec![a, d, e, f])));
|
|
|
|
// check against dijkstra
|
|
let dijkstra_run = dijkstra(&g, a, None, |e| *e.weight());
|
|
|
|
for end in g.node_indices() {
|
|
let astar_path = astar(
|
|
&g,
|
|
a,
|
|
|finish| finish == end,
|
|
|e| *e.weight(),
|
|
heuristic_for(end),
|
|
);
|
|
assert_eq!(dijkstra_run.get(&end).cloned(), astar_path.map(|t| t.0));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_astar_runtime_optimal() {
|
|
let mut g = Graph::new();
|
|
let a = g.add_node("A");
|
|
let b = g.add_node("B");
|
|
let c = g.add_node("C");
|
|
let d = g.add_node("D");
|
|
let e = g.add_node("E");
|
|
g.add_edge(a, b, 2);
|
|
g.add_edge(a, c, 3);
|
|
g.add_edge(b, d, 3);
|
|
g.add_edge(c, d, 1);
|
|
g.add_edge(d, e, 1);
|
|
|
|
let mut times_called = 0;
|
|
|
|
let _ = astar(
|
|
&g,
|
|
a,
|
|
|n| n == e,
|
|
|edge| {
|
|
times_called += 1;
|
|
*edge.weight()
|
|
},
|
|
|_| 0,
|
|
);
|
|
|
|
// A* is runtime optimal in the sense it won't expand more nodes than needed, for the given
|
|
// heuristic. Here, A* should expand, in order: A, B, C, D, E. This should should ask for the
|
|
// costs of edges (A, B), (A, C), (B, D), (C, D), (D, E). Node D will be added to `visit_next`
|
|
// twice, but should only be expanded once. If it is erroneously expanded twice, it will call
|
|
// for (D, E) again and `times_called` will be 6.
|
|
assert_eq!(times_called, 5);
|
|
}
|
|
|
|
#[test]
|
|
fn test_astar_admissible_inconsistent() {
|
|
let mut g = Graph::new();
|
|
let a = g.add_node("A");
|
|
let b = g.add_node("B");
|
|
let c = g.add_node("C");
|
|
let d = g.add_node("D");
|
|
g.add_edge(a, b, 3);
|
|
g.add_edge(b, c, 3);
|
|
g.add_edge(c, d, 3);
|
|
g.add_edge(a, c, 8);
|
|
g.add_edge(a, d, 10);
|
|
|
|
let admissible_inconsistent = |n: NodeIndex| match g[n] {
|
|
"A" => 9,
|
|
"B" => 6,
|
|
"C" => 0,
|
|
&_ => 0,
|
|
};
|
|
|
|
let optimal = astar(&g, a, |n| n == d, |e| *e.weight(), admissible_inconsistent);
|
|
assert_eq!(optimal, Some((9, vec![a, b, c, d])));
|
|
}
|
|
|
|
#[cfg(feature = "generate")]
|
|
#[test]
|
|
fn test_generate_undirected() {
|
|
for size in 0..4 {
|
|
let mut gen = pg::generate::Generator::<Undirected>::all(size, true);
|
|
let nedges = (size * size - size) / 2 + size;
|
|
let mut n = 0;
|
|
while let Some(g) = gen.next_ref() {
|
|
n += 1;
|
|
println!("{:?}", g);
|
|
}
|
|
|
|
assert_eq!(n, 1 << nedges);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "generate")]
|
|
#[test]
|
|
fn test_generate_directed() {
|
|
// Number of DAG out of all graphs (all permutations) per node size
|
|
// 0, 1, 2, 3, 4, 5 ..
|
|
let n_dag = &[1, 1, 3, 25, 543, 29281, 3781503];
|
|
for (size, &dags_exp) in (0..4).zip(n_dag) {
|
|
let mut gen = pg::generate::Generator::<Directed>::all(size, true);
|
|
let nedges = size * size;
|
|
let mut n = 0;
|
|
let mut dags = 0;
|
|
while let Some(g) = gen.next_ref() {
|
|
n += 1;
|
|
if !pg::algo::is_cyclic_directed(g) {
|
|
dags += 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
// check that all generated graphs have unique adjacency matrices
|
|
let mut adjmats = graphs.iter().map(Graph::adjacency_matrix).collect::<Vec<_>>();
|
|
adjmats.sort(); adjmats.dedup();
|
|
assert_eq!(adjmats.len(), n);
|
|
*/
|
|
assert_eq!(dags_exp, dags);
|
|
assert_eq!(n, 1 << nedges);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "generate")]
|
|
#[test]
|
|
fn test_generate_dag() {
|
|
use petgraph::visit::GetAdjacencyMatrix;
|
|
for size in 1..5 {
|
|
let gen = pg::generate::Generator::directed_acyclic(size);
|
|
let nedges = (size - 1) * size / 2;
|
|
let graphs = gen.collect::<Vec<_>>();
|
|
|
|
assert_eq!(graphs.len(), 1 << nedges);
|
|
|
|
// check that all generated graphs have unique adjacency matrices
|
|
let mut adjmats = graphs
|
|
.iter()
|
|
.map(Graph::adjacency_matrix)
|
|
.collect::<Vec<_>>();
|
|
adjmats.sort();
|
|
adjmats.dedup();
|
|
assert_eq!(adjmats.len(), graphs.len());
|
|
for gr in &graphs {
|
|
assert!(
|
|
!petgraph::algo::is_cyclic_directed(gr),
|
|
"Assertion failed: {:?} acyclic",
|
|
gr
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn without() {
|
|
let mut og = Graph::new_undirected();
|
|
let a = og.add_node(0);
|
|
let b = og.add_node(1);
|
|
let c = og.add_node(2);
|
|
let d = og.add_node(3);
|
|
let _ = og.add_edge(a, b, 0);
|
|
let _ = og.add_edge(a, c, 1);
|
|
let v: Vec<NodeIndex> = og.externals(Outgoing).collect();
|
|
assert_eq!(v, vec![d]);
|
|
|
|
let mut og = Graph::new();
|
|
let a = og.add_node(0);
|
|
let b = og.add_node(1);
|
|
let c = og.add_node(2);
|
|
let d = og.add_node(3);
|
|
let _ = og.add_edge(a, b, 0);
|
|
let _ = og.add_edge(a, c, 1);
|
|
let init: Vec<NodeIndex> = og.externals(Incoming).collect();
|
|
let term: Vec<NodeIndex> = og.externals(Outgoing).collect();
|
|
assert_eq!(init, vec![a, d]);
|
|
assert_eq!(term, vec![b, c, d]);
|
|
}
|
|
|
|
fn assert_is_topo_order<N, E>(gr: &Graph<N, E, Directed>, order: &[NodeIndex]) {
|
|
assert_eq!(gr.node_count(), order.len());
|
|
// check all the edges of the graph
|
|
for edge in gr.raw_edges() {
|
|
let a = edge.source();
|
|
let b = edge.target();
|
|
let ai = order.iter().position(|x| *x == a).unwrap();
|
|
let bi = order.iter().position(|x| *x == b).unwrap();
|
|
println!("Check that {:?} is before {:?}", a, b);
|
|
assert!(
|
|
ai < bi,
|
|
"Topo order: assertion that node {:?} is before {:?} failed",
|
|
a,
|
|
b
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_toposort() {
|
|
let mut gr = Graph::<_, _>::new();
|
|
let a = gr.add_node("A");
|
|
let b = gr.add_node("B");
|
|
let c = gr.add_node("C");
|
|
let d = gr.add_node("D");
|
|
let e = gr.add_node("E");
|
|
let f = gr.add_node("F");
|
|
let g = gr.add_node("G");
|
|
gr.extend_with_edges(&[
|
|
(a, b, 7.),
|
|
(a, d, 5.),
|
|
(d, b, 9.),
|
|
(b, c, 8.),
|
|
(b, e, 7.),
|
|
(c, e, 5.),
|
|
(d, e, 15.),
|
|
(d, f, 6.),
|
|
(f, e, 8.),
|
|
(f, g, 11.),
|
|
(e, g, 9.),
|
|
]);
|
|
|
|
// add a disjoint part
|
|
let h = gr.add_node("H");
|
|
let i = gr.add_node("I");
|
|
let j = gr.add_node("J");
|
|
gr.add_edge(h, i, 1.);
|
|
gr.add_edge(h, j, 3.);
|
|
gr.add_edge(i, j, 1.);
|
|
|
|
let order = petgraph::algo::toposort(&gr, None).unwrap();
|
|
println!("{:?}", order);
|
|
assert_eq!(order.len(), gr.node_count());
|
|
|
|
assert_is_topo_order(&gr, &order);
|
|
}
|
|
|
|
#[test]
|
|
fn test_toposort_eq() {
|
|
let mut g = Graph::<_, _>::new();
|
|
let a = g.add_node("A");
|
|
let b = g.add_node("B");
|
|
g.add_edge(a, b, ());
|
|
|
|
assert_eq!(petgraph::algo::toposort(&g, None), Ok(vec![a, b]));
|
|
}
|
|
|
|
#[test]
|
|
fn is_cyclic_directed() {
|
|
let mut gr = Graph::<_, _>::new();
|
|
let a = gr.add_node("A");
|
|
let b = gr.add_node("B");
|
|
let c = gr.add_node("C");
|
|
let d = gr.add_node("D");
|
|
let e = gr.add_node("E");
|
|
let f = gr.add_node("F");
|
|
let g = gr.add_node("G");
|
|
gr.add_edge(a, b, 7.0);
|
|
gr.add_edge(a, d, 5.);
|
|
gr.add_edge(d, b, 9.);
|
|
gr.add_edge(b, c, 8.);
|
|
gr.add_edge(b, e, 7.);
|
|
gr.add_edge(c, e, 5.);
|
|
gr.add_edge(d, e, 15.);
|
|
gr.add_edge(d, f, 6.);
|
|
gr.add_edge(f, e, 8.);
|
|
gr.add_edge(f, g, 11.);
|
|
gr.add_edge(e, g, 9.);
|
|
|
|
assert!(!petgraph::algo::is_cyclic_directed(&gr));
|
|
|
|
// add a disjoint part
|
|
let h = gr.add_node("H");
|
|
let i = gr.add_node("I");
|
|
let j = gr.add_node("J");
|
|
gr.add_edge(h, i, 1.);
|
|
gr.add_edge(h, j, 3.);
|
|
gr.add_edge(i, j, 1.);
|
|
assert!(!petgraph::algo::is_cyclic_directed(&gr));
|
|
|
|
gr.add_edge(g, e, 0.);
|
|
assert!(petgraph::algo::is_cyclic_directed(&gr));
|
|
}
|
|
|
|
/// Compare two scc sets. Inside each scc, the order does not matter,
|
|
/// but the order of the sccs is significant.
|
|
fn assert_sccs_eq(
|
|
mut res: Vec<Vec<NodeIndex>>,
|
|
mut answer: Vec<Vec<NodeIndex>>,
|
|
scc_order_matters: bool,
|
|
) {
|
|
// normalize the result and compare with the answer.
|
|
for scc in &mut res {
|
|
scc.sort();
|
|
}
|
|
for scc in &mut answer {
|
|
scc.sort();
|
|
}
|
|
if !scc_order_matters {
|
|
res.sort();
|
|
answer.sort();
|
|
}
|
|
assert_eq!(res, answer);
|
|
}
|
|
|
|
#[test]
|
|
fn scc() {
|
|
let gr: Graph<(), ()> = Graph::from_edges(&[
|
|
(6, 0),
|
|
(0, 3),
|
|
(3, 6),
|
|
(8, 6),
|
|
(8, 2),
|
|
(2, 5),
|
|
(5, 8),
|
|
(7, 5),
|
|
(1, 7),
|
|
(7, 4),
|
|
(4, 1),
|
|
]);
|
|
|
|
assert_sccs_eq(
|
|
petgraph::algo::kosaraju_scc(&gr),
|
|
vec![
|
|
vec![n(0), n(3), n(6)],
|
|
vec![n(2), n(5), n(8)],
|
|
vec![n(1), n(4), n(7)],
|
|
],
|
|
true,
|
|
);
|
|
// Reversed edges gives the same sccs (when sorted)
|
|
assert_sccs_eq(
|
|
petgraph::algo::kosaraju_scc(Reversed(&gr)),
|
|
vec![
|
|
vec![n(1), n(4), n(7)],
|
|
vec![n(2), n(5), n(8)],
|
|
vec![n(0), n(3), n(6)],
|
|
],
|
|
true,
|
|
);
|
|
|
|
// Test an undirected graph just for fun.
|
|
// Sccs are just connected components.
|
|
let mut hr = gr.into_edge_type::<Undirected>();
|
|
// Delete an edge to disconnect it
|
|
let ed = hr.find_edge(n(6), n(8)).unwrap();
|
|
assert!(hr.remove_edge(ed).is_some());
|
|
|
|
assert_sccs_eq(
|
|
petgraph::algo::kosaraju_scc(&hr),
|
|
vec![
|
|
vec![n(0), n(3), n(6)],
|
|
vec![n(1), n(2), n(4), n(5), n(7), n(8)],
|
|
],
|
|
false,
|
|
);
|
|
|
|
// acyclic non-tree, #14
|
|
let n = NodeIndex::new;
|
|
let mut gr = Graph::new();
|
|
gr.add_node(0);
|
|
gr.add_node(1);
|
|
gr.add_node(2);
|
|
gr.add_node(3);
|
|
gr.add_edge(n(3), n(2), ());
|
|
gr.add_edge(n(3), n(1), ());
|
|
gr.add_edge(n(2), n(0), ());
|
|
gr.add_edge(n(1), n(0), ());
|
|
|
|
assert_sccs_eq(
|
|
petgraph::algo::kosaraju_scc(&gr),
|
|
vec![vec![n(0)], vec![n(1)], vec![n(2)], vec![n(3)]],
|
|
true,
|
|
);
|
|
|
|
// Kosaraju bug from PR #60
|
|
let mut gr = Graph::<(), ()>::new();
|
|
gr.extend_with_edges(&[(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)]);
|
|
gr.add_node(());
|
|
// no order for the disconnected one
|
|
assert_sccs_eq(
|
|
petgraph::algo::kosaraju_scc(&gr),
|
|
vec![vec![n(0)], vec![n(1)], vec![n(2)], vec![n(3)]],
|
|
false,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn tarjan_scc() {
|
|
let gr: Graph<(), ()> = Graph::from_edges(&[
|
|
(6, 0),
|
|
(0, 3),
|
|
(3, 6),
|
|
(8, 6),
|
|
(8, 2),
|
|
(2, 5),
|
|
(5, 8),
|
|
(7, 5),
|
|
(1, 7),
|
|
(7, 4),
|
|
(4, 1),
|
|
]);
|
|
|
|
let mut tarjan_scc = petgraph::algo::TarjanScc::new();
|
|
|
|
let mut result = Vec::new();
|
|
tarjan_scc.run(&gr, |scc| result.push(scc.iter().rev().cloned().collect()));
|
|
assert_sccs_eq(
|
|
result,
|
|
vec![
|
|
vec![n(0), n(3), n(6)],
|
|
vec![n(2), n(5), n(8)],
|
|
vec![n(1), n(4), n(7)],
|
|
],
|
|
true,
|
|
);
|
|
|
|
// Test an undirected graph just for fun.
|
|
// Sccs are just connected components.
|
|
let mut hr = gr.into_edge_type::<Undirected>();
|
|
// Delete an edge to disconnect it
|
|
let ed = hr.find_edge(n(6), n(8)).unwrap();
|
|
assert!(hr.remove_edge(ed).is_some());
|
|
|
|
let mut result = Vec::new();
|
|
tarjan_scc.run(&hr, |scc| result.push(scc.iter().rev().cloned().collect()));
|
|
assert_sccs_eq(
|
|
result,
|
|
vec![
|
|
vec![n(1), n(2), n(4), n(5), n(7), n(8)],
|
|
vec![n(0), n(3), n(6)],
|
|
],
|
|
false,
|
|
);
|
|
|
|
// acyclic non-tree, #14
|
|
let n = NodeIndex::new;
|
|
let mut gr = Graph::new();
|
|
gr.add_node(0);
|
|
gr.add_node(1);
|
|
gr.add_node(2);
|
|
gr.add_node(3);
|
|
gr.add_edge(n(3), n(2), ());
|
|
gr.add_edge(n(3), n(1), ());
|
|
gr.add_edge(n(2), n(0), ());
|
|
gr.add_edge(n(1), n(0), ());
|
|
|
|
let mut result = Vec::new();
|
|
tarjan_scc.run(&gr, |scc| result.push(scc.iter().rev().cloned().collect()));
|
|
assert_sccs_eq(
|
|
result,
|
|
vec![vec![n(0)], vec![n(1)], vec![n(2)], vec![n(3)]],
|
|
true,
|
|
);
|
|
|
|
// Kosaraju bug from PR #60
|
|
let mut gr = Graph::<(), ()>::new();
|
|
gr.extend_with_edges(&[(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)]);
|
|
gr.add_node(());
|
|
// no order for the disconnected one
|
|
let mut result = Vec::new();
|
|
tarjan_scc.run(&gr, |scc| result.push(scc.iter().rev().cloned().collect()));
|
|
assert_sccs_eq(
|
|
result,
|
|
vec![vec![n(0)], vec![n(1)], vec![n(2)], vec![n(3)]],
|
|
false,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn condensation() {
|
|
let gr: Graph<(), ()> = Graph::from_edges(&[
|
|
(6, 0),
|
|
(0, 3),
|
|
(3, 6),
|
|
(8, 6),
|
|
(8, 2),
|
|
(2, 3),
|
|
(2, 5),
|
|
(5, 8),
|
|
(7, 5),
|
|
(1, 7),
|
|
(7, 4),
|
|
(4, 1),
|
|
]);
|
|
|
|
// make_acyclic = true
|
|
|
|
let cond = petgraph::algo::condensation(gr.clone(), true);
|
|
|
|
assert!(cond.node_count() == 3);
|
|
assert!(cond.edge_count() == 2);
|
|
assert!(
|
|
!petgraph::algo::is_cyclic_directed(&cond),
|
|
"Assertion failed: {:?} acyclic",
|
|
cond
|
|
);
|
|
|
|
// make_acyclic = false
|
|
|
|
let cond = petgraph::algo::condensation(gr.clone(), false);
|
|
|
|
assert!(cond.node_count() == 3);
|
|
assert!(cond.edge_count() == gr.edge_count());
|
|
}
|
|
|
|
#[test]
|
|
fn connected_comp() {
|
|
let n = NodeIndex::new;
|
|
let mut gr = Graph::new();
|
|
gr.add_node(0);
|
|
gr.add_node(1);
|
|
gr.add_node(2);
|
|
gr.add_node(3);
|
|
gr.add_node(4);
|
|
gr.add_node(5);
|
|
gr.add_node(6);
|
|
gr.add_node(7);
|
|
gr.add_node(8);
|
|
gr.add_edge(n(6), n(0), ());
|
|
gr.add_edge(n(0), n(3), ());
|
|
gr.add_edge(n(3), n(6), ());
|
|
gr.add_edge(n(8), n(6), ());
|
|
gr.add_edge(n(8), n(2), ());
|
|
gr.add_edge(n(2), n(5), ());
|
|
gr.add_edge(n(5), n(8), ());
|
|
gr.add_edge(n(7), n(5), ());
|
|
gr.add_edge(n(1), n(7), ());
|
|
gr.add_edge(n(7), n(4), ());
|
|
gr.add_edge(n(4), n(1), ());
|
|
assert_eq!(petgraph::algo::connected_components(&gr), 1);
|
|
|
|
gr.add_node(9);
|
|
gr.add_node(10);
|
|
assert_eq!(petgraph::algo::connected_components(&gr), 3);
|
|
|
|
gr.add_edge(n(9), n(10), ());
|
|
assert_eq!(petgraph::algo::connected_components(&gr), 2);
|
|
|
|
let gr = gr.into_edge_type::<Undirected>();
|
|
assert_eq!(petgraph::algo::connected_components(&gr), 2);
|
|
}
|
|
|
|
#[should_panic]
|
|
#[test]
|
|
fn oob_index() {
|
|
let mut gr = Graph::<_, ()>::new();
|
|
let a = gr.add_node(0);
|
|
let b = gr.add_node(1);
|
|
gr.remove_node(a);
|
|
gr[b];
|
|
}
|
|
|
|
#[test]
|
|
fn usize_index() {
|
|
let mut gr = Graph::<_, _, Directed, usize>::with_capacity(0, 0);
|
|
let a = gr.add_node(0);
|
|
let b = gr.add_node(1);
|
|
let e = gr.add_edge(a, b, 1.2);
|
|
let mut dfs = Dfs::new(&gr, a);
|
|
while let Some(nx) = dfs.next(&gr) {
|
|
gr[nx] += 1;
|
|
}
|
|
assert_eq!(gr[a], 1);
|
|
assert_eq!(gr[b], 2);
|
|
assert_eq!(gr[e], 1.2);
|
|
}
|
|
|
|
#[test]
|
|
fn u8_index() {
|
|
let mut gr = Graph::<_, (), Undirected, u8>::with_capacity(0, 0);
|
|
for _ in 0..255 {
|
|
gr.add_node(());
|
|
}
|
|
}
|
|
|
|
#[should_panic]
|
|
#[test]
|
|
fn u8_index_overflow() {
|
|
let mut gr = Graph::<_, (), Undirected, u8>::with_capacity(0, 0);
|
|
for _ in 0..256 {
|
|
gr.add_node(());
|
|
}
|
|
}
|
|
|
|
#[should_panic]
|
|
#[test]
|
|
fn u8_index_overflow_edges() {
|
|
let mut gr = Graph::<_, (), Undirected, u8>::with_capacity(0, 0);
|
|
let a = gr.add_node('a');
|
|
let b = gr.add_node('b');
|
|
for _ in 0..256 {
|
|
gr.add_edge(a, b, ());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_weight_iterators() {
|
|
let mut gr = Graph::<_, _>::new();
|
|
let a = gr.add_node("A");
|
|
let b = gr.add_node("B");
|
|
let c = gr.add_node("C");
|
|
let d = gr.add_node("D");
|
|
let e = gr.add_node("E");
|
|
let f = gr.add_node("F");
|
|
let g = gr.add_node("G");
|
|
gr.add_edge(a, b, 7.0);
|
|
gr.add_edge(a, d, 5.);
|
|
gr.add_edge(d, b, 9.);
|
|
gr.add_edge(b, c, 8.);
|
|
gr.add_edge(b, e, 7.);
|
|
gr.add_edge(c, e, 5.);
|
|
gr.add_edge(d, e, 15.);
|
|
gr.add_edge(d, f, 6.);
|
|
gr.add_edge(f, e, 8.);
|
|
gr.add_edge(f, g, 11.);
|
|
gr.add_edge(e, g, 9.);
|
|
|
|
assert_eq!(gr.node_weights_mut().count(), gr.node_count());
|
|
assert_eq!(gr.edge_weights_mut().count(), gr.edge_count());
|
|
|
|
// add a disjoint part
|
|
let h = gr.add_node("H");
|
|
let i = gr.add_node("I");
|
|
let j = gr.add_node("J");
|
|
gr.add_edge(h, i, 1.);
|
|
gr.add_edge(h, j, 3.);
|
|
gr.add_edge(i, j, 1.);
|
|
|
|
assert_eq!(gr.node_weights_mut().count(), gr.node_count());
|
|
assert_eq!(gr.edge_weights_mut().count(), gr.edge_count());
|
|
|
|
for nw in gr.node_weights_mut() {
|
|
*nw = "x";
|
|
}
|
|
for node in gr.raw_nodes() {
|
|
assert_eq!(node.weight, "x");
|
|
}
|
|
|
|
let old = gr.clone();
|
|
for (index, ew) in gr.edge_weights_mut().enumerate() {
|
|
assert_eq!(old[EdgeIndex::new(index)], *ew);
|
|
*ew = -*ew;
|
|
}
|
|
for (index, edge) in gr.raw_edges().iter().enumerate() {
|
|
assert_eq!(edge.weight, -1. * old[EdgeIndex::new(index)]);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn walk_edges() {
|
|
let mut gr = Graph::<_, _>::new();
|
|
let a = gr.add_node(0.);
|
|
let b = gr.add_node(1.);
|
|
let c = gr.add_node(2.);
|
|
let d = gr.add_node(3.);
|
|
let e0 = gr.add_edge(a, b, 0.);
|
|
let e1 = gr.add_edge(a, d, 0.);
|
|
let e2 = gr.add_edge(b, c, 0.);
|
|
let e3 = gr.add_edge(c, a, 0.);
|
|
|
|
// Set edge weights to difference: target - source.
|
|
let mut dfs = Dfs::new(&gr, a);
|
|
while let Some(source) = dfs.next(&gr) {
|
|
let mut edges = gr.neighbors_directed(source, Outgoing).detach();
|
|
while let Some((edge, target)) = edges.next(&gr) {
|
|
gr[edge] = gr[target] - gr[source];
|
|
}
|
|
}
|
|
assert_eq!(gr[e0], 1.);
|
|
assert_eq!(gr[e1], 3.);
|
|
assert_eq!(gr[e2], 1.);
|
|
assert_eq!(gr[e3], -2.);
|
|
|
|
let mut nedges = 0;
|
|
let mut dfs = Dfs::new(&gr, a);
|
|
while let Some(node) = dfs.next(&gr) {
|
|
let mut edges = gr.neighbors_directed(node, Incoming).detach();
|
|
while let Some((edge, source)) = edges.next(&gr) {
|
|
assert_eq!(gr.find_edge(source, node), Some(edge));
|
|
nedges += 1;
|
|
}
|
|
|
|
let mut edges = gr.neighbors_directed(node, Outgoing).detach();
|
|
while let Some((edge, target)) = edges.next(&gr) {
|
|
assert_eq!(gr.find_edge(node, target), Some(edge));
|
|
nedges += 1;
|
|
}
|
|
}
|
|
assert_eq!(nedges, 8);
|
|
}
|
|
|
|
#[test]
|
|
fn index_twice_mut() {
|
|
let mut gr = Graph::<_, _>::new();
|
|
let a = gr.add_node(0.);
|
|
let b = gr.add_node(0.);
|
|
let c = gr.add_node(0.);
|
|
let d = gr.add_node(0.);
|
|
let e = gr.add_node(0.);
|
|
let f = gr.add_node(0.);
|
|
let g = gr.add_node(0.);
|
|
gr.add_edge(a, b, 7.0);
|
|
gr.add_edge(a, d, 5.);
|
|
gr.add_edge(d, b, 9.);
|
|
gr.add_edge(b, c, 8.);
|
|
gr.add_edge(b, e, 7.);
|
|
gr.add_edge(c, e, 5.);
|
|
gr.add_edge(d, e, 15.);
|
|
gr.add_edge(d, f, 6.);
|
|
gr.add_edge(f, e, 8.);
|
|
gr.add_edge(f, g, 11.);
|
|
gr.add_edge(e, g, 9.);
|
|
|
|
for dir in &[Incoming, Outgoing] {
|
|
for nw in gr.node_weights_mut() {
|
|
*nw = 0.;
|
|
}
|
|
|
|
// walk the graph and sum incoming edges
|
|
let mut dfs = Dfs::new(&gr, a);
|
|
while let Some(node) = dfs.next(&gr) {
|
|
let mut edges = gr.neighbors_directed(node, *dir).detach();
|
|
while let Some(edge) = edges.next_edge(&gr) {
|
|
let (nw, ew) = gr.index_twice_mut(node, edge);
|
|
*nw += *ew;
|
|
}
|
|
}
|
|
|
|
// check the sums
|
|
for i in 0..gr.node_count() {
|
|
let ni = NodeIndex::new(i);
|
|
let s = gr
|
|
.edges_directed(ni, *dir)
|
|
.map(|e| *e.weight())
|
|
.fold(0., |a, b| a + b);
|
|
assert_eq!(s, gr[ni]);
|
|
}
|
|
println!("Sum {:?}: {:?}", dir, gr);
|
|
}
|
|
}
|
|
|
|
fn make_edge_iterator_graph<Ty: EdgeType>() -> Graph<f64, f64, Ty> {
|
|
let mut gr = Graph::default();
|
|
let a = gr.add_node(0.);
|
|
let b = gr.add_node(0.);
|
|
let c = gr.add_node(0.);
|
|
let d = gr.add_node(0.);
|
|
let e = gr.add_node(0.);
|
|
let f = gr.add_node(0.);
|
|
let g = gr.add_node(0.);
|
|
gr.add_edge(a, b, 7.0);
|
|
gr.add_edge(a, d, 5.);
|
|
gr.add_edge(d, b, 9.);
|
|
gr.add_edge(b, c, 8.);
|
|
gr.add_edge(b, e, 7.);
|
|
gr.add_edge(c, c, 8.);
|
|
gr.add_edge(c, e, 5.);
|
|
gr.add_edge(d, e, 15.);
|
|
gr.add_edge(d, f, 6.);
|
|
gr.add_edge(f, e, 8.);
|
|
gr.add_edge(f, g, 11.);
|
|
gr.add_edge(e, g, 9.);
|
|
|
|
gr
|
|
}
|
|
|
|
#[test]
|
|
fn test_edge_iterators_directed() {
|
|
let gr = make_edge_iterator_graph::<Directed>();
|
|
|
|
for i in gr.node_indices() {
|
|
itertools::assert_equal(gr.edges_directed(i, Outgoing), gr.edges(i));
|
|
|
|
// Reversed reverses edges, so target and source need to be reversed once more.
|
|
itertools::assert_equal(
|
|
gr.edges_directed(i, Outgoing)
|
|
.map(|edge| (edge.source(), edge.target())),
|
|
Reversed(&gr)
|
|
.edges_directed(i, Incoming)
|
|
.map(|edge| (edge.target(), edge.source())),
|
|
);
|
|
|
|
for edge in gr.edges_directed(i, Outgoing) {
|
|
assert_eq!(
|
|
edge.source(),
|
|
i,
|
|
"outgoing edges should have a fixed source"
|
|
);
|
|
}
|
|
for edge in Reversed(&gr).edges_directed(i, Incoming) {
|
|
assert_eq!(
|
|
edge.target(),
|
|
i,
|
|
"reversed incoming edges should have a fixed target"
|
|
);
|
|
}
|
|
}
|
|
|
|
let mut reversed_gr = gr.clone();
|
|
reversed_gr.reverse();
|
|
|
|
println!("{:#?}", gr);
|
|
for i in gr.node_indices() {
|
|
// Compare against reversed graphs two different ways: using .reverse() and Reversed.
|
|
itertools::assert_equal(gr.edges_directed(i, Incoming), reversed_gr.edges(i));
|
|
|
|
// Reversed reverses edges, so target and source need to be reversed once more.
|
|
itertools::assert_equal(
|
|
gr.edges_directed(i, Incoming)
|
|
.map(|edge| (edge.source(), edge.target())),
|
|
Reversed(&gr)
|
|
.edges(i)
|
|
.map(|edge| (edge.target(), edge.source())),
|
|
);
|
|
|
|
for edge in gr.edges_directed(i, Incoming) {
|
|
assert_eq!(
|
|
edge.target(),
|
|
i,
|
|
"incoming edges should have a fixed target"
|
|
);
|
|
}
|
|
for edge in Reversed(&gr).edges_directed(i, Outgoing) {
|
|
assert_eq!(
|
|
edge.source(),
|
|
i,
|
|
"reversed outgoing edges should have a fixed source"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_edge_filtered_iterators_directed() {
|
|
use petgraph::{
|
|
graph::EdgeReference,
|
|
visit::{EdgeFiltered, IntoEdgesDirected},
|
|
};
|
|
|
|
let gr = make_edge_iterator_graph::<Directed>();
|
|
let filter = |edge: EdgeReference<f64>| -> bool { *edge.weight() > 8.0 };
|
|
let filtered = EdgeFiltered::from_fn(&gr, filter);
|
|
|
|
for i in gr.node_indices() {
|
|
itertools::assert_equal(
|
|
filtered.edges_directed(i, Outgoing),
|
|
gr.edges_directed(i, Outgoing).filter(|edge| filter(*edge)),
|
|
);
|
|
itertools::assert_equal(
|
|
filtered.edges_directed(i, Incoming),
|
|
gr.edges_directed(i, Incoming).filter(|edge| filter(*edge)),
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_node_filtered_iterators_directed() {
|
|
use petgraph::{
|
|
graph::NodeIndex,
|
|
visit::{IntoEdgesDirected, NodeFiltered},
|
|
};
|
|
|
|
let gr = make_edge_iterator_graph::<Directed>();
|
|
let filter = |node: NodeIndex<u32>| node.index() < 4;
|
|
let filtered = NodeFiltered::from_fn(&gr, filter);
|
|
|
|
for i in gr.node_indices() {
|
|
itertools::assert_equal(
|
|
filtered.edges_directed(i, Outgoing),
|
|
gr.edges_directed(i, Outgoing)
|
|
.filter(|edge| filter(edge.source()) && filter(edge.target())),
|
|
);
|
|
itertools::assert_equal(
|
|
filtered.edges_directed(i, Incoming),
|
|
gr.edges_directed(i, Incoming)
|
|
.filter(|edge| filter(edge.source()) && filter(edge.target())),
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_edge_iterators_undir() {
|
|
let gr = make_edge_iterator_graph::<Undirected>();
|
|
|
|
for i in gr.node_indices() {
|
|
itertools::assert_equal(gr.edges_directed(i, Outgoing), gr.edges(i));
|
|
|
|
// Reversed reverses edges, so target and source need to be reversed once more.
|
|
itertools::assert_equal(
|
|
gr.edges_directed(i, Outgoing)
|
|
.map(|edge| (edge.source(), edge.target())),
|
|
Reversed(&gr)
|
|
.edges_directed(i, Incoming)
|
|
.map(|edge| (edge.target(), edge.source())),
|
|
);
|
|
|
|
for edge in gr.edges_directed(i, Outgoing) {
|
|
assert_eq!(
|
|
edge.source(),
|
|
i,
|
|
"outgoing edges should have a fixed source"
|
|
);
|
|
}
|
|
for edge in Reversed(&gr).edges_directed(i, Incoming) {
|
|
assert_eq!(
|
|
edge.target(),
|
|
i,
|
|
"reversed incoming edges should have a fixed target"
|
|
);
|
|
}
|
|
}
|
|
|
|
for i in gr.node_indices() {
|
|
itertools::assert_equal(gr.edges_directed(i, Incoming), gr.edges(i));
|
|
|
|
// Reversed reverses edges, so target and source need to be reversed once more.
|
|
itertools::assert_equal(
|
|
gr.edges_directed(i, Incoming)
|
|
.map(|edge| (edge.source(), edge.target())),
|
|
Reversed(&gr)
|
|
.edges(i)
|
|
.map(|edge| (edge.target(), edge.source())),
|
|
);
|
|
|
|
for edge in gr.edges_directed(i, Incoming) {
|
|
assert_eq!(
|
|
edge.target(),
|
|
i,
|
|
"incoming edges should have a fixed target"
|
|
);
|
|
}
|
|
for edge in Reversed(&gr).edges_directed(i, Outgoing) {
|
|
assert_eq!(
|
|
edge.source(),
|
|
i,
|
|
"reversed outgoing edges should have a fixed source"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn toposort_generic() {
|
|
// This is a DAG, visit it in order
|
|
let mut gr = Graph::<_, _>::new();
|
|
let b = gr.add_node(("B", 0.));
|
|
let a = gr.add_node(("A", 0.));
|
|
let c = gr.add_node(("C", 0.));
|
|
let d = gr.add_node(("D", 0.));
|
|
let e = gr.add_node(("E", 0.));
|
|
let f = gr.add_node(("F", 0.));
|
|
let g = gr.add_node(("G", 0.));
|
|
gr.add_edge(a, b, 7.0);
|
|
gr.add_edge(a, d, 5.);
|
|
gr.add_edge(d, b, 9.);
|
|
gr.add_edge(b, c, 8.);
|
|
gr.add_edge(b, e, 7.);
|
|
gr.add_edge(c, e, 5.);
|
|
gr.add_edge(d, e, 15.);
|
|
gr.add_edge(d, f, 6.);
|
|
gr.add_edge(f, e, 8.);
|
|
gr.add_edge(f, g, 11.);
|
|
gr.add_edge(e, g, 9.);
|
|
|
|
assert!(!pg::algo::is_cyclic_directed(&gr));
|
|
let mut index = 0.;
|
|
let mut topo = Topo::new(&gr);
|
|
while let Some(nx) = topo.next(&gr) {
|
|
gr[nx].1 = index;
|
|
index += 1.;
|
|
}
|
|
|
|
let mut order = Vec::new();
|
|
index = 0.;
|
|
let mut topo = Topo::new(&gr);
|
|
while let Some(nx) = topo.next(&gr) {
|
|
order.push(nx);
|
|
assert_eq!(gr[nx].1, index);
|
|
index += 1.;
|
|
}
|
|
println!("{:?}", gr);
|
|
assert_is_topo_order(&gr, &order);
|
|
|
|
{
|
|
order.clear();
|
|
let mut topo = Topo::new(&gr);
|
|
while let Some(nx) = topo.next(&gr) {
|
|
order.push(nx);
|
|
}
|
|
println!("{:?}", gr);
|
|
assert_is_topo_order(&gr, &order);
|
|
}
|
|
let mut gr2 = gr.clone();
|
|
gr.add_edge(e, d, -1.);
|
|
assert!(pg::algo::is_cyclic_directed(&gr));
|
|
assert!(pg::algo::toposort(&gr, None).is_err());
|
|
gr2.add_edge(d, d, 0.);
|
|
assert!(pg::algo::is_cyclic_directed(&gr2));
|
|
assert!(pg::algo::toposort(&gr2, None).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_has_path() {
|
|
// This is a DAG, visit it in order
|
|
let mut gr = Graph::<_, _>::new();
|
|
let b = gr.add_node(("B", 0.));
|
|
let a = gr.add_node(("A", 0.));
|
|
let c = gr.add_node(("C", 0.));
|
|
let d = gr.add_node(("D", 0.));
|
|
let e = gr.add_node(("E", 0.));
|
|
let f = gr.add_node(("F", 0.));
|
|
let g = gr.add_node(("G", 0.));
|
|
gr.add_edge(a, b, 7.0);
|
|
gr.add_edge(a, d, 5.);
|
|
gr.add_edge(d, b, 9.);
|
|
gr.add_edge(b, c, 8.);
|
|
gr.add_edge(b, e, 7.);
|
|
gr.add_edge(c, e, 5.);
|
|
gr.add_edge(d, e, 15.);
|
|
gr.add_edge(d, f, 6.);
|
|
gr.add_edge(f, e, 8.);
|
|
gr.add_edge(f, g, 11.);
|
|
gr.add_edge(e, g, 9.);
|
|
// disconnected island
|
|
|
|
let h = gr.add_node(("H", 0.));
|
|
let i = gr.add_node(("I", 0.));
|
|
gr.add_edge(h, i, 2.);
|
|
gr.add_edge(i, h, -2.);
|
|
|
|
let mut state = DfsSpace::default();
|
|
|
|
gr.add_edge(b, a, 99.);
|
|
|
|
assert!(has_path_connecting(&gr, c, c, None));
|
|
|
|
for edge in gr.edge_references() {
|
|
assert!(has_path_connecting(&gr, edge.source(), edge.target(), None));
|
|
}
|
|
assert!(has_path_connecting(&gr, a, g, Some(&mut state)));
|
|
assert!(!has_path_connecting(&gr, a, h, Some(&mut state)));
|
|
assert!(has_path_connecting(&gr, a, c, None));
|
|
assert!(has_path_connecting(&gr, a, c, Some(&mut state)));
|
|
assert!(!has_path_connecting(&gr, h, a, Some(&mut state)));
|
|
}
|
|
|
|
#[test]
|
|
fn map_filter_map() {
|
|
let mut g = Graph::new_undirected();
|
|
let a = g.add_node("A");
|
|
let b = g.add_node("B");
|
|
let c = g.add_node("C");
|
|
let d = g.add_node("D");
|
|
let e = g.add_node("E");
|
|
let f = g.add_node("F");
|
|
g.add_edge(a, b, 7);
|
|
g.add_edge(c, a, 9);
|
|
g.add_edge(a, d, 14);
|
|
g.add_edge(b, c, 10);
|
|
g.add_edge(d, c, 2);
|
|
g.add_edge(d, e, 9);
|
|
g.add_edge(b, f, 15);
|
|
g.add_edge(c, f, 11);
|
|
g.add_edge(e, f, 6);
|
|
println!("{:?}", g);
|
|
|
|
let g2 = g.filter_map(
|
|
|_, name| Some(*name),
|
|
|_, &weight| if weight >= 10 { Some(weight) } else { None },
|
|
);
|
|
assert_eq!(g2.edge_count(), 4);
|
|
for edge in g2.raw_edges() {
|
|
assert!(edge.weight >= 10);
|
|
}
|
|
|
|
let g3 = g.filter_map(
|
|
|i, &name| if i == a || i == e { None } else { Some(name) },
|
|
|i, &weight| {
|
|
let (source, target) = g.edge_endpoints(i).unwrap();
|
|
// don't map edges from a removed node
|
|
assert!(source != a);
|
|
assert!(target != a);
|
|
assert!(source != e);
|
|
assert!(target != e);
|
|
Some(weight)
|
|
},
|
|
);
|
|
assert_eq!(g3.node_count(), g.node_count() - 2);
|
|
assert_eq!(g3.edge_count(), g.edge_count() - 5);
|
|
assert_graph_consistent(&g3);
|
|
|
|
let mut g4 = g.clone();
|
|
g4.retain_edges(|gr, i| {
|
|
let (s, t) = gr.edge_endpoints(i).unwrap();
|
|
!(s == a || s == e || t == a || t == e)
|
|
});
|
|
assert_eq!(g4.edge_count(), g.edge_count() - 5);
|
|
assert_graph_consistent(&g4);
|
|
}
|
|
|
|
#[test]
|
|
fn from_edges() {
|
|
let n = NodeIndex::new;
|
|
let gr =
|
|
Graph::<(), (), Undirected>::from_edges(&[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]);
|
|
assert_eq!(gr.node_count(), 4);
|
|
assert_eq!(gr.edge_count(), 6);
|
|
assert_eq!(gr.neighbors(n(0)).count(), 3);
|
|
assert_eq!(gr.neighbors(n(1)).count(), 3);
|
|
assert_eq!(gr.neighbors(n(2)).count(), 3);
|
|
assert_eq!(gr.neighbors(n(3)).count(), 3);
|
|
assert_graph_consistent(&gr);
|
|
}
|
|
|
|
#[test]
|
|
fn retain() {
|
|
let mut gr = Graph::<i32, i32, Undirected>::from_edges(&[
|
|
(0, 1, 2),
|
|
(1, 1, 1),
|
|
(0, 2, 0),
|
|
(1, 2, 3),
|
|
(2, 3, 3),
|
|
]);
|
|
gr.retain_edges(|mut gr, i| {
|
|
if gr[i] <= 0 {
|
|
false
|
|
} else {
|
|
gr[i] -= 1;
|
|
let (s, t) = gr.edge_endpoints(i).unwrap();
|
|
gr[s] += 1;
|
|
gr[t] += 1;
|
|
|
|
gr[i] > 0
|
|
}
|
|
});
|
|
|
|
assert_eq!(gr.edge_count(), 3);
|
|
assert_eq!(gr[n(0)], 1);
|
|
assert_eq!(gr[n(1)], 4);
|
|
assert_eq!(gr[n(2)], 2);
|
|
assert_eq!(gr[n(3)], 1);
|
|
assert!(gr.find_edge(n(1), n(1)).is_none());
|
|
assert!(gr.find_edge(n(0), n(2)).is_none());
|
|
assert_graph_consistent(&gr);
|
|
}
|
|
|
|
fn assert_graph_consistent<N, E, Ty, Ix>(g: &Graph<N, E, Ty, Ix>)
|
|
where
|
|
Ty: EdgeType,
|
|
Ix: IndexType,
|
|
{
|
|
assert_eq!(g.node_count(), g.node_indices().count());
|
|
assert_eq!(g.edge_count(), g.edge_indices().count());
|
|
for edge in g.raw_edges() {
|
|
assert!(
|
|
g.find_edge(edge.source(), edge.target()).is_some(),
|
|
"Edge not in graph! {:?} to {:?}",
|
|
edge.source(),
|
|
edge.target()
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn neighbors_selfloops() {
|
|
// Directed graph
|
|
let mut gr = Graph::<_, ()>::new();
|
|
let a = gr.add_node("a");
|
|
let b = gr.add_node("b");
|
|
let c = gr.add_node("c");
|
|
gr.extend_with_edges(&[(a, a), (a, b), (c, a), (a, a)]);
|
|
|
|
let out_edges = [a, a, b];
|
|
let in_edges = [a, a, c];
|
|
let undir_edges = [a, a, b, c];
|
|
let mut seen_out = gr.neighbors(a).collect::<Vec<_>>();
|
|
seen_out.sort();
|
|
assert_eq!(&seen_out, &out_edges);
|
|
let mut seen_in = gr.neighbors_directed(a, Incoming).collect::<Vec<_>>();
|
|
seen_in.sort();
|
|
assert_eq!(&seen_in, &in_edges);
|
|
|
|
let mut seen_undir = gr.neighbors_undirected(a).collect::<Vec<_>>();
|
|
seen_undir.sort();
|
|
assert_eq!(&seen_undir, &undir_edges);
|
|
|
|
let mut seen_out = gr.edges(a).map(|e| e.target()).collect::<Vec<_>>();
|
|
seen_out.sort();
|
|
assert_eq!(&seen_out, &out_edges);
|
|
|
|
let mut seen_walk = Vec::new();
|
|
let mut walk = gr.neighbors(a).detach();
|
|
while let Some(n) = walk.next_node(&gr) {
|
|
seen_walk.push(n);
|
|
}
|
|
seen_walk.sort();
|
|
assert_eq!(&seen_walk, &out_edges);
|
|
|
|
seen_walk.clear();
|
|
let mut walk = gr.neighbors_directed(a, Incoming).detach();
|
|
while let Some(n) = walk.next_node(&gr) {
|
|
seen_walk.push(n);
|
|
}
|
|
seen_walk.sort();
|
|
assert_eq!(&seen_walk, &in_edges);
|
|
|
|
seen_walk.clear();
|
|
let mut walk = gr.neighbors_undirected(a).detach();
|
|
while let Some(n) = walk.next_node(&gr) {
|
|
seen_walk.push(n);
|
|
}
|
|
seen_walk.sort();
|
|
assert_eq!(&seen_walk, &undir_edges);
|
|
|
|
// Undirected graph
|
|
let mut gr: Graph<_, (), _> = Graph::new_undirected();
|
|
let a = gr.add_node("a");
|
|
let b = gr.add_node("b");
|
|
let c = gr.add_node("c");
|
|
gr.extend_with_edges(&[(a, a), (a, b), (c, a)]);
|
|
|
|
let out_edges = [a, b, c];
|
|
let in_edges = [a, b, c];
|
|
let undir_edges = [a, b, c];
|
|
let mut seen_out = gr.neighbors(a).collect::<Vec<_>>();
|
|
seen_out.sort();
|
|
assert_eq!(&seen_out, &out_edges);
|
|
|
|
let mut seen_out = gr.edges(a).map(|e| e.target()).collect::<Vec<_>>();
|
|
seen_out.sort();
|
|
assert_eq!(&seen_out, &out_edges);
|
|
|
|
let mut seen_in = gr.neighbors_directed(a, Incoming).collect::<Vec<_>>();
|
|
seen_in.sort();
|
|
assert_eq!(&seen_in, &in_edges);
|
|
|
|
let mut seen_undir = gr.neighbors_undirected(a).collect::<Vec<_>>();
|
|
seen_undir.sort();
|
|
assert_eq!(&seen_undir, &undir_edges);
|
|
}
|
|
|
|
fn degree<'a, G>(g: G, node: G::NodeId) -> usize
|
|
where
|
|
G: IntoNeighbors,
|
|
G::NodeId: PartialEq,
|
|
{
|
|
// self loops count twice
|
|
let original_node = node.clone();
|
|
let mut degree = 0;
|
|
for v in g.neighbors(node) {
|
|
degree += if v == original_node { 2 } else { 1 };
|
|
}
|
|
degree
|
|
}
|
|
|
|
#[cfg(feature = "graphmap")]
|
|
#[test]
|
|
fn degree_sequence() {
|
|
let mut gr = Graph::<usize, (), Undirected>::from_edges(&[
|
|
(0, 1),
|
|
(1, 2),
|
|
(1, 3),
|
|
(2, 4),
|
|
(3, 4),
|
|
(4, 4),
|
|
(4, 5),
|
|
(3, 5),
|
|
]);
|
|
gr.add_node(0); // add isolated node
|
|
let mut degree_sequence = gr
|
|
.node_indices()
|
|
.map(|i| degree(&gr, i))
|
|
.collect::<Vec<_>>();
|
|
|
|
degree_sequence.sort_by(|x, y| Ord::cmp(y, x));
|
|
assert_eq!(°ree_sequence, &[5, 3, 3, 2, 2, 1, 0]);
|
|
|
|
let mut gr = GraphMap::<_, (), Undirected>::from_edges(&[
|
|
(0, 1),
|
|
(1, 2),
|
|
(1, 3),
|
|
(2, 4),
|
|
(3, 4),
|
|
(4, 4),
|
|
(4, 5),
|
|
(3, 5),
|
|
]);
|
|
gr.add_node(6); // add isolated node
|
|
let mut degree_sequence = gr.nodes().map(|i| degree(&gr, i)).collect::<Vec<_>>();
|
|
|
|
degree_sequence.sort_by(|x, y| Ord::cmp(y, x));
|
|
assert_eq!(°ree_sequence, &[5, 3, 3, 2, 2, 1, 0]);
|
|
}
|
|
|
|
#[test]
|
|
fn neighbor_order() {
|
|
let mut gr = Graph::new();
|
|
let a = gr.add_node("a");
|
|
let b = gr.add_node("b");
|
|
let c = gr.add_node("c");
|
|
gr.add_edge(a, b, 0);
|
|
gr.add_edge(a, a, 1);
|
|
|
|
gr.add_edge(c, a, 2);
|
|
|
|
gr.add_edge(a, c, 3);
|
|
|
|
gr.add_edge(c, a, 4);
|
|
gr.add_edge(b, a, 5);
|
|
|
|
// neighbors (edges) are in lifo order, if it's a directed graph
|
|
assert_eq!(gr.neighbors(a).collect::<Vec<_>>(), vec![c, a, b]);
|
|
assert_eq!(
|
|
gr.neighbors_directed(a, Incoming).collect::<Vec<_>>(),
|
|
vec![b, c, c, a]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn dot() {
|
|
// test alternate formatting
|
|
#[derive(Debug)]
|
|
struct Record {
|
|
a: i32,
|
|
b: &'static str,
|
|
}
|
|
let mut gr = Graph::new();
|
|
let a = gr.add_node(Record { a: 1, b: r"abc\" });
|
|
gr.add_edge(a, a, (1, 2));
|
|
let dot_output = format!("{:?}", Dot::new(&gr));
|
|
assert_eq!(
|
|
dot_output,
|
|
// The single \ turns into four \\\\ because of Debug which turns it to \\ and then escaping each \ to \\.
|
|
r#"digraph {
|
|
0 [ label = "Record { a: 1, b: \"abc\\\\\" }" ]
|
|
0 -> 0 [ label = "(1, 2)" ]
|
|
}
|
|
"#
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn filtered() {
|
|
let mut g = Graph::new();
|
|
let a = g.add_node("A");
|
|
let b = g.add_node("B");
|
|
let c = g.add_node("C");
|
|
let d = g.add_node("D");
|
|
let e = g.add_node("E");
|
|
let f = g.add_node("F");
|
|
g.add_edge(a, b, 7);
|
|
g.add_edge(c, a, 9);
|
|
g.add_edge(a, d, 14);
|
|
g.add_edge(b, c, 10);
|
|
g.add_edge(d, c, 2);
|
|
g.add_edge(d, e, 9);
|
|
g.add_edge(b, f, 15);
|
|
g.add_edge(c, f, 11);
|
|
g.add_edge(e, f, 6);
|
|
println!("{:?}", g);
|
|
|
|
let filt = NodeFiltered(&g, |n: NodeIndex| n != c && n != e);
|
|
|
|
let mut dfs = DfsPostOrder::new(&filt, a);
|
|
let mut po = Vec::new();
|
|
while let Some(nx) = dfs.next(&filt) {
|
|
println!("Next: {:?}", nx);
|
|
po.push(nx);
|
|
}
|
|
assert_eq!(set(po), set(g.node_identifiers().filter(|n| (filt.1)(*n))));
|
|
}
|
|
|
|
#[test]
|
|
fn filtered_edge_reverse() {
|
|
use petgraph::visit::EdgeFiltered;
|
|
#[derive(Eq, PartialEq)]
|
|
enum E {
|
|
A,
|
|
B,
|
|
}
|
|
|
|
// Start with single node graph with loop
|
|
let mut g = Graph::new();
|
|
let a = g.add_node("A");
|
|
g.add_edge(a, a, E::A);
|
|
let ef_a = EdgeFiltered::from_fn(&g, |edge| *edge.weight() == E::A);
|
|
let mut po = Vec::new();
|
|
let mut dfs = Dfs::new(&ef_a, a);
|
|
while let Some(next_n_ix) = dfs.next(&ef_a) {
|
|
po.push(next_n_ix);
|
|
}
|
|
assert_eq!(set(po), set(vec![a]));
|
|
|
|
// Check in reverse
|
|
let mut po = Vec::new();
|
|
let mut dfs = Dfs::new(&Reversed(&ef_a), a);
|
|
while let Some(next_n_ix) = dfs.next(&Reversed(&ef_a)) {
|
|
po.push(next_n_ix);
|
|
}
|
|
assert_eq!(set(po), set(vec![a]));
|
|
|
|
let mut g = Graph::new();
|
|
let a = g.add_node("A");
|
|
let b = g.add_node("B");
|
|
let c = g.add_node("C");
|
|
let d = g.add_node("D");
|
|
let e = g.add_node("E");
|
|
let f = g.add_node("F");
|
|
let h = g.add_node("H");
|
|
let i = g.add_node("I");
|
|
let j = g.add_node("J");
|
|
|
|
g.add_edge(a, b, E::A);
|
|
g.add_edge(b, c, E::A);
|
|
g.add_edge(c, d, E::B);
|
|
g.add_edge(d, e, E::A);
|
|
g.add_edge(e, f, E::A);
|
|
g.add_edge(e, h, E::A);
|
|
g.add_edge(e, i, E::A);
|
|
g.add_edge(i, j, E::A);
|
|
|
|
let ef_a = EdgeFiltered::from_fn(&g, |edge| *edge.weight() == E::A);
|
|
let ef_b = EdgeFiltered::from_fn(&g, |edge| *edge.weight() == E::B);
|
|
|
|
// DFS down from a, filtered by E::A.
|
|
let mut po = Vec::new();
|
|
let mut dfs = Dfs::new(&ef_a, a);
|
|
while let Some(next_n_ix) = dfs.next(&ef_a) {
|
|
po.push(next_n_ix);
|
|
}
|
|
assert_eq!(set(po), set(vec![a, b, c]));
|
|
|
|
// Reversed DFS from f, filtered by E::A.
|
|
let mut dfs = Dfs::new(&Reversed(&ef_a), f);
|
|
let mut po = Vec::new();
|
|
while let Some(next_n_ix) = dfs.next(&Reversed(&ef_a)) {
|
|
po.push(next_n_ix);
|
|
}
|
|
assert_eq!(set(po), set(vec![d, e, f]));
|
|
|
|
// Reversed DFS from j, filtered by E::A.
|
|
let mut dfs = Dfs::new(&Reversed(&ef_a), j);
|
|
let mut po = Vec::new();
|
|
while let Some(next_n_ix) = dfs.next(&Reversed(&ef_a)) {
|
|
po.push(next_n_ix);
|
|
}
|
|
assert_eq!(set(po), set(vec![d, e, i, j]));
|
|
|
|
// Reversed DFS from c, filtered by E::A.
|
|
let mut dfs = Dfs::new(&Reversed(&ef_a), c);
|
|
let mut po = Vec::new();
|
|
while let Some(next_n_ix) = dfs.next(&Reversed(&ef_a)) {
|
|
po.push(next_n_ix);
|
|
}
|
|
assert_eq!(set(po), set(vec![a, b, c]));
|
|
|
|
// Reversed DFS from c, filtered by E::B.
|
|
let mut dfs = Dfs::new(&Reversed(&ef_b), c);
|
|
let mut po = Vec::new();
|
|
while let Some(next_n_ix) = dfs.next(&Reversed(&ef_b)) {
|
|
po.push(next_n_ix);
|
|
}
|
|
assert_eq!(set(po), set(vec![c]));
|
|
|
|
// Reversed DFS from d, filtered by E::B.
|
|
let mut dfs = Dfs::new(&Reversed(&ef_b), d);
|
|
let mut po = Vec::new();
|
|
while let Some(next_n_ix) = dfs.next(&Reversed(&ef_b)) {
|
|
po.push(next_n_ix);
|
|
}
|
|
assert_eq!(set(po), set(vec![c, d]));
|
|
|
|
// Now let's test the same graph but undirected
|
|
|
|
let mut g = Graph::new_undirected();
|
|
let a = g.add_node("A");
|
|
let b = g.add_node("B");
|
|
let c = g.add_node("C");
|
|
let d = g.add_node("D");
|
|
let e = g.add_node("E");
|
|
let f = g.add_node("F");
|
|
let h = g.add_node("H");
|
|
let i = g.add_node("I");
|
|
let j = g.add_node("J");
|
|
|
|
g.add_edge(a, b, E::A);
|
|
g.add_edge(b, c, E::A);
|
|
g.add_edge(c, d, E::B);
|
|
g.add_edge(d, e, E::A);
|
|
g.add_edge(e, f, E::A);
|
|
g.add_edge(e, h, E::A);
|
|
g.add_edge(e, i, E::A);
|
|
g.add_edge(i, j, E::A);
|
|
|
|
let ef_a = EdgeFiltered::from_fn(&g, |edge| *edge.weight() == E::A);
|
|
let ef_b = EdgeFiltered::from_fn(&g, |edge| *edge.weight() == E::B);
|
|
let mut po = Vec::new();
|
|
let mut dfs = Dfs::new(&Reversed(&ef_b), d);
|
|
while let Some(next_n_ix) = dfs.next(&Reversed(&ef_b)) {
|
|
po.push(next_n_ix);
|
|
}
|
|
assert_eq!(set(po), set(vec![c, d]));
|
|
|
|
let mut po = Vec::new();
|
|
let mut dfs = Dfs::new(&Reversed(&ef_a), h);
|
|
while let Some(next_n_ix) = dfs.next(&Reversed(&ef_a)) {
|
|
po.push(next_n_ix);
|
|
}
|
|
assert_eq!(set(po), set(vec![d, e, f, h, i, j]));
|
|
}
|
|
|
|
#[test]
|
|
fn dfs_visit() {
|
|
use petgraph::visit::Control;
|
|
use petgraph::visit::DfsEvent::*;
|
|
use petgraph::visit::{depth_first_search, Time};
|
|
use petgraph::visit::{VisitMap, Visitable};
|
|
let gr: Graph<(), ()> = Graph::from_edges(&[
|
|
(0, 5),
|
|
(0, 2),
|
|
(0, 3),
|
|
(0, 1),
|
|
(1, 3),
|
|
(2, 3),
|
|
(2, 4),
|
|
(4, 0),
|
|
(4, 5),
|
|
]);
|
|
|
|
let invalid_time = Time(!0);
|
|
let mut discover_time = vec![invalid_time; gr.node_count()];
|
|
let mut finish_time = vec![invalid_time; gr.node_count()];
|
|
let mut has_tree_edge = gr.visit_map();
|
|
let mut edges = HashSet::new();
|
|
depth_first_search(&gr, Some(n(0)), |evt| {
|
|
println!("Event: {:?}", evt);
|
|
match evt {
|
|
Discover(n, t) => discover_time[n.index()] = t,
|
|
Finish(n, t) => finish_time[n.index()] = t,
|
|
TreeEdge(u, v) => {
|
|
// v is an ancestor of u
|
|
assert!(has_tree_edge.visit(v), "Two tree edges to {:?}!", v);
|
|
assert!(discover_time[v.index()] == invalid_time);
|
|
assert!(discover_time[u.index()] != invalid_time);
|
|
assert!(finish_time[u.index()] == invalid_time);
|
|
edges.insert((u, v));
|
|
}
|
|
BackEdge(u, v) => {
|
|
// u is an ancestor of v
|
|
assert!(discover_time[v.index()] != invalid_time);
|
|
assert!(finish_time[v.index()] == invalid_time);
|
|
edges.insert((u, v));
|
|
}
|
|
CrossForwardEdge(u, v) => {
|
|
edges.insert((u, v));
|
|
}
|
|
}
|
|
});
|
|
assert!(discover_time.iter().all(|x| *x != invalid_time));
|
|
assert!(finish_time.iter().all(|x| *x != invalid_time));
|
|
assert_eq!(edges.len(), gr.edge_count());
|
|
assert_eq!(
|
|
edges,
|
|
set(gr.edge_references().map(|e| (e.source(), e.target())))
|
|
);
|
|
println!("{:?}", discover_time);
|
|
println!("{:?}", finish_time);
|
|
|
|
// find path from 0 to 4
|
|
let mut predecessor = vec![NodeIndex::end(); gr.node_count()];
|
|
let start = n(0);
|
|
let goal = n(4);
|
|
let ret = depth_first_search(&gr, Some(start), |event| {
|
|
if let TreeEdge(u, v) = event {
|
|
predecessor[v.index()] = u;
|
|
if v == goal {
|
|
return Control::Break(u);
|
|
}
|
|
}
|
|
Control::Continue
|
|
});
|
|
// assert we did terminate early
|
|
assert!(ret.break_value().is_some());
|
|
assert!(predecessor.iter().any(|x| *x == NodeIndex::end()));
|
|
|
|
let mut next = goal;
|
|
let mut path = vec![next];
|
|
while next != start {
|
|
let pred = predecessor[next.index()];
|
|
path.push(pred);
|
|
next = pred;
|
|
}
|
|
path.reverse();
|
|
assert_eq!(&path, &[n(0), n(2), n(4)]);
|
|
|
|
// check that if we prune 2, we never see 4.
|
|
let start = n(0);
|
|
let prune = n(2);
|
|
let nongoal = n(4);
|
|
let ret = depth_first_search(&gr, Some(start), |event| {
|
|
if let Discover(n, _) = event {
|
|
if n == prune {
|
|
return Control::Prune;
|
|
}
|
|
} else if let TreeEdge(u, v) = event {
|
|
if v == nongoal {
|
|
return Control::Break(u);
|
|
}
|
|
}
|
|
Control::Continue
|
|
});
|
|
assert!(ret.break_value().is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn filtered_post_order() {
|
|
use petgraph::visit::NodeFiltered;
|
|
|
|
let mut gr: Graph<(), ()> =
|
|
Graph::from_edges(&[(0, 2), (1, 2), (0, 3), (1, 4), (2, 4), (4, 5), (3, 5)]);
|
|
// map reachable nodes
|
|
let mut dfs = Dfs::new(&gr, n(0));
|
|
while let Some(_) = dfs.next(&gr) {}
|
|
|
|
let map = dfs.discovered;
|
|
gr.add_edge(n(0), n(1), ());
|
|
let mut po = Vec::new();
|
|
let mut dfs = DfsPostOrder::new(&gr, n(0));
|
|
let f = NodeFiltered(&gr, map);
|
|
while let Some(n) = dfs.next(&f) {
|
|
po.push(n);
|
|
}
|
|
assert!(!po.contains(&n(1)));
|
|
}
|
|
|
|
#[test]
|
|
fn filter_elements() {
|
|
use petgraph::data::Element::{Edge, Node};
|
|
use petgraph::data::ElementIterator;
|
|
use petgraph::data::FromElements;
|
|
let elements = vec![
|
|
Node { weight: "A" },
|
|
Node { weight: "B" },
|
|
Node { weight: "C" },
|
|
Node { weight: "D" },
|
|
Node { weight: "E" },
|
|
Node { weight: "F" },
|
|
Edge {
|
|
source: 0,
|
|
target: 1,
|
|
weight: 7,
|
|
},
|
|
Edge {
|
|
source: 2,
|
|
target: 0,
|
|
weight: 9,
|
|
},
|
|
Edge {
|
|
source: 0,
|
|
target: 3,
|
|
weight: 14,
|
|
},
|
|
Edge {
|
|
source: 1,
|
|
target: 2,
|
|
weight: 10,
|
|
},
|
|
Edge {
|
|
source: 3,
|
|
target: 2,
|
|
weight: 2,
|
|
},
|
|
Edge {
|
|
source: 3,
|
|
target: 4,
|
|
weight: 9,
|
|
},
|
|
Edge {
|
|
source: 1,
|
|
target: 5,
|
|
weight: 15,
|
|
},
|
|
Edge {
|
|
source: 2,
|
|
target: 5,
|
|
weight: 11,
|
|
},
|
|
Edge {
|
|
source: 4,
|
|
target: 5,
|
|
weight: 6,
|
|
},
|
|
];
|
|
let mut g = DiGraph::<_, _>::from_elements(elements.iter().cloned());
|
|
println!("{:#?}", g);
|
|
assert!(g.contains_edge(n(1), n(5)));
|
|
let g2 =
|
|
DiGraph::<_, _>::from_elements(elements.iter().cloned().filter_elements(|elt| match elt {
|
|
Node { ref weight } if **weight == "B" => false,
|
|
_ => true,
|
|
}));
|
|
println!("{:#?}", g2);
|
|
g.remove_node(n(1));
|
|
assert!(is_isomorphic_matching(
|
|
&g,
|
|
&g2,
|
|
PartialEq::eq,
|
|
PartialEq::eq
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn test_edge_filtered() {
|
|
use petgraph::algo::connected_components;
|
|
use petgraph::visit::EdgeFiltered;
|
|
use petgraph::visit::IntoEdgeReferences;
|
|
|
|
let gr = UnGraph::<(), _>::from_edges(&[
|
|
// cycle
|
|
(0, 1, 7),
|
|
(1, 2, 9),
|
|
(2, 1, 14),
|
|
// cycle
|
|
(3, 4, 10),
|
|
(4, 5, 2),
|
|
(5, 3, 9),
|
|
// cross edges
|
|
(0, 3, -1),
|
|
(1, 4, -2),
|
|
(2, 5, -3),
|
|
]);
|
|
assert_eq!(connected_components(&gr), 1);
|
|
let positive_edges = EdgeFiltered::from_fn(&gr, |edge| *edge.weight() >= 0);
|
|
assert_eq!(positive_edges.edge_references().count(), 6);
|
|
assert!(positive_edges
|
|
.edge_references()
|
|
.all(|edge| *edge.weight() >= 0));
|
|
assert_eq!(connected_components(&positive_edges), 2);
|
|
|
|
let mut dfs = DfsPostOrder::new(&positive_edges, n(0));
|
|
while let Some(_) = dfs.next(&positive_edges) {}
|
|
|
|
let n = n::<u32>;
|
|
for node in &[n(0), n(1), n(2)] {
|
|
assert!(dfs.discovered.is_visited(node));
|
|
}
|
|
for node in &[n(3), n(4), n(5)] {
|
|
assert!(!dfs.discovered.is_visited(node));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_dominators_simple_fast() {
|
|
// Construct the following graph:
|
|
//
|
|
// .-----.
|
|
// | <--------------------------------.
|
|
// .--------+--------------| r |--------------. |
|
|
// | | | | | |
|
|
// | | '-----' | |
|
|
// | .--V--. .--V--. |
|
|
// | | | | | |
|
|
// | | b | | c |--------. |
|
|
// | | | | | | |
|
|
// | '-----' '-----' | |
|
|
// .--V--. | | .--V--. |
|
|
// | | | | | | |
|
|
// | a <-----+ | .----| g | |
|
|
// | | | .----' | | | |
|
|
// '-----' | | | '-----' |
|
|
// | | | | | |
|
|
// .--V--. | .-----. .--V--. | | |
|
|
// | | | | | | | | | |
|
|
// | d <-----+----> e <----. | f | | | |
|
|
// | | | | | | | | | |
|
|
// '-----' '-----' | '-----' | | |
|
|
// | .-----. | | | | .--V--. |
|
|
// | | | | | | .-' | | |
|
|
// '-----> l | | | | | | j | |
|
|
// | | '--. | | | | | |
|
|
// '-----' | | | | '-----' |
|
|
// | .--V--. | | .--V--. | |
|
|
// | | | | | | | | |
|
|
// '-------> h |-' '---> i <------' |
|
|
// | | .---------> | |
|
|
// '-----' | '-----' |
|
|
// | .-----. | |
|
|
// | | | | |
|
|
// '----------> k <---------' |
|
|
// | | |
|
|
// '-----' |
|
|
// | |
|
|
// '----------------------------'
|
|
//
|
|
// This graph has the following dominator tree:
|
|
//
|
|
// r
|
|
// |-- a
|
|
// |-- b
|
|
// |-- c
|
|
// | |-- f
|
|
// | `-- g
|
|
// | `-- j
|
|
// |-- d
|
|
// | `-- l
|
|
// |-- e
|
|
// |-- i
|
|
// |-- k
|
|
// `-- h
|
|
//
|
|
// This graph and dominator tree are taken from figures 1 and 2 of "A Fast
|
|
// Algorithm for Finding Dominators in a Flowgraph" by Lengauer et al:
|
|
// http://www.cs.princeton.edu/courses/archive/spr03/cs423/download/dominators.pdf.
|
|
|
|
let mut graph = DiGraph::<_, _>::new();
|
|
|
|
let r = graph.add_node("r");
|
|
let a = graph.add_node("a");
|
|
let b = graph.add_node("b");
|
|
let c = graph.add_node("c");
|
|
let d = graph.add_node("d");
|
|
let e = graph.add_node("e");
|
|
let f = graph.add_node("f");
|
|
let g = graph.add_node("g");
|
|
let h = graph.add_node("h");
|
|
let i = graph.add_node("i");
|
|
let j = graph.add_node("j");
|
|
let k = graph.add_node("k");
|
|
let l = graph.add_node("l");
|
|
|
|
graph.add_edge(r, a, ());
|
|
graph.add_edge(r, b, ());
|
|
graph.add_edge(r, c, ());
|
|
graph.add_edge(a, d, ());
|
|
graph.add_edge(b, a, ());
|
|
graph.add_edge(b, d, ());
|
|
graph.add_edge(b, e, ());
|
|
graph.add_edge(c, f, ());
|
|
graph.add_edge(c, g, ());
|
|
graph.add_edge(d, l, ());
|
|
graph.add_edge(e, h, ());
|
|
graph.add_edge(f, i, ());
|
|
graph.add_edge(g, i, ());
|
|
graph.add_edge(g, j, ());
|
|
graph.add_edge(h, e, ());
|
|
graph.add_edge(h, k, ());
|
|
graph.add_edge(i, k, ());
|
|
graph.add_edge(j, i, ());
|
|
graph.add_edge(k, r, ());
|
|
graph.add_edge(k, i, ());
|
|
graph.add_edge(l, h, ());
|
|
|
|
let doms = dominators::simple_fast(&graph, r);
|
|
|
|
assert_eq!(doms.root(), r);
|
|
assert_eq!(
|
|
doms.immediate_dominator(r),
|
|
None,
|
|
"The root node has no idom"
|
|
);
|
|
|
|
assert_eq!(
|
|
doms.immediate_dominator(a),
|
|
Some(r),
|
|
"r is the immediate dominator of a"
|
|
);
|
|
assert_eq!(
|
|
doms.immediate_dominator(b),
|
|
Some(r),
|
|
"r is the immediate dominator of b"
|
|
);
|
|
assert_eq!(
|
|
doms.immediate_dominator(c),
|
|
Some(r),
|
|
"r is the immediate dominator of c"
|
|
);
|
|
assert_eq!(
|
|
doms.immediate_dominator(f),
|
|
Some(c),
|
|
"c is the immediate dominator of f"
|
|
);
|
|
assert_eq!(
|
|
doms.immediate_dominator(g),
|
|
Some(c),
|
|
"c is the immediate dominator of g"
|
|
);
|
|
assert_eq!(
|
|
doms.immediate_dominator(j),
|
|
Some(g),
|
|
"g is the immediate dominator of j"
|
|
);
|
|
assert_eq!(
|
|
doms.immediate_dominator(d),
|
|
Some(r),
|
|
"r is the immediate dominator of d"
|
|
);
|
|
assert_eq!(
|
|
doms.immediate_dominator(l),
|
|
Some(d),
|
|
"d is the immediate dominator of l"
|
|
);
|
|
assert_eq!(
|
|
doms.immediate_dominator(e),
|
|
Some(r),
|
|
"r is the immediate dominator of e"
|
|
);
|
|
assert_eq!(
|
|
doms.immediate_dominator(i),
|
|
Some(r),
|
|
"r is the immediate dominator of i"
|
|
);
|
|
assert_eq!(
|
|
doms.immediate_dominator(k),
|
|
Some(r),
|
|
"r is the immediate dominator of k"
|
|
);
|
|
assert_eq!(
|
|
doms.immediate_dominator(h),
|
|
Some(r),
|
|
"r is the immediate dominator of h"
|
|
);
|
|
|
|
let mut graph = graph.clone();
|
|
let z = graph.add_node("z");
|
|
let doms = dominators::simple_fast(&graph, r);
|
|
assert_eq!(
|
|
doms.immediate_dominator(z),
|
|
None,
|
|
"nodes that aren't reachable from the root do not have an idom"
|
|
);
|
|
}
|