diff --git a/doc/python_api_reference_vDev.md b/doc/python_api_reference_vDev.md
index 00e8d438..a9a7f926 100644
--- a/doc/python_api_reference_vDev.md
+++ b/doc/python_api_reference_vDev.md
@@ -91,6 +91,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
- [`stim.CircuitRepeatBlock.__repr__`](#stim.CircuitRepeatBlock.__repr__)
- [`stim.CircuitRepeatBlock.body_copy`](#stim.CircuitRepeatBlock.body_copy)
- [`stim.CircuitRepeatBlock.name`](#stim.CircuitRepeatBlock.name)
+ - [`stim.CircuitRepeatBlock.num_measurements`](#stim.CircuitRepeatBlock.num_measurements)
- [`stim.CircuitRepeatBlock.repeat_count`](#stim.CircuitRepeatBlock.repeat_count)
- [`stim.CircuitTargetsInsideInstruction`](#stim.CircuitTargetsInsideInstruction)
- [`stim.CircuitTargetsInsideInstruction.__init__`](#stim.CircuitTargetsInsideInstruction.__init__)
@@ -3762,6 +3763,30 @@ class CircuitErrorLocationStackFrame:
The full location of an instruction is a list of these frames,
drilling down from the top level circuit to the inner-most loop
that the instruction is within.
+
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... REPEAT 5 {
+ ... R 0
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... }
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> err[0].circuit_error_locations[0].stack_frames[0]
+ stim.CircuitErrorLocationStackFrame(
+ instruction_offset=0,
+ iteration_index=0,
+ instruction_repetitions_arg=5,
+ )
+ >>> err[0].circuit_error_locations[0].stack_frames[1]
+ stim.CircuitErrorLocationStackFrame(
+ instruction_offset=1,
+ iteration_index=4,
+ instruction_repetitions_arg=0,
+ )
"""
```
@@ -3778,6 +3803,14 @@ def __init__(
instruction_repetitions_arg: int,
) -> None:
"""Creates a stim.CircuitErrorLocationStackFrame.
+
+ Examples:
+ >>> import stim
+ >>> frame = stim.CircuitErrorLocationStackFrame(
+ ... instruction_offset=1,
+ ... iteration_index=2,
+ ... instruction_repetitions_arg=3,
+ ... )
"""
```
@@ -3795,6 +3828,18 @@ def instruction_offset(
from the line number, because blank lines and commented lines
don't count and also because the offset of the first instruction
is 0 instead of 1.
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... R 0
+ ... TICK
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> err[0].circuit_error_locations[0].stack_frames[0].instruction_offset
+ 2
"""
```
@@ -3810,6 +3855,23 @@ def instruction_repetitions_arg(
"""If the instruction being referred to is a REPEAT block,
this is the repetition count of that REPEAT block. Otherwise
this field defaults to 0.
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... REPEAT 5 {
+ ... R 0
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... }
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> full = err[0].circuit_error_locations[0].stack_frames[0]
+ >>> loop = err[0].circuit_error_locations[0].stack_frames[1]
+ >>> full.instruction_repetitions_arg
+ 5
+ >>> loop.instruction_repetitions_arg
+ 0
"""
```
@@ -3825,6 +3887,23 @@ def iteration_index(
"""Disambiguates which iteration of the loop containing this instruction
is being referred to. If the instruction isn't in a REPEAT block, this
field defaults to 0.
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... REPEAT 5 {
+ ... R 0
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... }
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> full = err[0].circuit_error_locations[0].stack_frames[0]
+ >>> loop = err[0].circuit_error_locations[0].stack_frames[1]
+ >>> full.iteration_index
+ 0
+ >>> loop.iteration_index
+ 4
"""
```
@@ -3873,19 +3952,30 @@ def __eq__(
def __init__(
self,
name: str,
- targets: List[object],
- gate_args: List[float] = (),
+ targets: Optional[Iterable[Union[int, stim.GateTarget]]] = None,
+ gate_args: Optional[Iterable[float]] = None,
) -> None:
- """Initializes a `stim.CircuitInstruction`.
+ """Creates or parses a `stim.CircuitInstruction`.
Args:
name: The name of the instruction being applied.
+ If `targets` and `gate_args` aren't specified, this can be a full
+ instruction line from a stim Circuit file, like "CX 0 1".
targets: The targets the instruction is being applied to. These can be raw
values like `0` and `stim.target_rec(-1)`, or instances of
`stim.GateTarget`.
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.
+
+ Examples:
+ >>> import stim
+
+ >>> print(stim.CircuitInstruction('DEPOLARIZE1', [5], [0.25]))
+ DEPOLARIZE1(0.25) 5
+
+ >>> stim.CircuitInstruction('CX rec[-1] 5 # comment')
+ stim.CircuitInstruction('CX', [stim.target_rec(-1), stim.GateTarget(5)], [])
"""
```
@@ -3989,7 +4079,7 @@ def num_measurements(
3
>>> stim.Circuit('MPP X0*X1 X0*Z1*Y2')[0].num_measurements
2
- >>> stim.CircuitInstruction('HERALDED_ERASE', [0]).num_measurements
+ >>> stim.CircuitInstruction('HERALDED_ERASE', [0], [0.25]).num_measurements
1
"""
```
@@ -4207,6 +4297,27 @@ def name(
"""
```
+
+```python
+# stim.CircuitRepeatBlock.num_measurements
+
+# (in class stim.CircuitRepeatBlock)
+@property
+def num_measurements(
+ self,
+) -> int:
+ """Returns the number of bits produced when running this loop.
+
+ Examples:
+ >>> import stim
+ >>> stim.CircuitRepeatBlock(
+ ... body=stim.Circuit("M 0 1"),
+ ... repeat_count=25,
+ ... ).num_measurements
+ 50
+ """
+```
+
```python
# stim.CircuitRepeatBlock.repeat_count
@@ -5336,13 +5447,15 @@ def __eq__(
def __init__(
self,
type: str,
- args: List[float],
- targets: List[object],
+ args: Optional[Iterable[float]] = None,
+ targets: Optional[Iterable[stim.DemTarget]] = None,
) -> None:
- """Creates a stim.DemInstruction.
+ """Creates or parses a stim.DemInstruction.
Args:
type: The name of the instruction type (e.g. "error" or "shift_detectors").
+ If `args` and `targets` aren't specified, this can also be set to a
+ full line of text from a dem file, like "error(0.25) D0".
args: Numeric values parameterizing the instruction (e.g. the 0.1 in
"error(0.1)").
targets: The objects the instruction involves (e.g. the "D0" and "L1" in
@@ -5356,6 +5469,9 @@ def __init__(
... [stim.target_relative_detector_id(5)])
>>> print(instruction)
error(0.125) D5
+
+ >>> print(stim.DemInstruction('error(0.125) D5 L6 ^ D4 # comment'))
+ error(0.125) D5 L6 ^ D4
"""
```
diff --git a/doc/stim.pyi b/doc/stim.pyi
index 83e57b43..370123ac 100644
--- a/doc/stim.pyi
+++ b/doc/stim.pyi
@@ -2921,6 +2921,30 @@ class CircuitErrorLocationStackFrame:
The full location of an instruction is a list of these frames,
drilling down from the top level circuit to the inner-most loop
that the instruction is within.
+
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... REPEAT 5 {
+ ... R 0
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... }
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> err[0].circuit_error_locations[0].stack_frames[0]
+ stim.CircuitErrorLocationStackFrame(
+ instruction_offset=0,
+ iteration_index=0,
+ instruction_repetitions_arg=5,
+ )
+ >>> err[0].circuit_error_locations[0].stack_frames[1]
+ stim.CircuitErrorLocationStackFrame(
+ instruction_offset=1,
+ iteration_index=4,
+ instruction_repetitions_arg=0,
+ )
"""
def __init__(
self,
@@ -2930,6 +2954,14 @@ class CircuitErrorLocationStackFrame:
instruction_repetitions_arg: int,
) -> None:
"""Creates a stim.CircuitErrorLocationStackFrame.
+
+ Examples:
+ >>> import stim
+ >>> frame = stim.CircuitErrorLocationStackFrame(
+ ... instruction_offset=1,
+ ... iteration_index=2,
+ ... instruction_repetitions_arg=3,
+ ... )
"""
@property
def instruction_offset(
@@ -2940,6 +2972,18 @@ class CircuitErrorLocationStackFrame:
from the line number, because blank lines and commented lines
don't count and also because the offset of the first instruction
is 0 instead of 1.
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... R 0
+ ... TICK
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> err[0].circuit_error_locations[0].stack_frames[0].instruction_offset
+ 2
"""
@property
def instruction_repetitions_arg(
@@ -2948,6 +2992,23 @@ class CircuitErrorLocationStackFrame:
"""If the instruction being referred to is a REPEAT block,
this is the repetition count of that REPEAT block. Otherwise
this field defaults to 0.
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... REPEAT 5 {
+ ... R 0
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... }
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> full = err[0].circuit_error_locations[0].stack_frames[0]
+ >>> loop = err[0].circuit_error_locations[0].stack_frames[1]
+ >>> full.instruction_repetitions_arg
+ 5
+ >>> loop.instruction_repetitions_arg
+ 0
"""
@property
def iteration_index(
@@ -2956,6 +3017,23 @@ class CircuitErrorLocationStackFrame:
"""Disambiguates which iteration of the loop containing this instruction
is being referred to. If the instruction isn't in a REPEAT block, this
field defaults to 0.
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... REPEAT 5 {
+ ... R 0
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... }
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> full = err[0].circuit_error_locations[0].stack_frames[0]
+ >>> loop = err[0].circuit_error_locations[0].stack_frames[1]
+ >>> full.iteration_index
+ 0
+ >>> loop.iteration_index
+ 4
"""
class CircuitInstruction:
"""An instruction, like `H 0 1` or `CNOT rec[-1] 5`, from a circuit.
@@ -2983,19 +3061,30 @@ class CircuitInstruction:
def __init__(
self,
name: str,
- targets: List[object],
- gate_args: List[float] = (),
+ targets: Optional[Iterable[Union[int, stim.GateTarget]]] = None,
+ gate_args: Optional[Iterable[float]] = None,
) -> None:
- """Initializes a `stim.CircuitInstruction`.
+ """Creates or parses a `stim.CircuitInstruction`.
Args:
name: The name of the instruction being applied.
+ If `targets` and `gate_args` aren't specified, this can be a full
+ instruction line from a stim Circuit file, like "CX 0 1".
targets: The targets the instruction is being applied to. These can be raw
values like `0` and `stim.target_rec(-1)`, or instances of
`stim.GateTarget`.
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.
+
+ Examples:
+ >>> import stim
+
+ >>> print(stim.CircuitInstruction('DEPOLARIZE1', [5], [0.25]))
+ DEPOLARIZE1(0.25) 5
+
+ >>> stim.CircuitInstruction('CX rec[-1] 5 # comment')
+ stim.CircuitInstruction('CX', [stim.target_rec(-1), stim.GateTarget(5)], [])
"""
def __ne__(
self,
@@ -3057,7 +3146,7 @@ class CircuitInstruction:
3
>>> stim.Circuit('MPP X0*X1 X0*Z1*Y2')[0].num_measurements
2
- >>> stim.CircuitInstruction('HERALDED_ERASE', [0]).num_measurements
+ >>> stim.CircuitInstruction('HERALDED_ERASE', [0], [0.25]).num_measurements
1
"""
def target_groups(
@@ -3211,6 +3300,20 @@ class CircuitRepeatBlock:
['H', 'REPEAT', 'S']
"""
@property
+ def num_measurements(
+ self,
+ ) -> int:
+ """Returns the number of bits produced when running this loop.
+
+ Examples:
+ >>> import stim
+ >>> stim.CircuitRepeatBlock(
+ ... body=stim.Circuit("M 0 1"),
+ ... repeat_count=25,
+ ... ).num_measurements
+ 50
+ """
+ @property
def repeat_count(
self,
) -> int:
@@ -4194,13 +4297,15 @@ class DemInstruction:
def __init__(
self,
type: str,
- args: List[float],
- targets: List[object],
+ args: Optional[Iterable[float]] = None,
+ targets: Optional[Iterable[stim.DemTarget]] = None,
) -> None:
- """Creates a stim.DemInstruction.
+ """Creates or parses a stim.DemInstruction.
Args:
type: The name of the instruction type (e.g. "error" or "shift_detectors").
+ If `args` and `targets` aren't specified, this can also be set to a
+ full line of text from a dem file, like "error(0.25) D0".
args: Numeric values parameterizing the instruction (e.g. the 0.1 in
"error(0.1)").
targets: The objects the instruction involves (e.g. the "D0" and "L1" in
@@ -4214,6 +4319,9 @@ class DemInstruction:
... [stim.target_relative_detector_id(5)])
>>> print(instruction)
error(0.125) D5
+
+ >>> print(stim.DemInstruction('error(0.125) D5 L6 ^ D4 # comment'))
+ error(0.125) D5 L6 ^ D4
"""
def __ne__(
self,
diff --git a/file_lists/pybind_files b/file_lists/pybind_files
index 068bbe0f..f29ee5ed 100644
--- a/file_lists/pybind_files
+++ b/file_lists/pybind_files
@@ -3,8 +3,8 @@ src/stim/circuit/circuit_instruction.pybind.cc
src/stim/circuit/circuit_repeat_block.pybind.cc
src/stim/circuit/gate_target.pybind.cc
src/stim/cmd/command_diagram.pybind.cc
+src/stim/dem/dem_instruction.pybind.cc
src/stim/dem/detector_error_model.pybind.cc
-src/stim/dem/detector_error_model_instruction.pybind.cc
src/stim/dem/detector_error_model_repeat_block.pybind.cc
src/stim/dem/detector_error_model_target.pybind.cc
src/stim/gates/gates.pybind.cc
diff --git a/glue/python/src/stim/__init__.pyi b/glue/python/src/stim/__init__.pyi
index 83e57b43..370123ac 100644
--- a/glue/python/src/stim/__init__.pyi
+++ b/glue/python/src/stim/__init__.pyi
@@ -2921,6 +2921,30 @@ class CircuitErrorLocationStackFrame:
The full location of an instruction is a list of these frames,
drilling down from the top level circuit to the inner-most loop
that the instruction is within.
+
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... REPEAT 5 {
+ ... R 0
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... }
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> err[0].circuit_error_locations[0].stack_frames[0]
+ stim.CircuitErrorLocationStackFrame(
+ instruction_offset=0,
+ iteration_index=0,
+ instruction_repetitions_arg=5,
+ )
+ >>> err[0].circuit_error_locations[0].stack_frames[1]
+ stim.CircuitErrorLocationStackFrame(
+ instruction_offset=1,
+ iteration_index=4,
+ instruction_repetitions_arg=0,
+ )
"""
def __init__(
self,
@@ -2930,6 +2954,14 @@ class CircuitErrorLocationStackFrame:
instruction_repetitions_arg: int,
) -> None:
"""Creates a stim.CircuitErrorLocationStackFrame.
+
+ Examples:
+ >>> import stim
+ >>> frame = stim.CircuitErrorLocationStackFrame(
+ ... instruction_offset=1,
+ ... iteration_index=2,
+ ... instruction_repetitions_arg=3,
+ ... )
"""
@property
def instruction_offset(
@@ -2940,6 +2972,18 @@ class CircuitErrorLocationStackFrame:
from the line number, because blank lines and commented lines
don't count and also because the offset of the first instruction
is 0 instead of 1.
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... R 0
+ ... TICK
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> err[0].circuit_error_locations[0].stack_frames[0].instruction_offset
+ 2
"""
@property
def instruction_repetitions_arg(
@@ -2948,6 +2992,23 @@ class CircuitErrorLocationStackFrame:
"""If the instruction being referred to is a REPEAT block,
this is the repetition count of that REPEAT block. Otherwise
this field defaults to 0.
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... REPEAT 5 {
+ ... R 0
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... }
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> full = err[0].circuit_error_locations[0].stack_frames[0]
+ >>> loop = err[0].circuit_error_locations[0].stack_frames[1]
+ >>> full.instruction_repetitions_arg
+ 5
+ >>> loop.instruction_repetitions_arg
+ 0
"""
@property
def iteration_index(
@@ -2956,6 +3017,23 @@ class CircuitErrorLocationStackFrame:
"""Disambiguates which iteration of the loop containing this instruction
is being referred to. If the instruction isn't in a REPEAT block, this
field defaults to 0.
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... REPEAT 5 {
+ ... R 0
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... }
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> full = err[0].circuit_error_locations[0].stack_frames[0]
+ >>> loop = err[0].circuit_error_locations[0].stack_frames[1]
+ >>> full.iteration_index
+ 0
+ >>> loop.iteration_index
+ 4
"""
class CircuitInstruction:
"""An instruction, like `H 0 1` or `CNOT rec[-1] 5`, from a circuit.
@@ -2983,19 +3061,30 @@ class CircuitInstruction:
def __init__(
self,
name: str,
- targets: List[object],
- gate_args: List[float] = (),
+ targets: Optional[Iterable[Union[int, stim.GateTarget]]] = None,
+ gate_args: Optional[Iterable[float]] = None,
) -> None:
- """Initializes a `stim.CircuitInstruction`.
+ """Creates or parses a `stim.CircuitInstruction`.
Args:
name: The name of the instruction being applied.
+ If `targets` and `gate_args` aren't specified, this can be a full
+ instruction line from a stim Circuit file, like "CX 0 1".
targets: The targets the instruction is being applied to. These can be raw
values like `0` and `stim.target_rec(-1)`, or instances of
`stim.GateTarget`.
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.
+
+ Examples:
+ >>> import stim
+
+ >>> print(stim.CircuitInstruction('DEPOLARIZE1', [5], [0.25]))
+ DEPOLARIZE1(0.25) 5
+
+ >>> stim.CircuitInstruction('CX rec[-1] 5 # comment')
+ stim.CircuitInstruction('CX', [stim.target_rec(-1), stim.GateTarget(5)], [])
"""
def __ne__(
self,
@@ -3057,7 +3146,7 @@ class CircuitInstruction:
3
>>> stim.Circuit('MPP X0*X1 X0*Z1*Y2')[0].num_measurements
2
- >>> stim.CircuitInstruction('HERALDED_ERASE', [0]).num_measurements
+ >>> stim.CircuitInstruction('HERALDED_ERASE', [0], [0.25]).num_measurements
1
"""
def target_groups(
@@ -3211,6 +3300,20 @@ class CircuitRepeatBlock:
['H', 'REPEAT', 'S']
"""
@property
+ def num_measurements(
+ self,
+ ) -> int:
+ """Returns the number of bits produced when running this loop.
+
+ Examples:
+ >>> import stim
+ >>> stim.CircuitRepeatBlock(
+ ... body=stim.Circuit("M 0 1"),
+ ... repeat_count=25,
+ ... ).num_measurements
+ 50
+ """
+ @property
def repeat_count(
self,
) -> int:
@@ -4194,13 +4297,15 @@ class DemInstruction:
def __init__(
self,
type: str,
- args: List[float],
- targets: List[object],
+ args: Optional[Iterable[float]] = None,
+ targets: Optional[Iterable[stim.DemTarget]] = None,
) -> None:
- """Creates a stim.DemInstruction.
+ """Creates or parses a stim.DemInstruction.
Args:
type: The name of the instruction type (e.g. "error" or "shift_detectors").
+ If `args` and `targets` aren't specified, this can also be set to a
+ full line of text from a dem file, like "error(0.25) D0".
args: Numeric values parameterizing the instruction (e.g. the 0.1 in
"error(0.1)").
targets: The objects the instruction involves (e.g. the "D0" and "L1" in
@@ -4214,6 +4319,9 @@ class DemInstruction:
... [stim.target_relative_detector_id(5)])
>>> print(instruction)
error(0.125) D5
+
+ >>> print(stim.DemInstruction('error(0.125) D5 L6 ^ D4 # comment'))
+ error(0.125) D5 L6 ^ D4
"""
def __ne__(
self,
diff --git a/src/stim/circuit/circuit_instruction.pybind.cc b/src/stim/circuit/circuit_instruction.pybind.cc
index 5c1cce13..fe5bf4a4 100644
--- a/src/stim/circuit/circuit_instruction.pybind.cc
+++ b/src/stim/circuit/circuit_instruction.pybind.cc
@@ -9,15 +9,38 @@ using namespace stim;
using namespace stim_pybind;
PyCircuitInstruction::PyCircuitInstruction(
- const char *name, const std::vector &init_targets, const std::vector &gate_args)
+ std::string_view name, const std::vector &init_targets, const std::vector &gate_args)
: gate_type(GATE_DATA.at(name).id), gate_args(gate_args) {
for (const auto &obj : init_targets) {
targets.push_back(obj_to_gate_target(obj));
}
+ as_operation_ref().validate();
}
PyCircuitInstruction::PyCircuitInstruction(
GateType gate_type, std::vector targets, std::vector gate_args)
: gate_type(gate_type), targets(targets), gate_args(gate_args) {
+ as_operation_ref().validate();
+}
+
+PyCircuitInstruction PyCircuitInstruction::from_str(std::string_view text) {
+ Circuit host;
+ host.append_from_text(text);
+ if (host.operations.size() != 1 || host.operations[0].gate_type == GateType::REPEAT) {
+ throw std::invalid_argument("Given text didn't parse to a single CircuitInstruction.");
+ }
+ return PyCircuitInstruction::from_instruction(host.operations[0]);
+}
+
+PyCircuitInstruction PyCircuitInstruction::from_instruction(CircuitInstruction instruction) {
+ std::vector arguments;
+ std::vector targets;
+ arguments.insert(arguments.begin(), instruction.args.begin(), instruction.args.end());
+ targets.insert(targets.begin(), instruction.targets.begin(), instruction.targets.end());
+ return PyCircuitInstruction{
+ instruction.gate_type,
+ targets,
+ arguments,
+ };
}
bool PyCircuitInstruction::operator==(const PyCircuitInstruction &other) const {
@@ -115,21 +138,46 @@ pybind11::class_ stim_pybind::pybind_circuit_instruction(p
}
void stim_pybind::pybind_circuit_instruction_methods(pybind11::module &m, pybind11::class_ &c) {
c.def(
- pybind11::init, std::vector>(),
+ pybind11::init([](std::string_view name, pybind11::object targets, pybind11::object gate_args) -> PyCircuitInstruction {
+ if (targets.is_none() and gate_args.is_none()) {
+ return PyCircuitInstruction::from_str(name);
+ }
+ std::vector conv_args;
+ std::vector conv_targets;
+ if (!gate_args.is_none()) {
+ conv_args = pybind11::cast>(gate_args);
+ }
+ if (!targets.is_none()) {
+ conv_targets = pybind11::cast>(targets);
+ }
+ return PyCircuitInstruction(name, conv_targets, conv_args);
+ }),
pybind11::arg("name"),
- pybind11::arg("targets"),
- pybind11::arg("gate_args") = std::make_tuple(),
+ pybind11::arg("targets") = pybind11::none(),
+ pybind11::arg("gate_args") = pybind11::none(),
clean_doc_string(R"DOC(
- Initializes a `stim.CircuitInstruction`.
+ @signature def __init__(self, name: str, targets: Optional[Iterable[Union[int, stim.GateTarget]]] = None, gate_args: Optional[Iterable[float]] = None) -> None:
+ Creates or parses a `stim.CircuitInstruction`.
Args:
name: The name of the instruction being applied.
+ If `targets` and `gate_args` aren't specified, this can be a full
+ instruction line from a stim Circuit file, like "CX 0 1".
targets: The targets the instruction is being applied to. These can be raw
values like `0` and `stim.target_rec(-1)`, or instances of
`stim.GateTarget`.
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.
+
+ Examples:
+ >>> import stim
+
+ >>> print(stim.CircuitInstruction('DEPOLARIZE1', [5], [0.25]))
+ DEPOLARIZE1(0.25) 5
+
+ >>> stim.CircuitInstruction('CX rec[-1] 5 # comment')
+ stim.CircuitInstruction('CX', [stim.target_rec(-1), stim.GateTarget(5)], [])
)DOC")
.data());
@@ -246,7 +294,7 @@ void stim_pybind::pybind_circuit_instruction_methods(pybind11::module &m, pybind
3
>>> stim.Circuit('MPP X0*X1 X0*Z1*Y2')[0].num_measurements
2
- >>> stim.CircuitInstruction('HERALDED_ERASE', [0]).num_measurements
+ >>> stim.CircuitInstruction('HERALDED_ERASE', [0], [0.25]).num_measurements
1
)DOC")
.data());
diff --git a/src/stim/circuit/circuit_instruction.pybind.h b/src/stim/circuit/circuit_instruction.pybind.h
index a1a36bf2..69907915 100644
--- a/src/stim/circuit/circuit_instruction.pybind.h
+++ b/src/stim/circuit/circuit_instruction.pybind.h
@@ -29,9 +29,11 @@ struct PyCircuitInstruction {
std::vector gate_args;
PyCircuitInstruction(
- const char *name, const std::vector &targets, const std::vector &gate_args);
+ std::string_view name, const std::vector &targets, const std::vector &gate_args);
PyCircuitInstruction(
stim::GateType gate_type, std::vector targets, std::vector gate_args);
+ static PyCircuitInstruction from_str(std::string_view text);
+ static PyCircuitInstruction from_instruction(stim::CircuitInstruction instruction);
stim::CircuitInstruction as_operation_ref() const;
operator stim::CircuitInstruction() const;
diff --git a/src/stim/circuit/circuit_instruction_pybind_test.py b/src/stim/circuit/circuit_instruction_pybind_test.py
index 1939d70b..d61e226c 100644
--- a/src/stim/circuit/circuit_instruction_pybind_test.py
+++ b/src/stim/circuit/circuit_instruction_pybind_test.py
@@ -76,3 +76,29 @@ def test_target_groups():
assert stim.CircuitInstruction("MPP", []).target_groups() == []
assert stim.CircuitInstruction("MPAD", []).target_groups() == []
assert stim.CircuitInstruction("QUBIT_COORDS", [1, 2]).target_groups() == [[stim.GateTarget(1)], [stim.GateTarget(2)]]
+
+
+def test_eager_validate():
+ with pytest.raises(ValueError, match="0, 1, 2"):
+ stim.CircuitInstruction("CX", [0, 1, 2])
+
+
+def test_init_from_str():
+ assert stim.CircuitInstruction("CX", [0, 1]) == stim.CircuitInstruction("CX 0 1")
+
+ with pytest.raises(ValueError, match="single CircuitInstruction"):
+ stim.CircuitInstruction("")
+
+ with pytest.raises(ValueError, match="single CircuitInstruction"):
+ stim.CircuitInstruction("""
+ REPEAT 5 {
+ H 0
+ X 1
+ }
+ """)
+
+ with pytest.raises(ValueError, match="single CircuitInstruction"):
+ stim.CircuitInstruction("""
+ H 0
+ X 1
+ """)
diff --git a/src/stim/circuit/circuit_repeat_block.pybind.cc b/src/stim/circuit/circuit_repeat_block.pybind.cc
index 4877a091..cb736e6a 100644
--- a/src/stim/circuit/circuit_repeat_block.pybind.cc
+++ b/src/stim/circuit/circuit_repeat_block.pybind.cc
@@ -131,6 +131,24 @@ void stim_pybind::pybind_circuit_repeat_block_methods(pybind11::module &m, pybin
)DOC")
.data());
+ c.def_property_readonly(
+ "num_measurements",
+ [](const CircuitRepeatBlock &self) -> uint64_t {
+ return self.body.count_measurements() * self.repeat_count;
+ },
+ clean_doc_string(R"DOC(
+ Returns the number of bits produced when running this loop.
+
+ Examples:
+ >>> import stim
+ >>> stim.CircuitRepeatBlock(
+ ... body=stim.Circuit("M 0 1"),
+ ... repeat_count=25,
+ ... ).num_measurements
+ 50
+ )DOC")
+ .data());
+
c.def_readonly(
"repeat_count",
&CircuitRepeatBlock::repeat_count,
diff --git a/src/stim/dem/detector_error_model_instruction.pybind.cc b/src/stim/dem/dem_instruction.pybind.cc
similarity index 73%
rename from src/stim/dem/detector_error_model_instruction.pybind.cc
rename to src/stim/dem/dem_instruction.pybind.cc
index ceac444d..7d6c4dad 100644
--- a/src/stim/dem/detector_error_model_instruction.pybind.cc
+++ b/src/stim/dem/dem_instruction.pybind.cc
@@ -1,4 +1,4 @@
-#include "stim/dem/detector_error_model_instruction.pybind.h"
+#include "stim/dem/dem_instruction.pybind.h"
#include "stim/dem/detector_error_model_target.pybind.h"
#include "stim/py/base.pybind.h"
@@ -23,6 +23,27 @@ DemInstruction ExposedDemInstruction::as_dem_instruction() const {
return DemInstruction{arguments, targets, type};
}
+ExposedDemInstruction ExposedDemInstruction::from_dem_instruction(stim::DemInstruction instruction) {
+ std::vector arguments;
+ std::vector targets;
+ arguments.insert(arguments.begin(), instruction.arg_data.begin(), instruction.arg_data.end());
+ targets.insert(targets.begin(), instruction.target_data.begin(), instruction.target_data.end());
+ return ExposedDemInstruction{
+ arguments,
+ targets,
+ instruction.type
+ };
+}
+
+ExposedDemInstruction ExposedDemInstruction::from_str(std::string_view text) {
+ DetectorErrorModel host;
+ host.append_from_text(text);
+ if (host.instructions.size() != 1 || host.instructions[0].type == DemInstructionType::DEM_REPEAT_BLOCK) {
+ throw std::invalid_argument("Given text didn't parse to a single DemInstruction.");
+ }
+ return ExposedDemInstruction::from_dem_instruction(host.instructions[0]);
+}
+
std::string ExposedDemInstruction::type_name() const {
std::stringstream out;
out << type;
@@ -108,10 +129,14 @@ void stim_pybind::pybind_detector_error_model_instruction_methods(
pybind11::module &m, pybind11::class_ &c) {
c.def(
pybind11::init(
- [](const char *type, const std::vector &arguments, const std::vector &targets) {
+ [](std::string_view type, pybind11::object &arguments, pybind11::object &targets) -> ExposedDemInstruction {
+ if (arguments.is_none() && targets.is_none()) {
+ return ExposedDemInstruction::from_str(type);
+ }
+
std::string lower;
- for (const char *c = type; *c != '\0'; c++) {
- lower.push_back(tolower(*c));
+ for (char c : type) {
+ lower.push_back(tolower(c));
}
DemInstructionType conv_type;
std::vector conv_targets;
@@ -126,41 +151,50 @@ void stim_pybind::pybind_detector_error_model_instruction_methods(
} else {
throw std::invalid_argument("Unrecognized instruction name '" + lower + "'.");
}
- if (conv_type == DemInstructionType::DEM_SHIFT_DETECTORS) {
- for (const auto &e : targets) {
- try {
- conv_targets.push_back(DemTarget{pybind11::cast(e)});
- } catch (pybind11::cast_error &ex) {
- throw std::invalid_argument(
- "Instruction '" + lower + "' only takes unsigned integer targets.");
+ if (!targets.is_none()) {
+ if (conv_type == DemInstructionType::DEM_SHIFT_DETECTORS) {
+ for (const auto &e : targets) {
+ try {
+ conv_targets.push_back(DemTarget{pybind11::cast(e)});
+ } catch (pybind11::cast_error &ex) {
+ throw std::invalid_argument(
+ "Instruction '" + lower + "' only takes unsigned integer targets.");
+ }
}
- }
- } else {
- for (const auto &e : targets) {
- try {
- conv_targets.push_back(pybind11::cast(e).internal());
- } catch (pybind11::cast_error &ex) {
- throw std::invalid_argument(
- "Instruction '" + lower +
- "' only takes stim.target_relative_detector_id(k), "
- "stim.target_logical_observable_id(k), "
- "stim.target_separator() targets.");
+ } else {
+ for (const auto &e : targets) {
+ try {
+ conv_targets.push_back(pybind11::cast(e).internal());
+ } catch (pybind11::cast_error &ex) {
+ throw std::invalid_argument(
+ "Instruction '" + lower +
+ "' only takes stim.target_relative_detector_id(k), "
+ "stim.target_logical_observable_id(k), "
+ "stim.target_separator() targets.");
+ }
}
}
}
- ExposedDemInstruction result{arguments, std::move(conv_targets), conv_type};
+ std::vector conv_args;
+ if (!arguments.is_none()) {
+ conv_args = pybind11::cast>(arguments);
+ }
+ ExposedDemInstruction result{std::move(conv_args), std::move(conv_targets), conv_type};
result.as_dem_instruction().validate();
return result;
}),
pybind11::arg("type"),
- pybind11::arg("args"),
- pybind11::arg("targets"),
+ pybind11::arg("args") = pybind11::none(),
+ pybind11::arg("targets") = pybind11::none(),
clean_doc_string(R"DOC(
- Creates a stim.DemInstruction.
+ @signature def __init__(self, type: str, args: Optional[Iterable[float]] = None, targets: Optional[Iterable[stim.DemTarget]] = None) -> None:
+ Creates or parses a stim.DemInstruction.
Args:
type: The name of the instruction type (e.g. "error" or "shift_detectors").
+ If `args` and `targets` aren't specified, this can also be set to a
+ full line of text from a dem file, like "error(0.25) D0".
args: Numeric values parameterizing the instruction (e.g. the 0.1 in
"error(0.1)").
targets: The objects the instruction involves (e.g. the "D0" and "L1" in
@@ -174,6 +208,9 @@ void stim_pybind::pybind_detector_error_model_instruction_methods(
... [stim.target_relative_detector_id(5)])
>>> print(instruction)
error(0.125) D5
+
+ >>> print(stim.DemInstruction('error(0.125) D5 L6 ^ D4 # comment'))
+ error(0.125) D5 L6 ^ D4
)DOC")
.data());
diff --git a/src/stim/dem/detector_error_model_instruction.pybind.h b/src/stim/dem/dem_instruction.pybind.h
similarity index 87%
rename from src/stim/dem/detector_error_model_instruction.pybind.h
rename to src/stim/dem/dem_instruction.pybind.h
index 247a713a..f7f973bf 100644
--- a/src/stim/dem/detector_error_model_instruction.pybind.h
+++ b/src/stim/dem/dem_instruction.pybind.h
@@ -12,6 +12,9 @@ struct ExposedDemInstruction {
std::vector targets;
stim::DemInstructionType type;
+ static ExposedDemInstruction from_str(std::string_view text);
+ static ExposedDemInstruction from_dem_instruction(stim::DemInstruction instruction);
+
std::vector> target_groups() const;
std::vector args_copy() const;
std::vector targets_copy() const;
diff --git a/src/stim/dem/detector_error_model_instruction_pybind_test.py b/src/stim/dem/dem_instruction_pybind_test.py
similarity index 86%
rename from src/stim/dem/detector_error_model_instruction_pybind_test.py
rename to src/stim/dem/dem_instruction_pybind_test.py
index 2afbe0e6..ba55f504 100644
--- a/src/stim/dem/detector_error_model_instruction_pybind_test.py
+++ b/src/stim/dem/dem_instruction_pybind_test.py
@@ -78,3 +78,24 @@ def test_hashable():
def test_target_groups():
dem = stim.DetectorErrorModel("detector D0")
assert dem[0].target_groups() == [[stim.DemTarget("D0")]]
+
+
+def test_init_from_str():
+ assert stim.DemInstruction("detector D0") == stim.DemInstruction("detector", [], [stim.target_relative_detector_id(0)])
+
+ with pytest.raises(ValueError, match="single DemInstruction"):
+ stim.DemInstruction("")
+
+ with pytest.raises(ValueError, match="single DemInstruction"):
+ stim.DemInstruction("""
+ repeat 5 {
+ error(0.25) D0
+ shift_detectors 1
+ }
+ """)
+
+ with pytest.raises(ValueError, match="single DemInstruction"):
+ stim.DemInstruction("""
+ detector D0
+ detector D1
+ """)
diff --git a/src/stim/dem/detector_error_model.h b/src/stim/dem/detector_error_model.h
index 616c2458..3f8b972c 100644
--- a/src/stim/dem/detector_error_model.h
+++ b/src/stim/dem/detector_error_model.h
@@ -1,19 +1,3 @@
-/*
- * Copyright 2021 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
#ifndef _STIM_DEM_DETECTOR_ERROR_MODEL_H
#define _STIM_DEM_DETECTOR_ERROR_MODEL_H
diff --git a/src/stim/dem/detector_error_model.pybind.cc b/src/stim/dem/detector_error_model.pybind.cc
index f823f25f..3292d930 100644
--- a/src/stim/dem/detector_error_model.pybind.cc
+++ b/src/stim/dem/detector_error_model.pybind.cc
@@ -18,7 +18,7 @@
#include "stim/circuit/circuit.pybind.h"
#include "stim/cmd/command_diagram.pybind.h"
-#include "stim/dem/detector_error_model_instruction.pybind.h"
+#include "stim/dem/dem_instruction.pybind.h"
#include "stim/dem/detector_error_model_repeat_block.pybind.h"
#include "stim/dem/detector_error_model_target.pybind.h"
#include "stim/io/raii_file.h"
diff --git a/src/stim/dem/detector_error_model_pybind_test.py b/src/stim/dem/detector_error_model_pybind_test.py
index 955937b8..25fc80ad 100644
--- a/src/stim/dem/detector_error_model_pybind_test.py
+++ b/src/stim/dem/detector_error_model_pybind_test.py
@@ -536,3 +536,7 @@ def test_shortest_graphlike_error_remnant():
assert len(d.shortest_graphlike_error(ignore_ungraphlike_errors=True)) == 8
assert len(c.shortest_graphlike_error()) == 8
assert len(d.shortest_graphlike_error()) == 8
+
+
+def test_init_parse():
+ assert stim.DemInstruction("error(0.125) D0 D1") == stim.DemInstruction("error", [0.125], [stim.DemTarget("D0"), stim.DemTarget("D1")])
diff --git a/src/stim/dem/detector_error_model_repeat_block.pybind.cc b/src/stim/dem/detector_error_model_repeat_block.pybind.cc
index 91b2ed98..a032aba6 100644
--- a/src/stim/dem/detector_error_model_repeat_block.pybind.cc
+++ b/src/stim/dem/detector_error_model_repeat_block.pybind.cc
@@ -14,8 +14,8 @@
#include "stim/dem/detector_error_model_repeat_block.pybind.h"
+#include "stim/dem/dem_instruction.pybind.h"
#include "stim/dem/detector_error_model.pybind.h"
-#include "stim/dem/detector_error_model_instruction.pybind.h"
#include "stim/py/base.pybind.h"
using namespace stim;
diff --git a/src/stim/py/stim.pybind.cc b/src/stim/py/stim.pybind.cc
index 44ca33b5..4200d2e0 100644
--- a/src/stim/py/stim.pybind.cc
+++ b/src/stim/py/stim.pybind.cc
@@ -20,8 +20,8 @@
#include "stim/circuit/circuit_repeat_block.pybind.h"
#include "stim/circuit/gate_target.pybind.h"
#include "stim/cmd/command_diagram.pybind.h"
+#include "stim/dem/dem_instruction.pybind.h"
#include "stim/dem/detector_error_model.pybind.h"
-#include "stim/dem/detector_error_model_instruction.pybind.h"
#include "stim/dem/detector_error_model_repeat_block.pybind.h"
#include "stim/dem/detector_error_model_target.pybind.h"
#include "stim/gates/gates.pybind.h"
diff --git a/src/stim/simulators/matched_error.pybind.cc b/src/stim/simulators/matched_error.pybind.cc
index 6587750a..81c2fb7f 100644
--- a/src/stim/simulators/matched_error.pybind.cc
+++ b/src/stim/simulators/matched_error.pybind.cc
@@ -150,7 +150,31 @@ pybind11::class_ stim_pybind::pybind_circuit_err
The full location of an instruction is a list of these frames,
drilling down from the top level circuit to the inner-most loop
that the instruction is within.
- )DOC")
+
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... REPEAT 5 {
+ ... R 0
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... }
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> err[0].circuit_error_locations[0].stack_frames[0]
+ stim.CircuitErrorLocationStackFrame(
+ instruction_offset=0,
+ iteration_index=0,
+ instruction_repetitions_arg=5,
+ )
+ >>> err[0].circuit_error_locations[0].stack_frames[1]
+ stim.CircuitErrorLocationStackFrame(
+ instruction_offset=1,
+ iteration_index=4,
+ instruction_repetitions_arg=0,
+ )
+ )DOC")
.data());
}
void stim_pybind::pybind_circuit_error_location_stack_frame_methods(
@@ -164,6 +188,18 @@ void stim_pybind::pybind_circuit_error_location_stack_frame_methods(
from the line number, because blank lines and commented lines
don't count and also because the offset of the first instruction
is 0 instead of 1.
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... R 0
+ ... TICK
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> err[0].circuit_error_locations[0].stack_frames[0].instruction_offset
+ 2
)DOC")
.data());
@@ -174,6 +210,23 @@ void stim_pybind::pybind_circuit_error_location_stack_frame_methods(
Disambiguates which iteration of the loop containing this instruction
is being referred to. If the instruction isn't in a REPEAT block, this
field defaults to 0.
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... REPEAT 5 {
+ ... R 0
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... }
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> full = err[0].circuit_error_locations[0].stack_frames[0]
+ >>> loop = err[0].circuit_error_locations[0].stack_frames[1]
+ >>> full.iteration_index
+ 0
+ >>> loop.iteration_index
+ 4
)DOC")
.data());
@@ -184,6 +237,23 @@ void stim_pybind::pybind_circuit_error_location_stack_frame_methods(
If the instruction being referred to is a REPEAT block,
this is the repetition count of that REPEAT block. Otherwise
this field defaults to 0.
+
+ Examples:
+ >>> import stim
+ >>> err = stim.Circuit('''
+ ... REPEAT 5 {
+ ... R 0
+ ... Y_ERROR(0.125) 0
+ ... M 0
+ ... }
+ ... OBSERVABLE_INCLUDE(0) rec[-1]
+ ... ''').shortest_graphlike_error()
+ >>> full = err[0].circuit_error_locations[0].stack_frames[0]
+ >>> loop = err[0].circuit_error_locations[0].stack_frames[1]
+ >>> full.instruction_repetitions_arg
+ 5
+ >>> loop.instruction_repetitions_arg
+ 0
)DOC")
.data());
@@ -209,6 +279,14 @@ void stim_pybind::pybind_circuit_error_location_stack_frame_methods(
pybind11::arg("instruction_repetitions_arg"),
clean_doc_string(R"DOC(
Creates a stim.CircuitErrorLocationStackFrame.
+
+ Examples:
+ >>> import stim
+ >>> frame = stim.CircuitErrorLocationStackFrame(
+ ... instruction_offset=1,
+ ... iteration_index=2,
+ ... instruction_repetitions_arg=3,
+ ... )
)DOC")
.data());
c.def("__str__", &CircuitErrorLocationStackFrame_repr);
diff --git a/src/stim/simulators/tableau_simulator_pybind_test.py b/src/stim/simulators/tableau_simulator_pybind_test.py
index 067bc0e1..e9b86af5 100644
--- a/src/stim/simulators/tableau_simulator_pybind_test.py
+++ b/src/stim/simulators/tableau_simulator_pybind_test.py
@@ -286,9 +286,9 @@ def test_classical_control_cnot():
def test_collision():
s = stim.TableauSimulator()
- with pytest.raises(ValueError, match="same qubit"):
+ with pytest.raises(ValueError, match="same target"):
s.cnot(0, 0)
- with pytest.raises(ValueError, match="same qubit"):
+ with pytest.raises(ValueError, match="same target"):
s.swap(0, 1, 2, 2)
s.swap(0, 2, 2, 1)