Skip to content

RFC: Facilitation of squin → stim translation with more flexible Reset/MeasureAndReset statements #180

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
johnzl-777 opened this issue Apr 22, 2025 · 4 comments
Labels
breaking breaking changes or proposed changes that would break existing APIs enhancement New feature or request rfc Request for Comments squin squin related issues triage request for triage. A decision will be made on the issue or PR.

Comments

@johnzl-777
Copy link
Contributor

johnzl-777 commented Apr 22, 2025

This has some relation to the (nearly concluded?) Measurement RFC (#158 ) so I’ll do my best here to carry over what seems to be the consensus on that RFC over to what’s going on here.

Basically, while Measure in squin has the issue of not quite translating well to Stim (or at least, requiring a bit more complexity to do so), MeasureAndReset/Reset seem more restrictive in their definitions than what the Stim dialect can support.

For example, In the ongoing squin → stim rewrite #148 (which is moving towards codegen btw) I assumed a Reset in squin maps to an RZ in the stim dialect and MeasureAndReset is an MZ followed by an RZ in Stim. However, it seems to me potentially beneficial (the QEC folks would have to weigh in on this) to allow a user to, just like in the Measurement RFC, define a basis for Reset:

# Proposed modifications to the squin dialect
## in wire
class Reset:
	basis: ir.SSAValue = info.argument(OpType)
	wire: ir.SSAValue = info.argument(WireType)
	

## in qubit
class Reset:
	basis: ir.SSAValue = info.argument(OpType)
	qubits: ir.SSAValue = info.argument(IListType[Qubit, ...])

Now, as for MeasureAndReset I think the following could work (note the lack of “result” as a part of the statements):

# in wire
class MeasureAndReset:
	measure_basis: ir.SSAValue = info.argument(OpType)
	reset_basis: ir.SSAValue = info.argument(OpType)
	out_wire: ir.ResultValue = info.result(WireType)

# in qubit
class MeasureAndReset:
	measure_basis: ir.SSAValue = info.argument(OpType)
	reset_basis: ir.SSAValue = info.argument(OpType)
	qubits: ir.SSAValue = info.argument(ilist.IListType[QubitType])

Currently, RZ, RY, and RX exist in the Stim dialect along with MZ, MY, MX, MZZ, MYY, and MXX. I think the Stim dialect we have would also need MRZ, MRX, and MRY (which I understand to be Measure in this pauli basis, then reset to whatever you consider |0> in that basis - although now I wonder would we have to let the user specify that? That seems overly complex. Like MeasureAndReset(measure_basis = ..., reset_basis = ..., reset_value = ?))

If somebody chooses to do something like Measure in X and then reset to 0 in X, I can directly translate that to MRX but if somebody does something like Measure in Y and then Reset in Z, it would be interpreted as something like MY followed by RZ because there’s no native Stim representation for it.

I should say that the only compelling reason I thought of this optimization of going straight to a Stim Measure And Reset was because @ChenZhao44 mentioned there might be some performance benefit in Stim in cases where you can use a combined Measure and Reset instead of two separate statements.

@johnzl-777 johnzl-777 added breaking breaking changes or proposed changes that would break existing APIs enhancement New feature or request squin squin related issues labels Apr 22, 2025
@weinbe58
Copy link
Member

weinbe58 commented Apr 22, 2025

Where are the results of the measurements? In stim, they are implicitly stored but we need a value in squin to store the measurement result:

  1. The MeasureAndReset in the qubit dialect should return the measurement result
  2. The MeasureAndReset of the wire dialect also needs to return a measurement result. This is a terminator of the wire so if you want continue execution you need to unwrap the qubit again.

The wire dialect does lead to an interesting thing where you could have:

...
old_wire_0 = apply(...)
next_wire = unwrap(qubit[0])
measure_0 = ResetAndMeasure(..., old_wire_0)
op_next_wire_0 = apply(..., next_wire)
...

We can interpret the unwrap as happening after the reset but still a bit confusing.

@johnzl-777 johnzl-777 added the rfc Request for Comments label Apr 25, 2025
@johnzl-777
Copy link
Contributor Author

johnzl-777 commented Apr 25, 2025

Where are the results of the measurements? In stim, they are implicitly stored but we need a value in squin to store the measurement result:

  1. The MeasureAndReset in the qubit dialect should return the measurement result
  2. The MeasureAndReset of the wire dialect also needs to return a measurement result. This is a terminator of the wire so if you want continue execution you need to unwrap the qubit again.

Quite right, I must have been thinking too much on making things stim friendly versus letting squin not be so bound to what how stim operates.

I believe MeasureAndReset should become the following then, factoring in the corrections:

# in wire
## I understand there's a complication here with the WireTerminator behavior of the default Measure
class MeasureAndReset:
	measure_basis: ir.SSAValue = info.argument(OpType)
	reset_basis: ir.SSAValue = info.argument(OpType)
        wire: ir.SSAValue = info.argument(WireType)
        result: ir.ResultValue = info.result(types.Bool)
	out_wire: ir.ResultValue = info.result(WireType)

# in qubit
class MeasureAndReset:
	measure_basis: ir.SSAValue = info.argument(OpType)
	reset_basis: ir.SSAValue = info.argument(OpType)
	qubits: ir.SSAValue = info.argument(ilist.IListType[QubitType])
        result: ir.ResultValue = info.result(ilist.IListType[types.Bool])

I assume, per the latest developments on #158 , that MeasureAndReset in the qubit instance should be able to do the "shape alignment" as well? (that is, if you plug in one qubit, you get a standalone bool and if I plug in a list of them, I get a list of bools)?

The wire dialect does lead to an interesting thing where you could have:

...
old_wire_0 = apply(...)
next_wire = unwrap(qubit[0])
measure_0 = ResetAndMeasure(..., old_wire_0)
op_next_wire_0 = apply(..., next_wire)
...
We can interpret the unwrap as happening after the reset but still a bit confusing.

Wait, could you clarify why the statement is now ResetAndMeasure as opposed to having parity with MeasureAndReset? Is it literally because unwrap has to happen after reset?

"Under the hood" because Measure usually means you must wrap back to the qubit, does it mean for wire MeasureAndReset (if we assume I could break it down somehow) should look like the following:

bool_result = measure(wire_0) # this has WireTerminator trait, must immediately wrap things back
wrap(original_qubit, wire_0)
next_wire = unwrap(original_qubit) # get a new wire from the qubit we just wrapped back to previously
reset_wire = reset(next_wire) # safely apply reset and keep going
...

I also am still curious as (and I could see this being its own RFC but I wonder if I'm just overthinking things here) if we explicitly define which eigenstate Reset will go to or if we want to let users customize it (like in the Z basis, do you want to reset to |0> or |1>? I know practically every SDK goes to |0> but would it be worth allowing somebody to say I want |1>?)

@cduck
Copy link

cduck commented Apr 28, 2025

I don't understand this part @weinbe58:

The wire dialect does lead to an interesting thing where you could have:

...
old_wire_0 = apply(...)
next_wire = unwrap(qubit[0])
measure_0 = ResetAndMeasure(..., old_wire_0)
op_next_wire_0 = apply(..., next_wire)
...

We can interpret the unwrap as happening after the reset but still a bit confusing.

Doesn't this make much more sense? (And if multi-value return doesn't work then return a tuple.)

...
old_wire_0 = apply(...)
next_wire, measure_0 = MeasureAndReset(..., old_wire_0)
op_next_wire_0 = apply(..., next_wire)
...

@johnzl-777
Copy link
Contributor Author

johnzl-777 commented Apr 29, 2025

Just had a chat with @weinbe58 about the RFC in general, here are some conclusions we agreed upon:

NOTE: Phillip told me it would probably be best to go forward with putting this in triage which I think is best because I know the fluctuating nature of the statements in squin makes @david-pl 's work pretty difficult along with blocking work on #179

  • The wire dialect should not be what users interface with to build circuits (that's delegated to the wire dialect) (hope this answers part of @cduck 's concern over the somewhat clunkier proposal)
    • As a consequence of that, MeasureAndReset does not have to exist in the wire dialect. The wire just has to go back to a qubit -> get MeasureAndReset applied -> and then get the wire back.
  • My concern over allowing users the most flexibility in terms of choosing different measurement AND reset bases as well as which eigenstate of the basis they want to select is not something worth worrying about right now. Considering that Stim also has native Measure and Reset operations (although the basis indicated is the one you both measure AND reset to) I think the final version of the statements should be:
# in qubit
class MeasureAndReset:
        basis: ir.SSAValue = info.argument(OpType)
	qubits: ir.SSAValue = info.argument(ilist.IListType[QubitType])
         result: ir.ResultValue = info.result(ilist.IListType[types.Bool])

# no MeasureAndReset in wire dialect

# Reset initially proposed can stay

@johnzl-777 johnzl-777 added the triage request for triage. A decision will be made on the issue or PR. label Apr 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking breaking changes or proposed changes that would break existing APIs enhancement New feature or request rfc Request for Comments squin squin related issues triage request for triage. A decision will be made on the issue or PR.
Projects
None yet
Development

No branches or pull requests

3 participants