-
Notifications
You must be signed in to change notification settings - Fork 9
feat: Add ResourceScope #1052
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: Add ResourceScope #1052
Conversation
e58de1d
to
57a2f0a
Compare
57a2f0a
to
6b94a7d
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1052 +/- ##
==========================================
+ Coverage 78.19% 78.45% +0.26%
==========================================
Files 102 106 +4
Lines 12783 13175 +392
Branches 12504 12896 +392
==========================================
+ Hits 9996 10337 +341
- Misses 2130 2175 +45
- Partials 657 663 +6
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:
|
Three things on my mind
|
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 general I quite like this - the approach seems entirely reasonable within TKET, and perhaps a bit odd/overly-specialized in portgraph :). Haven't done a detailed code review but a few thoughts mostly on docs here.
My main question is - wtf CopyableValueID? Do you really want/use it, or did you just think you had to put something in for nonlinear ports? If the latter, would this PR work/make sense if you stripped it out?
//! the circuit. Each resource has a unique [`ResourceId`] and operations on | ||
//! the same resource are ordered by a [`Position`]. | ||
//! - **Copyable values**: Regular values that can be copied and discarded | ||
//! freely. Each is identified by a unique [`CopyableValueId`]. |
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 guess I will find out, but might be good to say that these are different/not-resource-tracked/something here.
pub trait ResourceFlow { | ||
/// Map resource IDs from operation inputs to outputs. | ||
/// | ||
/// Takes an operation type and the resource IDs of the operation's inputs. |
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.
/// Takes an operation type and the resource IDs of the operation's inputs. | |
/// Takes an [`OpType`] and a resource IDs for each linear input. | |
/// `None` is passed in place of any nonlinear inputs. | |
/// Returns.... |
/// The i-th entry is Some(resource_id) if the i-th port is a linear type, | ||
/// None otherwise. Returns the resource IDs of the operation's outputs | ||
/// in port order. Output resource IDs should be one of the input resource | ||
/// IDs for resource-preserving operations, or None for new resources or |
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'm inferring here that every None
is a new resource-id exactly if (iff) it's linear
|
||
/// Default implementation of ResourceFlow. | ||
/// | ||
/// This implementation considers that an operation is resource-preserving 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.
Maybe be a bit more explicit:
This implementation defines an operation as "resource-preserving" if:
* for all port indices i,
* if there is an input port of linear type, *or* an output port of linear type, then both input and output ports must exist and have that same linear type
For resource-preserving operations, linear inputs are then mapped to the corresponding output. (All outputs with no corresponding input, must be classical.)
For other operations, all input resources are discarded and all outputs will be given fresh resource IDs.
Can get that if-then out of the definition of resource-preserving by:
* for each port indices i, either
* input i or output i does not exist
* neither input i nor output i is linear
* both are linear and have the same type
a bit longer but I think I like the latter more?
@@ -0,0 +1,294 @@ | |||
//! Subgraph representation using intervals on resource paths. |
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.
Does this relate to intervals of lines in LineConvexChecker
? Not for this PR, but I have been wondering if LineConvexChecker
belongs in tket rather than hugr, too
//! | ||
//! - **Linear resources**: Non-copyable values that form resource paths through | ||
//! the circuit. Each resource has a unique [`ResourceId`] and operations on | ||
//! the same resource are ordered by a [`Position`]. |
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.
Say something like ResourceFlow allows to determine how resources are passed through, discarded or created by an op (for linear ports)
@@ -0,0 +1,596 @@ | |||
//! Main ResourceScope implementation for tracking resources in HUGR subgraphs. |
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: Can you drop the first line and just run with "Provides the [ResourceScope] struct which...." or does clippy/etc. complain?
) -> impl Iterator<Item = H::Node> + '_ { | ||
let mut curr_node = start_node; | ||
|
||
iter::from_fn(move || { |
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: does iter::successors
let you avoid the mut curr_node
?
// order. | ||
for node in toposort_subgraph(&self.hugr, &self.subgraph, self.find_sources()) { | ||
if self.hugr.get_optype(node).dataflow_signature().is_none() { | ||
// ignore non-dataflow ops |
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.
Whilst I think this is fine, I suspect non-dataflow nodes - like "dataflow" ops with no inputs or outputs - would just end any flows (and not start any new flows!) ?
} | ||
|
||
/// All copyable values on the ports of `node` in the given direction. | ||
pub fn get_copyable_values( |
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 note this isn't used....)
I mean, if this went into Portgraph, it'd have to do so without any mention of linearity, so probably leaving out any (useful!) implementation of the ResourceFlow trait entirely; that would then have to be done in tket... |
Alright, couldn't resist it. Here's a PR that moves a lot of the logic that I tried to put into
portgraph
intotket
. I think it makes much more sense this way.I'm interested to see what you think! (a lot of the diff is docs and snapshot tests, so don't worry :P)