Skip to content

Commit

Permalink
feat!: impl HugrView for any &(mut) to a HugrView (#1678)
Browse files Browse the repository at this point in the history
closes #1636 .

Ideally we'd like a blanket implementation over all things that Deref to
anything that's a HugrView, but AFAICS this isn't possible in Rust ATM,
although might become so in the future given constrained HRTBs
(higher-ranked type bounds). So, implement manually for `&T`, `&mut T`,
`Rc<T>`, `Arc<T>`, `Box<T>`, `Cow<...,T>` and also `RootChecked`, using
a macro in views/impls.rs.

BREAKING CHANGE: types which implement AsRef<Hugr> - both library ones
such as Rc<Hugr> and custom ones - no longer get a blanket impl of
HugrView. Workaround by manually calling `as_ref()` and using the
`&Hugr` yourself.

---------

Co-authored-by: Douglas Wilson <141026920+doug-q@users.noreply.github.com>
  • Loading branch information
acl-cqc and doug-q authored Dec 4, 2024
1 parent 04073ca commit fee16d3
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 38 deletions.
6 changes: 5 additions & 1 deletion hugr-core/src/builder/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,11 @@ impl<B: AsMut<Hugr> + AsRef<Hugr>> BlockBuilder<B> {
Dataflow::set_outputs(self, [branch_wire].into_iter().chain(outputs))
}
fn create(base: B, block_n: Node) -> Result<Self, BuildError> {
let block_op = base.get_optype(block_n).as_dataflow_block().unwrap();
let block_op = base
.as_ref()
.get_optype(block_n)
.as_dataflow_block()
.unwrap();
let signature = block_op.inner_signature();
let db = DFGBuilder::create_with_io(base, block_n, signature)?;
Ok(BlockBuilder::from_dfg_builder(db))
Expand Down
95 changes: 91 additions & 4 deletions hugr-core/src/hugr/internal.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
//! Internal traits, not exposed in the public `hugr` API.
use std::borrow::Cow;
use std::ops::Range;
use std::rc::Rc;
use std::sync::Arc;

use delegate::delegate;
use portgraph::{LinkView, MultiPortGraph, PortMut, PortView};

use crate::ops::handle::NodeHandle;
Expand Down Expand Up @@ -31,28 +35,111 @@ pub trait HugrInternals {
fn root_node(&self) -> Node;
}

impl<T: AsRef<Hugr>> HugrInternals for T {
impl HugrInternals for Hugr {
type Portgraph<'p>
= &'p MultiPortGraph
where
Self: 'p;

#[inline]
fn portgraph(&self) -> Self::Portgraph<'_> {
&self.as_ref().graph
&self.graph
}

#[inline]
fn base_hugr(&self) -> &Hugr {
self.as_ref()
self
}

#[inline]
fn root_node(&self) -> Node {
self.as_ref().root.into()
self.root.into()
}
}

impl<T: HugrInternals> HugrInternals for &T {
type Portgraph<'p>
= T::Portgraph<'p>
where
Self: 'p;
delegate! {
to (**self) {
fn portgraph(&self) -> Self::Portgraph<'_>;
fn base_hugr(&self) -> &Hugr;
fn root_node(&self) -> Node;
}
}
}

impl<T: HugrInternals> HugrInternals for &mut T {
type Portgraph<'p>
= T::Portgraph<'p>
where
Self: 'p;
delegate! {
to (**self) {
fn portgraph(&self) -> Self::Portgraph<'_>;
fn base_hugr(&self) -> &Hugr;
fn root_node(&self) -> Node;
}
}
}

impl<T: HugrInternals> HugrInternals for Rc<T> {
type Portgraph<'p>
= T::Portgraph<'p>
where
Self: 'p;
delegate! {
to (**self) {
fn portgraph(&self) -> Self::Portgraph<'_>;
fn base_hugr(&self) -> &Hugr;
fn root_node(&self) -> Node;
}
}
}

impl<T: HugrInternals> HugrInternals for Arc<T> {
type Portgraph<'p>
= T::Portgraph<'p>
where
Self: 'p;
delegate! {
to (**self) {
fn portgraph(&self) -> Self::Portgraph<'_>;
fn base_hugr(&self) -> &Hugr;
fn root_node(&self) -> Node;
}
}
}

impl<T: HugrInternals> HugrInternals for Box<T> {
type Portgraph<'p>
= T::Portgraph<'p>
where
Self: 'p;
delegate! {
to (**self) {
fn portgraph(&self) -> Self::Portgraph<'_>;
fn base_hugr(&self) -> &Hugr;
fn root_node(&self) -> Node;
}
}
}

impl<T: HugrInternals + ToOwned> HugrInternals for Cow<'_, T> {
type Portgraph<'p>
= T::Portgraph<'p>
where
Self: 'p;
delegate! {
to self.as_ref() {
fn portgraph(&self) -> Self::Portgraph<'_>;
fn base_hugr(&self) -> &Hugr;
fn root_node(&self) -> Node;
}
}
}
/// Trait for accessing the mutable internals of a Hugr(Mut).
///
/// Specifically, this trait lets you apply arbitrary modifications that may
Expand Down
51 changes: 19 additions & 32 deletions hugr-core/src/hugr/views.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Read-only access into HUGR graphs and subgraphs.
pub mod descendants;
mod impls;
pub mod petgraph;
pub mod render;
mod root_checked;
Expand Down Expand Up @@ -519,41 +520,35 @@ impl ExtractHugr for &mut Hugr {
}
}

