Skip to content

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

feat: Add ResourceScope #1052

wants to merge 3 commits into from

Conversation

lmondada
Copy link
Contributor

@lmondada lmondada commented Aug 19, 2025

Alright, couldn't resist it. Here's a PR that moves a lot of the logic that I tried to put into portgraph into tket. 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)

@lmondada lmondada requested a review from aborgna-q August 19, 2025 14:32
@lmondada lmondada marked this pull request as ready for review August 19, 2025 14:36
@lmondada lmondada requested a review from a team as a code owner August 19, 2025 14:36
Copy link

codecov bot commented Aug 19, 2025

Codecov Report

❌ Patch coverage is 85.71429% with 57 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.45%. Comparing base (51bc0ec) to head (b07590e).

Files with missing lines Patch % Lines
tket/src/resource/types.rs 73.86% 23 Missing ⚠️
tket/src/resource/scope.rs 91.47% 13 Missing and 6 partials ⚠️
tket/src/resource/flow.rs 81.25% 6 Missing and 3 partials ⚠️
tket/src/resource.rs 85.00% 3 Missing and 3 partials ⚠️
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     
Flag Coverage Δ
python 81.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@lmondada
Copy link
Contributor Author

Three things on my mind

  • could we make this share more code/structs with the CircuitBuilder, esp. CircuitUnit?
  • idem, with the pytket encoder/decoder
  • imo this is a candidate to replace the Circuit struct if we get rid of it 😅

@lmondada lmondada requested review from acl-cqc and tatiana-s and removed request for aborgna-q August 22, 2025 12:14
Copy link
Contributor

@acl-cqc acl-cqc left a 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`].
Copy link
Contributor

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// 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
Copy link
Contributor

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
Copy link
Contributor

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.
Copy link
Contributor

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`].
Copy link
Contributor

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.
Copy link
Contributor

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 || {
Copy link
Contributor

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
Copy link
Contributor

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(
Copy link
Contributor

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....)

@acl-cqc
Copy link
Contributor

acl-cqc commented Aug 22, 2025

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...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants