From 681db937452f11656a79b31945b3465f90e21324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Borgna?= <121866228+aborgna-q@users.noreply.github.com> Date: Thu, 7 Sep 2023 17:52:44 +0200 Subject: [PATCH] feat: Implement petgraph traits on Hugr (#498) This lets us treat HUGRs in the same way as views everywhere (e.g. use them as Circuits) requiring `T: PetgraphHugr` where before we had `T: HierarchyView`. --------- Co-authored-by: Alan Lawrence --- src/hugr/validate.rs | 9 +- src/hugr/views.rs | 27 ++-- src/hugr/views/descendants.rs | 4 +- src/hugr/views/petgraph.rs | 284 ++++++++++++++++------------------ src/hugr/views/sibling.rs | 4 +- 5 files changed, 154 insertions(+), 174 deletions(-) diff --git a/src/hugr/validate.rs b/src/hugr/validate.rs index f45b7c5fc..67016ac5f 100644 --- a/src/hugr/validate.rs +++ b/src/hugr/validate.rs @@ -101,7 +101,7 @@ impl<'a, 'b> ValidationContext<'a, 'b> { fn compute_dominator(&self, parent: Node) -> Dominators { let region: SiblingGraph = SiblingGraph::new(self.hugr, parent); let entry_node = self.hugr.children(parent).next().unwrap(); - dominators::simple_fast(®ion, entry_node) + dominators::simple_fast(®ion.as_petgraph(), entry_node) } /// Check the constraints on a single node. @@ -365,8 +365,11 @@ impl<'a, 'b> ValidationContext<'a, 'b> { }; let region: SiblingGraph = SiblingGraph::new(self.hugr, parent); - let postorder = Topo::new(®ion); - let nodes_visited = postorder.iter(®ion).filter(|n| *n != parent).count(); + let postorder = Topo::new(®ion.as_petgraph()); + let nodes_visited = postorder + .iter(®ion.as_petgraph()) + .filter(|n| *n != parent) + .count(); let node_count = self.hugr.children(parent).count(); if nodes_visited != node_count { return Err(ValidationError::NotADag { diff --git a/src/hugr/views.rs b/src/hugr/views.rs index 5b31ab4fe..f26bfb3e9 100644 --- a/src/hugr/views.rs +++ b/src/hugr/views.rs @@ -1,13 +1,14 @@ //! Read-only access into HUGR graphs and subgraphs. pub mod descendants; -mod petgraph; +pub mod petgraph; pub mod sibling; pub mod sibling_subgraph; #[cfg(test)] mod tests; +pub use self::petgraph::PetgraphWrapper; pub use descendants::DescendantsGraph; pub use sibling::SiblingGraph; pub use sibling_subgraph::SiblingSubgraph; @@ -22,7 +23,6 @@ use crate::ops::handle::NodeHandle; use crate::ops::{FuncDecl, FuncDefn, OpName, OpTag, OpType, DFG}; use crate::types::{EdgeKind, FunctionType}; use crate::{Direction, Node, Port}; -use ::petgraph::visit as pv; /// A trait for inspecting HUGRs. /// For end users we intend this to be superseded by region-specific APIs. @@ -181,6 +181,15 @@ pub trait HugrView: sealed::HugrInternals { /// type. Otherwise return None. fn get_function_type(&self) -> Option<&FunctionType>; + /// Return a wrapper over the view that can be used in petgraph algorithms. + #[inline] + fn as_petgraph(&self) -> PetgraphWrapper<'_, Self> + where + Self: Sized, + { + PetgraphWrapper { hugr: self } + } + /// Return dot string showing underlying graph and hierarchy side by side. fn dot_string(&self) -> String { let hugr = self.base_hugr(); @@ -232,19 +241,7 @@ pub trait HugrView: sealed::HugrInternals { } /// A common trait for views of a HUGR hierarchical subgraph. -pub trait HierarchyView<'a>: - HugrView - + pv::GraphBase - + pv::GraphProp - + pv::NodeCount - + pv::NodeIndexable - + pv::EdgeCount - + pv::Visitable - + pv::GetAdjacencyMatrix - + pv::Visitable -where - for<'g> &'g Self: pv::IntoNeighborsDirected + pv::IntoNodeIdentifiers, -{ +pub trait HierarchyView<'a>: HugrView { /// The base from which the subgraph is derived. type Base; diff --git a/src/hugr/views/descendants.rs b/src/hugr/views/descendants.rs index cf0262be3..508517f40 100644 --- a/src/hugr/views/descendants.rs +++ b/src/hugr/views/descendants.rs @@ -21,8 +21,8 @@ type RegionGraph<'g> = portgraph::view::Region<'g, &'g MultiPortGraph>; /// its immediate children. Prefer using [`SiblingGraph`] when possible, /// as it is more efficient. /// -/// Implements the [`HierarchyView`] trait, as well as [`HugrView`] and petgraph's -/// _visit_ traits, so can be used interchangeably with [`SiblingGraph`]. +/// Implements the [`HierarchyView`] trait, as well as [`HugrView`], it can be +/// used interchangeably with [`SiblingGraph`]. /// /// [`SiblingGraph`]: super::SiblingGraph pub struct DescendantsGraph<'g, Root = Node, Base = Hugr> diff --git a/src/hugr/views/petgraph.rs b/src/hugr/views/petgraph.rs index 29de1506f..751b3ac4f 100644 --- a/src/hugr/views/petgraph.rs +++ b/src/hugr/views/petgraph.rs @@ -1,9 +1,6 @@ //! Implementations of petgraph's traits for Hugr Region views. -use super::{DescendantsGraph, SiblingGraph}; -use crate::hugr::views::sealed::HugrInternals; use crate::hugr::HugrView; -use crate::ops::handle::NodeHandle; use crate::ops::OpType; use crate::types::EdgeKind; use crate::{Node, Port}; @@ -12,176 +9,159 @@ use context_iterators::{ContextIterator, IntoContextIterator, MapWithCtx}; use petgraph::visit as pv; use portgraph::NodeIndex; -macro_rules! impl_region_petgraph_traits { - ($hugr:ident) => { - impl<'a, Root, Base> pv::GraphBase for $hugr<'a, Root, Base> - where - Root: NodeHandle, - Base: HugrInternals + HugrView, - { - type NodeId = Node; - type EdgeId = ((Node, Port), (Node, Port)); - } +/// Wrapper for a HugrView that implements petgraph's traits. +/// +/// It can be used to apply petgraph's algorithms to a Hugr. +#[derive(Debug, Clone, Copy)] +pub struct PetgraphWrapper<'a, T> { + pub(crate) hugr: &'a T, +} - impl<'a, Root, Base> pv::GraphProp for $hugr<'a, Root, Base> - where - Root: NodeHandle, - Base: HugrInternals + HugrView, - { - type EdgeType = petgraph::Directed; - } +impl<'a, T> pv::GraphBase for PetgraphWrapper<'a, T> +where + T: HugrView, +{ + type NodeId = Node; + type EdgeId = ((Node, Port), (Node, Port)); +} - impl<'a, Root, Base> pv::NodeCount for $hugr<'a, Root, Base> - where - Root: NodeHandle, - Base: HugrInternals + HugrView, - { - fn node_count(&self) -> usize { - HugrView::node_count(self) - } - } +impl<'a, T> pv::GraphProp for PetgraphWrapper<'a, T> +where + T: HugrView, +{ + type EdgeType = petgraph::Directed; +} - impl<'a, Root, Base> pv::NodeIndexable for $hugr<'a, Root, Base> - where - Root: NodeHandle, - Base: HugrInternals + HugrView, - { - fn node_bound(&self) -> usize { - HugrView::node_count(self) - } +impl<'a, T> pv::NodeCount for PetgraphWrapper<'a, T> +where + T: HugrView, +{ + fn node_count(&self) -> usize { + HugrView::node_count(self.hugr) + } +} - fn to_index(&self, ix: Self::NodeId) -> usize { - ix.index.into() - } +impl<'a, T> pv::NodeIndexable for PetgraphWrapper<'a, T> +where + T: HugrView, +{ + fn node_bound(&self) -> usize { + HugrView::node_count(self.hugr) + } - fn from_index(&self, ix: usize) -> Self::NodeId { - NodeIndex::new(ix).into() - } - } + fn to_index(&self, ix: Self::NodeId) -> usize { + ix.index.into() + } - impl<'a, Root, Base> pv::EdgeCount for $hugr<'a, Root, Base> - where - Root: NodeHandle, - Base: HugrInternals + HugrView, - { - fn edge_count(&self) -> usize { - HugrView::edge_count(self) - } - } + fn from_index(&self, ix: usize) -> Self::NodeId { + NodeIndex::new(ix).into() + } +} - impl<'a, Root, Base> pv::Data for $hugr<'a, Root, Base> - where - Root: NodeHandle, - Base: HugrInternals + HugrView, - { - type NodeWeight = OpType; - type EdgeWeight = EdgeKind; - } +impl<'a, T> pv::EdgeCount for PetgraphWrapper<'a, T> +where + T: HugrView, +{ + fn edge_count(&self) -> usize { + HugrView::edge_count(self.hugr) + } +} - impl<'g, 'a, Root, Base> pv::IntoNodeIdentifiers for &'g $hugr<'a, Root, Base> - where - Root: NodeHandle, - Base: HugrInternals + HugrView, - { - type NodeIdentifiers = <$hugr<'a, Root, Base> as HugrView>::Nodes<'g>; +impl<'a, T> pv::Data for PetgraphWrapper<'a, T> +where + T: HugrView, +{ + type NodeWeight = OpType; + type EdgeWeight = EdgeKind; +} - fn node_identifiers(self) -> Self::NodeIdentifiers { - self.nodes() - } - } +impl<'g, 'a, T> pv::IntoNodeReferences for &'g PetgraphWrapper<'a, T> +where + T: HugrView, +{ + type NodeRef = HugrNodeRef<'g>; + type NodeReferences = MapWithCtx<::Nodes<'g>, Self, HugrNodeRef<'g>>; + + fn node_references(self) -> Self::NodeReferences { + self.hugr + .nodes() + .with_context(self) + .map_with_context(|n, &wrapper| HugrNodeRef::from_node(n, wrapper.hugr)) + } +} - impl<'g, 'a, Root, Base> pv::IntoNodeReferences for &'g $hugr<'a, Root, Base> - where - Root: NodeHandle, - 'g: 'a, - Base: HugrInternals + HugrView, - { - type NodeRef = HugrNodeRef<'a>; - type NodeReferences = - MapWithCtx<<$hugr<'a, Root, Base> as HugrView>::Nodes<'a>, Self, HugrNodeRef<'a>>; - - fn node_references(self) -> Self::NodeReferences { - self.nodes() - .with_context(self) - .map_with_context(|n, &hugr| HugrNodeRef::from_node(n, hugr)) - } - } +impl<'g, 'a, T> pv::IntoNodeIdentifiers for &'g PetgraphWrapper<'a, T> +where + T: HugrView, +{ + type NodeIdentifiers = ::Nodes<'g>; - impl<'g, 'a, Root, Base> pv::IntoNeighbors for &'g $hugr<'a, Root, Base> - where - Root: NodeHandle, - Base: HugrInternals + HugrView, - { - type Neighbors = <$hugr<'a, Root, Base> as HugrView>::Neighbours<'g>; + fn node_identifiers(self) -> Self::NodeIdentifiers { + self.hugr.nodes() + } +} - fn neighbors(self, n: Self::NodeId) -> Self::Neighbors { - self.output_neighbours(n) - } - } +impl<'g, 'a, T> pv::IntoNeighbors for &'g PetgraphWrapper<'a, T> +where + T: HugrView, +{ + type Neighbors = ::Neighbours<'g>; - impl<'g, 'a, Root, Base> pv::IntoNeighborsDirected for &'g $hugr<'a, Root, Base> - where - Root: NodeHandle, - Base: HugrInternals + HugrView, - { - type NeighborsDirected = <$hugr<'a, Root, Base> as HugrView>::Neighbours<'g>; - - fn neighbors_directed( - self, - n: Self::NodeId, - d: petgraph::Direction, - ) -> Self::NeighborsDirected { - self.neighbours(n, d.into()) - } - } + fn neighbors(self, n: Self::NodeId) -> Self::Neighbors { + self.hugr.output_neighbours(n) + } +} - impl<'a, Root, Base> pv::Visitable for $hugr<'a, Root, Base> - where - Root: NodeHandle, - Base: HugrInternals + HugrView, - { - type Map = std::collections::HashSet; +impl<'g, 'a, T> pv::IntoNeighborsDirected for &'g PetgraphWrapper<'a, T> +where + T: HugrView, +{ + type NeighborsDirected = ::Neighbours<'g>; + + fn neighbors_directed( + self, + n: Self::NodeId, + d: petgraph::Direction, + ) -> Self::NeighborsDirected { + self.hugr.neighbours(n, d.into()) + } +} - fn visit_map(&self) -> Self::Map { - std::collections::HashSet::new() - } +impl<'a, T> pv::Visitable for PetgraphWrapper<'a, T> +where + T: HugrView, +{ + type Map = std::collections::HashSet; - fn reset_map(&self, map: &mut Self::Map) { - map.clear(); - } - } + fn visit_map(&self) -> Self::Map { + std::collections::HashSet::new() + } - impl<'a, Root, Base> pv::GetAdjacencyMatrix for $hugr<'a, Root, Base> - where - Root: NodeHandle, - Base: HugrInternals + HugrView, - { - type AdjMatrix = std::collections::HashSet<(Self::NodeId, Self::NodeId)>; - - fn adjacency_matrix(&self) -> Self::AdjMatrix { - let mut matrix = std::collections::HashSet::new(); - for node in self.nodes() { - for neighbour in self.output_neighbours(node) { - matrix.insert((node, neighbour)); - } - } - matrix - } + fn reset_map(&self, map: &mut Self::Map) { + map.clear(); + } +} - fn is_adjacent( - &self, - matrix: &Self::AdjMatrix, - a: Self::NodeId, - b: Self::NodeId, - ) -> bool { - matrix.contains(&(a, b)) +impl<'a, T> pv::GetAdjacencyMatrix for PetgraphWrapper<'a, T> +where + T: HugrView, +{ + type AdjMatrix = std::collections::HashSet<(Self::NodeId, Self::NodeId)>; + + fn adjacency_matrix(&self) -> Self::AdjMatrix { + let mut matrix = std::collections::HashSet::new(); + for node in self.hugr.nodes() { + for neighbour in self.hugr.output_neighbours(node) { + matrix.insert((node, neighbour)); } } - }; -} + matrix + } -impl_region_petgraph_traits!(SiblingGraph); -impl_region_petgraph_traits!(DescendantsGraph); + fn is_adjacent(&self, matrix: &Self::AdjMatrix, a: Self::NodeId, b: Self::NodeId) -> bool { + matrix.contains(&(a, b)) + } +} /// Reference to a Hugr node and its associated OpType. #[derive(Debug, Clone, Copy)] diff --git a/src/hugr/views/sibling.rs b/src/hugr/views/sibling.rs index 84532831a..86bc142d3 100644 --- a/src/hugr/views/sibling.rs +++ b/src/hugr/views/sibling.rs @@ -21,8 +21,8 @@ type FlatRegionGraph<'g> = portgraph::view::FlatRegion<'g, &'g MultiPortGraph>; /// /// See [`DescendantsGraph`] for a view that includes all descendants of the root. /// -/// Implements the [`HierarchyView`] trait, as well as [`HugrView`] and petgraph's -/// _visit_ traits, so can be used interchangeably with [`DescendantsGraph`]. +/// Implements the [`HierarchyView`] trait, as well as [`HugrView`], it can be +/// used interchangeably with [`DescendantsGraph`]. /// /// [`DescendantsGraph`]: super::DescendantsGraph pub struct SiblingGraph<'g, Root = Node, Base = Hugr>