Skip to content

Commit

Permalink
Add the ability to tag circuit instructions (#848)
Browse files Browse the repository at this point in the history
- For example, `TICK[100ns]` instead of `TICK` or `I[DYNAMICDECOUPLE] 2
3 5` instead of `I 2 3 5`
- Stim ignores tags, except for propagating them through circuit
transformations
- Users can use tags for debugging or to communicate information from
tools generating stim circuits to tools consuming them

```python
import stim
circuit = stim.Circuit("""
    R[mlr] 0 1
    TICK[200ns]

    H 0
    TICK[10ns]

    REPEAT[unroll] 3 {
        CZ[adiabatic] 0 1
        TICK[20ns]
    }

    H 0
    TICK[20ns]

    I[DYNAMIC_DECOUPLE_NEW_LATEST_VERSION_5_FINAL_ACTUALLY_FINAL_YXY] 2 3 4 5
    M[include_two_state] 0 1
    DETECTOR[subgraph_2] rec[-1] rec[-2]
""")
assert circuit[0].tag == "mlr"
assert circuit[1].tag == "200ns"
assert circuit[2].tag == ""
```

Fixes #843
  • Loading branch information
Strilanc authored Nov 1, 2024
1 parent e1de0e6 commit 2581b90
Show file tree
Hide file tree
Showing 53 changed files with 1,616 additions and 589 deletions.
34 changes: 33 additions & 1 deletion doc/file_format_stim_circuit.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ Each line is either blank, an instruction, a block initiator, or a block termina
Also, each line may be indented with spacing characters and may end with a comment indicated by a hash (`#`).
Comments and indentation are purely decorative; they carry no semantic significance.

Here is a formal definition of the above paragraph.
Entries like `/this/` are regular expressions.
Entries like `<this>` are named expressions.
Entries like `'this'` are literal string expressions.
The `::=` operator means "defined as".
The `|` binary operator means "or".
The `?` suffix operator means "zero-or-one".
The `*` suffix operator means "zero-or-many".
Parens are used to group expressions.
Adjacent expressions are combined by concatenation.

```
<CIRCUIT> ::= <LINE>*
<LINE> ::= <INDENT> (<INSTRUCTION> | <BLOCK_START> | <BLOCK_END>)? <COMMENT>? '\n'
Expand All @@ -42,13 +53,15 @@ Comments and indentation are purely decorative; they carry no semantic significa
```

An *instruction* is composed of a name,
then (introduced in stim v1.15) an optional tag inside square brackets,
then an optional comma-separated list of arguments inside of parentheses,
then a list of space-separated targets.
For example, the line `X_ERROR(0.1) 5 6` is an instruction with a name (`X_ERROR`),
one argument (`0.1`), and two targets (`5` and `6`).

```
<INSTRUCTION> ::= <NAME> <PARENS_ARGUMENTS>? <TARGETS>
<INSTRUCTION> ::= <NAME> <TAG>? <PARENS_ARGUMENTS>? <TARGETS>
<TAG> ::= '[' /[^\r\]\n]/* ']'
<PARENS_ARGUMENTS> ::= '(' <ARGUMENTS> ')'
<ARGUMENTS> ::= /[ \t]*/ <ARG> /[ \t]*/ (',' <ARGUMENTS>)?
<TARGETS> ::= /[ \t]+/ <TARG> <TARGETS>?
Expand All @@ -57,6 +70,14 @@ one argument (`0.1`), and two targets (`5` and `6`).
An instruction *name* starts with a letter and then contains a series of letters, digits, and underscores.
Names are case-insensitive.

An instruction *tag* is an arbitrary string enclosed by square brackets.
Certain characters cannot appear directly in the tag, and must instead be included using escape sequences.
The closing square bracket character `]` cannot appear directly, and is instead encoded using the escape sequence `\C`.
The carriage return character cannot appear directly, and is instead encoded using the escape sequence `\r`.
The line feed character cannot appear directly, and is instead encoded using the escape sequence `\n`.
The backslash character `\` cannot appear directly, and is instead encoded using the escape sequence `\B`.
(This backslash escape sequence differs from the common escape sequence `\\` because that sequence causes exponential explosions when escaping multiple times.)

An *argument* is a double precision floating point number.

A *target* can either be a qubit target (a non-negative integer),
Expand Down Expand Up @@ -124,6 +145,17 @@ Currently, control flow is limited to *repetition*.
A circuit can contain `REPEAT K { ... }` blocks,
which indicate that the block's instructions should be iterated over `K` times instead of just once.

### Tags

Instruction tags have no effect on the function of a circuit.
In general, tools should attempt to propagate tags through circuit transformations and otherwise ignore them.
The intent is that users and tools can use tags to specify custom behavior that stim is not aware of.
For example, consider the tagged instruction `TICK[100ns]`.
In most situations, the `100ns` tag does nothing.
But if you are using a tool that adds noise to circuits, and it's programmed to look at tags to get hints about what
noise to add, then the `100ns` tag could be a message to that tool (specifying a duration, which the tool could use when
computing the probability argument of an inserted `DEPOLARIZE1` instruction).

### Target Types

There are four types of targets that can be given to instructions:
Expand Down
107 changes: 107 additions & 0 deletions doc/python_api_reference_vDev.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
- [`stim.CircuitInstruction.gate_args_copy`](#stim.CircuitInstruction.gate_args_copy)
- [`stim.CircuitInstruction.name`](#stim.CircuitInstruction.name)
- [`stim.CircuitInstruction.num_measurements`](#stim.CircuitInstruction.num_measurements)
- [`stim.CircuitInstruction.tag`](#stim.CircuitInstruction.tag)
- [`stim.CircuitInstruction.target_groups`](#stim.CircuitInstruction.target_groups)
- [`stim.CircuitInstruction.targets_copy`](#stim.CircuitInstruction.targets_copy)
- [`stim.CircuitRepeatBlock`](#stim.CircuitRepeatBlock)
Expand All @@ -94,6 +95,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
- [`stim.CircuitRepeatBlock.name`](#stim.CircuitRepeatBlock.name)
- [`stim.CircuitRepeatBlock.num_measurements`](#stim.CircuitRepeatBlock.num_measurements)
- [`stim.CircuitRepeatBlock.repeat_count`](#stim.CircuitRepeatBlock.repeat_count)
- [`stim.CircuitRepeatBlock.tag`](#stim.CircuitRepeatBlock.tag)
- [`stim.CircuitTargetsInsideInstruction`](#stim.CircuitTargetsInsideInstruction)
- [`stim.CircuitTargetsInsideInstruction.__init__`](#stim.CircuitTargetsInsideInstruction.__init__)
- [`stim.CircuitTargetsInsideInstruction.args`](#stim.CircuitTargetsInsideInstruction.args)
Expand Down Expand Up @@ -4017,6 +4019,8 @@ def __init__(
name: str,
targets: Optional[Iterable[Union[int, stim.GateTarget]]] = None,
gate_args: Optional[Iterable[float]] = None,
*,
tag: str = "",
) -> None:
"""Creates or parses a `stim.CircuitInstruction`.
Expand All @@ -4030,6 +4034,11 @@ def __init__(
gate_args: The sequence of numeric arguments parameterizing a gate. For
noise gates this is their probabilities. For `OBSERVABLE_INCLUDE`
instructions it's the index of the logical observable to affect.
tag: Defaults to "". A custom string attached to the instruction. For
example, for a TICK instruction, this could a string specifying an
amount of time which is used by custom code for adding noise to a
circuit. In general, stim will attempt to propagate tags across circuit
transformations but will otherwise completely ignore them.
Examples:
>>> import stim
Expand All @@ -4039,6 +4048,9 @@ def __init__(
>>> stim.CircuitInstruction('CX rec[-1] 5 # comment')
stim.CircuitInstruction('CX', [stim.target_rec(-1), stim.GateTarget(5)], [])
>>> print(stim.CircuitInstruction('I', [2], tag='100ns'))
I[100ns] 2
"""
```

