Skip to content

Commit cb7e771

Browse files
authored
Add some circuit and flip sim utility methods (#991)
- Add `stim.Circuit.missing_detectors` - Add `unknown_input=False` argument to `stim.Circuit.count_determined_measurements` - Add `stim.FlipSimulator.append_measurement_flips` Fixes #973 Fixes #981 Fixes #987
1 parent 543b80e commit cb7e771

22 files changed

+1371
-6
lines changed

doc/file_format_stim_circuit.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ and annotations for tasks such as detection event sampling and drawing the circu
2525
## Encoding
2626

2727
Stim circuit files are always encoded using UTF-8.
28-
Furthermore, the only place in the file where non-ASCII characters are permitted is inside of comments.
28+
29+
(Also, the only place in the file where non-ASCII characters can validly appear is inside of comments and tags.)
2930

3031
## Syntax
3132

doc/gates.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1784,7 +1784,7 @@ Example:
17841784
# Apply Y to qubit 5 controlled by qubit 2.
17851785
CY 2 5
17861786

1787-
# Perform CY 2 5 then CX 4 2.
1787+
# Perform CY 2 5 then CY 4 2.
17881788
CY 2 5 4 2
17891789

17901790
# Apply Y to qubit 6 if the most recent measurement result was TRUE.

doc/python_api_reference_vDev.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
4343
- [`stim.Circuit.insert`](#stim.Circuit.insert)
4444
- [`stim.Circuit.inverse`](#stim.Circuit.inverse)
4545
- [`stim.Circuit.likeliest_error_sat_problem`](#stim.Circuit.likeliest_error_sat_problem)
46+
- [`stim.Circuit.missing_detectors`](#stim.Circuit.missing_detectors)
4647
- [`stim.Circuit.num_detectors`](#stim.Circuit.num_detectors)
4748
- [`stim.Circuit.num_measurements`](#stim.Circuit.num_measurements)
4849
- [`stim.Circuit.num_observables`](#stim.Circuit.num_observables)
@@ -197,6 +198,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
197198
- [`stim.ExplainedError.dem_error_terms`](#stim.ExplainedError.dem_error_terms)
198199
- [`stim.FlipSimulator`](#stim.FlipSimulator)
199200
- [`stim.FlipSimulator.__init__`](#stim.FlipSimulator.__init__)
201+
- [`stim.FlipSimulator.append_measurement_flips`](#stim.FlipSimulator.append_measurement_flips)
200202
- [`stim.FlipSimulator.batch_size`](#stim.FlipSimulator.batch_size)
201203
- [`stim.FlipSimulator.broadcast_pauli_errors`](#stim.FlipSimulator.broadcast_pauli_errors)
202204
- [`stim.FlipSimulator.clear`](#stim.FlipSimulator.clear)
@@ -1249,6 +1251,8 @@ def copy(
12491251
# (in class stim.Circuit)
12501252
def count_determined_measurements(
12511253
self,
1254+
*,
1255+
unknown_input: bool = False,
12521256
) -> int:
12531257
"""Counts the number of predictable measurements in the circuit.
12541258

@@ -1280,6 +1284,13 @@ def count_determined_measurements(
12801284
the Z basis. Typically this relationship is not declared as a detector, because
12811285
it's not local, or as an observable, because it doesn't store a qubit.
12821286

1287+
Args:
1288+
unknown_input: Defaults to False (inputs assumed to be in the |0> state).
1289+
When set to True, the inputs are instead treated as being in unknown
1290+
random states. For example, this means that Z-basis measurements at
1291+
the very beginning of the circuit will be considered random rather
1292+
than determined.
1293+
12831294
Returns:
12841295
The number of measurements that were predictable.
12851296

@@ -1299,6 +1310,24 @@ def count_determined_measurements(
12991310
... ''').count_determined_measurements()
13001311
0
13011312

1313+
>>> stim.Circuit('''
1314+
... M 0
1315+
... ''').count_determined_measurements()
1316+
1
1317+
1318+
>>> stim.Circuit('''
1319+
... M 0
1320+
... ''').count_determined_measurements(unknown_input=True)
1321+
0
1322+
1323+
>>> stim.Circuit('''
1324+
... M 0
1325+
... M 0 1
1326+
... M 0 1 2
1327+
... M 0 1 2 3
1328+
... ''').count_determined_measurements(unknown_input=True)
1329+
6
1330+
13021331
>>> stim.Circuit('''
13031332
... R 0 1
13041333
... MZZ 0 1
@@ -2542,6 +2571,72 @@ def likeliest_error_sat_problem(
25422571
"""
25432572
```
25442573

2574+
<a name="stim.Circuit.missing_detectors"></a>
2575+
```python
2576+
# stim.Circuit.missing_detectors
2577+
2578+
# (in class stim.Circuit)
2579+
def missing_detectors(
2580+
self,
2581+
*,
2582+
unknown_input: bool = False,
2583+
) -> int:
2584+
"""Finds deterministic measurements independent of declared detectors/observables.
2585+
2586+
This method is useful for debugging missing detectors in a circuit, because it
2587+
identifies generators for uncovered degrees of freedom.
2588+
2589+
It's not recommended to use this method to solve for the detectors of a circuit.
2590+
The returned detectors are not guaranteed to be stable across versions, and
2591+
aren't optimized to be "good" (e.g. form a low weight basis or be matchable
2592+
if possible). It will also identify things that are technically determined
2593+
but that the user may not want to use as a detector, such as the fact that
2594+
in the first round after transversal Z basis initialization of a toric code
2595+
the product of all X stabilizer measurements is deterministic even though the
2596+
individual measurements are all random.
2597+
2598+
Args:
2599+
unknown_input: Defaults to False (inputs assumed to be in the |0> state).
2600+
When set to True, the inputs are instead treated as being in unknown
2601+
random states. For example, this means that Z-basis measurements at
2602+
the very beginning of the circuit will be considered random rather
2603+
than determined.
2604+
2605+
Returns:
2606+
A circuit containing DETECTOR instructions that specify the uncovered
2607+
degrees of freedom in the deterministic measurement sets of the input
2608+
circuit. The returned circuit can be appended to the input circuit to
2609+
get a circuit with no missing detectors.
2610+
2611+
Examples:
2612+
>>> import stim
2613+
2614+
>>> stim.Circuit('''
2615+
... R 0
2616+
... M 0
2617+
... ''').missing_detectors()
2618+
stim.Circuit('''
2619+
DETECTOR rec[-1]
2620+
''')
2621+
2622+
>>> stim.Circuit('''
2623+
... MZZ 0 1
2624+
... MYY 0 1
2625+
... MXX 0 1
2626+
... DEPOLARIZE1(0.1) 0 1
2627+
... MZZ 0 1
2628+
... MYY 0 1
2629+
... MXX 0 1
2630+
... DETECTOR rec[-1] rec[-4]
2631+
... DETECTOR rec[-2] rec[-5]
2632+
... DETECTOR rec[-3] rec[-6]
2633+
... ''').missing_detectors(unknown_input=True)
2634+
stim.Circuit('''
2635+
DETECTOR rec[-3] rec[-2] rec[-1]
2636+
''')
2637+
"""
2638+
```
2639+
25452640
<a name="stim.Circuit.num_detectors"></a>
25462641
```python
25472642
# stim.Circuit.num_detectors
@@ -8050,6 +8145,86 @@ def __init__(
80508145
"""
80518146
```
80528147

8148+
<a name="stim.FlipSimulator.append_measurement_flips"></a>
8149+
```python
8150+
# stim.FlipSimulator.append_measurement_flips
8151+
8152+
# (in class stim.FlipSimulator)
8153+
def append_measurement_flips(
8154+
self,
8155+
measurement_flip_data: np.ndarray,
8156+
) -> None:
8157+
"""Appends measurement flip data to the simulator's measurement record.
8158+
8159+
Args:
8160+
measurement_flip_data: The flip data to append. The following shape/dtype
8161+
combinations are supported.
8162+
8163+
Single measurement without bit packing:
8164+
shape=(self.batch_size,)
8165+
dtype=np.bool_
8166+
8167+
Single measurement with bit packing:
8168+
shape=(math.ceil(self.batch_size / 8),)
8169+
dtype=np.uint8
8170+
8171+
Multiple measurements without bit packing:
8172+
shape=(num_measurements, self.batch_size)
8173+
dtype=np.bool_
8174+
8175+
Multiple measurements with bit packing:
8176+
shape=(num_measurements, math.ceil(self.batch_size / 8))
8177+
dtype=np.uint8
8178+
8179+
Examples:
8180+
>>> import stim
8181+
>>> import numpy as np
8182+
>>> sim = stim.FlipSimulator(batch_size=9)
8183+
>>> sim.append_measurement_flips(np.array(
8184+
... [0, 1, 0, 0, 1, 0, 0, 1, 1],
8185+
... dtype=np.bool_,
8186+
... ))
8187+
8188+
>>> sim.get_measurement_flips()
8189+
array([[False, True, False, False, True, False, False, True, True]])
8190+
8191+
>>> sim.append_measurement_flips(np.array(
8192+
... [0b11001001, 0],
8193+
... dtype=np.uint8,
8194+
... ))
8195+
8196+
>>> sim.get_measurement_flips()
8197+
array([[False, True, False, False, True, False, False, True, True],
8198+
[ True, False, False, True, False, False, True, True, False]])
8199+
8200+
>>> sim.append_measurement_flips(np.array(
8201+
... [[0b11111111, 0b1], [0b00000000, 0b0], [0b11111111, 0b1]],
8202+
... dtype=np.uint8,
8203+
... ))
8204+
8205+
>>> sim.get_measurement_flips()
8206+
array([[False, True, False, False, True, False, False, True, True],
8207+
[ True, False, False, True, False, False, True, True, False],
8208+
[ True, True, True, True, True, True, True, True, True],
8209+
[False, False, False, False, False, False, False, False, False],
8210+
[ True, True, True, True, True, True, True, True, True]])
8211+
8212+
>>> sim.append_measurement_flips(np.array(
8213+
... [[1, 0, 1, 0, 1, 0, 1, 0, 1], [0, 1, 0, 1, 0, 1, 0, 1, 0]],
8214+
... dtype=np.bool_,
8215+
... ))
8216+
8217+
>>> sim.get_measurement_flips()
8218+
array([[False, True, False, False, True, False, False, True, True],
8219+
[ True, False, False, True, False, False, True, True, False],
8220+
[ True, True, True, True, True, True, True, True, True],
8221+
[False, False, False, False, False, False, False, False, False],
8222+
[ True, True, True, True, True, True, True, True, True],
8223+
[ True, False, True, False, True, False, True, False, True],
8224+
[False, True, False, True, False, True, False, True, False]])
8225+
"""
8226+
```
8227+
80538228
<a name="stim.FlipSimulator.batch_size"></a>
80548229
```python
80558230
# stim.FlipSimulator.batch_size

0 commit comments

Comments
 (0)