Skip to content

Commit bd60b73

Browse files
authored
Add tags to detector error models (#896)
Adds the ability to tag instructions in detector error models (like `error[crosstalk](0.1) D1 D2`). Tags have no semantic effect, other than to be propagated. Outside systems can read the tags for custom behavior, or to help with debugging. `stim.Circuit.detector_error_model` also now flows tags from the circuit into the dem. Tags on noise instructions end up as tags on error instructions, tags on detectors are kept, and so forth. For example: ```python print(stim.Circuit(""" R 0 X_ERROR[hello-world](0.25) 0 M 0 DETECTOR[alarm] rec[-1] """).detector_error_model()) ``` prints: ``` error[hello-world](0.25) D0 detector[alarm] D0 ``` Fixes #872
1 parent 0127ec0 commit bd60b73

30 files changed

+855
-342
lines changed

doc/file_format_dem_detector_error_model.md

+14-3
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,16 @@ Also, each line may be indented with spacing characters and may end with a comme
4646
```
4747

4848
An *instruction* is composed of a name,
49+
then (introduced in stim v1.15) an optional tag inside square brackets,
4950
then an optional comma-separated list of arguments inside of parentheses,
5051
then a list of space-separated targets.
51-
For example, the line `error(0.1) D5 D6 L0` is an instruction with a name (`error`),
52-
one argument (`0.1`), and three targets (`D5`, `D6`, and `L0`).
52+
For example, the line `error[test](0.1) D5 D6 L0` is an instruction with
53+
a name (`error`), a tag (`test`), one argument (`0.1`), and three targets
54+
(`D5`, `D6`, and `L0`).
5355

5456
```
55-
<INSTRUCTION> ::= <NAME> <PARENS_ARGUMENTS>? <TARGETS>
57+
<INSTRUCTION> ::= <NAME> <TAG>? <PARENS_ARGUMENTS>? <TARGETS>
58+
<TAG> ::= '[' /[^\r\]\n]/* ']'
5659
<PARENS_ARGUMENTS> ::= '(' <ARGUMENTS> ')'
5760
<ARGUMENTS> ::= /[ \t]*/ <ARG> /[ \t]*/ (',' <ARGUMENTS>)?
5861
<TARGETS> ::= /[ \t]+/ <TARG> <TARGETS>?
@@ -61,6 +64,14 @@ one argument (`0.1`), and three targets (`D5`, `D6`, and `L0`).
6164
An instruction *name* starts with a letter and then contains a series of letters, digits, and underscores.
6265
Names are case-insensitive.
6366

67+
An instruction *tag* is an arbitrary string enclosed by square brackets.
68+
Certain characters cannot appear directly in the tag, and must instead be included using escape sequences.
69+
The closing square bracket character `]` cannot appear directly, and is instead encoded using the escape sequence `\C`.
70+
The carriage return character cannot appear directly, and is instead encoded using the escape sequence `\r`.
71+
The line feed character cannot appear directly, and is instead encoded using the escape sequence `\n`.
72+
The backslash character `\` cannot appear directly, and is instead encoded using the escape sequence `\B`.
73+
(This backslash escape sequence differs from the common escape sequence `\\` because that sequence causes exponential explosions when escaping multiple times.)
74+
6475
An *argument* is a double precision floating point number.
6576

6677
A *target* can either be a relative detector target (a non-negative integer prefixed by `D`),

doc/file_format_stim_circuit.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ An *instruction* is composed of a name,
5656
then (introduced in stim v1.15) an optional tag inside square brackets,
5757
then an optional comma-separated list of arguments inside of parentheses,
5858
then a list of space-separated targets.
59-
For example, the line `X_ERROR(0.1) 5 6` is an instruction with a name (`X_ERROR`),
60-
one argument (`0.1`), and two targets (`5` and `6`).
59+
For example, the line `X_ERROR[test](0.1) 5 6` is an instruction with a
60+
name (`X_ERROR`), a tag (`test`), one argument (`0.1`), and two targets
61+
(`5` and `6`).
6162

6263
```
6364
<INSTRUCTION> ::= <NAME> <TAG>? <PARENS_ARGUMENTS>? <TARGETS>