Expand Down Expand Up @@ -4147,6 +4159,29 @@ def num_measurements(
"""
```

<a name="stim.CircuitInstruction.tag"></a>
```python
# stim.CircuitInstruction.tag

# (in class stim.CircuitInstruction)
@property
def tag(
self,
) -> str:
"""The custom tag attached to the instruction.
The tag is an arbitrary string.
The default tag, when none is specified, is the empty string.
Examples:
>>> import stim
>>> stim.Circuit("H[test] 0")[0].tag
'test'
>>> stim.Circuit("H 0")[0].tag
''
"""
```

<a name="stim.CircuitInstruction.target_groups"></a>
```python
# stim.CircuitInstruction.target_groups
Expand Down Expand Up @@ -4266,12 +4301,15 @@ def __init__(
self,
repeat_count: int,
body: stim.Circuit,
*,
tag: str = '',
) -> None:
"""Initializes a `stim.CircuitRepeatBlock`.
Args:
repeat_count: The number of times to repeat the block.
body: The body of the block, as a circuit.
tag: Defaults to empty. A custom string attached to the REPEAT instruction.
"""
```

Expand Down Expand Up @@ -4407,6 +4445,39 @@ def repeat_count(
"""
```

<a name="stim.CircuitRepeatBlock.tag"></a>
```python
# stim.CircuitRepeatBlock.tag

# (in class stim.CircuitRepeatBlock)
@property
def tag(
self,
) -> str:
"""The custom tag attached to the REPEAT instruction.
The tag is an arbitrary string.
The default tag, when none is specified, is the empty string.
Examples:
>>> import stim
>>> stim.Circuit('''
... REPEAT[test] 5 {
... H 0
... }
... ''')[0].tag
'test'
>>> stim.Circuit('''
... REPEAT 5 {
... H 0
... }
... ''')[0].tag
''
"""
```

<a name="stim.CircuitTargetsInsideInstruction"></a>
```python
# stim.CircuitTargetsInsideInstruction
Expand Down Expand Up @@ -6149,6 +6220,18 @@ def __init__(
coords: List[float],
) -> None:
"""Creates a stim.DemTargetWithCoords.
Examples:
>>> import stim
>>> err = stim.Circuit('''
... R 0 1
... X_ERROR(0.25) 0 1
... M 0 1
... DETECTOR(2, 3) rec[-1] rec[-2]
... OBSERVABLE_INCLUDE(0) rec[-1]
... ''').shortest_graphlike_error()
>>> err[0].dem_error_terms[0]
stim.DemTargetWithCoords(dem_target=stim.DemTarget('D0'), coords=[2, 3])
"""
```

Expand All @@ -6164,6 +6247,18 @@ def coords(
"""Returns the associated coordinate information as a list of floats.
If there is no coordinate information, returns an empty list.
Examples:
>>> import stim
>>> err = stim.Circuit('''
... R 0 1
... X_ERROR(0.25) 0 1
... M 0 1
... DETECTOR(2, 3) rec[-1] rec[-2]
... OBSERVABLE_INCLUDE(0) rec[-1]
... ''').shortest_graphlike_error()
>>> err[0].dem_error_terms[0].coords
[2.0, 3.0]
"""
```

Expand All @@ -6177,6 +6272,18 @@ def dem_target(
self,
) -> stim.DemTarget:
"""Returns the actual DEM target as a `stim.DemTarget`.
Examples:
>>> import stim
>>> err = stim.Circuit('''
... R 0 1
... X_ERROR(0.25) 0 1
... M 0 1
... DETECTOR(2, 3) rec[-1] rec[-2]
... OBSERVABLE_INCLUDE(0) rec[-1]
... ''').shortest_graphlike_error()
>>> err[0].dem_error_terms[0].dem_target
stim.DemTarget('D0')
"""
```

Expand Down
Loading

0 comments on commit 2581b90

Please sign in to comment.