#![forbid(unsafe_code)]
#![warn(missing_docs)]
pub use petgraph;
use petgraph as pg;
use petgraph::algo::{has_path_connecting, DfsSpace};
use petgraph::graph::{DefaultIx, DiGraph, GraphIndex, IndexType};
use petgraph::visit::{
GetAdjacencyMatrix, GraphBase, GraphProp, IntoEdgeReferences, IntoEdges, IntoEdgesDirected,
IntoNeighbors, IntoNeighborsDirected, IntoNodeIdentifiers, IntoNodeReferences,
NodeCompactIndexable, NodeCount, NodeIndexable, Visitable,
};
use petgraph::IntoWeightedEdge;
use std::marker::PhantomData;
use std::ops::{Index, IndexMut};
pub use petgraph::graph::{EdgeIndex, EdgeWeightsMut, NodeIndex, NodeWeightsMut};
pub use petgraph::visit::Walker;
#[cfg(feature = "serde-1")]
mod serde;
#[cfg(feature = "stable_dag")]
pub mod stable_dag;
pub mod walker;
pub type RawNodes<'a, N, Ix> = &'a [pg::graph::Node<N, Ix>];
pub type RawEdges<'a, E, Ix> = &'a [pg::graph::Edge<E, Ix>];
pub type Edges<'a, E, Ix> = pg::graph::Edges<'a, E, pg::Directed, Ix>;
#[derive(Clone, Debug)]
pub struct Dag<N, E, Ix: IndexType = DefaultIx> {
graph: DiGraph<N, E, Ix>,
cycle_state: DfsSpace<NodeIndex<Ix>, <DiGraph<N, E, Ix> as Visitable>::Map>,
}
pub struct Children<N, E, Ix: IndexType> {
walk_edges: pg::graph::WalkNeighbors<Ix>,
_node: PhantomData<N>,
_edge: PhantomData<E>,
}
pub struct Parents<N, E, Ix: IndexType> {
walk_edges: pg::graph::WalkNeighbors<Ix>,
_node: PhantomData<N>,
_edge: PhantomData<E>,
}
pub struct EdgeIndices<Ix: IndexType> {
indices: std::ops::Range<usize>,
_phantom: PhantomData<Ix>,
}
pub type RecursiveWalk<N, E, Ix, F> = walker::Recursive<Dag<N, E, Ix>, F>;
#[derive(Copy, Clone)]
pub struct WouldCycle<E>(pub E);
impl<N, E, Ix> Dag<N, E, Ix>
where
Ix: IndexType,
{
pub fn new() -> Self {
Self::with_capacity(1, 1)
}
pub fn with_capacity(nodes: usize, edges: usize) -> Self {
Dag {
graph: DiGraph::with_capacity(nodes, edges),
cycle_state: DfsSpace::default(),
}
}
pub fn from_edges<I>(edges: I) -> Result<Self, WouldCycle<E>>
where
I: IntoIterator,
I::Item: IntoWeightedEdge<E>,
<I::Item as IntoWeightedEdge<E>>::NodeId: Into<NodeIndex<Ix>>,
N: Default,
{
let mut dag = Self::default();
dag.extend_with_edges(edges)?;
Ok(dag)
}
pub fn extend_with_edges<I>(&mut self, edges: I) -> Result<(), WouldCycle<E>>
where
I: IntoIterator,
I::Item: IntoWeightedEdge<E>,
<I::Item as IntoWeightedEdge<E>>::NodeId: Into<NodeIndex<Ix>>,
N: Default,
{
for edge in edges {
let (source, target, weight) = edge.into_weighted_edge();
let (source, target) = (source.into(), target.into());
let nx = std::cmp::max(source, target);
while nx.index() >= self.node_count() {
self.add_node(N::default());
}
self.add_edge(source, target, weight)?;
}
Ok(())
}
pub fn from_elements<I>(elements: I) -> Result<Self, WouldCycle<E>>
where
Self: Sized,
I: IntoIterator<Item = pg::data::Element<N, E>>,
{
let mut dag = Self::default();
for elem in elements {
match elem {
pg::data::Element::Node { weight } => {
dag.add_node(weight);
}
pg::data::Element::Edge {
source,
target,
weight,
} => {
let n = NodeIndex::new(source);
let e = NodeIndex::new(target);
dag.update_edge(n, e, weight)?;
}
}
}
Ok(dag)
}
pub fn map<'a, F, G, N2, E2>(&'a self, node_map: F, edge_map: G) -> Dag<N2, E2, Ix>
where
F: FnMut(NodeIndex<Ix>, &'a N) -> N2,
G: FnMut(EdgeIndex<Ix>, &'a E) -> E2,
{
let graph = self.graph.map(node_map, edge_map);
let cycle_state = self.cycle_state.clone();
Dag { graph, cycle_state }
}
pub fn filter_map<'a, F, G, N2, E2>(&'a self, node_map: F, edge_map: G) -> Dag<N2, E2, Ix>
where
F: FnMut(NodeIndex<Ix>, &'a N) -> Option<N2>,
G: FnMut(EdgeIndex<Ix>, &'a E) -> Option<E2>,
{
let graph = self.graph.filter_map(node_map, edge_map);
let cycle_state = DfsSpace::new(&graph);
Dag { graph, cycle_state }
}
pub fn clear(&mut self) {
self.graph.clear();
}
pub fn node_count(&self) -> usize {
self.graph.node_count()
}
pub fn edge_count(&self) -> usize {
self.graph.edge_count()
}
pub fn reserve_nodes(&mut self, additional: usize) {
self.graph.reserve_nodes(additional)
}
pub fn reserve_exact_nodes(&mut self, additional: usize) {
self.graph.reserve_exact_nodes(additional)
}
pub fn reserve_edges(&mut self, additional: usize) {
self.graph.reserve_edges(additional)
}
pub fn reserve_exact_edges(&mut self, additional: usize) {
self.graph.reserve_exact_edges(additional)
}
pub fn shrink_to_fit(&mut self) {
self.graph.shrink_to_fit();
}
pub fn shrink_to_fit_nodes(&mut self) {
self.graph.shrink_to_fit_nodes();
}
pub fn shrink_to_fit_edges(&mut self) {
self.graph.shrink_to_fit_edges();
}
pub fn graph(&self) -> &DiGraph<N, E, Ix> {
&self.graph
}
pub fn into_graph(self) -> DiGraph<N, E, Ix> {
let Dag { graph, .. } = self;
graph
}
pub fn add_node(&mut self, weight: N) -> NodeIndex<Ix> {
self.graph.add_node(weight)
}
pub fn add_edge(
&mut self,
a: NodeIndex<Ix>,
b: NodeIndex<Ix>,
weight: E,
) -> Result<EdgeIndex<Ix>, WouldCycle<E>> {
let should_check_for_cycle = must_check_for_cycle(self, a, b);
let state = Some(&mut self.cycle_state);
if should_check_for_cycle && has_path_connecting(&self.graph, b, a, state) {
return Err(WouldCycle(weight));
}
Ok(self.graph.add_edge(a, b, weight))
}
pub fn add_edges<I>(&mut self, edges: I) -> Result<EdgeIndices<Ix>, WouldCycle<Vec<E>>>
where
I: IntoIterator<Item = (NodeIndex<Ix>, NodeIndex<Ix>, E)>,
{
let mut num_edges = 0;
let mut should_check_for_cycle = false;
for (a, b, weight) in edges {
if !should_check_for_cycle && must_check_for_cycle(self, a, b) {
should_check_for_cycle = true;
}
self.graph.add_edge(a, b, weight);
num_edges += 1;
}
let total_edges = self.edge_count();
let new_edges_range = total_edges - num_edges..total_edges;
if should_check_for_cycle && pg::algo::is_cyclic_directed(&self.graph) {
let removed_edges = new_edges_range.rev().filter_map(|i| {
let idx = EdgeIndex::new(i);
self.graph.remove_edge(idx)
});
Err(WouldCycle(removed_edges.collect()))
} else {
Ok(EdgeIndices {
indices: new_edges_range,
_phantom: std::marker::PhantomData,
})
}
}
pub fn update_edge(
&mut self,
a: NodeIndex<Ix>,
b: NodeIndex<Ix>,
weight: E,
) -> Result<EdgeIndex<Ix>, WouldCycle<E>> {
if let Some(edge_idx) = self.find_edge(a, b) {
if let Some(edge) = self.edge_weight_mut(edge_idx) {
*edge = weight;
return Ok(edge_idx);
}
}
self.add_edge(a, b, weight)
}
pub fn find_edge(&self, a: NodeIndex<Ix>, b: NodeIndex<Ix>) -> Option<EdgeIndex<Ix>> {
self.graph.find_edge(a, b)
}
pub fn edge_endpoints(&self, e: EdgeIndex<Ix>) -> Option<(NodeIndex<Ix>, NodeIndex<Ix>)> {
self.graph.edge_endpoints(e)
}
pub fn clear_edges(&mut self) {
self.graph.clear_edges()
}
pub fn add_parent(
&mut self,
child: NodeIndex<Ix>,
edge: E,
node: N,
) -> (EdgeIndex<Ix>, NodeIndex<Ix>) {
let parent_node = self.graph.add_node(node);
let parent_edge = self.graph.add_edge(parent_node, child, edge);
(parent_edge, parent_node)
}
pub fn add_child(
&mut self,
parent: NodeIndex<Ix>,
edge: E,
node: N,
) -> (EdgeIndex<Ix>, NodeIndex<Ix>) {
let child_node = self.graph.add_node(node);
let child_edge = self.graph.add_edge(parent, child_node, edge);
(child_edge, child_node)
}
pub fn node_weight(&self, node: NodeIndex<Ix>) -> Option<&N> {
self.graph.node_weight(node)
}
pub fn node_weight_mut(&mut self, node: NodeIndex<Ix>) -> Option<&mut N> {
self.graph.node_weight_mut(node)
}
pub fn raw_nodes(&self) -> RawNodes<N, Ix> {
self.graph.raw_nodes()
}
pub fn node_weights_mut(&mut self) -> NodeWeightsMut<N, Ix> {
self.graph.node_weights_mut()
}
pub fn edge_weight(&self, edge: EdgeIndex<Ix>) -> Option<&E> {
self.graph.edge_weight(edge)
}
pub fn edge_weight_mut(&mut self, edge: EdgeIndex<Ix>) -> Option<&mut E> {
self.graph.edge_weight_mut(edge)
}
pub fn raw_edges(&self) -> RawEdges<E, Ix> {
self.graph.raw_edges()
}
pub fn edge_weights_mut(&mut self) -> EdgeWeightsMut<E, Ix> {
self.graph.edge_weights_mut()
}
#[allow(clippy::type_complexity)]
pub fn index_twice_mut<A, B>(
&mut self,
a: A,
b: B,
) -> (
&mut <DiGraph<N, E, Ix> as Index<A>>::Output,
&mut <DiGraph<N, E, Ix> as Index<B>>::Output,
)
where
DiGraph<N, E, Ix>: IndexMut<A> + IndexMut<B>,
A: GraphIndex,
B: GraphIndex,
{
self.graph.index_twice_mut(a, b)
}
pub fn remove_node(&mut self, node: NodeIndex<Ix>) -> Option<N> {
self.graph.remove_node(node)
}
pub fn remove_edge(&mut self, e: EdgeIndex<Ix>) -> Option<E> {
self.graph.remove_edge(e)
}
pub fn parents(&self, child: NodeIndex<Ix>) -> Parents<N, E, Ix> {
let walk_edges = self.graph.neighbors_directed(child, pg::Incoming).detach();
Parents {
walk_edges,
_node: PhantomData,
_edge: PhantomData,
}
}
pub fn children(&self, parent: NodeIndex<Ix>) -> Children<N, E, Ix> {
let walk_edges = self.graph.neighbors_directed(parent, pg::Outgoing).detach();
Children {
walk_edges,
_node: PhantomData,
_edge: PhantomData,
}
}
pub fn recursive_walk<F>(
&self,
start: NodeIndex<Ix>,
recursive_fn: F,
) -> RecursiveWalk<N, E, Ix, F>
where
F: FnMut(&Self, NodeIndex<Ix>) -> Option<(EdgeIndex<Ix>, NodeIndex<Ix>)>,
{
walker::Recursive::new(start, recursive_fn)
}
fn transitive_reduce_iter(
&mut self,
curr_node: NodeIndex<Ix>,
ancestors: &mut Vec<NodeIndex<Ix>>,
) {
let mut redundant_edges = Vec::new();
for (_, child) in self.children(curr_node).iter(self) {
for (edge, coparent) in self.parents(child).iter(self) {
if ancestors.contains(&coparent) {
redundant_edges.push(edge);
}
}
}
for edge in redundant_edges {
self.remove_edge(edge);
}
ancestors.push(curr_node);
let mut children = self.children(curr_node);
while let Some((_, child)) = children.walk_next(self) {
self.transitive_reduce_iter(child, ancestors)
}
ancestors.pop();
}
pub fn transitive_reduce(&mut self, roots: Vec<NodeIndex<Ix>>) {
for root in roots {
self.transitive_reduce_iter(root, &mut Vec::new())
}
}
}
fn must_check_for_cycle<N, E, Ix>(dag: &Dag<N, E, Ix>, a: NodeIndex<Ix>, b: NodeIndex<Ix>) -> bool
where
Ix: IndexType,
{
if a == b {
return true;
}
dag.parents(a).walk_next(dag).is_some()
&& dag.children(b).walk_next(dag).is_some()
&& dag.find_edge(a, b).is_none()
}
impl<N, E, Ix> From<Dag<N, E, Ix>> for DiGraph<N, E, Ix>
where
Ix: IndexType,
{
fn from(val: Dag<N, E, Ix>) -> Self {
val.into_graph()
}
}
impl<N, E, Ix> Default for Dag<N, E, Ix>
where
Ix: IndexType,
{
fn default() -> Self {
Dag::new()
}
}
impl<N, E, Ix> GraphBase for Dag<N, E, Ix>
where
Ix: IndexType,
{
type NodeId = NodeIndex<Ix>;
type EdgeId = EdgeIndex<Ix>;
}
impl<N, E, Ix> NodeCount for Dag<N, E, Ix>
where
Ix: IndexType,
{
fn node_count(&self) -> usize {
Dag::node_count(self)
}
}
impl<N, E, Ix> GraphProp for Dag<N, E, Ix>
where
Ix: IndexType,
{
type EdgeType = pg::Directed;
fn is_directed(&self) -> bool {
true
}
}
impl<N, E, Ix> pg::visit::Visitable for Dag<N, E, Ix>
where
Ix: IndexType,
{
type Map = <DiGraph<N, E, Ix> as Visitable>::Map;
fn visit_map(&self) -> Self::Map {
self.graph.visit_map()
}
fn reset_map(&self, map: &mut Self::Map) {
self.graph.reset_map(map)
}
}
impl<N, E, Ix> pg::visit::Data for Dag<N, E, Ix>
where
Ix: IndexType,
{
type NodeWeight = N;
type EdgeWeight = E;
}
impl<N, E, Ix> pg::data::DataMap for Dag<N, E, Ix>
where
Ix: IndexType,
{
fn node_weight(&self, id: Self::NodeId) -> Option<&Self::NodeWeight> {
self.graph.node_weight(id)
}
fn edge_weight(&self, id: Self::EdgeId) -> Option<&Self::EdgeWeight> {
self.graph.edge_weight(id)
}
}
impl<N, E, Ix> pg::data::DataMapMut for Dag<N, E, Ix>
where
Ix: IndexType,
{
fn node_weight_mut(&mut self, id: Self::NodeId) -> Option<&mut Self::NodeWeight> {
self.graph.node_weight_mut(id)
}
fn edge_weight_mut(&mut self, id: Self::EdgeId) -> Option<&mut Self::EdgeWeight> {
self.graph.edge_weight_mut(id)
}
}
impl<'a, N, E, Ix> IntoNeighbors for &'a Dag<N, E, Ix>
where
Ix: IndexType,
{
type Neighbors = pg::graph::Neighbors<'a, E, Ix>;
fn neighbors(self, n: NodeIndex<Ix>) -> Self::Neighbors {
self.graph.neighbors(n)
}
}
impl<'a, N, E, Ix> IntoNeighborsDirected for &'a Dag<N, E, Ix>
where
Ix: IndexType,
{
type NeighborsDirected = pg::graph::Neighbors<'a, E, Ix>;
fn neighbors_directed(self, n: NodeIndex<Ix>, d: pg::Direction) -> Self::Neighbors {
self.graph.neighbors_directed(n, d)
}
}
impl<'a, N, E, Ix> IntoEdgeReferences for &'a Dag<N, E, Ix>
where
Ix: IndexType,
{
type EdgeRef = pg::graph::EdgeReference<'a, E, Ix>;
type EdgeReferences = pg::graph::EdgeReferences<'a, E, Ix>;
fn edge_references(self) -> Self::EdgeReferences {
self.graph.edge_references()
}
}
impl<'a, N, E, Ix> IntoEdges for &'a Dag<N, E, Ix>
where
Ix: IndexType,
{
type Edges = Edges<'a, E, Ix>;
fn edges(self, a: Self::NodeId) -> Self::Edges {
self.graph.edges(a)
}
}
impl<'a, N, E, Ix> IntoEdgesDirected for &'a Dag<N, E, Ix>
where
Ix: IndexType,
{
type EdgesDirected = Edges<'a, E, Ix>;
fn edges_directed(self, a: Self::NodeId, dir: pg::Direction) -> Self::EdgesDirected {
self.graph.edges_directed(a, dir)
}
}
impl<N, E, Ix> IntoNodeIdentifiers for &Dag<N, E, Ix>
where
Ix: IndexType,
{
type NodeIdentifiers = pg::graph::NodeIndices<Ix>;
fn node_identifiers(self) -> Self::NodeIdentifiers {
self.graph.node_identifiers()
}
}
impl<'a, N, E, Ix> IntoNodeReferences for &'a Dag<N, E, Ix>
where
Ix: IndexType,
{
type NodeRef = (NodeIndex<Ix>, &'a N);
type NodeReferences = pg::graph::NodeReferences<'a, N, Ix>;
fn node_references(self) -> Self::NodeReferences {
self.graph.node_references()
}
}
impl<N, E, Ix> NodeIndexable for Dag<N, E, Ix>
where
Ix: IndexType,
{
fn node_bound(&self) -> usize {
self.graph.node_bound()
}
fn to_index(&self, ix: NodeIndex<Ix>) -> usize {
self.graph.to_index(ix)
}
fn from_index(&self, ix: usize) -> Self::NodeId {
self.graph.from_index(ix)
}
}
impl<N, E, Ix> NodeCompactIndexable for Dag<N, E, Ix> where Ix: IndexType {}
impl<N, E, Ix> Index<NodeIndex<Ix>> for Dag<N, E, Ix>
where
Ix: IndexType,
{
type Output = N;
fn index(&self, index: NodeIndex<Ix>) -> &N {
&self.graph[index]
}
}
impl<N, E, Ix> IndexMut<NodeIndex<Ix>> for Dag<N, E, Ix>
where
Ix: IndexType,
{
fn index_mut(&mut self, index: NodeIndex<Ix>) -> &mut N {
&mut self.graph[index]
}
}
impl<N, E, Ix> Index<EdgeIndex<Ix>> for Dag<N, E, Ix>
where
Ix: IndexType,
{
type Output = E;
fn index(&self, index: EdgeIndex<Ix>) -> &E {
&self.graph[index]
}
}
impl<N, E, Ix> IndexMut<EdgeIndex<Ix>> for Dag<N, E, Ix>
where
Ix: IndexType,
{
fn index_mut(&mut self, index: EdgeIndex<Ix>) -> &mut E {
&mut self.graph[index]
}
}
impl<N, E, Ix> GetAdjacencyMatrix for Dag<N, E, Ix>
where
Ix: IndexType,
{
type AdjMatrix = <DiGraph<N, E, Ix> as GetAdjacencyMatrix>::AdjMatrix;
fn adjacency_matrix(&self) -> Self::AdjMatrix {
self.graph.adjacency_matrix()
}
fn is_adjacent(&self, matrix: &Self::AdjMatrix, a: NodeIndex<Ix>, b: NodeIndex<Ix>) -> bool {
self.graph.is_adjacent(matrix, a, b)
}
}
impl<'a, N, E, Ix> Walker<&'a Dag<N, E, Ix>> for Children<N, E, Ix>
where
Ix: IndexType,
{
type Item = (EdgeIndex<Ix>, NodeIndex<Ix>);
#[inline]
fn walk_next(&mut self, dag: &'a Dag<N, E, Ix>) -> Option<Self::Item> {
self.walk_edges.next(&dag.graph)
}
}
impl<'a, N, E, Ix> Walker<&'a Dag<N, E, Ix>> for Parents<N, E, Ix>
where
Ix: IndexType,
{
type Item = (EdgeIndex<Ix>, NodeIndex<Ix>);
#[inline]
fn walk_next(&mut self, dag: &'a Dag<N, E, Ix>) -> Option<Self::Item> {
self.walk_edges.next(&dag.graph)
}
}
impl<Ix> Iterator for EdgeIndices<Ix>
where
Ix: IndexType,
{
type Item = EdgeIndex<Ix>;
fn next(&mut self) -> Option<EdgeIndex<Ix>> {
self.indices.next().map(|i| EdgeIndex::new(i))
}
}
impl<E> std::fmt::Debug for WouldCycle<E> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(f, "WouldCycle")
}
}
impl<E> std::fmt::Display for WouldCycle<E> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
writeln!(f, "{:?}", self)
}
}
impl<E> std::error::Error for WouldCycle<E> {
fn description(&self) -> &str {
"Adding this edge would have created a cycle"
}
}