doc/python_api_reference_vDev.md

+100-10
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
6767
- [`stim.CircuitErrorLocation.flipped_measurement`](#stim.CircuitErrorLocation.flipped_measurement)
6868
- [`stim.CircuitErrorLocation.flipped_pauli_product`](#stim.CircuitErrorLocation.flipped_pauli_product)
6969
- [`stim.CircuitErrorLocation.instruction_targets`](#stim.CircuitErrorLocation.instruction_targets)
70+
- [`stim.CircuitErrorLocation.noise_tag`](#stim.CircuitErrorLocation.noise_tag)
7071
- [`stim.CircuitErrorLocation.stack_frames`](#stim.CircuitErrorLocation.stack_frames)
7172
- [`stim.CircuitErrorLocation.tick_offset`](#stim.CircuitErrorLocation.tick_offset)
7273
- [`stim.CircuitErrorLocationStackFrame`](#stim.CircuitErrorLocationStackFrame)
@@ -100,6 +101,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
100101
- [`stim.CircuitTargetsInsideInstruction.__init__`](#stim.CircuitTargetsInsideInstruction.__init__)
101102
- [`stim.CircuitTargetsInsideInstruction.args`](#stim.CircuitTargetsInsideInstruction.args)
102103
- [`stim.CircuitTargetsInsideInstruction.gate`](#stim.CircuitTargetsInsideInstruction.gate)
104+
- [`stim.CircuitTargetsInsideInstruction.tag`](#stim.CircuitTargetsInsideInstruction.tag)
103105
- [`stim.CircuitTargetsInsideInstruction.target_range_end`](#stim.CircuitTargetsInsideInstruction.target_range_end)
104106
- [`stim.CircuitTargetsInsideInstruction.target_range_start`](#stim.CircuitTargetsInsideInstruction.target_range_start)
105107
- [`stim.CircuitTargetsInsideInstruction.targets_in_range`](#stim.CircuitTargetsInsideInstruction.targets_in_range)
@@ -128,6 +130,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
128130
- [`stim.DemInstruction.__repr__`](#stim.DemInstruction.__repr__)
129131
- [`stim.DemInstruction.__str__`](#stim.DemInstruction.__str__)
130132
- [`stim.DemInstruction.args_copy`](#stim.DemInstruction.args_copy)
133+
- [`stim.DemInstruction.tag`](#stim.DemInstruction.tag)
131134
- [`stim.DemInstruction.target_groups`](#stim.DemInstruction.target_groups)
132135
- [`stim.DemInstruction.targets_copy`](#stim.DemInstruction.targets_copy)
133136
- [`stim.DemInstruction.type`](#stim.DemInstruction.type)
@@ -3627,6 +3630,7 @@ def __init__(
36273630
flipped_measurement: object,
36283631
instruction_targets: stim.CircuitTargetsInsideInstruction,
36293632
stack_frames: List[stim.CircuitErrorLocationStackFrame],
3633+
noise_tag: str = '',
36303634
) -> None:
36313635
"""Creates a stim.CircuitErrorLocation.
36323636

@@ -3661,9 +3665,11 @@ def __init__(
36613665
... instruction_repetitions_arg=0,
36623666
... ),
36633667
... ),
3668+
... noise_tag='test-tag',
36643669
... )
36653670
>>> print(err)
36663671
CircuitErrorLocation {
3672+
noise_tag: test-tag
36673673
flipped_pauli_product: X0
36683674
Circuit location stack trace:
36693675
(after 1 TICKs)
@@ -3762,6 +3768,30 @@ def instruction_targets(
37623768
"""
37633769
```
37643770

3771+
<a name="stim.CircuitErrorLocation.noise_tag"></a>
3772+
```python
3773+
# stim.CircuitErrorLocation.noise_tag
3774+
3775+
# (in class stim.CircuitErrorLocation)
3776+
@property
3777+
def noise_tag(
3778+
self,
3779+
) -> str:
3780+
"""The tag on the noise instruction that caused the error.
3781+
3782+
Examples:
3783+
>>> import stim
3784+
>>> err = stim.Circuit('''
3785+
... R 0
3786+
... Y_ERROR[test-tag](0.125) 0
3787+
... M 0
3788+
... OBSERVABLE_INCLUDE(0) rec[-1]
3789+
... ''').shortest_graphlike_error()
3790+
>>> err[0].circuit_error_locations[0].noise_tag
3791+
'test-tag'
3792+
"""
3793+
```
3794+
37653795
<a name="stim.CircuitErrorLocation.stack_frames"></a>
37663796
```python
37673797
# stim.CircuitErrorLocation.stack_frames
@@ -4515,6 +4545,7 @@ def __init__(
45154545
self,
45164546
*,
45174547
gate: str,
4548+
tag: str = '',
45184549
args: List[float],
45194550
target_range_start: int,
45204551
target_range_end: int,
@@ -4526,6 +4557,7 @@ def __init__(
45264557
>>> import stim
45274558
>>> val = stim.CircuitTargetsInsideInstruction(
45284559
... gate='X_ERROR',
4560+
... tag='',
45294561
... args=[0.25],
45304562
... target_range_start=0,
45314563
... target_range_end=1,
@@ -4586,6 +4618,32 @@ def gate(
45864618
"""
45874619
```
45884620

4621+
<a name="stim.CircuitTargetsInsideInstruction.tag"></a>
4622+
```python
4623+
# stim.CircuitTargetsInsideInstruction.tag
4624+
4625+
# (in class stim.CircuitTargetsInsideInstruction)
4626+
@property
4627+
def tag(
4628+
self,
4629+
) -> str:
4630+
"""Returns the tag of the gate / instruction that was being executed.
4631+
4632+
Examples:
4633+
>>> import stim
4634+
>>> err = stim.Circuit('''
4635+
... R 0 1
4636+
... X_ERROR[look-at-me-imma-tag](0.25) 0 1
4637+
... M 0 1
4638+
... DETECTOR(2, 3) rec[-1] rec[-2]
4639+
... OBSERVABLE_INCLUDE(0) rec[-1]
4640+
... ''').shortest_graphlike_error()
4641+
>>> loc: stim.CircuitErrorLocation = err[0].circuit_error_locations[0]
4642+
>>> loc.instruction_targets.tag
4643+
'look-at-me-imma-tag'
4644+
"""
4645+
```
4646+
45894647
<a name="stim.CircuitTargetsInsideInstruction.target_range_end"></a>
45904648
```python
45914649
# stim.CircuitTargetsInsideInstruction.target_range_end
@@ -5693,6 +5751,8 @@ def __init__(
56935751
type: str,
56945752
args: Optional[Iterable[float]] = None,
56955753
targets: Optional[Iterable[stim.DemTarget]] = None,
5754+
*,
5755+
tag: str = "",
56965756
) -> None:
56975757
"""Creates or parses a stim.DemInstruction.
56985758

@@ -5704,15 +5764,18 @@ def __init__(
57045764
"error(0.1)").
57055765
targets: The objects the instruction involves (e.g. the "D0" and "L1" in
57065766
"error(0.1) D0 L1").
5767+
tag: An arbitrary piece of text attached to the instruction.
57075768

57085769
Examples:
57095770
>>> import stim
57105771
>>> instruction = stim.DemInstruction(
57115772
... 'error',
57125773
... [0.125],
5713-
... [stim.target_relative_detector_id(5)])
5774+
... [stim.target_relative_detector_id(5)],
5775+
... tag='test-tag',
5776+
... )
57145777
>>> print(instruction)
5715-
error(0.125) D5
5778+
error[test-tag](0.125) D5
57165779

57175780
>>> print(stim.DemInstruction('error(0.125) D5 L6 ^ D4 # comment'))
57185781
error(0.125) D5 L6 ^ D4
@@ -5785,6 +5848,30 @@ def args_copy(
57855848
"""
57865849
```
57875850

5851+
<a name="stim.DemInstruction.tag"></a>
5852+
```python
5853+
# stim.DemInstruction.tag
5854+
5855+
# (in class stim.DemInstruction)
5856+
@property
5857+
def tag(
5858+
self,
5859+
) -> str:
5860+
"""Returns the arbitrary text tag attached to the instruction.
5861+
5862+
Examples:
5863+
>>> import stim
5864+
>>> dem = stim.DetectorErrorModel('''
5865+
... error[test-tag](0.125) D0
5866+
... error(0.125) D0
5867+
... ''')
5868+
>>> dem[0].tag
5869+
'test-tag'
5870+
>>> dem[1].tag
5871+
''
5872+
"""
5873+
```
5874+
57885875
<a name="stim.DemInstruction.target_groups"></a>
57895876
```python
57905877
# stim.DemInstruction.target_groups
@@ -6795,18 +6882,21 @@ def append(
67956882
instruction: object,
67966883
parens_arguments: object = None,
67976884
targets: List[object] = (),
6885+
*,
6886+
tag: str = '',
67986887
) -> None:
67996888
"""Appends an instruction to the detector error model.
68006889

68016890
Args:
68026891
instruction: Either the name of an instruction, a stim.DemInstruction, or a
6803-
stim.DemRepeatBlock. The `parens_arguments` and `targets` arguments are
6804-
given if and only if the instruction is a name.
6892+
stim.DemRepeatBlock. The `parens_arguments`, `targets`, and 'tag'
6893+
arguments should be given if and only if the instruction is a name.
68056894
parens_arguments: Numeric values parameterizing the instruction. The numbers
68066895
inside parentheses in a detector error model file (eg. the `0.25` in
68076896
`error(0.25) D0`). This argument can be given either a list of doubles,
68086897
or a single double (which will be implicitly wrapped into a list).
68096898
targets: The instruction targets, such as the `D0` in `error(0.25) D0`.
6899+
tag: An arbitrary piece of text attached to the repeat instruction.
68106900

68116901
Examples:
68126902
>>> import stim
@@ -6819,18 +6909,18 @@ def append(
68196909
... stim.DemTarget.separator(),
68206910
... stim.DemTarget.relative_detector_id(2),
68216911
... stim.DemTarget.logical_observable_id(3),
6822-
... ])
6912+
... ], tag='test-tag')
68236913
>>> print(repr(m))
68246914
stim.DetectorErrorModel('''
68256915
error(0.125) D1
6826-
error(0.25) D1 ^ D2 L3
6916+
error[test-tag](0.25) D1 ^ D2 L3
68276917
''')
68286918

68296919
>>> m.append("shift_detectors", (1, 2, 3), [5])
68306920
>>> print(repr(m))
68316921
stim.DetectorErrorModel('''
68326922
error(0.125) D1
6833-
error(0.25) D1 ^ D2 L3
6923+
error[test-tag](0.25) D1 ^ D2 L3
68346924
shift_detectors(1, 2, 3) 5
68356925
''')
68366926

@@ -6840,17 +6930,17 @@ def append(
68406930
>>> print(repr(m))
68416931
stim.DetectorErrorModel('''
68426932
error(0.125) D1
6843-
error(0.25) D1 ^ D2 L3
6933+
error[test-tag](0.25) D1 ^ D2 L3
68446934
shift_detectors(1, 2, 3) 5
68456935
repeat 3 {
68466936
error(0.125) D1
6847-
error(0.25) D1 ^ D2 L3
6937+
error[test-tag](0.25) D1 ^ D2 L3
68486938
shift_detectors(1, 2, 3) 5
68496939
}
68506940
error(0.125) D1
68516941
repeat 3 {
68526942
error(0.125) D1
6853-
error(0.25) D1 ^ D2 L3
6943+
error[test-tag](0.25) D1 ^ D2 L3
68546944
shift_detectors(1, 2, 3) 5
68556945
}
68566946
''')

0 commit comments

Comments
 (0)