Skip to content

Commit

Permalink
Add SPP{_DAG} gate and some crumble fixes (#720)
Browse files Browse the repository at this point in the history
- Fix crumble not falling back to emulated keyboard when copy works but
paste fails
- Fix crumble XCZ ordering
- Fix not being able to hold 'e' or 'q' to zip around in crumble
- Add "generalized S gate" `SPP`
- Add `SPP_DAG` gate
- Add `stim.GateData.flows`

Fixes #709
  • Loading branch information
Strilanc authored Mar 18, 2024
1 parent 26416fc commit ca1fbc3
Show file tree
Hide file tree
Showing 80 changed files with 2,159 additions and 764 deletions.
37 changes: 37 additions & 0 deletions dev/canvas_with_texture_for_3d_diagrams.html
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,19 @@
ctx.fillText('ERASE', x * 32 + 16 - ctx.measureText('ERASE').width / 2, y * 32 + 18);
}

function drawCpp(ctx, c1, c2, i) {
let {x, y} = pickRect(i);
ctx.fillStyle = '#' + (c1 === 'X' || c2 === 'X' ? 'f' : '4') + (c1 === 'Y' || c2 === 'Y' ? 'f' : '4') + (c1 === 'Z' || c2 === 'Z' ? 'f' : '4');
ctx.fillRect(x * 32, y * 32, 32, 32);
ctx.fillStyle = 'black';
ctx.font = '12pt serif';
let t1 = 'CPP';
ctx.fillText(t1, x * 32 + 16 - ctx.measureText(t1).width / 2, y * 32 + 5);
let t2 = c1 + ':' + c2;
ctx.font = '12pt serif';
ctx.fillText(t2, x * 32 + 16 - ctx.measureText(t2).width / 2, y * 32 + 18);
}

function drawHeraldPauliError1(ctx, i) {
let {x, y} = pickRect(i);
ctx.fillStyle = 'black';
Expand Down Expand Up @@ -203,6 +216,30 @@
drawRect(ctx, "M_PAD", "#888", "#000", n++);
drawHeraldErase(ctx, n++);
drawHeraldPauliError1(ctx, n++);

drawRect(ctx, "SPP_X", "#F44", "#000", n++);
drawRect(ctx, "SPP_Y", "#4F4", "#000", n++);
drawRect(ctx, "SPP_Z", "#44F", "#000", n++);
drawRect(ctx, "SPP_X†", "#F44", "#000", n++);
drawRect(ctx, "SPP_Y†", "#4F4", "#000", n++);
drawRect(ctx, "SPP_Z†", "#44F", "#000", n++);

n = 128 + 48;
drawCpp(ctx, 'I', 'X', n++);
drawCpp(ctx, 'I', 'Y', n++);
drawCpp(ctx, 'I', 'Z', n++);
drawCpp(ctx, 'X', 'I', n++);
drawCpp(ctx, 'X', 'X', n++);
drawCpp(ctx, 'X', 'Y', n++);
drawCpp(ctx, 'X', 'Z', n++);
drawCpp(ctx, 'Y', 'I', n++);
drawCpp(ctx, 'Y', 'X', n++);
drawCpp(ctx, 'Y', 'Y', n++);
drawCpp(ctx, 'Y', 'Z', n++);
drawCpp(ctx, 'Z', 'I', n++);
drawCpp(ctx, 'Z', 'X', n++);
drawCpp(ctx, 'Z', 'Y', n++);
drawCpp(ctx, 'Z', 'Z', n++);
}

