Skip to content

support reset and measure in qasmparser - #402

Merged
jvdwetering merged 4 commits into
zxcalc:masterfrom
dlyongemallo:192-qasmparser_reset_measure
Feb 26, 2026
Merged

support reset and measure in qasmparser#402
jvdwetering merged 4 commits into
zxcalc:masterfrom
dlyongemallo:192-qasmparser_reset_measure

Conversation

@dlyongemallo

@dlyongemallo dlyongemallo commented Feb 5, 2026

Copy link
Copy Markdown
Contributor

Fixes #192. Relates to #345.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR adds support for reset and measure instructions in the OpenQASM parser, addressing issues #192 and #345. The implementation introduces a new Reset gate class and extends the parser to handle both single-qubit and register-broadcast forms of these instructions.

Changes:

  • Added Reset gate class that models reset as post-selection followed by state preparation in ZX-diagrams
  • Extended QASM parser to handle reset instruction for single qubits and entire registers
  • Enhanced measure instruction to support register broadcast syntax (measure q -> c)
  • Added classical register tracking and automatic declaration in QASM output
  • Implemented graph conversion logic to handle reset as effect + state vertices

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pyzx/circuit/gates.py Added Reset gate class with to_qasm, reposition, and equality methods; added to_qasm method for Measurement
pyzx/circuit/qasmparser.py Added parsing for reset and register-broadcast measure; added cregisters tracking
pyzx/circuit/graphparser.py Added circuit_to_graph logic for Reset (effect+state vertices); added graph_to_circuit detection of Reset vs InitAncilla
pyzx/circuit/init.py Added automatic classical register declaration inference from Measurement gates
tests/test_qasm.py Added comprehensive tests for reset and measure including QASM round-trips and graph conversions
tests/test_init_postselect.py Added unit tests for Reset gate functionality

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pyzx/circuit/gates.py
Comment thread pyzx/circuit/qasmparser.py Outdated
@jvdwetering

Copy link
Copy Markdown
Collaborator

This looks great, thanks!
I'm a little uncertain on whether post-selecting on <0| is the right semantics for reset. It is more like "discard" the qubit, and reset it, so that you don't care about what the measurement outcome is.
For instance, if you have a qubit in the |1> state, and you reset it with the current implementation, the whole diagram goes to zero, since you post-select to <0|.
Maybe a better implementation would be to introduce a symbolic variable that represents the measurement outcome, or even to make a spider attached to a ground.

@jvdwetering

Copy link
Copy Markdown
Collaborator

Yes, looking at the official documention of openqasm, discarding is indeed the right semantics: https://openqasm.com/versions/3.0/language/insts.html
image

@dlyongemallo

Copy link
Copy Markdown
Contributor Author

You're right. The DiscardBit pattern is already in the code and it makes sense to use it here, to be consistent with how it's done in openqasm.

@dlyongemallo
dlyongemallo force-pushed the 192-qasmparser_reset_measure branch 5 times, most recently from 0a31e54 to 28a58cc Compare February 14, 2026 21:56
@dlyongemallo

Copy link
Copy Markdown
Contributor Author

Based on suggestions in tqec/tqec#708, I added a test (HTHTHT) to guide the development of feedforward and classical control, and some tests for verifying/documenting interoperability with Stim.

@dlyongemallo
dlyongemallo force-pushed the 192-qasmparser_reset_measure branch 2 times, most recently from 53fc11a to 15a9496 Compare February 15, 2026 09:34
@jvdwetering

Copy link
Copy Markdown
Collaborator

Is this now ready to merge?
It might be worthwhile adding something to the AllFeatures notebook to explain these features and show what the resulting diagrams look like.

@dlyongemallo
dlyongemallo force-pushed the 192-qasmparser_reset_measure branch from 15a9496 to 7911198 Compare February 17, 2026 08:44
@dlyongemallo

Copy link
Copy Markdown
Contributor Author

I was hoping for more comments from the TQEC folks first, in case they had something to add, but it's ready to merge now. I think we can just merge this to move things along. It looks like you already have a volunteer to update the AllFeatures notebook in #374.

@KabirDubey

Copy link
Copy Markdown

