-
Notifications
You must be signed in to change notification settings - Fork 8
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
Add SiblingSubgraph
view
#336
Conversation
I've been trying to implement the
For now, I've added a todo flag and will leave this for later. |
Hmmm. But if we want a general term, how about Area? |
Or maybe |
206577a
to
3b4a3cc
Compare
I like your ideas @acl-cqc , will try to come up with a sensible naming scheme. |
I've implemented HugrView here: #355 |
Hi @acl-cqc, I've made a few changes that build on top of #347, #323 and @doug-q 's #355, I think the PR is now in much better shape:
Regarding change 2: I find the file organisation much cleaner this way, apologies to @ss2165 for changing this twice in a row... I hope you will agree with me, but if you dislike the additional nesting or anything else, happy to revert the change of course! |
NB (important!): do not merge this until CQCL/portgraph#100 is merged, a new portgraph is released and EDIT: done. |
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.
Ok, thanks Luca, there is some fairly serious stuff here, although the really hard work of convexity checking is in Portgraph so I'm not looking at that :-). I've quite a few comments on restructuring though and TBH I'm not certain I've grokked all of what's here (hopefully I will after I've clarified the bits I'm commenting on). So, sorry for the long review, but I reckon we can make this significantly easier to understand and reason about, here goes....
src/hugr/views/sibling.rs
Outdated
//! | ||
//! Views into subgraphs of HUGRs within a single level of the | ||
//! hierarchy, i.e. within a sibling graph. Such a subgraph is | ||
//! given by a root node, of which all nodes in the sibling subgraph are |
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.
nit: given -> represented?
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.
Done
src/hugr/views/sibling.rs
Outdated
//! Views into subgraphs of HUGRs within a single level of the | ||
//! hierarchy, i.e. within a sibling graph. Such a subgraph is | ||
//! given by a root node, of which all nodes in the sibling subgraph are | ||
//! children, as well as a subgraph boundary, defining the separation between |
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.
maybe "a set of edges forming the subgraph boundary" ?
src/hugr/views/sibling.rs
Outdated
/// of the target of the edge. Source edges are not unique as they can involve | ||
/// copies. | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Into)] | ||
struct BoundaryEdge(Node, Port); |
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.
I wonder if it might be better to call this by what it is (maybe "TargetPort" or "InPort") rather than by what it's used for later - hopefully the uses will make that clear...
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.
Yes, I agree with you. I've opted instead to implement an actual boundary edge: now this struct also includes whether it's an incoming or outgoing boundary edge, and it panics if the edge does not exist.
src/hugr/views/sibling.rs
Outdated
struct BoundaryEdge(Node, Port); | ||
|
||
impl BoundaryEdge { | ||
fn target_port_index<G: PortView>(&self, g: &G) -> PortIndex { |
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.
maybe call this portgraph_index
or pg_index
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.
I've renamed to portgraph_target
, along with a bunch of other renames.
src/hugr/views/sibling.rs
Outdated
fn target_port_index<G: PortView>(&self, g: &G) -> PortIndex { | ||
let Node { index: node } = self.0; | ||
let Port { offset: port } = self.1; | ||
g.port_index(node, port).unwrap() |
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.
g.port_index(node, port).unwrap() | |
g.port_index(self.0.index, self.1.offset).unwrap() |
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.
Took your suggestion + some rewriting.
src/hugr/views/sibling.rs
Outdated
/// - a HugrView to the underlying HUGR, | ||
/// - a root node, of which all nodes in the [`SiblingSubgraph`] must be | ||
/// children, | ||
/// - incoming/outgoing boundary edges, pointing into/out of the subgraph. |
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.
nit: "incoming and outgoing" - making clear there are two sets
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.
Rewrote this.
src/hugr/views/sibling.rs
Outdated
/// The incoming/outgoing edges must be contained within the sibling graph of the | ||
/// root node. Their ordering matters when using the [`SiblingSubgraph`] for | ||
/// replacements, as it will match the ordering of the ports in the replacement. | ||
/// The list of incoming and/or outgoing ports may be empty. |
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.
...", in which case the SiblingSubgraph contains all children of the parent."
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.
Rewrote this.
src/hugr/views/sibling.rs
Outdated
/// `replacement` must be a hugr with DFG root and its signature must | ||
/// match the signature of the subgraph. | ||
/// | ||
/// Panics if |
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.
Note that specification/hugr.md
defines SimpleReplace as requiring the replaced nodes to be convex. That check isn't implemented there, but you could check that here. (That might reduce the utility of allowing non-convex SiblingSubgraphs, too...)
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.
I've changed create_simple_replacement
to return a Result
and to check that the subgraph is convex.
src/hugr/views/hierarchy.rs
Outdated
@@ -48,7 +48,7 @@ where | |||
graph: FlatRegionGraph<'g, Base>, | |||
|
|||
/// The rest of the HUGR. | |||
hugr: &'g Base, | |||
pub(super) hugr: &'g Base, |
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.
You can revert this now (yay!)
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.
indeed!
}); | ||
let to_pg = |(n, p): (Node, Port)| pg.port_index(n.index, p.offset).expect("invalid port"); | ||
let subpg = Subgraph::new_subgraph(pg, incoming.chain(outgoing).map(to_pg)); | ||
if !subpg.is_convex_with_checker(checker) { |
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.
presumably it's more efficient to do the convexity check this way, rather than by checker.is_node_convex
on nodes
below ? If the latter is ok, you could replace all these last checks by returning try_new_with_checker
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.
It's not more efficient, however doing the convexity on nodes only will miss some edge cases. Calling try_new_with_checker
after the convexity check with the nodes would do the convexity check twice, so I think this is the only way.
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.
Thanks @lmondada for running the marathon :) and some of my misreadings. (Maybe this wasn't a good first-task-after-a-holiday "gearchange"!)
I'm pretty happy with this now except that I think there is an issue with state edges and I'm not sure what we should do about that here.
Happy to drop that one, sorry for barking up the wrong tree. I've posted 4 more comments above, 3 of which I think are pretty small/straightforward. That leaves the state edges - it's possible that the best thing to do there is to leave that as a TODO, I'm not sure. |
(i.e. relating to #432) |
Co-authored-by: Alan Lawrence <[email protected]>
Not at all! All your remarks and questions were very pertinent @acl-cqc. Thank you so much! This review process was very necessary and more than worth it! If anything I'm sorry it took so much of your time! For the time being, I suggest leaving the order edges question open and merging this in. I've added a TODO for this. We will come back once we have a clear answer, or @aborgna-q and me figure out that we need order edges after all 😛 |
What I'd suggest for the time being is to panic if we find any order edges. I think there are two cases:
Other than that, I think all good, I'll have a final check through in the morning! |
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.
Looks great, thanks Luca!
src/hugr/views/sibling.rs
Outdated
/// The incoming boundary (resp. outgoing boundary) is given by the input (resp. | ||
/// output) ports of the subgraph that are linked to nodes outside of the subgraph. | ||
/// The signature of the subgraph is then given by the types of the incoming | ||
/// and outgoing boundary edges. Given a replacement with the same signature, |
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.
nit: edges
-> ports
? If I understand correctly and you include a node with a port that has many edges to outside the subgraph, the port gets included once, not once per edge?
src/hugr/views/sibling.rs
Outdated
/// | ||
/// More formally, the sibling subgraph defined by $B_I$ and $B_O$ is the | ||
/// graph given by the connected components of the graph with edges | ||
/// $E\B_I\B_O$ which contain at least one node that is either |
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.
nit: note we haven't defined $E$
here! I quite liked the "connected components of the graph G'=(V, E \ B_I \ B_O)` too as it's then clearer that "which contain at least one node..." refers to the connected components rather than the edges. (But it's not really ambiguous as edges don't contain nodes so ok.)
.outgoing_ports() | ||
.filter_map(|(n, p)| self.base.get_optype(n).signature().get(p).cloned()) | ||
.collect_vec(); | ||
FunctionType::new(input, output) |
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 yes, you can use FunctionType - nice :)
src/hugr/views/sibling.rs
Outdated
} | ||
let rep_inputs = replacement | ||
.node_outputs(rep_input) | ||
// filter out any non-dataflow ports |
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.
I'd probably panic here rather than filter out (then we'll know when the TODO comes into play) but ok
src/hugr/views/sibling.rs
Outdated
EmptySubgraph, | ||
} | ||
|
||
// /// A common trait for views of a HUGR sibling subgraph. |
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.
Probably just remove this? Unless you want to impl
it for SiblingGraph and SiblingSubgraph as a no-methods marker trait
src/hugr/views/sibling.rs
Outdated
/// | ||
/// This will return an [`InvalidSubgraph::EmptySubgraph`] error if the | ||
/// subgraph is empty. | ||
pub fn from_sibling_graph(sibling_graph: &'g Base) -> Result<Self, InvalidSubgraph> |
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.
nit: no need/point in making this pub
, as tests
is hidden. (For now!)
|
||
hugr.apply_rewrite(rep).unwrap(); | ||
|
||
assert_eq!(hugr.node_count(), 4); // Module + Def + In + Out |
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.
Would be good to have a contrasting assert before you apply the rewrite
Direction::Incoming => base.linked_ports(n, p).collect(), | ||
Direction::Outgoing => vec![(n, p)], | ||
}); | ||
let to_pg = |(n, p): (Node, Port)| pg.port_index(n.index, p.offset).expect("invalid port"); |
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 might be a place to look for state-order edges and panic(?) if you find them. Else you might comment here that we probably need to deal with such here too (?)
Co-authored-by: Alan Lawrence <[email protected]>
Preventively fixes CI errors before rust 1.72 is released, and rustfmt starts having opinions on let-else formatting. https://blog.rust-lang.org/2023/07/01/rustfmt-supports-let-else-statements.html
e583069
to
539d56e
Compare
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.
LGTM :)
No description provided.