feat!: Follow pass scopes in composable passes#1429
Conversation
a337a40 to
6119e18
Compare
|
This PR contains breaking changes to the public Rust API. cargo-semver-checks summary |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1429 +/- ##
==========================================
+ Coverage 79.56% 79.98% +0.42%
==========================================
Files 155 155
Lines 20245 20204 -41
Branches 19254 19213 -41
==========================================
+ Hits 16107 16160 +53
+ Misses 3186 3090 -96
- Partials 952 954 +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:
|
ca4ba4b to
8620e7e
Compare
cb4d32c to
2b29cdb
Compare
Updates the hugr dependencies to `0.26.0`, and fixes all the breaking changes. I only added no-op stubs implementing `WithScope` for the local passes. We will add the actual implementation in #1429. Requires updating LLVM too, so we should merge #1422 into this PR. We also need to delete the now removed `stack_array` lowering. BREAKING CHANGE: Updated public `hugr` dependency to `0.26.0`. Requires a `hugr-passes 0.26.1` patch release to include Quantinuum/hugr#2954 --------- Co-authored-by: George Hodgkins <[email protected]> Co-authored-by: Jake Arkinstall <[email protected]>
8620e7e to
899a12e
Compare
There was a problem hiding this comment.
This diff is a bit noisy since I cleaned up the definitions, but the logic should be the same;
we are just passing the scope to the internal calls, and using scope.root instead of the entrypoint in force_order.
e6b696e to
558bc91
Compare
acl-cqc
left a comment
There was a problem hiding this comment.
Thanks @aborgna-q this looks great - only one significant concern: https://github.com/Quantinuum/tket2/pull/1429/changes#r2974713146 which I think can be addressed by doing the (legacy!) "module-root support" much earlier, although have a think about whether we really wanna allow doing QSystemPass on an entrypoint scope.
Other than that a bunch of nits but nothing at all major :-)
| /// | ||
| /// Returns an error if the replacement fails. | ||
| pub fn lower_tk2_op(hugr: &mut impl HugrMut<Node = Node>) -> Result<Vec<Node>, LowerTk2Error> { | ||
| pub fn lower_tk2_op( |
There was a problem hiding this comment.
drive-by super-nit: this really should be lower_tk2_ops, there are many of them, it does not take an argument saying which one to lower!
There was a problem hiding this comment.
Renamed it. This is a breaking change already, so not bothering with deprecation.
| // Only perform multi-op replacement for global passes, as we | ||
| // cannot define new functions for local entrypoint scopes. | ||
| if let PassScope::Global(_) = scope { | ||
| let template = match funcs.entry(tket_op) { |
There was a problem hiding this comment.
isn't this funcs.entry(tket_op).get_or_insert_with(|| ...).clone()
There was a problem hiding this comment.
Almost, because build_func returns a Result.
| self.force_order(hugr)?; | ||
| } | ||
|
|
||
| // Backwards compatibility: If the entrypoint is a module, find a function named "main" and set that as |
There was a problem hiding this comment.
Yeah, so if we are running with PassScope::Entrypoint(Flat|Recursive) and the entrypoint is the module, then we've been doing nothing up to this point, and now we suddenly start running on just the main function...
I slightly question whether it even makes sense to run this on an entrypoint scope, i.e. whether this should even be a ComposablePass (or should just take a Preserve - to distinguish between library/executable modes); indeed whether it makes sense to run this on a library, or only post-linking. (Ok if we want to do LLVM linking then probably we do want to run this in library mode first).
There was a problem hiding this comment.
I mean, we say "expects the hugr to have a function entrypoint" - and if it doesn't, we'll error if we don't have a main. So (a) might want to mention this legacy support in the doc (or might not!); (b) we are committed not to be compiling a library, so definitely should use Preserve::Entrypoint (even if not in this PR), but def. should do this at the beginning I think.
There was a problem hiding this comment.
I do prefer having a standardized definition for passes. Perhaps we should have defined a "only global" pass type, but an alternative here could be just erroring out when called with a local scope (to avoid the user dealing with unexpected behaviour).
At this point I'm not sure what workflows are depending on the backwards compatibility for module entrypoints.
I didn't want to break things in this PR, but I agree that we should be erroring out if the entrypoint is not a function.
That may need some extra discussion and testing though to make sure we don't break downstream. I'll open an issue to decide what we should do here, but merge this with the compat option still in.
| use ModifierResolverErrors::*; | ||
|
|
||
| let entry_points: VecDeque<_> = entry_points.into_iter().collect(); | ||
| let entry_points: Vec<_> = entry_points.into_iter().collect(); |
There was a problem hiding this comment.
Why have you changed this VecDeque -> Vec ?
There was a problem hiding this comment.
It was a leftover from a previous change. Rolled it back to VecDeque.
tket/src/passes/borrow_squash.rs
Outdated
| while let Some(region) = regions.pop_front() { | ||
| let is_dataflow_region = OpTag::DataflowParent >= hugr.get_optype(region).tag(); | ||
| seen.clear(); | ||
| local_queue.clear(); |
There was a problem hiding this comment.
nit: define local_queue here, then no need to clear it. (We end with a while let Some(...) = local_queue.pop() so it really should be empty anyway)
Maybe call it op_queue too
There was a problem hiding this comment.
It's a simple opt to avoid reallocating memory on every loop.
We can just reuse the container instead.
| .flat_map(|n| all_outs(hugr, n)), | ||
| ); | ||
| for child in hugr.children(region) { | ||
| // If the node is not reachable along dataflow edges from other nodes, |
There was a problem hiding this comment.
I think this just repeats the comment two lines before
## 🤖 New release * `tket`: 0.17.0 -> 0.18.0 (✓ API compatible changes) * `tket-qsystem`: 0.23.0 -> 0.24.0 (✓ API compatible changes) <details><summary><i><b>Changelog</b></i></summary><p> ## `tket` <blockquote> ## [0.18.0](tket-v0.17.0...tket-v0.18.0) - 2026-04-02 ### Bug Fixes - *(pytket decoder)* Panic on repeated bit registers in pytket decoded output ([#1445](#1445)) - pytket encoder drops order edges to the output node ([#1466](#1466)) ### Documentation - Fix tket README introductory example ([#1463](#1463)) ### New Features - [**breaking**] Use raw Hugrs in pytket encoding/decoding API ([#1418](#1418)) - [**breaking**] Remove unused `lower_to_pytket` pass ([#1431](#1431)) - [**breaking**] Replace CircuitHash with hugr's implementation ([#1420](#1420)) - [**breaking**] Update MSRV to rust 1.91 ([#1446](#1446)) - [**breaking**] Update to hugr 0.26.0 ([#1448](#1448)) - [**breaking**] Follow pass scopes in composable passes ([#1429](#1429)) - Implemented `post_opdef` for `RotationOp` for constant folding ([#1468](#1468)) - [**breaking**] Reorganize `tket::passes` and add `hugr_passes` re-exports ([#1472](#1472)) - [**breaking**] Bump `hugr` dependency to 0.27.0 ([#1488](#1488)) - Move hugr-passes implementations to tket::passes ([#1487](#1487)) - Pass scopes in python API, update to hugr-py 0.16 ([#1464](#1464)) ### Refactor - *(llvm)* use llvm.is.fpclass for from_halfturns ([#1457](#1457)) ### Testing - Fixed signatures when decoding pytket circuits ([#1405](#1405)) </blockquote> ## `tket-qsystem` <blockquote> ## [0.24.0](tket-qsystem-v0.23.0...tket-qsystem-v0.24.0) - 2026-04-02 ### Bug Fixes - pytket encoder drops order edges to the output node ([#1466](#1466)) - Constant Folding with PassScope::Global should act globally, not just beneath the entrypoint ([#1470](#1470)) ### New Features - [**breaking**] Use raw Hugrs in pytket encoding/decoding API ([#1418](#1418)) - Add qsystem.rz pytket decoder ([#1432](#1432)) - [**breaking**] Update MSRV to rust 1.91 ([#1446](#1446)) - [**breaking**] Update to hugr 0.26.0 ([#1448](#1448)) - [**breaking**] Follow pass scopes in composable passes ([#1429](#1429)) - [**breaking**] Reorganize `tket::passes` and add `hugr_passes` re-exports ([#1472](#1472)) - Move hugr-passes implementations to tket::passes ([#1487](#1487)) </blockquote> </p></details> --- This PR was generated with [release-plz](https://github.com/release-plz/release-plz/). --------- Co-authored-by: Agustín Borgna <[email protected]>

Adds support for
PassScopes in the pass definitions. See Quantinuum/hugr#2772.Requires a hugr release with thePassScopedefinition and Quantinuum/hugr#2910.Many of these also call other passes fromhugr_passes. While we pass the scope config along, proper support requires Quantinuum/hugr#2836 and Quantinuum/hugr#2871.This is a rust-only change, the python interface will follow up.
BREAKING CHANGE: Multiple unit-like pass structs must now be constructed using a
::default()call instead.BREAKING CHANGE:
QSystemPassis now aComposablePass. Import the trait to callrun.BREAKING CHANGE:
QSystemPassno longer implementsCopy.BREAKING CHANGE: Renamed
tket_qsystem::extension::qsystem::lower_tket_optolower_tket_ops.