use std::fmt::{self, Debug, Formatter};
use std::marker::PhantomData;
#[cfg(feature = "serde-1")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use petgraph::data::{Build, DataMap, DataMapMut};
use petgraph::visit::{
Data, EdgeCount, EdgeRef, GraphBase, GraphProp, IntoEdges, IntoEdgesDirected, NodeCount,
};
use petgraph::{
acyclic::Acyclic,
graph::{DefaultIx, EdgeIndex, IndexType, NodeIndex},
stable_graph::StableGraph,
Directed, Direction, EdgeType, Graph,
};
use crate::{BoundedAcyclicGraphError, BoundedGraph, BoundedGraphError, BoundedNode};
#[derive(Clone)]
pub struct BoundedAcyclicGraph<
N,
E,
G: petgraph::visit::Visitable + GraphBase = Graph<N, E, Directed, DefaultIx>,
> {
pub(crate) inner: BoundedGraph<N, E, Acyclic<G>>,
pub(crate) _phantom: PhantomData<(N, E)>,
}
impl<N, E, G> Debug for BoundedAcyclicGraph<N, E, G>
where
G: petgraph::visit::Visitable + GraphBase + Debug,
G::NodeId: Debug,
BoundedGraph<N, E, Acyclic<G>>: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("BoundedAcyclicGraph")
.field("inner", &self.inner)
.finish()
}
}
#[cfg(feature = "serde-1")]
impl<N, E, G> Serialize for BoundedAcyclicGraph<N, E, G>
where
G: petgraph::visit::Visitable + GraphBase + Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.inner.graph.inner().serialize(serializer)
}
}
#[cfg(feature = "serde-1")]
impl<'de, N, E, Ix> Deserialize<'de> for BoundedAcyclicGraph<N, E, Graph<N, E, Directed, Ix>>
where
Ix: IndexType,
N: BoundedNode<Ix> + Deserialize<'de>,
E: Clone + Deserialize<'de>,
Graph<N, E, Directed, Ix>: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let graph = Graph::<N, E, Directed, Ix>::deserialize(deserializer)?;
let acyclic = Acyclic::try_from(graph)
.map_err(|_| serde::de::Error::custom("graph contains a cycle"))?;
Ok(BoundedAcyclicGraph {
inner: BoundedGraph {
graph: acyclic,
_phantom: PhantomData,
},
_phantom: PhantomData,
})
}
}
#[cfg(feature = "serde-1")]
impl<'de, N, E, Ix> Deserialize<'de> for BoundedAcyclicGraph<N, E, StableGraph<N, E, Directed, Ix>>
where
Ix: IndexType,
N: BoundedNode<Ix> + Deserialize<'de>,
E: Clone + Deserialize<'de>,
StableGraph<N, E, Directed, Ix>: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let graph = StableGraph::<N, E, Directed, Ix>::deserialize(deserializer)?;
let acyclic = Acyclic::try_from(graph)
.map_err(|_| serde::de::Error::custom("graph contains a cycle"))?;
Ok(BoundedAcyclicGraph {
inner: BoundedGraph {
graph: acyclic,
_phantom: PhantomData,
},
_phantom: PhantomData,
})
}
}
pub type BoundedAcyclicDiGraph<N, E> = BoundedAcyclicGraph<N, E, Graph<N, E, Directed, DefaultIx>>;
pub type BoundedAcyclicStableDiGraph<N, E> =
BoundedAcyclicGraph<N, E, StableGraph<N, E, Directed, DefaultIx>>;
impl<N, E, Ty, Ix, G> BoundedAcyclicGraph<N, E, G>
where
Ty: EdgeType,
Ix: IndexType,
N: BoundedNode<Ix>,
E: Clone,
G: GraphBase<NodeId = NodeIndex<Ix>, EdgeId = EdgeIndex<Ix>>
+ Data<NodeWeight = N, EdgeWeight = E>
+ GraphProp<EdgeType = Ty>
+ DataMap
+ DataMapMut
+ Build
+ NodeCount
+ EdgeCount
+ petgraph::visit::Visitable
+ petgraph::visit::NodeIndexable,
for<'a> &'a G: petgraph::visit::IntoNeighborsDirected
+ petgraph::visit::IntoNodeIdentifiers
+ petgraph::visit::IntoEdgesDirected
+ petgraph::visit::IntoEdges
+ GraphBase<NodeId = NodeIndex<Ix>>,
{
pub fn new() -> Self
where
G: Default,
{
Self {
inner: BoundedGraph {
graph: Acyclic::new(),
_phantom: PhantomData,
},
_phantom: PhantomData,
}
}
#[must_use = "checking edge validity is only useful if the result is used"]
pub fn can_add_edge(&self, source: NodeIndex<Ix>, target: NodeIndex<Ix>) -> bool {
let (source_weight, target_weight) = match (
self.inner.graph.node_weight(source),
self.inner.graph.node_weight(target),
) {
(Some(s), Some(t)) => (s, t),
_ => return false,
};
let outgoing_count = self
.inner
.graph
.inner()
.edges_directed(source, Direction::Outgoing)
.count();
let incoming_count = self
.inner
.graph
.inner()
.edges_directed(target, Direction::Incoming)
.count();
let bounded_ok =
source_weight.can_add_edge(Direction::Outgoing, outgoing_count, target_weight)
&& target_weight.can_add_edge(Direction::Incoming, incoming_count, source_weight);
let acyclic_ok = self.inner.graph.is_valid_edge(source, target);
bounded_ok && acyclic_ok
}
#[must_use = "the node index is needed to reference the added node"]
pub fn add_node(&mut self, node: N) -> NodeIndex<Ix> {
self.inner.graph.add_node(node)
}
#[must_use = "edge addition may fail due to capacity or acyclicity constraints"]
pub fn add_edge(
&mut self,
source: NodeIndex<Ix>,
target: NodeIndex<Ix>,
edge: E,
) -> Result<EdgeIndex<Ix>, BoundedAcyclicGraphError<Ix>> {
let (source_weight, target_weight) = match (
self.inner.graph.node_weight(source),
self.inner.graph.node_weight(target),
) {
(Some(s), Some(t)) => (s, t),
(None, None) => return Err(BoundedGraphError::not_found(None, None).into()),
(None, Some(_)) => return Err(BoundedGraphError::not_found(None, Some(target)).into()),
(Some(_), None) => return Err(BoundedGraphError::not_found(Some(source), None).into()),
};
let outgoing_count = self
.inner
.graph
.inner()
.edges_directed(source, Direction::Outgoing)
.count();
let incoming_count = self
.inner
.graph
.inner()
.edges_directed(target, Direction::Incoming)
.count();
let source_has_space =
source_weight.can_add_edge(Direction::Outgoing, outgoing_count, target_weight);
let target_has_space =
target_weight.can_add_edge(Direction::Incoming, incoming_count, source_weight);
match (source_has_space, target_has_space) {
(false, true) => return Err(BoundedGraphError::source_rejected(source, target).into()),
(true, false) => return Err(BoundedGraphError::target_rejected(source, target).into()),
(false, false) => return Err(BoundedGraphError::both_rejected(source, target).into()),
(true, true) => {} }
self.inner
.graph
.try_add_edge(source, target, edge)
.map_err(Into::into)
}
#[must_use = "edge update may fail due to capacity or acyclicity constraints"]
pub fn update_edge(
&mut self,
source: NodeIndex<Ix>,
target: NodeIndex<Ix>,
weight: E,
) -> Result<EdgeIndex<Ix>, BoundedAcyclicGraphError<Ix>> {
let edge_exists = self
.inner
.graph
.inner()
.edges(source)
.any(|e| e.target() == target);
if edge_exists {
self.inner
.graph
.try_update_edge(source, target, weight)
.map_err(Into::into)
} else {
self.add_edge(source, target, weight)
}
}
#[must_use]
pub fn inner(&self) -> &BoundedGraph<N, E, Acyclic<G>> {
&self.inner
}
#[must_use]
pub fn as_acyclic(&self) -> &Acyclic<G> {
&self.inner.graph
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub fn topological_iter(&self) -> impl Iterator<Item = NodeIndex<Ix>> + '_ {
self.inner.graph.nodes_iter()
}
#[must_use]
pub fn get_position(
&self,
node: NodeIndex<Ix>,
) -> Option<petgraph::acyclic::TopologicalPosition> {
Some(self.inner.graph.get_position(node))
}
#[must_use]
pub fn at_position(
&self,
pos: petgraph::acyclic::TopologicalPosition,
) -> Option<NodeIndex<Ix>> {
self.inner.graph.at_position(pos)
}
pub fn node_count(&self) -> usize {
self.inner.node_count()
}
pub fn edge_count(&self) -> usize {
self.inner.edge_count()
}
}
impl<N, E, Ty, Ix, G> BoundedAcyclicGraph<N, E, G>
where
Ty: EdgeType,
Ix: IndexType,
N: BoundedNode<Ix> + crate::ImmutableEdgeBounds,
E: Clone,
G: GraphBase<NodeId = NodeIndex<Ix>, EdgeId = EdgeIndex<Ix>>
+ Data<NodeWeight = N, EdgeWeight = E>
+ GraphProp<EdgeType = Ty>
+ DataMap
+ DataMapMut
+ Build
+ NodeCount
+ EdgeCount
+ petgraph::visit::Visitable
+ petgraph::visit::NodeIndexable,
for<'a> &'a G: petgraph::visit::IntoNeighborsDirected
+ petgraph::visit::IntoNodeIdentifiers
+ petgraph::visit::IntoEdgesDirected
+ petgraph::visit::IntoEdges
+ GraphBase<NodeId = NodeIndex<Ix>>,
{
pub fn inner_mut(&mut self) -> &mut BoundedGraph<N, E, Acyclic<G>> {
&mut self.inner
}
}
impl<N, E, Ix> BoundedAcyclicGraph<N, E, Graph<N, E, Directed, Ix>>
where
N: BoundedNode,
Ix: IndexType,
{
pub fn remove_edge(&mut self, edge: EdgeIndex<Ix>) -> Option<E> {
self.inner.graph.remove_edge(edge)
}
pub fn remove_node(&mut self, node: NodeIndex<Ix>) -> Option<N> {
self.inner.graph.remove_node(node)
}
}
impl<N, E, Ix> BoundedAcyclicGraph<N, E, StableGraph<N, E, Directed, Ix>>
where
N: BoundedNode,
Ix: IndexType,
{
pub fn remove_edge(&mut self, edge: EdgeIndex<Ix>) -> Option<E> {
self.inner.graph.remove_edge(edge)
}
pub fn remove_node(&mut self, node: NodeIndex<Ix>) -> Option<N> {
self.inner.graph.remove_node(node)
}
}
impl<N, E, Ty, Ix, G> Default for BoundedAcyclicGraph<N, E, G>
where
Ty: EdgeType,
Ix: IndexType,
N: BoundedNode<Ix>,
E: Clone,
G: GraphBase<NodeId = NodeIndex<Ix>, EdgeId = EdgeIndex<Ix>>
+ Data<NodeWeight = N, EdgeWeight = E>
+ GraphProp<EdgeType = Ty>
+ DataMap
+ DataMapMut
+ Build
+ NodeCount
+ EdgeCount
+ petgraph::visit::Visitable
+ petgraph::visit::NodeIndexable
+ Default,
for<'a> &'a G: petgraph::visit::IntoNeighborsDirected
+ petgraph::visit::IntoNodeIdentifiers
+ petgraph::visit::IntoEdgesDirected
+ petgraph::visit::IntoEdges
+ GraphBase<NodeId = NodeIndex<Ix>>,
{
fn default() -> Self {
Self::new()
}
}
impl<N, E, Ix> TryFrom<Graph<N, E, Directed, Ix>>
for BoundedAcyclicGraph<N, E, Graph<N, E, Directed, Ix>>
where
Ix: IndexType,
N: BoundedNode<Ix>,
E: Clone,
{
type Error = BoundedAcyclicGraphError<Ix>;
fn try_from(graph: Graph<N, E, Directed, Ix>) -> Result<Self, Self::Error> {
for node in graph.node_indices() {
let node_weight = graph
.node_weight(node)
.ok_or_else(|| BoundedGraphError::not_found(Some(node), None))?;
let outgoing_count = graph.edges_directed(node, Direction::Outgoing).count();
let incoming_count = graph.edges_directed(node, Direction::Incoming).count();
for edge in graph.edges_directed(node, Direction::Outgoing) {
let target = edge.target();
let target_weight = graph
.node_weight(target)
.ok_or_else(|| BoundedGraphError::not_found(None, Some(target)))?;
if !node_weight.can_add_edge(Direction::Outgoing, outgoing_count - 1, target_weight)
{
return Err(BoundedGraphError::source_rejected(node, target).into());
}
}
for edge in graph.edges_directed(node, Direction::Incoming) {
let source = edge.source();
let source_weight = graph
.node_weight(source)
.ok_or_else(|| BoundedGraphError::not_found(Some(source), None))?;
if !node_weight.can_add_edge(Direction::Incoming, incoming_count - 1, source_weight)
{
return Err(BoundedGraphError::target_rejected(source, node).into());
}
}
}
let acyclic = Acyclic::try_from(graph).map_err(|_| {
use petgraph::acyclic::AcyclicEdgeError;
BoundedAcyclicGraphError::Acyclic(AcyclicEdgeError::InvalidEdge)
})?;
Ok(BoundedAcyclicGraph {
inner: crate::BoundedGraph {
graph: acyclic,
_phantom: PhantomData,
},
_phantom: PhantomData,
})
}
}
impl<N, E, Ix> TryFrom<StableGraph<N, E, Directed, Ix>>
for BoundedAcyclicGraph<N, E, StableGraph<N, E, Directed, Ix>>
where
Ix: IndexType,
N: BoundedNode<Ix>,
E: Clone,
{
type Error = BoundedAcyclicGraphError<Ix>;
fn try_from(graph: StableGraph<N, E, Directed, Ix>) -> Result<Self, Self::Error> {
for node in graph.node_indices() {
let node_weight = graph
.node_weight(node)
.ok_or_else(|| BoundedGraphError::not_found(Some(node), None))?;
let outgoing_count = graph.edges_directed(node, Direction::Outgoing).count();
let incoming_count = graph.edges_directed(node, Direction::Incoming).count();
for edge in graph.edges_directed(node, Direction::Outgoing) {
let target = edge.target();
let target_weight = graph
.node_weight(target)
.ok_or_else(|| BoundedGraphError::not_found(None, Some(target)))?;
if !node_weight.can_add_edge(Direction::Outgoing, outgoing_count - 1, target_weight)
{
return Err(BoundedGraphError::source_rejected(node, target).into());
}
}
for edge in graph.edges_directed(node, Direction::Incoming) {
let source = edge.source();
let source_weight = graph
.node_weight(source)
.ok_or_else(|| BoundedGraphError::not_found(Some(source), None))?;
if !node_weight.can_add_edge(Direction::Incoming, incoming_count - 1, source_weight)
{
return Err(BoundedGraphError::target_rejected(source, node).into());
}
}
}
let acyclic = Acyclic::try_from(graph).map_err(|_| {
use petgraph::acyclic::AcyclicEdgeError;
BoundedAcyclicGraphError::Acyclic(AcyclicEdgeError::InvalidEdge)
})?;
Ok(BoundedAcyclicGraph {
inner: crate::BoundedGraph {
graph: acyclic,
_phantom: PhantomData,
},
_phantom: PhantomData,
})
}
}