Hi @dlyongemallo, I'm in the TQEC group, thank you for implementing this!! I was able to parse the HTHTHT... test circuit into ZX graph and also simplify it. However, both full_reduce and compute_pauli_webs(g, backwards=True) triggered the following error:

ValueError: No terminal matches '[' in the current parser
     context, at line 1 col 2

     c[0]
      ^
     Expected one of:
        * "⋅"
        *

Nodes from measurement gates have phase c[0], but the the Lark grammar only accepts CNAME ([a-zA-Z_][a-zA-Z0-9_]*), so c[0] fails on deserialization. Is this supposed to happen? If not, then may be worth adding a test so that classical registers survive the round trip between ZX graphs and json.

I was able to bypass the rule by customizing it rule to allow brackets. Then, full_reduce succeeded, but compute_pauli_webs_ raises ValueError: Graph must have gFlow. I can confirm the details in our group meeting tomorrow, but we expect this graph to have Pauli webs which can be found. Do you think this will be able to happen seamlessly once the classical feedforward response is being parsed as well? Thanks again!

@dlyongemallo

Copy link
Copy Markdown
Contributor Author

A circuit with measurements but no feedforward corrections represents a non-deterministic computation, so the lack of gFlow is expected. The test circuits added in this PR omits them because they haven't been implemented yet; that's PR #403. After that PR is merged, the gFlow problem should go away.

Can you tell me a bit more about where Lark comes in? I don't quite understand how you triggered the error.

@dlyongemallo
dlyongemallo force-pushed the 192-qasmparser_reset_measure branch from 7911198 to 5935ba1 Compare February 25, 2026 15:39
@dlyongemallo

Copy link
Copy Markdown
Contributor Author

Oh, never mind, I got it. All that's actually needed is a small change to symbolic.py.

@dlyongemallo

Copy link
Copy Markdown
Contributor Author

@jvdwetering I think this PR is ready to merge.

@KabirDubey Can you test if #403 fixes your gFlow problem? You can leave a comment in that PR if you have any issues.

Thanks.

@jbolns

jbolns commented Feb 26, 2026

Copy link
Copy Markdown

The DiscardBit pattern is already in the code and it makes sense to use it here, to be consistent with how it's done in openqasm.

Could I be absolutely picky and ask a little more context on why the pattern is kept even in the final graph?

I understand why it makes sense to process it. A QASM file will have such things, one needs to pick them up and do something with them.

I don't understand why it makes sense to then not to get rid of them.

What else could one do with the Z spiders circled in red? Furthermore, if one can get rid of those, is there a need for the lonely spider in the yellow box?

two_q_raw_w-gadgets

Please ignore the detached Ts. Those I added separate to QASM parsing. I forgot that picture was taken after doing that. The spiders in highlights come by default with the parsing.

@dlyongemallo

Copy link
Copy Markdown
Contributor Author

@jbolns Are you calling to_graph_ground explicitly instead of to_graph? Or calling to_graph with ground explicitly set to True?

Originally, the "ground-based representation" was the only way to convert measurements to a graph. PR #348 (by @Zhaoyilunnn) effectively renamed the original to_graph to to_graph_ground, and gated the new to_graph on the value of the ground parameter. By default, ground is False, which causes measurements to be represented as symbolic boolean phases. Setting ground to True preserves the old behaviour (and calls to_graph_ground).

I'm not sure if anyone is actually using the to_graph_ground path, but I kept it in this PR simply because it was already there. Other than the test I added to exercise this path, nothing I did actually uses the ground=True code path.

@Zhaoyilunnn Did you keep the legacy to_graph_ground for any reason?

@jvdwetering If it's not being used and was kept "just in case", can to_graph_ground be removed?

@jbolns

jbolns commented Feb 26, 2026

Copy link
Copy Markdown

@dlyongemallo

I used the standard Qiskit -> QASM -> PyZX (with c.to_graph()) pathway that I imagine most users would use. I wanted to check the full sequence could be realised.

It can, which is very good.

.

To be on the safe side, I just checked using the test you added in the separate commit. Result is indeed equal.

Bare import:
raw_original

Reduces to:
optim_original