impl<T: AsRef<Hugr>> HugrView for T {
impl HugrView for Hugr {
#[inline]
fn contains_node(&self, node: Node) -> bool {
self.as_ref().graph.contains_node(node.pg_index())
self.graph.contains_node(node.pg_index())
}

#[inline]
fn node_count(&self) -> usize {
self.as_ref().graph.node_count()
self.graph.node_count()
}

#[inline]
fn edge_count(&self) -> usize {
self.as_ref().graph.link_count()
self.graph.link_count()
}

#[inline]
fn nodes(&self) -> impl Iterator<Item = Node> + Clone {
self.as_ref().graph.nodes_iter().map_into()
self.graph.nodes_iter().map_into()
}

#[inline]
fn node_ports(&self, node: Node, dir: Direction) -> impl Iterator<Item = Port> + Clone {
self.as_ref()
.graph
.port_offsets(node.pg_index(), dir)
.map_into()
self.graph.port_offsets(node.pg_index(), dir).map_into()
}

#[inline]
fn all_node_ports(&self, node: Node) -> impl Iterator<Item = Port> + Clone {
self.as_ref()
.graph
.all_port_offsets(node.pg_index())
.map_into()
self.graph.all_port_offsets(node.pg_index()).map_into()
}

#[inline]
Expand All @@ -563,54 +558,46 @@ impl<T: AsRef<Hugr>> HugrView for T {
port: impl Into<Port>,
) -> impl Iterator<Item = (Node, Port)> + Clone {
let port = port.into();
let hugr = self.as_ref();
let port = hugr

let port = self
.graph
.port_index(node.pg_index(), port.pg_offset())
.unwrap();
hugr.graph.port_links(port).map(|(_, link)| {
self.graph.port_links(port).map(|(_, link)| {
let port = link.port();
let node = hugr.graph.port_node(port).unwrap();
let offset = hugr.graph.port_offset(port).unwrap();
let node = self.graph.port_node(port).unwrap();
let offset = self.graph.port_offset(port).unwrap();
(node.into(), offset.into())
})
}

#[inline]
fn node_connections(&self, node: Node, other: Node) -> impl Iterator<Item = [Port; 2]> + Clone {
let hugr = self.as_ref();

hugr.graph
self.graph
.get_connections(node.pg_index(), other.pg_index())
.map(|(p1, p2)| {
[p1, p2].map(|link| hugr.graph.port_offset(link.port()).unwrap().into())
[p1, p2].map(|link| self.graph.port_offset(link.port()).unwrap().into())
})
}

#[inline]
fn num_ports(&self, node: Node, dir: Direction) -> usize {
self.as_ref().graph.num_ports(node.pg_index(), dir)
self.graph.num_ports(node.pg_index(), dir)
}

#[inline]
fn children(&self, node: Node) -> impl DoubleEndedIterator<Item = Node> + Clone {
self.as_ref().hierarchy.children(node.pg_index()).map_into()
self.hierarchy.children(node.pg_index()).map_into()
}

#[inline]
fn neighbours(&self, node: Node, dir: Direction) -> impl Iterator<Item = Node> + Clone {
self.as_ref()
.graph
.neighbours(node.pg_index(), dir)
.map_into()
self.graph.neighbours(node.pg_index(), dir).map_into()
}

#[inline]
fn all_neighbours(&self, node: Node) -> impl Iterator<Item = Node> + Clone {
self.as_ref()
.graph
.all_neighbours(node.pg_index())
.map_into()
self.graph.all_neighbours(node.pg_index()).map_into()
}
}

Expand Down
101 changes: 101 additions & 0 deletions hugr-core/src/hugr/views/impls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use std::{borrow::Cow, rc::Rc, sync::Arc};

use delegate::delegate;

use super::{HugrView, RootChecked};
use crate::{Direction, Hugr, Node, Port};

macro_rules! hugr_view_methods {
// The extra ident here is because invocations of the macro cannot pass `self` as argument
($arg:ident, $e:expr) => {
delegate! {
to ({let $arg=self; $e}) {
fn contains_node(&self, node: Node) -> bool;
fn node_count(&self) -> usize;
fn edge_count(&self) -> usize;
fn nodes(&self) -> impl Iterator<Item = Node> + Clone;
fn node_ports(&self, node: Node, dir: Direction) -> impl Iterator<Item = Port> + Clone;
fn all_node_ports(&self, node: Node) -> impl Iterator<Item = Port> + Clone;
fn linked_ports(
&self,
node: Node,
port: impl Into<Port>,
) -> impl Iterator<Item = (Node, Port)> + Clone;
fn node_connections(&self, node: Node, other: Node) -> impl Iterator<Item = [Port; 2]> + Clone;
fn num_ports(&self, node: Node, dir: Direction) -> usize;
fn children(&self, node: Node) -> impl DoubleEndedIterator<Item = Node> + Clone;
fn neighbours(&self, node: Node, dir: Direction) -> impl Iterator<Item = Node> + Clone;
fn all_neighbours(&self, node: Node) -> impl Iterator<Item = Node> + Clone;
}
}
}
}

impl<T: HugrView> HugrView for &T {
hugr_view_methods! {this, *this}
}

impl<T: HugrView> HugrView for &mut T {
hugr_view_methods! {this, &**this}
}

impl<T: HugrView> HugrView for Rc<T> {
hugr_view_methods! {this, this.as_ref()}
}

impl<T: HugrView> HugrView for Arc<T> {
hugr_view_methods! {this, this.as_ref()}
}

impl<T: HugrView> HugrView for Box<T> {
hugr_view_methods! {this, this.as_ref()}
}

impl<T: HugrView + ToOwned> HugrView for Cow<'_, T> {
hugr_view_methods! {this, this.as_ref()}
}

impl<H: AsRef<Hugr>, Root> HugrView for RootChecked<H, Root> {
hugr_view_methods! {this, this.as_ref()}
}

#[cfg(test)]
mod test {
use std::{rc::Rc, sync::Arc};

use crate::hugr::views::{DescendantsGraph, HierarchyView};
use crate::{Hugr, HugrView, Node};

struct ViewWrapper<H>(H);
impl<H: HugrView> ViewWrapper<H> {
fn nodes(&self) -> impl Iterator<Item = Node> + '_ {
self.0.nodes()
}
}

#[test]
fn test_refs_to_view() {
let h = Hugr::default();
let v = ViewWrapper(&h);
let c = h.nodes().count();
assert_eq!(v.nodes().count(), c);
let v2 = ViewWrapper(DescendantsGraph::<Node>::try_new(&h, h.root()).unwrap());
// v2 owns the DescendantsGraph, but that only borrows `h`, so we still have both
assert_eq!(v2.nodes().count(), v.nodes().count());
// And we can borrow the DescendantsGraph, even just a reference to that counts as a HugrView
assert_eq!(ViewWrapper(&v2.0).nodes().count(), v.nodes().count());

let vh = ViewWrapper(h);
assert_eq!(vh.nodes().count(), c);
let h: Hugr = vh.0;
assert_eq!(h.nodes().count(), c);

let vb = ViewWrapper(Box::new(&h));
assert_eq!(vb.nodes().count(), c);
let va = ViewWrapper(Arc::new(h));
assert_eq!(va.nodes().count(), c);
let h = Arc::try_unwrap(va.0).unwrap();
let vr = Rc::new(&h);
assert_eq!(ViewWrapper(&vr).nodes().count(), h.nodes().count());
}
}
19 changes: 18 additions & 1 deletion hugr-core/src/hugr/views/root_checked.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::marker::PhantomData;

use crate::hugr::internal::HugrMutInternals;
use delegate::delegate;
use portgraph::MultiPortGraph;

use crate::hugr::internal::{HugrInternals, HugrMutInternals};
use crate::hugr::{HugrError, HugrMut};
use crate::ops::handle::NodeHandle;
use crate::{Hugr, Node};
Expand Down Expand Up @@ -45,6 +48,20 @@ impl<Root> RootChecked<&mut Hugr, Root> {
}
}

impl<H: AsRef<Hugr>, Root> HugrInternals for RootChecked<H, Root> {
type Portgraph<'p>
= &'p MultiPortGraph
where
Self: 'p;
delegate! {
to self.as_ref() {
fn portgraph(&self) -> Self::Portgraph<'_>;
fn base_hugr(&self) -> &Hugr;
fn root_node(&self) -> Node;
}
}
}

impl<H: AsRef<Hugr>, Root: NodeHandle> RootTagged for RootChecked<H, Root> {
type RootHandle = Root;
}
Expand Down

0 comments on commit fee16d3

Please sign in to comment.