blob: 9563606d158aecb591d2489a894eee01ca9e4f6c [file] [edit]
//! Graphs that are used for testing:
//!
//! * G1: a ⤸
//! * G2: a → b
//! * G3: a ⇆ b → c
//! * G4: a ⇉ b
mod common;
use common::assert_graph_consistency;
use petgraph_core::{
edge::{Directed, Direction},
id::{DefaultIx, IndexType},
visit::EdgeRef,
};
use petgraph_graph::{EdgeIndex, Graph, NodeIndex};
use crate::common::{graphs::FromDefault, walk_collect};
type GraphSelfLoop<N, E> = common::graphs::GraphSelfLoop<Graph<N, E>>;
// IntelliJ: false-positive
impl GraphSelfLoop<(), ()> {
fn new() -> Self {
Self::from_default()
}
}
type GraphLink<N, E> = common::graphs::GraphLink<Graph<N, E>>;
// IntelliJ: false-positive
impl GraphLink<(), ()> {
fn new() -> Self {
Self::from_default()
}
}
type GraphDoubleLink<N, E> = common::graphs::GraphDoubleLink<Graph<N, E>>;
// IntelliJ: false-positive
impl GraphDoubleLink<(), ()> {
fn new() -> Self {
Self::from_default()
}
}
type GraphDoubleSameDirection<N, E> = common::graphs::GraphDoubleSameDirection<Graph<N, E>>;
// IntelliJ: false-positive
impl GraphDoubleSameDirection<(), ()> {
fn new() -> Self {
Self::from_default()
}
}
// Graph: a ⤸
#[test]
fn self_loop() {
let GraphSelfLoop { mut graph, a, aa } = GraphSelfLoop::new();
assert_eq!(graph.node_count(), 1);
assert_eq!(graph.edge_count(), 1);
assert_eq!(graph.find_edge(a, a), Some(aa));
graph.remove_edge(aa);
assert_eq!(graph.find_edge(a, a), None);
}
#[test]
fn find_directed() {
let GraphLink { graph, a, b, ab } = GraphLink::new();
assert_eq!(graph.find_edge(a, b), Some(ab));
assert_eq!(graph.find_edge(b, a), None);
assert_graph_consistency(&graph);
}
#[test]
fn find_undirected() {
let GraphLink { graph, a, b, ab } = GraphLink::new();
assert_eq!(
graph.find_edge_undirected(a, b),
Some((ab, Direction::Outgoing))
);
assert_eq!(
graph.find_edge_undirected(b, a),
Some((ab, Direction::Incoming))
);
assert_graph_consistency(&graph);
}
#[test]
fn find_undirected_self_loop() {
let GraphSelfLoop { graph, a, aa } = GraphSelfLoop::new();
assert_eq!(
graph.find_edge_undirected(a, a),
Some((aa, Direction::Outgoing))
);
assert_graph_consistency(&graph);
}
#[test]
fn find_directed_after_removal() {
let GraphLink {
mut graph,
a,
b,
ab,
} = GraphLink::new();
graph.remove_edge(ab);
assert_eq!(graph.find_edge(a, b), None);
assert_eq!(graph.find_edge(b, a), None);
assert_graph_consistency(&graph);
}
#[test]
fn find_undirected_after_removal() {
let GraphLink {
mut graph,
a,
b,
ab,
} = GraphLink::new();
graph.remove_edge(ab);
assert_eq!(graph.find_edge_undirected(a, b), None);
assert_eq!(graph.find_edge_undirected(b, a), None);
assert_graph_consistency(&graph);
}
#[test]
fn neighbours() {
let GraphDoubleLink { graph, a, b, c, .. } = GraphDoubleLink::new();
assert_eq!(graph.neighbors(a).collect::<Vec<_>>(), vec![b]);
assert_eq!(graph.neighbors(b).collect::<Vec<_>>(), vec![c, a]);
assert_eq!(graph.neighbors(c).collect::<Vec<_>>(), vec![]);
assert_graph_consistency(&graph);
}
#[test]
fn neighbours_detach() {
let GraphDoubleLink { graph, a, b, c, .. } = GraphDoubleLink::new();
let walk = graph.neighbors(a).detach();
assert_eq!(walk_collect(walk, &graph), vec![b]);
let walk = graph.neighbors(b).detach();
assert_eq!(walk_collect(walk, &graph), vec![c, a]);
let walk = graph.neighbors(c).detach();
assert_eq!(walk_collect(walk, &graph), vec![]);
}
#[test]
fn neighbours_directed() {
let GraphDoubleLink { graph, a, b, c, .. } = GraphDoubleLink::new();
assert_eq!(
graph
.neighbors_directed(a, Direction::Outgoing)
.collect::<Vec<_>>(),
vec![b]
);
assert_eq!(
graph
.neighbors_directed(b, Direction::Outgoing)
.collect::<Vec<_>>(),
vec![c, a]
);
assert_eq!(
graph
.neighbors_directed(c, Direction::Outgoing)
.collect::<Vec<_>>(),
vec![]
);
assert_eq!(
graph
.neighbors_directed(a, Direction::Incoming)
.collect::<Vec<_>>(),
vec![b]
);
assert_eq!(
graph
.neighbors_directed(b, Direction::Incoming)
.collect::<Vec<_>>(),
vec![a]
);
assert_eq!(
graph
.neighbors_directed(c, Direction::Incoming)
.collect::<Vec<_>>(),
vec![b]
);
}
#[test]
fn neighbours_directed_detach() {
let GraphDoubleLink { graph, a, b, c, .. } = GraphDoubleLink::new();
let walk = graph.neighbors_directed(a, Direction::Outgoing).detach();
assert_eq!(walk_collect(walk, &graph), vec![b]);
let walk = graph.neighbors_directed(b, Direction::Outgoing).detach();
assert_eq!(walk_collect(walk, &graph), vec![c, a]);
let walk = graph.neighbors_directed(c, Direction::Outgoing).detach();
assert_eq!(walk_collect(walk, &graph), vec![]);
let walk = graph.neighbors_directed(a, Direction::Incoming).detach();
assert_eq!(walk_collect(walk, &graph), vec![b]);
let walk = graph.neighbors_directed(b, Direction::Incoming).detach();
assert_eq!(walk_collect(walk, &graph), vec![a]);
let walk = graph.neighbors_directed(c, Direction::Incoming).detach();
assert_eq!(walk_collect(walk, &graph), vec![b]);
}
#[test]
fn neighbours_order() {
let GraphLink { graph, a, b, .. } = GraphLink::new();
// neighbours are LIFO
assert_eq!(graph.neighbors(a).collect::<Vec<_>>(), vec![b]);
assert_eq!(
graph
.neighbors_directed(a, Direction::Outgoing)
.collect::<Vec<_>>(),
vec![b]
);
assert_eq!(
graph
.neighbors_directed(a, Direction::Incoming)
.collect::<Vec<_>>(),
vec![]
);
}
#[test]
fn neighbours_after_removal() {
let GraphDoubleLink {
mut graph, a, b, c, ..
} = GraphDoubleLink::new();
graph.remove_node(c);
assert_eq!(graph.neighbors(a).collect::<Vec<_>>(), vec![b]);
assert_eq!(graph.neighbors(b).collect::<Vec<_>>(), vec![a]);
assert_graph_consistency(&graph);
}
#[test]
fn neighbours_self_loop() {
let GraphSelfLoop { graph, a, .. } = GraphSelfLoop::new();
assert_eq!(graph.neighbors(a).collect::<Vec<_>>(), vec![a]);
assert_eq!(
graph
.neighbors_directed(a, Direction::Outgoing)
.collect::<Vec<_>>(),
vec![a]
);
assert_eq!(
graph
.neighbors_directed(a, Direction::Incoming)
.collect::<Vec<_>>(),
vec![a]
);
}
#[test]
fn multiple() {
let GraphDoubleSameDirection {
graph, a, b, ab2, ..
} = GraphDoubleSameDirection::new();
assert_eq!(graph.edge_count(), 2);
assert_eq!(graph.edges(a).count(), 2);
assert_eq!(graph.edges(b).count(), 0);
assert_eq!(graph.neighbors(a).count(), 2);
assert_eq!(graph.neighbors(b).count(), 0);
assert_eq!(graph.find_edge(a, b), Some(ab2));
}
#[test]
fn edges_directed() {
let GraphDoubleSameDirection {
graph,
a,
b,
ab1,
ab2,
} = GraphDoubleSameDirection::new();
assert_eq!(
graph
.edges_directed(a, Direction::Outgoing)
.map(|edge| edge.id())
.collect::<Vec<_>>(),
vec![ab2, ab1]
);
assert_eq!(
graph
.edges_directed(b, Direction::Outgoing)
.map(|edge| edge.id())
.collect::<Vec<_>>(),
vec![]
);
assert_eq!(
graph
.edges_directed(a, Direction::Incoming)
.map(|edge| edge.id())
.collect::<Vec<_>>(),
vec![]
);
assert_eq!(
graph
.edges_directed(b, Direction::Incoming)
.map(|edge| edge.id())
.collect::<Vec<_>>(),
vec![ab2, ab1]
);
}
#[test]
fn reverse() {
let GraphDoubleLink {
mut graph,
a,
b,
c,
ab,
ba,
bc,
} = GraphDoubleLink::new();
// if we would be in a unit test we would be able to use `raw_nodes` here, which would make this
// test a whole lot more concise.
assert_graph_consistency(&graph);
graph.reverse();
assert_graph_consistency(&graph);
assert_eq!(
graph
.edges_directed(a, Direction::Outgoing)
.map(|edge| edge.id())
.collect::<Vec<_>>(),
vec![ba]
);
assert_eq!(
graph
.edges_directed(a, Direction::Incoming)
.map(|edge| edge.id())
.collect::<Vec<_>>(),
vec![ab]
);
assert_eq!(
graph
.edges_directed(b, Direction::Outgoing)
.map(|edge| edge.id())
.collect::<Vec<_>>(),
vec![ab]
);
assert_eq!(
graph
.edges_directed(b, Direction::Incoming)
.map(|edge| edge.id())
.collect::<Vec<_>>(),
vec![bc, ba]
);
assert_eq!(
graph
.edges_directed(c, Direction::Outgoing)
.map(|edge| edge.id())
.collect::<Vec<_>>(),
vec![bc]
);
assert_eq!(
graph
.edges_directed(c, Direction::Incoming)
.map(|edge| edge.id())
.collect::<Vec<_>>(),
vec![]
);
}
// TODO: reverse
#[test]
fn iter_multiple() {
let GraphDoubleSameDirection {
graph,
a,
b,
ab1,
ab2,
} = GraphDoubleSameDirection::new();
let expected = vec![ab1, ab2];
for edge in graph.edges_connecting(a, b) {
assert!(expected.contains(&edge.id()));
}
assert_eq!(graph.edges_connecting(b, a).count(), 0);
assert_graph_consistency(&graph);
}
#[test]
fn update_edge() {
let GraphLink {
mut graph,
a,
b,
ab,
} = GraphLink::<(), u32>::from_default();
assert_eq!(graph.edge_weight(ab), Some(&0));
assert_eq!(graph.find_edge(b, a), None);
graph.update_edge(a, b, 1);
assert_eq!(graph.edge_weight(ab), Some(&1));
assert_graph_consistency(&graph);
let ba = graph.update_edge(b, a, 2);
assert_eq!(graph.find_edge(b, a), Some(ba));
assert_eq!(graph.edge_weight(ba), Some(&2));
}
#[test]
fn externals() {
let GraphLink { graph, a, b, .. } = GraphLink::<(), u32>::from_default();
assert_eq!(graph.externals(Direction::Incoming).count(), 1);
assert_eq!(graph.externals(Direction::Outgoing).count(), 1);
assert_eq!(graph.externals(Direction::Incoming).next(), Some(a));
assert_eq!(graph.externals(Direction::Outgoing).next(), Some(b));
assert_graph_consistency(&graph);
}
#[test]
fn externals_empty() {
let GraphSelfLoop { graph, .. } = GraphSelfLoop::new();
assert_eq!(graph.externals(Direction::Incoming).count(), 0);
assert_eq!(graph.externals(Direction::Outgoing).count(), 0);
assert_graph_consistency(&graph);
}
// Test different node indices, this will always pass when testing, but fail during compilation.
#[test]
const fn node_indices() {
const fn graph_index<T>()
where
T: IndexType,
{
}
graph_index::<u8>();
#[cfg(any(
target_pointer_width = "16",
target_pointer_width = "32",
target_pointer_width = "64"
))]
graph_index::<u16>();
#[cfg(any(
target_pointer_width = "32", //
target_pointer_width = "64"
))]
graph_index::<u32>();
#[cfg(any(
target_pointer_width = "64" //
))]
graph_index::<u64>();
graph_index::<usize>();
}
#[test]
fn node_weight_iterator() {
let mut graph = Graph::<_, ()>::new();
graph.add_node(0);
graph.add_node(1);
graph.add_node(2);
assert_eq!(graph.node_weights_mut().count(), 3);
assert_eq!(
graph.node_weights_mut().count(),
graph.node_weights().count()
);
assert_eq!(graph.node_weights_mut().count(), graph.node_count());
assert_eq!(graph.node_weights().collect::<Vec<_>>(), vec![&0, &1, &2]);
assert_eq!(graph.node_weights_mut().collect::<Vec<_>>(), vec![
&mut 0, &mut 1, &mut 2
]);
}
#[test]
fn edge_weight_iterator() {
let mut graph = Graph::<(), _>::from_edges([
(0, 1, 0), //
(1, 2, 1),
(2, 0, 2),
]);
assert_eq!(graph.edge_weights_mut().count(), 3);
assert_eq!(
graph.edge_weights_mut().count(),
graph.edge_weights().count()
);
assert_eq!(graph.edge_weights_mut().count(), graph.edge_count());
assert_eq!(graph.edge_weights().collect::<Vec<_>>(), vec![&0, &1, &2]);
assert_eq!(graph.edge_weights_mut().collect::<Vec<_>>(), vec![
&mut 0, &mut 1, &mut 2
]);
}
#[test]
// miri is disabled, as the implementation of `index_twice_mut` is not compatible with miri
// it will be removed in the future (probably) anyway, but for now needs to be disabled
// See: https://github.com/rust-lang/miri/issues/2906
#[cfg(not(miri))]
fn index_twice_mut() {
let GraphLink {
mut graph, a, b, ..
} = GraphLink::<i32, ()>::from_default();
let (a_weight, b_weight) = graph.index_twice_mut(a, b);
assert_eq!(*a_weight, 0);
assert_eq!(*b_weight, 0);
*a_weight = 1;
*b_weight = 2;
assert_eq!(graph[a], 1);
assert_eq!(graph[b], 2);
}
#[cfg(feature = "std")]
#[cfg(not(miri))]
#[test]
fn index_twice_mut_same_index() {
let GraphLink { mut graph, a, .. } = GraphLink::<i32, ()>::from_default();
let result = std::panic::catch_unwind(move || {
graph.index_twice_mut(a, a);
});
result.expect_err("index_twice_mut should panic when given the same index twice");
}
#[cfg(feature = "std")]
#[cfg(not(miri))]
#[test]
fn index_twice_mut_out_of_range() {
let GraphLink {
mut graph, a, b, ..
} = GraphLink::<i32, ()>::from_default();
let c = NodeIndex::new(b.index() + 1);
let result = std::panic::catch_unwind(move || {
graph.index_twice_mut(a, c);
});
result.expect_err("index_twice_mut should panic when given an out of range index");
}
#[cfg(feature = "std")]
#[cfg(not(miri))]
#[test]
fn index_twice_mut_out_of_range_same_index() {
let GraphLink { mut graph, a, .. } = GraphLink::<i32, ()>::from_default();
let c = NodeIndex::new(a.index() + 1);
let result = std::panic::catch_unwind(move || {
graph.index_twice_mut(c, c);
});
result.expect_err("index_twice_mut should panic when given an out of range index");
}
#[test]
#[cfg(not(miri))]
fn index_twice_mut_node_and_edge() {
let GraphLink {
mut graph, a, ab, ..
} = GraphLink::<i32, i32>::from_default();
let (node_weight, edge_weight) = graph.index_twice_mut(a, ab);
assert_eq!(*node_weight, 0);
assert_eq!(*edge_weight, 0);
*node_weight = 1;
*edge_weight = 2;
assert_eq!(graph[a], 1);
assert_eq!(graph[ab], 2);
}
#[test]
fn from_edges() {
let graph = Graph::<(), ()>::from_edges([
(0, 1), //
(1, 2),
(2, 0),
]);
let a = NodeIndex::new(0);
let b = NodeIndex::new(1);
let c = NodeIndex::new(2);
assert_eq!(graph.node_count(), 3);
assert_eq!(graph.edge_count(), 3);
assert_eq!(graph.neighbors(a).collect::<Vec<_>>(), vec![b]);
assert_eq!(graph.neighbors(b).collect::<Vec<_>>(), vec![c]);
assert_eq!(graph.neighbors(c).collect::<Vec<_>>(), vec![a]);
assert_graph_consistency(&graph);
}
#[test]
fn from_edges_weighted() {
let graph = Graph::<(), i32>::from_edges([
(0, 1, 0), //
(1, 2, 1),
(2, 0, 2),
]);
let a = NodeIndex::new(0);
let b = NodeIndex::new(1);
let c = NodeIndex::new(2);
let ab = EdgeIndex::new(0);
let bc = EdgeIndex::new(1);
let ca = EdgeIndex::new(2);
assert_eq!(graph.node_count(), 3);
assert_eq!(graph.edge_count(), 3);
assert_eq!(
graph
.edge_references()
.map(|reference| (
reference.id(),
reference.source(),
reference.target(),
reference.weight()
))
.collect::<Vec<_>>(),
vec![(ab, a, b, &0), (bc, b, c, &1), (ca, c, a, &2),]
);
}
#[test]
fn retain_nodes() {
let mut graph = Graph::<(), ()>::from_edges([
(0, 1), //
(1, 2),
(2, 0),
]);
// remove the last node, this way we do not switch indices
graph.retain_nodes(|_, node| node != NodeIndex::new(2));
let a = NodeIndex::new(0);
let b = NodeIndex::new(1);
assert_eq!(graph.node_count(), 2);
assert_eq!(graph.edge_count(), 1);
assert_eq!(graph.neighbors(a).collect::<Vec<_>>(), vec![b]);
assert_eq!(graph.neighbors(b).collect::<Vec<_>>(), vec![]);
assert_graph_consistency(&graph);
}
#[test]
fn retain_edges() {
let mut graph = Graph::<(), ()>::from_edges([
(0, 1), //
(1, 2),
(2, 0),
]);
// remove the last edge, this way we do not switch indices
graph.retain_edges(|_, edge| edge != EdgeIndex::new(2));
let a = NodeIndex::new(0);
let b = NodeIndex::new(1);
let c = NodeIndex::new(2);
assert_eq!(graph.node_count(), 3);
assert_eq!(graph.edge_count(), 2);
assert_eq!(graph.neighbors(a).collect::<Vec<_>>(), vec![b]);
assert_eq!(graph.neighbors(b).collect::<Vec<_>>(), vec![c]);
assert_eq!(graph.neighbors(c).collect::<Vec<_>>(), vec![]);
assert_graph_consistency(&graph);
}
#[test]
fn map() {
let GraphLink { graph, .. } = GraphLink::<i32, i32>::from_default();
assert_eq!(graph.node_weights().collect::<Vec<_>>(), vec![&0, &0]);
assert_eq!(graph.edge_weights().collect::<Vec<_>>(), vec![&0]);
let graph = graph.map(|_, _| 1, |_, _| 2);
assert_eq!(graph.node_weights().collect::<Vec<_>>(), vec![&1, &1]);
assert_eq!(graph.edge_weights().collect::<Vec<_>>(), vec![&2]);
}
#[test]
fn filter_map() {
let GraphDoubleLink { mut graph, b, .. } = GraphDoubleLink::<i32, i32>::from_default();
let d = graph.add_node(0);
graph.add_edge(b, d, 0);
graph.add_edge(d, b, 0);
assert_eq!(graph.node_weights().collect::<Vec<_>>(), vec![
&0, &0, &0, &0
]);
assert_eq!(graph.edge_weights().collect::<Vec<_>>(), vec![
&0, &0, &0, &0, &0
]);
// Important to note is that `filter_map` is not perfect, it removes all nodes first and then
// only edges to nodes that passed the filter are passed to the `edge_map` function.
// The node filter removes `a` and `c`, only leaving `b` and `d` behind (which is why we added d
// in the first place)
// This means that the only edge that pass through the edge_map is `bd` and `db`, from those
// we only select `bd`.
// During the filtering the edge index is not updated, so the edge index of `bd` is still 3 even
// though `ab`, `ba` and `cd` were removed in the node filter.
let graph = graph.filter_map(
|index, _| ((index.index() % 2) == 1).then_some(index.index()),
// to ensure that edges are not run through the node filter we increment the edge weight
|index, _| ((index.index() % 2) == 1).then_some(index.index() + 1),
);
assert_eq!(graph.node_weights().collect::<Vec<_>>(), vec![&1, &3]);
assert_eq!(graph.edge_weights().collect::<Vec<_>>(), vec![&4]);
assert_graph_consistency(&graph);
}