To be clear, I am actually asking rather than a suggestion framed as a question (hate it when people do that, actually: if one has a suggestion, one makes a suggestion).

I do think the lonely spider seems unnecessary. Having said that, the other spiders I am indeed only asking about. Truth be said, they might even be needed for the actual computation (everything but the lone spider) to reduce nicely.

@jvdwetering

Copy link
Copy Markdown
Collaborator

I'm not sure what to do with the ground features. They were added a long time ago, but weren't really documented and nobody really uses them as far as I know, but it is a natural way to represent mixed processes in ZX-diagrams. My personal opinion is that using symbolic variables for representing indeterminancy is better for most cases, but I coul definitely see it being useful for some people to have the ground representation. So if it is not hurting anyone, then keeping it would be fine by me.

Someone would really need to document really well how to deal with non-determinism in pyzx at some point though.

@jvdwetering
jvdwetering merged commit 27dfba7 into zxcalc:master Feb 26, 2026
3 checks passed
@dlyongemallo
dlyongemallo deleted the 192-qasmparser_reset_measure branch February 27, 2026 03:40
@Zhaoyilunnn

Zhaoyilunnn commented Feb 27, 2026

Copy link
Copy Markdown
Contributor

@jbolns Are you calling to_graph_ground explicitly instead of to_graph? Or calling to_graph with ground explicitly set to True?

Originally, the "ground-based representation" was the only way to convert measurements to a graph. PR #348 (by @Zhaoyilunnn) effectively renamed the original to_graph to to_graph_ground, and gated the new to_graph on the value of the ground parameter. By default, ground is False, which causes measurements to be represented as symbolic boolean phases. Setting ground to True preserves the old behaviour (and calls to_graph_ground).

I'm not sure if anyone is actually using the to_graph_ground path, but I kept it in this PR simply because it was already there. Other than the test I added to exercise this path, nothing I did actually uses the ground=True code path.

@Zhaoyilunnn Did you keep the legacy to_graph_ground for any reason?

@jvdwetering If it's not being used and was kept "just in case", can to_graph_ground be removed?

Seems that Wetering prefer to keep it. At my side, I didn't keep it for a particular reason

@dlyongemallo

Copy link
Copy Markdown
Contributor Author

@Zhaoyilunnn Tangentially related to this issue: are you one of the maintainers of QASMBench? I'm using it to benchmark my qiskit-zx-transpiler, and the implementation of reset and ground in pyzx are actually related to that (dlyongemallo/qiskit-zx-transpiler#18). However, some of the benchmarking files have wrong variable names (pnnl/QASMBench#9) which prevents me from fully enabling all the circuits in my tests. Can you please merge the fix (pnnl/QASMBench#10) into the repo, or let me know who can? Thanks.

@KabirDubey

Copy link
Copy Markdown

@Zhaoyilunnn Tangentially related to this issue: are you one of the maintainers of QASMBench? I'm using it to benchmark my qiskit-zx-transpiler, and the implementation of reset and ground in pyzx are actually related to that (dlyongemallo/qiskit-zx-transpiler#18). However, some of the benchmarking files have wrong variable names (pnnl/QASMBench#9) which prevents me from fully enabling all the circuits in my tests. Can you please merge the fix (pnnl/QASMBench#10) into the repo, or let me know who can? Thanks.

For what it's worth, when I tested your parser, I sourced benchmarks from FTCircuitBench. I think it's slightly more likely to be maintained because it's more recent but it may not have the circuits you're looking for.

@Zhaoyilunnn

Copy link
Copy Markdown
Contributor

@Zhaoyilunnn Tangentially related to this issue: are you one of the maintainers of QASMBench? I'm using it to benchmark my qiskit-zx-transpiler, and the implementation of reset and ground in pyzx are actually related to that (dlyongemallo/qiskit-zx-transpiler#18). However, some of the benchmarking files have wrong variable names (pnnl/QASMBench#9) which prevents me from fully enabling all the circuits in my tests. Can you please merge the fix (pnnl/QASMBench#10) into the repo, or let me know who can? Thanks.

Sorry about the late response, unfortunately I am not a maintainer. As far as I know, you may reach out to https://github.com/uuudown

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.

support reset and measure in qasmparser

6 participants