[qref 3.8] Convert subroutines in plxpr to qref catxpr#2781
Merged
paul0403 merged 19 commits intoMay 28, 2026
Conversation
10 tasks
paul0403
commented
May 11, 2026
paul0403
commented
May 11, 2026
rniczh
reviewed
May 27, 2026
paul0403
added a commit
that referenced
this pull request
Jun 2, 2026
…#2663) **Context:** Migrate capture frontend to produce reference semantics MLIR. This is the main PR. Each item will come into this PR as its own PR. **Description of the Change:** - Expose python bindings for operations in the `qref` MLIR dialect, and define their corresponding jax primitives and lowering rules on these primitives - The conversion from plxpr to catalyst jaxpr now produces qref primitives whenever possible. This includes allocation, deallocation, extract (now `qref.get`; note that reference semantics does not have an "insert" op), observables, gates and adjoints - For the following primitives, the conversion from the PL version to catalyst version is no longer done. Instead, we define lowering rule on the PL primitives directly: `for_loop`, `while_loop`, `if`, `adjoint`. This is because with reference semantics, their MLIR operation now take in and return purely classical values, so the special handling regarding the quantum values in their operands is not needed anymore. - At the beginning of the default pipeline, and at the beginning of various xdsl tools, the following passes are added (in this order): - `--canonicalize` - `--verify-no-quantum-use-after-free` - `--convert-to-value-semantics` - `--canonicalize` - `QubitHandler` (along with the entirety of `from_plxpr/qubit_handler.py`, and its tests `test_from_plxpr_qubit_handler.py`) has been removed 🥳 This was the machinery to convert reference semantics plxpr to value semantics catxpr, but this conversion is no longer needed. - Tests are edited correspondingly, mostly to update their expected results to check for reference semantics, i.e. all checks that are looking for `quantum.blah` ops in the mlir compiled from frontend now look for `qref.blah`. There are some tests that experienced extra updates: 1. In `pytest/from_plxpr/test_from_plxpr.py`: - Tests comparing catxpr generated from capture and legacy frontends are removed. They are converted to proper lit tests (under `frontend/tests/lit/test_qref`), checking for the qref MLIR generated from the capture path. As a result, the `compare_call_jaxpr` test util has been removed. Note that the checks for execution results on these tests are kept. - Tests checking for value semantics catalyst primitives have been updated to check for the corresponding qref primitives. 2. In `pytest/test_dynamic_qubit_alllocation.py`, dynamically allocated qubits from outside an adjoint can now be used by the adjoint, so the xfailed test is changed to a regular test 3. In `lit/test_from_plxpr.py`, there are many tests checking for the value semantcis qubit extract/insert topology. I have removed these tests as the `from_plxpr` conversion is no longer responsible for creating the value semantics extract/insert operations.The value semantics extract/insert op strategy is now exclusively documented and tested in the `--convert-to-value-semantics` pass itself. **Benefits:** 1. Significantly, and I mean truly significantly, simplifies the capture frontend. Many plxpr primitives are used directly, which is a big step towards lowering plxpr and removing catxpr from the pipeline altogether. 6. Many tech debts are automatically fixed. Here's an (incomplete) list: - Adjoints can take in dynamically allocated wires [sc-102216] - Fixes dataflow in value semantics: no longer missing an insert before gates on dynamically indexed wires #2526 - Fixes dataflow in value semantics: no longer missing an insert before observables on dynamically indexed wires #2527 - Passes that need to identify the qubit label does not have to walk back all the way back up the gate chain in value semantics. For example, judgement of non-commuting observables is much easier (to check if two observables are on the same qubit or not). 3. Greatly improve data flow around control flow/adjoint/subroutine regions. We no longer have to insert all qubits into their registers and let these regions take in the registers. Frontend just generates mlir where the regions see qref values from above via closure, and the `--convert-to-value-semantics` pass will only create value semantics regions that take in individual qubit values, instead of entire register values. **Related GitHub Issues:** closes #2526 [sc-112704] closes #2527 [sc-112706] - [x] [sc-115868]: alloc and observables #2664 - [x] [sc-115869]: gates #2672 , also handles basic dynamic qubit allocation - [x] [sc-115870]: for loops #2694 - [x] [sc-115871]: while loops #2717 - [x] [sc-115872]: if statements, also handles MCMs with reset #2740 - [x] [sc-115873]: adjoints #2720 - [x] [sc-115874]: subroutines and calls #2781 - [x] [sc-118243] xdsl pipeline converts qref mlir to value seamntics at the beginning #2757 - [x] [sc-119731] graph decomp frontend is reference semantics #2834 - [x] update old frontend lit tests to check for `qref` instead of `quantum` #2878 --------- Co-authored-by: albi3ro <chrissie.c.l@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context:
Convert subroutines in plxpr to qref catxpr.
The use of
QubitHandleris removed, since both plxpr and catalyst jaxpr is now reference semantics.When calling a subroutine, there are some different cases. In python, a subroutine is always called with "wires", which can either be integers or dynamically allocated wires:
In plxpr, dynamic allocations return a specific
AbstractQubittype instead of a generic integer type (PennyLaneAI/pennylane#9400). This means in the signature of the subroutine function (and the corresponding calls), the arguments can either be integers or an abstract qubit type.When the arguments are integers, we must not implicitly convert the func args to
qref.bittypes, because the subroutine function can use them as actual integers as well. Therefore, on top of the integer, the global register must also be taken in.When arguments are
AbstractQubittypes, the args can directly beqref.bittypes. Therefore the above will compile toIn this scheme, inside the subroutine:
qref.bitargs. When both extract indices are dynamic, we directly assert that they are different inside the value semantics conversion pass. In cases of dynamic indices, we add ancatalyst.assertop to check they are different in runtime.[sc-115874]