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 · 3 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

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

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
Projects
None yet
Development

No branches or pull requests

3 participants