draw(document.getElementById('cv').getContext('2d'))
Expand Down
261 changes: 204 additions & 57 deletions doc/gates.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
- [Z_ERROR](#Z_ERROR)
- Collapsing Gates
- [M](#M)
- [MPP](#MPP)
- [MR](#MR)
- [MRX](#MRX)
- [MRY](#MRY)
Expand All @@ -78,6 +77,10 @@
- [MXX](#MXX)
- [MYY](#MYY)
- [MZZ](#MZZ)
- Generalized Pauli Product Gates
- [MPP](#MPP)
- [SPP](#SPP)
- [SPP_DAG](#SPP_DAG)
- Control Flow
- [REPEAT](#REPEAT)
- Annotations
Expand Down Expand Up @@ -2435,62 +2438,6 @@ Decomposition (into H, S, CX, M, R):
# (The decomposition is trivial because this gate is in the target gate set.)


<a name="MPP"></a>
### The 'MPP' Instruction

Measure Pauli products.

Parens Arguments:

Optional.
A single float specifying the probability of flipping each reported measurement result.

Targets:

A series of Pauli products to measure.

Each Pauli product is a series of Pauli targets (`[XYZ]#`) separated by combiners (`*`).
Products can be negated by prefixing a Pauli target in the product with an inverter (`!`)

Examples:

# Measure the two-body +X1*Y2 observable.
MPP X1*Y2

# Measure the one-body -Z5 observable.
MPP !Z5

# Measure the two-body +X1*Y2 observable and also the three-body -Z3*Z4*Z5 observable.
MPP X1*Y2 !Z3*Z4*Z5

# Noisily measure +Z1+Z2 and +X1*X2 (independently flip each reported result 0.1% of the time).
MPP(0.001) Z1*Z2 X1*X2

Stabilizer Generators (for `MPP X0*Y1*Z2 X3*X4`):

XYZ__ -> rec[-2]
___XX -> rec[-1]
X____ -> X____
_Y___ -> _Y___
__Z__ -> __Z__
___X_ -> ___X_
____X -> ____X
ZZ___ -> ZZ___
_XX__ -> _XX__
___ZZ -> ___ZZ

Decomposition (into H, S, CX, M, R):

# The following circuit is equivalent (up to global phase) to `MPP X0*Y1*Z2 X3*X4`
S 1 1 1
H 0 1 3 4
CX 2 0 1 0 4 3
M 0 3
CX 2 0 1 0 4 3
H 0 1 3 4
S 1


<a name="MR"></a>
### The 'MR' Instruction

Expand Down Expand Up @@ -3027,6 +2974,206 @@ Decomposition (into H, S, CX, M, R):
CX 0 1


## Generalized Pauli Product Gates

<a name="MPP"></a>
### The 'MPP' Instruction

Measures general pauli product operators, like X1*Y2*Z3.

Parens Arguments:

An optional failure probability.
If no argument is given, all measurements are perfect.
If one argument is given, it's the chance of reporting measurement results incorrectly.

Targets:

A series of Pauli products to measure.

Each Pauli product is a series of Pauli targets (like `X1`, `Y2`, or `Z3`) separated by
combiners (`*`). Each Pauli term can be inverted (like `!Y2` instead of `Y2`). A negated
product will record the opposite measurement result.

Note that, although you can write down instructions that measure anti-Hermitian products,
like `MPP X1*Z1`, doing this will cause exceptions when you simulate or analyze the
circuit since measuring an anti-Hermitian operator doesn't have well defined semantics.

Using overly-complicated Hermitian products, like saying `MPP X1*Y1*Y2*Z2` instead of
`MPP !Z1*X2`, is technically allowed. But probably not a great idea since tools consuming
the circuit may have assumed that each qubit would appear at most once in each product.

Examples:

# Measure the two-body +X1*Y2 observable.
MPP X1*Y2

# Measure the one-body -Z5 observable.
MPP !Z5

# Measure the two-body +X1*Y2 observable and also the three-body -Z3*Z4*Z5 observable.
MPP X1*Y2 !Z3*Z4*Z5

# Noisily measure +Z1+Z2 and +X1*X2 (independently flip each reported result 0.1% of the time).
MPP(0.001) Z1*Z2 X1*X2

Stabilizer Generators (for `MPP X0*Y1*Z2 X3*X4`):

XYZ__ -> rec[-2]
___XX -> rec[-1]
X____ -> X____
_Y___ -> _Y___
__Z__ -> __Z__
___X_ -> ___X_
____X -> ____X
ZZ___ -> ZZ___
_XX__ -> _XX__
___ZZ -> ___ZZ

Decomposition (into H, S, CX, M, R):

# The following circuit is equivalent (up to global phase) to `MPP X0*Y1*Z2 X3*X4`
S 1 1 1
H 0 1 3 4
CX 2 0 1 0 4 3
M 0 3
CX 2 0 1 0 4 3
H 0 1 3 4
S 1


<a name="SPP"></a>
### The 'SPP' Instruction

The generalized S gate. Phases the -1 eigenspace of Pauli product observables by i.

Parens Arguments:

This instruction takes no parens arguments.

Targets:

A series of Pauli products to phase.

Each Pauli product is a series of Pauli targets (like `X1`, `Y2`, or `Z3`) separated by
combiners (`*`). Each Pauli term can be inverted (like `!Y2` instead of `Y2`), to negate
the product.

Note that, although you can write down instructions that phase anti-Hermitian products,
like `SPP X1*Z1`, doing this will cause exceptions when you simulate or analyze the
circuit since phasing an anti-Hermitian operator doesn't have well defined semantics.

Using overly-complicated Hermitian products, like saying `SPP X1*Y1*Y2*Z2` instead of
`SPP !Z1*X2`, is technically allowed. But probably not a great idea since tools consuming
the circuit may have assumed that each qubit would appear at most once in each product.

Examples:

# Perform an S gate on qubit 1.
SPP Z1

# Perform a SQRT_X gate on qubit 1.
SPP X1

# Perform a SQRT_X_DAG gate on qubit 1.
SPP !X1

# Perform a SQRT_XX gate between qubit 1 and qubit 2.
SPP X1*X2

# Perform a SQRT_YY gate between qubit 1 and 2, and a SQRT_ZZ_DAG between qubit 3 and 4.
SPP Y1*Y2 !Z1*Z2

# Phase the -1 eigenspace of -X1*Y2*Z3 by i.
SPP !X1*Y2*Z3

Stabilizer Generators (for `SPP X0*Y1*Z2`):

X__ -> X__
Z__ -> -YYZ
_X_ -> -XZZ
_Z_ -> XXZ
__X -> XYY
__Z -> __Z

Decomposition (into H, S, CX, M, R):

# The following circuit is equivalent (up to global phase) to `SPP X0*Y1*Z2`
CX 2 1
CX 1 0
S 1
S 1
H 1
CX 1 0
CX 2 1


<a name="SPP_DAG"></a>
### The 'SPP_DAG' Instruction

The generalized S gate. Phases the -1 eigenspace of Pauli product observables by i.

Parens Arguments:

This instruction takes no parens arguments.

Targets:

A series of Pauli products to phase.

Each Pauli product is a series of Pauli targets (like `X1`, `Y2`, or `Z3`) separated by
combiners (`*`). Each Pauli term can be inverted (like `!Y2` instead of `Y2`), to negate
the product.

Note that, although you can write down instructions that phase anti-Hermitian products,
like `SPP X1*Z1`, doing this will cause exceptions when you simulate or analyze the
circuit since phasing an anti-Hermitian operator doesn't have well defined semantics.

Using overly-complicated Hermitian products, like saying `SPP X1*Y1*Y2*Z2` instead of
`SPP !Z1*X2`, is technically allowed. But probably not a great idea since tools consuming
the circuit may have assumed that each qubit would appear at most once in each product.

Examples:

# Perform an S_DAG gate on qubit 1.
SPP_DAG Z1

# Perform a SQRT_X_DAG gate on qubit 1.
SPP_DAG X1

# Perform a SQRT_X gate on qubit 1.
SPP_DAG !X1

# Perform a SQRT_XX_DAG gate between qubit 1 and qubit 2.
SPP_DAG X1*X2

# Perform a SQRT_YY_DAG gate between qubit 1 and 2, and a SQRT_ZZ between qubit 3 and 4.
SPP_DAG Y1*Y2 !Z1*Z2

# Phase the -1 eigenspace of -X1*Y2*Z3 by -i.
SPP_DAG !X1*Y2*Z3

Stabilizer Generators (for `SPP_DAG X0*Y1*Z2`):

X__ -> X__
Z__ -> YYZ
_X_ -> XZZ
_Z_ -> -XXZ
__X -> -XYY
__Z -> __Z

Decomposition (into H, S, CX, M, R):

# The following circuit is equivalent (up to global phase) to `SPP_DAG X0*Y1*Z2`
CX 2 1
CX 1 0
H 1
S 1
S 1
CX 1 0
CX 2 1


## Control Flow

<a name="REPEAT"></a>
Expand Down
45 changes: 45 additions & 0 deletions doc/python_api_reference_vDev.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
- [`stim.GateData.__repr__`](#stim.GateData.__repr__)
- [`stim.GateData.__str__`](#stim.GateData.__str__)
- [`stim.GateData.aliases`](#stim.GateData.aliases)
- [`stim.GateData.flows`](#stim.GateData.flows)
- [`stim.GateData.generalized_inverse`](#stim.GateData.generalized_inverse)
- [`stim.GateData.inverse`](#stim.GateData.inverse)
- [`stim.GateData.is_noisy_gate`](#stim.GateData.is_noisy_gate)
Expand Down Expand Up @@ -7501,6 +7502,50 @@ def aliases(
"""
```

<a name="stim.GateData.flows"></a>
```python
# stim.GateData.flows

# (in class stim.GateData)
@property
def flows(
self,
) -> Optional[List[stim.Flow]]:
"""Returns stabilizer flow generators for the gate, or else None.
A stabilizer flow describes an input-output relationship that the gate
satisfies, where an input pauli string is transformed into an output
pauli string mediated by certain measurement results.
Caution: this method returns None for variable-target-count gates like MPP.
Not because MPP has no stabilizer flows, but because its stabilizer flows
depend on how many qubits it targets and what basis it targets them in.
Returns:
A list of stim.Flow instances representing the generators.
Examples:
>>> import stim
>>> stim.gate_data('H').flows
[stim.Flow("X -> Z"), stim.Flow("Z -> X")]
>>> for e in stim.gate_data('ISWAP').flows:
... print(e)
X_ -> ZY
Z_ -> _Z
_X -> YZ
_Z -> Z_
>>> for e in stim.gate_data('MXX').flows:
... print(e)
X_ -> X_
_X -> _X
ZZ -> ZZ
XX -> rec[-1]
"""
```

<a name="stim.GateData.generalized_inverse"></a>
```python
# stim.GateData.generalized_inverse
Expand Down
Loading

0 comments on commit ca1fbc3

Please sign in to comment.