-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Insert/extract subgraphs from a HugrView #552
Changes from 4 commits
48a5dc5
442ae3b
2d1f1ea
32d33e7
d3c6c7c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
use std::collections::HashMap; | ||
use std::ops::Range; | ||
|
||
use portgraph::view::{NodeFilter, NodeFiltered}; | ||
use portgraph::{LinkMut, NodeIndex, PortMut, PortView, SecondaryMap}; | ||
|
||
use crate::hugr::{Direction, HugrError, HugrView, Node, NodeType}; | ||
|
@@ -12,6 +13,7 @@ use crate::{Hugr, Port}; | |
|
||
use self::sealed::HugrMutInternals; | ||
|
||
use super::views::SiblingSubgraph; | ||
use super::{NodeMetadata, PortIndex, Rewrite}; | ||
|
||
/// Functions for low-level building of a HUGR. | ||
|
@@ -158,6 +160,23 @@ pub trait HugrMut: HugrView + HugrMutInternals { | |
self.hugr_mut().insert_from_view(root, other) | ||
} | ||
|
||
/// Copy a subgraph from another hugr into this one, under a given root node. | ||
/// | ||
/// Sibling order is not preserved. | ||
// | ||
// TODO: Try to preserve the order when possible? We cannot always ensure | ||
// it, since the subgraph may have arbitrary nodes without including their | ||
// parent. | ||
fn insert_subgraph( | ||
&mut self, | ||
root: Node, | ||
other: &impl HugrView, | ||
subgraph: &SiblingSubgraph, | ||
) -> Result<InsertionResult, HugrError> { | ||
self.valid_node(root)?; | ||
self.hugr_mut().insert_subgraph(root, other, subgraph) | ||
} | ||
|
||
/// Applies a rewrite to the graph. | ||
fn apply_rewrite<R, E>(&mut self, rw: impl Rewrite<ApplyResult = R, Error = E>) -> Result<R, E> | ||
where | ||
|
@@ -171,15 +190,21 @@ pub trait HugrMut: HugrView + HugrMutInternals { | |
/// via [HugrMut::insert_hugr] or [HugrMut::insert_from_view] | ||
pub struct InsertionResult { | ||
/// The node, after insertion, that was the root of the inserted Hugr. | ||
/// (That is, the value in [InsertionResult::node_map] under the key that was the [HugrView::root])) | ||
pub new_root: Node, | ||
/// | ||
/// That is, the value in [InsertionResult::node_map] under the key that was the [HugrView::root] | ||
/// | ||
/// When inserting a subgraph, this value is `None`. | ||
pub new_root: Option<Node>, | ||
/// Map from nodes in the Hugr/view that was inserted, to their new | ||
/// positions in the Hugr into which said was inserted. | ||
pub node_map: HashMap<Node, Node>, | ||
} | ||
|
||
impl InsertionResult { | ||
fn translating_indices(new_root: Node, node_map: HashMap<NodeIndex, NodeIndex>) -> Self { | ||
fn translating_indices( | ||
new_root: Option<Node>, | ||
node_map: HashMap<NodeIndex, NodeIndex>, | ||
) -> Self { | ||
Self { | ||
new_root, | ||
node_map: HashMap::from_iter(node_map.into_iter().map(|(k, v)| (k.into(), v.into()))), | ||
|
@@ -276,10 +301,13 @@ where | |
let optype = other.op_types.take(node); | ||
self.as_mut().op_types.set(new_node, optype); | ||
let meta = other.metadata.take(node); | ||
self.as_mut().set_metadata(node.into(), meta).unwrap(); | ||
self.as_mut().set_metadata(new_node.into(), meta).unwrap(); | ||
} | ||
debug_assert_eq!(Some(&other_root.index), node_map.get(&other.root().index)); | ||
Ok(InsertionResult::translating_indices(other_root, node_map)) | ||
Ok(InsertionResult::translating_indices( | ||
Some(other_root), | ||
node_map, | ||
)) | ||
} | ||
|
||
fn insert_from_view( | ||
|
@@ -294,11 +322,40 @@ where | |
self.as_mut().op_types.set(new_node, nodetype.clone()); | ||
let meta = other.get_metadata(node.into()); | ||
self.as_mut() | ||
.set_metadata(node.into(), meta.clone()) | ||
.set_metadata(new_node.into(), meta.clone()) | ||
.unwrap(); | ||
} | ||
debug_assert_eq!(Some(&other_root.index), node_map.get(&other.root().index)); | ||
Ok(InsertionResult::translating_indices(other_root, node_map)) | ||
Ok(InsertionResult::translating_indices( | ||
Some(other_root), | ||
node_map, | ||
)) | ||
} | ||
|
||
fn insert_subgraph( | ||
&mut self, | ||
root: Node, | ||
other: &impl HugrView, | ||
subgraph: &SiblingSubgraph, | ||
) -> Result<InsertionResult, HugrError> { | ||
// Create a portgraph view with the explicit list of nodes defined by the subgraph. | ||
let portgraph: NodeFiltered<_, NodeFilter<&[Node]>, &[Node]> = | ||
NodeFiltered::new_node_filtered( | ||
other.portgraph(), | ||
|node, ctx| ctx.contains(&node.into()), | ||
subgraph.nodes(), | ||
); | ||
let node_map = insert_hugr_internal_with_portgraph(self.as_mut(), root, other, &portgraph)?; | ||
// Update the optypes and metadata, copying them from the other graph. | ||
for (&node, &new_node) in node_map.iter() { | ||
let nodetype = other.get_nodetype(node.into()); | ||
self.as_mut().op_types.set(new_node, nodetype.clone()); | ||
let meta = other.get_metadata(node.into()); | ||
self.as_mut() | ||
.set_metadata(new_node.into(), meta.clone()) | ||
.unwrap(); | ||
} | ||
Ok(InsertionResult::translating_indices(None, node_map)) | ||
} | ||
} | ||
|
||
|
@@ -341,6 +398,37 @@ fn insert_hugr_internal( | |
Ok((other_root.into(), node_map)) | ||
} | ||
|
||
/// Internal implementation of the `insert_subgraph` method for AsMut<Hugr>. | ||
/// | ||
/// Returns a mapping from the nodes in the inserted graph to their new indices | ||
/// in `hugr`. | ||
/// | ||
/// This function does not update the optypes of the inserted nodes, so the | ||
/// caller must do that. | ||
/// | ||
/// In contrast to `insert_hugr_internal`, this function does not preserve | ||
/// sibling order in the hierarchy. | ||
fn insert_hugr_internal_with_portgraph( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand why this function is being defined instead of using the existing one? I assume in the existing function the graph insertion is done as a side effect, plus returning the inserted root node in the graph seems preferable to putting an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When inserting a subgraph, we may potentially include nodes from any part of the hugr. We then preserve the hierarchy when both parent and children are being inserted, but if a node's parent is missing then we connect it to the indicated Because of that, we cannot do the efficient traversal of As for the other point, note the definition of the
For a subgraph there is no previous root. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah I see! Can you add some of this to docstring please? And maybe something like |
||
hugr: &mut Hugr, | ||
root: Node, | ||
other: &impl HugrView, | ||
portgraph: &impl portgraph::LinkView, | ||
) -> Result<HashMap<NodeIndex, NodeIndex>, HugrError> { | ||
let node_map = hugr.graph.insert_graph(&portgraph)?; | ||
|
||
// A map for nodes that we inserted before their parent, so we couldn't | ||
// update the hierarchy with their new id. | ||
for (&node, &new_node) in node_map.iter() { | ||
let new_parent = other | ||
.get_parent(node.into()) | ||
.and_then(|parent| node_map.get(&parent.index).copied()) | ||
.unwrap_or(root.index); | ||
hugr.hierarchy.push_child(new_node, new_parent)?; | ||
} | ||
|
||
Ok(node_map) | ||
} | ||
|
||
pub(crate) mod sealed { | ||
use super::*; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was done in an earlier PR, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh no, that was outline_cfg. but same error 😲
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, seems like an easy to miss bug :/
And we never do anything with the metadata, so the error is never triggered.