-
Notifications
You must be signed in to change notification settings - Fork 8
feat: LocalizeEdges pass #2237
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?
feat: LocalizeEdges pass #2237
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2237 +/- ##
==========================================
+ Coverage 82.00% 82.17% +0.17%
==========================================
Files 234 238 +4
Lines 41618 43305 +1687
Branches 37532 39217 +1685
==========================================
+ Hits 34127 35587 +1460
- Misses 5518 5722 +204
- Partials 1973 1996 +23
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:
|
clippy objected to use of `mut` inside a debug_assert!, even though the mutated thing was local to the assert...
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.
Adding some comments, I still need to finish localize.rs
/// [ComposablePass] that converts all non-local edges in a Hugr | ||
/// into local ones, by inserting extra inputs to container nodes | ||
/// and extra outports to Input nodes. | ||
pub struct LocalizeEdges; |
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.
pub struct LocalizeEdges; | |
#[derive(Clone, Debug, Hash)] | |
pub struct LocalizeEdges; |
|
||
#[derive(derive_more::Error, derive_more::Display, derive_more::From, Debug, PartialEq)] | ||
#[non_exhaustive] | ||
pub enum LocalizeEdgesError {} |
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.
We really need to start checking for rustdocs...
Line 2 in 2d97665
#![expect(missing_docs)] // TODO: Fix... |
fn run(&self, hugr: &mut H) -> Result<Self::Result, Self::Error> { | ||
remove_nonlocal_edges(hugr) | ||
} | ||
} | ||
|
||
/// Returns an iterator over all non local edges in a 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.
drive-by: This is no longer "all nl edges", but the ones that interact with the entrypoint descendants.
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.
Right. Or rather, it's ones that are incoming to the subtree. So actually only one endpoint need be in the subtree.
So my thinking here was that there were two reasonable policies:
- All nonlocal edges within the Hugr, ignoring
hugr.entrypoint()
. - All nonlocal edges entirely beneath the entrypoint.
I meant to go for the latter, on the grounds that you can then get the former behaviour by passing entrypoint==root. Clearly I missed the latter ;-), I think the sensible thing is to put in that check.
However, if you think it'd be better to do this in stages (e.g. have two methods _root
and _entrypoint
, make the unsuffixed version delegate to _root
but @deprecate
it, and maybe eventually rename the _entrypoint
variant back to no-suffix) then happy to do that
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.
Agreed that "All nonlocal edges entirely beneath the entrypoint" is the way to go.
I think most passes should avoid out-of-tree modifications unless explicitly stated (e.g. defining a function in the module).
This should be compatible with the pre-entrypoint API.
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.
Agreed that "All nonlocal edges entirely beneath the entrypoint" is the way to go.
I think most passes should avoid out-of-tree modifications unless explicitly stated (e.g. defining a function in the module).
This should be compatible with the pre-entrypoint API.
hugr.linked_outputs(node, in_p) | ||
.any(|(neighbour_node, _)| parent != hugr.get_parent(neighbour_node)) | ||
.then_some((node, in_p)) | ||
let (src, _) = hugr.single_linked_output(node, in_p)?; |
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.
We are also now missing edges that connect out of the entrypoint descendants. (I realize it's annoying to check and avoid duplicates :/)
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.
Argh. Yes.
So my thinking was that for transformation, we should mutate the Hugr only beneath the entrypoint, in which case we only want edges with both ends beneath the entrypoint.
Counterexamples?
- An edge entering the subtree from a sibling of the entrypoint. This requires retargetting that edge to the entrypoint, and mutating the entrypoint node. Since no node outside the subtree is changed, I guess you could argue this is allowed, although I don't think I would myself....(looks to me like you've changed an edge outside the subtree!)
- Unless the entrypoint is a DataflowBlock, the only possible NL edge leaving it would be an
Ext
edge from the entrypoint itself (to a descendant of a sibling). I think that edge is outside the subtree....(indeed, localizing it does not even mutate the entrypoint node) - If the entrypoint is a Dataflow Block, then
Dom
edges could leave its children to (descendants of) other DFB siblings-of-the-entrypoint, i.e. outside the tree. But clearly localizing those is gonna involve widespread changes outside the subtree (as well as to the entrypoint node itself).
hugr-passes/src/non_local.rs
Outdated
// Group all the non-local edges in the graph by target node, | ||
// storing for each the source and type (well-defined as these are Value edges). | ||
let nonlocal_edges_map: HashMap<_, _> = nonlocal_edges(hugr) | ||
.filter_map(|(node, inport)| { |
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.
Why is this a filter_map
instead of a map
?
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 spot, we've already skipped over inports without a single_linked_output
in nonlocal_edges(hugr)
. Thanks!
hugr-passes/src/non_local.rs
Outdated
else { | ||
panic!("impossible") | ||
}; | ||
Some((node, (source, ty))) |
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.
Can't we have multiple nl edges going to the same node?
This will drop all but one while collect
ing
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.
Ooh good point. ("All compliments accrue to Doug but all shortcomings are my fault" 😉 😉 ). Yes, we dedup sources in the ExtraSourceReqs map (which is correct) but should not do so here. Thanks!
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.
Will add a test.
v.into_iter().map(|(_, t)| t.clone()) | ||
} | ||
|
||
pub fn remove_nonlocal_edges<H: HugrMut>(hugr: &mut H) -> Result<(), LocalizeEdgesError> { |
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.
Docs!
closes #1234
Based off @doug-q's #1912 but with "outside-in" transformation step avoiding much data storage (e.g. ParentSourceMap).