-
Notifications
You must be signed in to change notification settings - Fork 9
feat: PersistentHugr Walker API #2168
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
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2168 +/- ##
==========================================
+ Coverage 81.99% 82.15% +0.15%
==========================================
Files 237 239 +2
Lines 42767 42977 +210
Branches 38679 38889 +210
==========================================
+ Hits 35068 35307 +239
+ Misses 5706 5679 -27
+ Partials 1993 1991 -2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
a7729f3
to
cb812be
Compare
fd0c21f
to
3b27b4d
Compare
2206368
to
b81ed94
Compare
EDIT: resolved!
|
782e1ba
to
ca5d841
Compare
ca5d841
to
7874237
Compare
7874237
to
b1685fd
Compare
b1685fd
to
654c4fc
Compare
654c4fc
to
b4bae59
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.
Thanks @lmondada, this is great! Not what you'd call easy stuff, but great job in making it comprehensible :-). I have about 60 small suggestions, there are only two big enough to justify not immediately approving:
- Have I understand PinnedWire always has at least one endpoint pinned? I wasn't clear from the doc so I had to guess a bit, if that's not right then this may need some more thought
- That
equivalent_descendant_ports
method....what is all the direction-changing doing...
@@ -0,0 +1,624 @@ | |||
//! Exploration of a [`PersistentHugr`] through incremental traversal of |
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 agree with "exploration of a [CommitStateSpace
]" just below, but a PersistentHugr is a non-conflicting subgraph of a CommitStateSpace, right?
Maybe "Explores a CommitStateSpace (and allows) to establish/identify/build a PersistentHugr by incrementally" and then either "traversing connected subgraphs" as you have here (not sure if I've quite followed that yet though) or "pinning nodes resulting from particular rewrites" ?
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're right! How do you find the following
Incremental traversal and construction of [
PersistentHugr
]s from [CommitStateSpace
]s
?
It's hard to keep it to a high-level one-line summary
//! This module provides the [`Walker`] type, which enables intuitive | ||
//! exploration of a [`CommitStateSpace`] by traversing wires and gradually | ||
//! pinning (selecting) nodes that match a desired pattern. Unlike direct | ||
//! manipulation of a [`PersistentHugr`], the Walker presents a familiar |
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.
Not sure what this "direct manipulation of a PersistentHugr" is that is so different from the "familiar nodes-and-wires interface". PersistentHugr implements HugrView, which is exactly that familiar nodes-and-wires interface....?
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.
Sorry, PersistentHugr
-> CommitStateSpace
. I've added a bit more context to this sentence, does it make sense in its new form?
//! exploration typically branches out into several parallel walkers. | ||
//! 4. As walkers are expanded, more nodes get pinned, narrowing down the space | ||
//! of possible HUGRs. | ||
//! 5. Once exploration is complete (e.g. a pattern was fully matched), the |
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! Nice :)
//! 5. Once exploration is complete (e.g. a pattern was fully matched), the | ||
//! walker can be converted into a [`PersistentHugr`] instance using | ||
//! [`Walker::into_hugr`]. The matched nodes and ports can then be used to | ||
//! create a [`SimpleReplacement`](crate::SimpleReplacement) object, which |
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'll suspend disbelief here - the SimpleReplacement is the combination of all of the selected patches?
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.
No, you've done well to highlight this point. What I intended to say is that the pinned nodes can be used to specify a subgraph of the current PersistentHugr
, which in turn can be used to define a new SimpleReplacement
.
hugr-core/tests/walker.rs
Outdated
.contains(&pinned_node.1), | ||
"pinned node is deleted" | ||
); | ||
for walker in walker.expand(&wire, None) { |
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.
for walker in walker.expand(&wire, None) { | |
for subwalker in walker.expand(&wire, None) { |
hugr-core/tests/walker.rs
Outdated
|
||
// enqueue new wires added by the replacement | ||
// (this will also add a lot of already visited wires, but they will | ||
// be deduplicated) |
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.
IIUC, if you moved the outermost loop in enqueue_all
to the first callsite, then you could skip that loop here because you are adding only a single replacement? Would that address this comment?
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.
no -- you'd need to consider all commits that could be neighbours (but disjoint) with the newly added commit. It's a bit iffy, another area where the API will probably have to improve in the future.
} | ||
|
||
/// Get all pinned ports of the wire. | ||
pub fn all_ports(&self) -> impl Iterator<Item = (PatchNode, 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.
Consider calling these pinned_outport
, pinned_inports
and all_pinned_ports
...reading the client code it's easy to miss that you have a PinnedWire not a Wire
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.
Good names!
08e3eb0
to
7cd85d7
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.
Great stuff Luca, looks good to me, thanks! :). I think you can take it from here, indeed I think some of my comments actually pertain to code from other approved/merged PRs rather than this one...(but the opportunity to remove IteratorNonEmpty might be best taken before we forget 😉)
@@ -144,6 +144,65 @@ fn create_not_and_to_xor_replacement(hugr: &Hugr) -> SimpleReplacement { | |||
SimpleReplacement::try_new(subgraph, hugr, replacement_hugr).unwrap() | |||
} | |||
|
|||
/// Creates a state space with 4 commits on top of the base hugr `simple_hugr`: |
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 is great, but let's just note that these are not commits are not intended to be functionally equivalent (we do not mean that a and (not b)
is equivalent to a xor b
)
// incoming ports are of interest to us if | ||
// (i) they are connected to the output of a replacement (then there will be a | ||
// linked port in a parent commit), or | ||
// (ii) they are deleted by a child commit and is not the same as the out_node |
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.
// (ii) they are deleted by a child commit and is not the same as the out_node | |
// (ii) they are deleted by a child commit and are not (equal to) the out_node |
is -> are as we are talking "they" ;). TBH I think the "equal to" is unnecessary.
This is a really great comment, though, thank you :)
{ | ||
match opp_port.as_directed() { | ||
Either::Left(opp_port) => { | ||
let Some((n, p)) = self |
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.
super-nit: might be better to if let
rather than continue
} | ||
|
||
impl CommitStateSpace { | ||
/// Given a node and port, return all children commit of the current `node` |
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.
/// Given a node and port, return all children commit of the current `node` | |
/// Given a node and port, return all child commits of the current `node` |
|
||
impl CommitStateSpace { | ||
/// Given a node and port, return all children commit of the current `node` | ||
/// that delete `node` but do not delete a linked port of `(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.
What does a linked port of ...
mean?
any port linked to ...
? (if they deleted anything linked, they are not returned)?a specified port linked to ....
? (I don't think this is what you mean because I don't see anything specified)all ports linked to ....
(they can delete some ports linked to that, as long as there is at least one they don't delete)? I think that would be better expressed asthat delete ``node`` but keep at least one port linked to ``(node, port)``
- something else?
} | ||
} else { | ||
let commit = self.state_space.get_commit(commit_id).clone(); | ||
// TODO: we should be able to check for an AlreadyPinned error at |
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.
// TODO: we should be able to check for an AlreadyPinned error at | |
// TODO/Optimize: we should be able to check for an AlreadyPinned error at |
Closes #2074
Closes #2190