diff --git a/doc/python_api_reference_vDev.md b/doc/python_api_reference_vDev.md
index e030cbf0b..bf19a5642 100644
--- a/doc/python_api_reference_vDev.md
+++ b/doc/python_api_reference_vDev.md
@@ -75,6 +75,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
- [`stim.CircuitRepeatBlock.__ne__`](#stim.CircuitRepeatBlock.__ne__)
- [`stim.CircuitRepeatBlock.__repr__`](#stim.CircuitRepeatBlock.__repr__)
- [`stim.CircuitRepeatBlock.body_copy`](#stim.CircuitRepeatBlock.body_copy)
+ - [`stim.CircuitRepeatBlock.name`](#stim.CircuitRepeatBlock.name)
- [`stim.CircuitRepeatBlock.repeat_count`](#stim.CircuitRepeatBlock.repeat_count)
- [`stim.CircuitTargetsInsideInstruction`](#stim.CircuitTargetsInsideInstruction)
- [`stim.CircuitTargetsInsideInstruction.__init__`](#stim.CircuitTargetsInsideInstruction.__init__)
@@ -117,6 +118,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
- [`stim.DemRepeatBlock.__repr__`](#stim.DemRepeatBlock.__repr__)
- [`stim.DemRepeatBlock.body_copy`](#stim.DemRepeatBlock.body_copy)
- [`stim.DemRepeatBlock.repeat_count`](#stim.DemRepeatBlock.repeat_count)
+ - [`stim.DemRepeatBlock.type`](#stim.DemRepeatBlock.type)
- [`stim.DemTarget`](#stim.DemTarget)
- [`stim.DemTarget.__eq__`](#stim.DemTarget.__eq__)
- [`stim.DemTarget.__ne__`](#stim.DemTarget.__ne__)
@@ -315,6 +317,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
- [`stim.TableauSimulator.peek_x`](#stim.TableauSimulator.peek_x)
- [`stim.TableauSimulator.peek_y`](#stim.TableauSimulator.peek_y)
- [`stim.TableauSimulator.peek_z`](#stim.TableauSimulator.peek_z)
+ - [`stim.TableauSimulator.postselect_observable`](#stim.TableauSimulator.postselect_observable)
- [`stim.TableauSimulator.postselect_x`](#stim.TableauSimulator.postselect_x)
- [`stim.TableauSimulator.postselect_y`](#stim.TableauSimulator.postselect_y)
- [`stim.TableauSimulator.postselect_z`](#stim.TableauSimulator.postselect_z)
@@ -2859,6 +2862,35 @@ def body_copy(
"""
```
+
+```python
+# stim.CircuitRepeatBlock.name
+
+# (in class stim.CircuitRepeatBlock)
+@property
+def name(
+ self,
+) -> object:
+ """Returns the name "REPEAT".
+
+ This is a duck-typing convenience method. It exists so that code that doesn't
+ know whether it has a `stim.CircuitInstruction` or a `stim.CircuitRepeatBlock`
+ can check the object's name without having to do an `instanceof` check first.
+
+ Examples:
+ >>> import stim
+ >>> circuit = stim.Circuit('''
+ ... H 0
+ ... REPEAT 5 {
+ ... CX 1 2
+ ... }
+ ... S 1
+ ... ''')
+ >>> [instruction.name for instruction in circuit]
+ ['H', 'REPEAT', 'S']
+ """
+```
+
```python
# stim.CircuitRepeatBlock.repeat_count
@@ -4213,6 +4245,36 @@ def repeat_count(
"""
```
+
+```python
+# stim.DemRepeatBlock.type
+
+# (in class stim.DemRepeatBlock)
+@property
+def type(
+ self,
+) -> object:
+ """Returns the type name "repeat".
+
+ This is a duck-typing convenience method. It exists so that code that doesn't
+ know whether it has a `stim.DemInstruction` or a `stim.DemRepeatBlock`
+ can check the type field without having to do an `instanceof` check first.
+
+ Examples:
+ >>> import stim
+ >>> dem = stim.DetectorErrorModel('''
+ ... error(0.1) D0 L0
+ ... repeat 5 {
+ ... error(0.1) D0 D1
+ ... shift_detectors 1
+ ... }
+ ... logical_observable L0
+ ... ''')
+ >>> [instruction.type for instruction in dem]
+ ['error', 'repeat', 'logical_observable']
+ """
+```
+
```python
# stim.DemTarget
@@ -10844,6 +10906,46 @@ def peek_z(
"""
```
+
+```python
+# stim.TableauSimulator.postselect_observable
+
+# (in class stim.TableauSimulator)
+def postselect_observable(
+ self,
+ observable: stim.PauliString,
+ *,
+ desired_value: bool = False,
+) -> None:
+ """Projects into a desired observable, or raises an exception if it was impossible.
+
+ Postselecting an observable forces it to collapse to a specific eigenstate,
+ as if it was measured and that state was the result of the measurement.
+
+ Args:
+ observable: The observable to postselect, specified as a pauli string.
+ The pauli string's sign must be -1 or +1 (not -i or +i).
+ desired_value:
+ False (default): Postselect into the +1 eigenstate of the observable.
+ True: Postselect into the -1 eigenstate of the observable.
+
+ Raises:
+ ValueError:
+ The given observable had an imaginary sign.
+ OR
+ The postselection was impossible. The observable was in the opposite
+ eigenstate, so measuring it would never ever return the desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.postselect_observable(stim.PauliString("+XX"))
+ >>> s.postselect_observable(stim.PauliString("+ZZ"))
+ >>> s.peek_observable_expectation(stim.PauliString("+YY"))
+ -1
+ """
+```
+
```python
# stim.TableauSimulator.postselect_x
@@ -10872,6 +10974,21 @@ def postselect_x(
orthogonal to the desired state, so it was literally
impossible for a measurement of the qubit to return the
desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.peek_x(0)
+ 0
+ >>> s.postselect_x(0, desired_value=False)
+ >>> s.peek_x(0)
+ 1
+ >>> s.h(0)
+ >>> s.peek_x(0)
+ 0
+ >>> s.postselect_x(0, desired_value=True)
+ >>> s.peek_x(0)
+ -1
"""
```
@@ -10903,6 +11020,21 @@ def postselect_y(
orthogonal to the desired state, so it was literally
impossible for a measurement of the qubit to return the
desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.peek_y(0)
+ 0
+ >>> s.postselect_y(0, desired_value=False)
+ >>> s.peek_y(0)
+ 1
+ >>> s.reset_x(0)
+ >>> s.peek_y(0)
+ 0
+ >>> s.postselect_y(0, desired_value=True)
+ >>> s.peek_y(0)
+ -1
"""
```
@@ -10934,6 +11066,22 @@ def postselect_z(
orthogonal to the desired state, so it was literally
impossible for a measurement of the qubit to return the
desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.h(0)
+ >>> s.peek_z(0)
+ 0
+ >>> s.postselect_z(0, desired_value=False)
+ >>> s.peek_z(0)
+ 1
+ >>> s.h(0)
+ >>> s.peek_z(0)
+ 0
+ >>> s.postselect_z(0, desired_value=True)
+ >>> s.peek_z(0)
+ -1
"""
```
diff --git a/doc/stim.pyi b/doc/stim.pyi
index a8812e3c4..170c5b615 100644
--- a/doc/stim.pyi
+++ b/doc/stim.pyi
@@ -2039,6 +2039,28 @@ class CircuitRepeatBlock:
''')
"""
@property
+ def name(
+ self,
+ ) -> object:
+ """Returns the name "REPEAT".
+
+ This is a duck-typing convenience method. It exists so that code that doesn't
+ know whether it has a `stim.CircuitInstruction` or a `stim.CircuitRepeatBlock`
+ can check the object's name without having to do an `instanceof` check first.
+
+ Examples:
+ >>> import stim
+ >>> circuit = stim.Circuit('''
+ ... H 0
+ ... REPEAT 5 {
+ ... CX 1 2
+ ... }
+ ... S 1
+ ... ''')
+ >>> [instruction.name for instruction in circuit]
+ ['H', 'REPEAT', 'S']
+ """
+ @property
def repeat_count(
self,
) -> int:
@@ -3154,6 +3176,29 @@ class DemRepeatBlock:
) -> int:
"""The number of times the repeat block's body is supposed to execute.
"""
+ @property
+ def type(
+ self,
+ ) -> object:
+ """Returns the type name "repeat".
+
+ This is a duck-typing convenience method. It exists so that code that doesn't
+ know whether it has a `stim.DemInstruction` or a `stim.DemRepeatBlock`
+ can check the type field without having to do an `instanceof` check first.
+
+ Examples:
+ >>> import stim
+ >>> dem = stim.DetectorErrorModel('''
+ ... error(0.1) D0 L0
+ ... repeat 5 {
+ ... error(0.1) D0 D1
+ ... shift_detectors 1
+ ... }
+ ... logical_observable L0
+ ... ''')
+ >>> [instruction.type for instruction in dem]
+ ['error', 'repeat', 'logical_observable']
+ """
class DemTarget:
"""An instruction target from a detector error model (.dem) file.
"""
@@ -8446,6 +8491,39 @@ class TableauSimulator:
>>> s.peek_z(0)
-1
"""
+ def postselect_observable(
+ self,
+ observable: stim.PauliString,
+ *,
+ desired_value: bool = False,
+ ) -> None:
+ """Projects into a desired observable, or raises an exception if it was impossible.
+
+ Postselecting an observable forces it to collapse to a specific eigenstate,
+ as if it was measured and that state was the result of the measurement.
+
+ Args:
+ observable: The observable to postselect, specified as a pauli string.
+ The pauli string's sign must be -1 or +1 (not -i or +i).
+ desired_value:
+ False (default): Postselect into the +1 eigenstate of the observable.
+ True: Postselect into the -1 eigenstate of the observable.
+
+ Raises:
+ ValueError:
+ The given observable had an imaginary sign.
+ OR
+ The postselection was impossible. The observable was in the opposite
+ eigenstate, so measuring it would never ever return the desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.postselect_observable(stim.PauliString("+XX"))
+ >>> s.postselect_observable(stim.PauliString("+ZZ"))
+ >>> s.peek_observable_expectation(stim.PauliString("+YY"))
+ -1
+ """
def postselect_x(
self,
targets: Union[int, Iterable[int]],
@@ -8469,6 +8547,21 @@ class TableauSimulator:
orthogonal to the desired state, so it was literally
impossible for a measurement of the qubit to return the
desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.peek_x(0)
+ 0
+ >>> s.postselect_x(0, desired_value=False)
+ >>> s.peek_x(0)
+ 1
+ >>> s.h(0)
+ >>> s.peek_x(0)
+ 0
+ >>> s.postselect_x(0, desired_value=True)
+ >>> s.peek_x(0)
+ -1
"""
def postselect_y(
self,
@@ -8493,6 +8586,21 @@ class TableauSimulator:
orthogonal to the desired state, so it was literally
impossible for a measurement of the qubit to return the
desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.peek_y(0)
+ 0
+ >>> s.postselect_y(0, desired_value=False)
+ >>> s.peek_y(0)
+ 1
+ >>> s.reset_x(0)
+ >>> s.peek_y(0)
+ 0
+ >>> s.postselect_y(0, desired_value=True)
+ >>> s.peek_y(0)
+ -1
"""
def postselect_z(
self,
@@ -8517,6 +8625,22 @@ class TableauSimulator:
orthogonal to the desired state, so it was literally
impossible for a measurement of the qubit to return the
desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.h(0)
+ >>> s.peek_z(0)
+ 0
+ >>> s.postselect_z(0, desired_value=False)
+ >>> s.peek_z(0)
+ 1
+ >>> s.h(0)
+ >>> s.peek_z(0)
+ 0
+ >>> s.postselect_z(0, desired_value=True)
+ >>> s.peek_z(0)
+ -1
"""
def reset(
self,
diff --git a/glue/python/src/stim/__init__.pyi b/glue/python/src/stim/__init__.pyi
index a8812e3c4..170c5b615 100644
--- a/glue/python/src/stim/__init__.pyi
+++ b/glue/python/src/stim/__init__.pyi
@@ -2039,6 +2039,28 @@ class CircuitRepeatBlock:
''')
"""
@property
+ def name(
+ self,
+ ) -> object:
+ """Returns the name "REPEAT".
+
+ This is a duck-typing convenience method. It exists so that code that doesn't
+ know whether it has a `stim.CircuitInstruction` or a `stim.CircuitRepeatBlock`
+ can check the object's name without having to do an `instanceof` check first.
+
+ Examples:
+ >>> import stim
+ >>> circuit = stim.Circuit('''
+ ... H 0
+ ... REPEAT 5 {
+ ... CX 1 2
+ ... }
+ ... S 1
+ ... ''')
+ >>> [instruction.name for instruction in circuit]
+ ['H', 'REPEAT', 'S']
+ """
+ @property
def repeat_count(
self,
) -> int:
@@ -3154,6 +3176,29 @@ class DemRepeatBlock:
) -> int:
"""The number of times the repeat block's body is supposed to execute.
"""
+ @property
+ def type(
+ self,
+ ) -> object:
+ """Returns the type name "repeat".
+
+ This is a duck-typing convenience method. It exists so that code that doesn't
+ know whether it has a `stim.DemInstruction` or a `stim.DemRepeatBlock`
+ can check the type field without having to do an `instanceof` check first.
+
+ Examples:
+ >>> import stim
+ >>> dem = stim.DetectorErrorModel('''
+ ... error(0.1) D0 L0
+ ... repeat 5 {
+ ... error(0.1) D0 D1
+ ... shift_detectors 1
+ ... }
+ ... logical_observable L0
+ ... ''')
+ >>> [instruction.type for instruction in dem]
+ ['error', 'repeat', 'logical_observable']
+ """
class DemTarget:
"""An instruction target from a detector error model (.dem) file.
"""
@@ -8446,6 +8491,39 @@ class TableauSimulator:
>>> s.peek_z(0)
-1
"""
+ def postselect_observable(
+ self,
+ observable: stim.PauliString,
+ *,
+ desired_value: bool = False,
+ ) -> None:
+ """Projects into a desired observable, or raises an exception if it was impossible.
+
+ Postselecting an observable forces it to collapse to a specific eigenstate,
+ as if it was measured and that state was the result of the measurement.
+
+ Args:
+ observable: The observable to postselect, specified as a pauli string.
+ The pauli string's sign must be -1 or +1 (not -i or +i).
+ desired_value:
+ False (default): Postselect into the +1 eigenstate of the observable.
+ True: Postselect into the -1 eigenstate of the observable.
+
+ Raises:
+ ValueError:
+ The given observable had an imaginary sign.
+ OR
+ The postselection was impossible. The observable was in the opposite
+ eigenstate, so measuring it would never ever return the desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.postselect_observable(stim.PauliString("+XX"))
+ >>> s.postselect_observable(stim.PauliString("+ZZ"))
+ >>> s.peek_observable_expectation(stim.PauliString("+YY"))
+ -1
+ """
def postselect_x(
self,
targets: Union[int, Iterable[int]],
@@ -8469,6 +8547,21 @@ class TableauSimulator:
orthogonal to the desired state, so it was literally
impossible for a measurement of the qubit to return the
desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.peek_x(0)
+ 0
+ >>> s.postselect_x(0, desired_value=False)
+ >>> s.peek_x(0)
+ 1
+ >>> s.h(0)
+ >>> s.peek_x(0)
+ 0
+ >>> s.postselect_x(0, desired_value=True)
+ >>> s.peek_x(0)
+ -1
"""
def postselect_y(
self,
@@ -8493,6 +8586,21 @@ class TableauSimulator:
orthogonal to the desired state, so it was literally
impossible for a measurement of the qubit to return the
desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.peek_y(0)
+ 0
+ >>> s.postselect_y(0, desired_value=False)
+ >>> s.peek_y(0)
+ 1
+ >>> s.reset_x(0)
+ >>> s.peek_y(0)
+ 0
+ >>> s.postselect_y(0, desired_value=True)
+ >>> s.peek_y(0)
+ -1
"""
def postselect_z(
self,
@@ -8517,6 +8625,22 @@ class TableauSimulator:
orthogonal to the desired state, so it was literally
impossible for a measurement of the qubit to return the
desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.h(0)
+ >>> s.peek_z(0)
+ 0
+ >>> s.postselect_z(0, desired_value=False)
+ >>> s.peek_z(0)
+ 1
+ >>> s.h(0)
+ >>> s.peek_z(0)
+ 0
+ >>> s.postselect_z(0, desired_value=True)
+ >>> s.peek_z(0)
+ -1
"""
def reset(
self,
diff --git a/src/stim/circuit/circuit_pybind_test.py b/src/stim/circuit/circuit_pybind_test.py
index e5f69f152..529e76358 100644
--- a/src/stim/circuit/circuit_pybind_test.py
+++ b/src/stim/circuit/circuit_pybind_test.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import pathlib
+import re
import tempfile
from typing import cast
@@ -716,7 +717,7 @@ def test_shortest_graphlike_error_empty():
def test_shortest_graphlike_error_msgs():
with pytest.raises(
ValueError,
- match="Circuit defines no observables. Circuit defines no detectors. Circuit defines no errors that can flip detectors or observables."
+ match="NO OBSERVABLES"
):
stim.Circuit().shortest_graphlike_error()
@@ -724,14 +725,16 @@ def test_shortest_graphlike_error_msgs():
M 0
OBSERVABLE_INCLUDE(0) rec[-1]
""")
- with pytest.raises(ValueError, match="Circuit defines no detectors. Circuit defines no errors that can flip detectors or observables."):
+ with pytest.raises(ValueError, match="NO DETECTORS"):
c.shortest_graphlike_error()
c = stim.Circuit("""
X_ERROR(0.1) 0
M 0
""")
- with pytest.raises(ValueError, match="Circuit defines no observables. Circuit defines no detectors. Circuit defines no errors that can flip detectors or observables."):
+ with pytest.raises(ValueError, match=r"NO OBSERVABLES(.|\n)*NO DETECTORS"):
+ c.shortest_graphlike_error()
+ with pytest.raises(ValueError, match=""):
c.shortest_graphlike_error()
c = stim.Circuit("""
@@ -739,7 +742,17 @@ def test_shortest_graphlike_error_msgs():
DETECTOR rec[-1]
OBSERVABLE_INCLUDE(0) rec[-1]
""")
- with pytest.raises(ValueError, match="Circuit defines no errors that can flip detectors or observables."):
+ with pytest.raises(ValueError, match="NO ERRORS"):
+ c.shortest_graphlike_error()
+
+ c = stim.Circuit("""
+ M(0.1) 0
+ DETECTOR rec[-1]
+ DETECTOR rec[-1]
+ DETECTOR rec[-1]
+ OBSERVABLE_INCLUDE(0) rec[-1]
+ """)
+ with pytest.raises(ValueError, match="NO GRAPHLIKE ERRORS"):
c.shortest_graphlike_error()
c = stim.Circuit("""
@@ -747,7 +760,7 @@ def test_shortest_graphlike_error_msgs():
M 0
DETECTOR rec[-1]
""")
- with pytest.raises(ValueError, match="Circuit defines no observables."):
+ with pytest.raises(ValueError, match="NO OBSERVABLES"):
c.shortest_graphlike_error()
@@ -761,10 +774,7 @@ def test_search_for_undetectable_logical_errors_empty():
def test_search_for_undetectable_logical_errors_msgs():
- with pytest.raises(
- ValueError,
- match="Circuit defines no observables. Circuit defines no detectors. Circuit defines no errors that can flip detectors or observables."
- ):
+ with pytest.raises(ValueError, match=r"NO OBSERVABLES(.|\n)*NO DETECTORS"):
stim.Circuit().search_for_undetectable_logical_errors(
dont_explore_edges_increasing_symptom_degree=True,
dont_explore_edges_with_degree_above=4,
@@ -775,8 +785,7 @@ def test_search_for_undetectable_logical_errors_msgs():
M 0
OBSERVABLE_INCLUDE(0) rec[-1]
""")
- with pytest.raises(ValueError,
- match="Circuit defines no detectors. Circuit defines no errors that can flip detectors or observables."):
+ with pytest.raises(ValueError, match=r"NO DETECTORS(.|\n)*NO ERRORS"):
c.search_for_undetectable_logical_errors(
dont_explore_edges_increasing_symptom_degree=True,
dont_explore_edges_with_degree_above=4,
@@ -787,8 +796,7 @@ def test_search_for_undetectable_logical_errors_msgs():
X_ERROR(0.1) 0
M 0
""")
- with pytest.raises(ValueError,
- match="Circuit defines no observables. Circuit defines no detectors. Circuit defines no errors that can flip detectors or observables."):
+ with pytest.raises(ValueError, match=r"NO OBSERVABLES(.|\n)*NO DETECTORS(.|\n)*NO ERRORS"):
c.search_for_undetectable_logical_errors(
dont_explore_edges_increasing_symptom_degree=True,
dont_explore_edges_with_degree_above=4,
@@ -800,7 +808,7 @@ def test_search_for_undetectable_logical_errors_msgs():
DETECTOR rec[-1]
OBSERVABLE_INCLUDE(0) rec[-1]
""")
- with pytest.raises(ValueError, match="Circuit defines no errors that can flip detectors or observables."):
+ with pytest.raises(ValueError, match="NO ERRORS"):
c.search_for_undetectable_logical_errors(
dont_explore_edges_increasing_symptom_degree=True,
dont_explore_edges_with_degree_above=4,
@@ -812,7 +820,7 @@ def test_search_for_undetectable_logical_errors_msgs():
M 0
DETECTOR rec[-1]
""")
- with pytest.raises(ValueError, match="Circuit defines no observables."):
+ with pytest.raises(ValueError, match="NO OBSERVABLES"):
c.search_for_undetectable_logical_errors(
dont_explore_edges_increasing_symptom_degree=True,
dont_explore_edges_with_degree_above=4,
diff --git a/src/stim/circuit/circuit_repeat_block.pybind.cc b/src/stim/circuit/circuit_repeat_block.pybind.cc
index 8cbc7071f..ae4f640a0 100644
--- a/src/stim/circuit/circuit_repeat_block.pybind.cc
+++ b/src/stim/circuit/circuit_repeat_block.pybind.cc
@@ -105,6 +105,53 @@ void stim_pybind::pybind_circuit_repeat_block_methods(pybind11::module &m, pybin
)DOC")
.data());
+ c.def_property_readonly(
+ "name",
+ [](const CircuitRepeatBlock &self) -> pybind11::object {
+ return pybind11::cast("REPEAT");
+ },
+ clean_doc_string(R"DOC(
+ Returns the name "REPEAT".
+
+ This is a duck-typing convenience method. It exists so that code that doesn't
+ know whether it has a `stim.CircuitInstruction` or a `stim.CircuitRepeatBlock`
+ can check the object's name without having to do an `instanceof` check first.
+
+ Examples:
+ >>> import stim
+ >>> circuit = stim.Circuit('''
+ ... H 0
+ ... REPEAT 5 {
+ ... CX 1 2
+ ... }
+ ... S 1
+ ... ''')
+ >>> [instruction.name for instruction in circuit]
+ ['H', 'REPEAT', 'S']
+ )DOC")
+ .data());
+
+ c.def_readonly(
+ "repeat_count",
+ &CircuitRepeatBlock::repeat_count,
+ clean_doc_string(R"DOC(
+ The repetition count of the repeat block.
+
+ Examples:
+ >>> import stim
+ >>> circuit = stim.Circuit('''
+ ... H 0
+ ... REPEAT 5 {
+ ... CX 0 1
+ ... CZ 1 2
+ ... }
+ ... ''')
+ >>> repeat_block = circuit[1]
+ >>> repeat_block.repeat_count
+ 5
+ )DOC")
+ .data());
+
c.def(
"body_copy",
&CircuitRepeatBlock::body_copy,
diff --git a/src/stim/circuit/circuit_repeat_block_test.py b/src/stim/circuit/circuit_repeat_block_test.py
index 874a4edf8..a9d3fdefc 100644
--- a/src/stim/circuit/circuit_repeat_block_test.py
+++ b/src/stim/circuit/circuit_repeat_block_test.py
@@ -39,3 +39,13 @@ def test_init_and_equality():
def test_repr(value):
assert eval(repr(value), {'stim': stim}) == value
assert repr(eval(repr(value), {'stim': stim})) == repr(value)
+
+
+def test_name():
+ assert [e.name for e in stim.Circuit('''
+ H 0
+ REPEAT 5 {
+ CX 1 2
+ }
+ S 1
+ ''')] == ['H', 'REPEAT', 'S']
diff --git a/src/stim/circuit/gate_data.test.cc b/src/stim/circuit/gate_data.test.cc
index 2d8197803..5e7a80d09 100644
--- a/src/stim/circuit/gate_data.test.cc
+++ b/src/stim/circuit/gate_data.test.cc
@@ -71,8 +71,7 @@ TEST(gate_data, hash_matches_storage_location) {
}
template
-std::pair>, std::vector>>
-circuit_output_eq_val(const Circuit &circuit) {
+std::pair>, std::vector>> circuit_output_eq_val(const Circuit &circuit) {
if (circuit.count_measurements() > 1) {
throw std::invalid_argument("count_measurements > 1");
}
diff --git a/src/stim/cmd/command_diagram.pybind.cc b/src/stim/cmd/command_diagram.pybind.cc
index 8daa8622f..888e0e420 100644
--- a/src/stim/cmd/command_diagram.pybind.cc
+++ b/src/stim/cmd/command_diagram.pybind.cc
@@ -44,17 +44,29 @@ pybind11::class_ stim_pybind::pybind_diagram(pybind11::module &m)
return c;
}
-std::string escape_html_for_srcdoc(const std::string& src) {
+std::string escape_html_for_srcdoc(const std::string &src) {
// From https://stackoverflow.com/a/9907752
std::stringstream dst;
for (char ch : src) {
switch (ch) {
- case '&': dst << "&"; break;
- case '\'': dst << "'"; break;
- case '"': dst << """; break;
- case '<': dst << "<"; break;
- case '>': dst << ">"; break;
- default: dst << ch; break;
+ case '&':
+ dst << "&";
+ break;
+ case '\'':
+ dst << "'";
+ break;
+ case '"':
+ dst << """;
+ break;
+ case '<':
+ dst << "<";
+ break;
+ case '>':
+ dst << ">";
+ break;
+ default:
+ dst << ch;
+ break;
}
}
return dst.str();
@@ -71,11 +83,10 @@ void stim_pybind::pybind_diagram_methods(pybind11::module &m, pybind11::class_)HTML";
- // out << R"HTML()HTML";
- // output = out.str();
+ // out << R"HTML()HTML"; out << R"HTML(
)HTML"; output = out.str();
}
if (self.type == DIAGRAM_TYPE_GLTF) {
std::stringstream out;
@@ -85,8 +96,8 @@ void stim_pybind::pybind_diagram_methods(pybind11::module &m, pybind11::class_)HTML";
+ std::string framed =
+ R"HTML()HTML";
return pybind11::cast(framed);
});
c.def("_repr_svg_", [](const DiagramHelper &self) -> pybind11::object {
diff --git a/src/stim/cmd/command_gen.test.cc b/src/stim/cmd/command_gen.test.cc
index 830814904..8e98145bf 100644
--- a/src/stim/cmd/command_gen.test.cc
+++ b/src/stim/cmd/command_gen.test.cc
@@ -45,8 +45,7 @@ TEST_EACH_WORD_SIZE_W(command_gen, no_noise_no_detections, {
}
CircuitGenParameters params(r, d, func.second.first);
auto circuit = func.second.second(params).circuit;
- auto [det_samples, obs_samples] =
- sample_batch_detection_events(circuit, 256, SHARED_TEST_RNG());
+ auto [det_samples, obs_samples] = sample_batch_detection_events(circuit, 256, SHARED_TEST_RNG());
EXPECT_FALSE(det_samples.data.not_zero() || obs_samples.data.not_zero())
<< "d=" << d << ", r=" << r << ", task=" << func.second.first << ", func=" << func.first;
}
diff --git a/src/stim/dem/detector_error_model_pybind_test.py b/src/stim/dem/detector_error_model_pybind_test.py
index 0a309dae6..fab07f2c0 100644
--- a/src/stim/dem/detector_error_model_pybind_test.py
+++ b/src/stim/dem/detector_error_model_pybind_test.py
@@ -275,24 +275,21 @@ def test_shortest_graphlike_error_rep_code():
def test_shortest_graphlike_error_msgs():
- with pytest.raises(
- ValueError,
- match="Circuit defines no observables. Circuit defines no detectors. Circuit defines no errors that can flip detectors or observables."
- ):
+ with pytest.raises(ValueError, match=r"NO OBSERVABLES(.|\n)*NO DETECTORS(.|\n)*NO ERRORS"):
stim.Circuit().detector_error_model(decompose_errors=True).shortest_graphlike_error()
c = stim.Circuit("""
M 0
OBSERVABLE_INCLUDE(0) rec[-1]
""")
- with pytest.raises(ValueError, match="Circuit defines no detectors. Circuit defines no errors that can flip detectors or observables."):
+ with pytest.raises(ValueError, match=r"NO DETECTORS(.|\n)*NO ERRORS"):
c.detector_error_model(decompose_errors=True).shortest_graphlike_error()
c = stim.Circuit("""
X_ERROR(0.1) 0
M 0
""")
- with pytest.raises(ValueError, match="Circuit defines no observables. Circuit defines no detectors. Circuit defines no errors that can flip detectors or observables."):
+ with pytest.raises(ValueError, match=r"NO OBSERVABLES(.|\n)*NO DETECTORS(.|\n)*NO ERRORS"):
c.detector_error_model(decompose_errors=True).shortest_graphlike_error()
c = stim.Circuit("""
@@ -300,7 +297,7 @@ def test_shortest_graphlike_error_msgs():
DETECTOR rec[-1]
OBSERVABLE_INCLUDE(0) rec[-1]
""")
- with pytest.raises(ValueError, match="Circuit defines no errors that can flip detectors or observables."):
+ with pytest.raises(ValueError, match=r"NO ERRORS"):
c.detector_error_model(decompose_errors=True).shortest_graphlike_error()
c = stim.Circuit("""
@@ -308,7 +305,7 @@ def test_shortest_graphlike_error_msgs():
M 0
DETECTOR rec[-1]
""")
- with pytest.raises(ValueError, match="Circuit defines no observables."):
+ with pytest.raises(ValueError, match=r"NO OBSERVABLES"):
c.detector_error_model(decompose_errors=True).shortest_graphlike_error()
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 30410d352..cc6f37cab 100644
--- a/src/stim/dem/detector_error_model_repeat_block.pybind.cc
+++ b/src/stim/dem/detector_error_model_repeat_block.pybind.cc
@@ -80,6 +80,32 @@ void stim_pybind::pybind_detector_error_model_repeat_block_methods(
Returns a copy of the block's body, as a stim.DetectorErrorModel.
)DOC")
.data());
+ c.def_property_readonly(
+ "type",
+ [](const ExposedDemRepeatBlock &self) -> pybind11::object {
+ return pybind11::cast("repeat");
+ },
+ clean_doc_string(R"DOC(
+ Returns the type name "repeat".
+
+ This is a duck-typing convenience method. It exists so that code that doesn't
+ know whether it has a `stim.DemInstruction` or a `stim.DemRepeatBlock`
+ can check the type field without having to do an `instanceof` check first.
+
+ Examples:
+ >>> import stim
+ >>> dem = stim.DetectorErrorModel('''
+ ... error(0.1) D0 L0
+ ... repeat 5 {
+ ... error(0.1) D0 D1
+ ... shift_detectors 1
+ ... }
+ ... logical_observable L0
+ ... ''')
+ >>> [instruction.type for instruction in dem]
+ ['error', 'repeat', 'logical_observable']
+ )DOC")
+ .data());
c.def(pybind11::self == pybind11::self, "Determines if two repeat blocks are identical.");
c.def(pybind11::self != pybind11::self, "Determines if two repeat blocks are different.");
diff --git a/src/stim/dem/detector_error_model_repeat_block_pybind_test.py b/src/stim/dem/detector_error_model_repeat_block_pybind_test.py
index a6716a77f..227cb61c9 100644
--- a/src/stim/dem/detector_error_model_repeat_block_pybind_test.py
+++ b/src/stim/dem/detector_error_model_repeat_block_pybind_test.py
@@ -35,3 +35,13 @@ def test_equality():
def test_repr():
v = stim.DemRepeatBlock(5, stim.DetectorErrorModel('error(0.125) D1 L2'))
assert eval(repr(v), {"stim": stim}) == v
+
+
+def test_type():
+ assert [e.type for e in stim.DetectorErrorModel('''
+ detector D0
+ REPEAT 5 {
+ error(0.1) D0
+ }
+ logical_observable L0
+ ''')] == ['detector', 'repeat', 'logical_observable']
diff --git a/src/stim/diagram/detector_slice/detector_slice_set.cc b/src/stim/diagram/detector_slice/detector_slice_set.cc
index 8b42e333e..24fff18b7 100644
--- a/src/stim/diagram/detector_slice/detector_slice_set.cc
+++ b/src/stim/diagram/detector_slice/detector_slice_set.cc
@@ -55,7 +55,9 @@ bool DetectorSliceSetComputer::process_op_rev(const Circuit &parent, const Circu
uint64_t max_skip = std::max(tick_cur, stop_iter) - stop_iter;
uint64_t reps = op.repeat_block_rep_count();
uint64_t ticks_per_iteration = loop_body.count_ticks();
- uint64_t skipped_iterations = max_skip == 0 ? 0 : ticks_per_iteration == 0 ? reps : std::min(reps, max_skip / ticks_per_iteration);
+ uint64_t skipped_iterations = max_skip == 0 ? 0
+ : ticks_per_iteration == 0 ? reps
+ : std::min(reps, max_skip / ticks_per_iteration);
if (skipped_iterations) {
// We can allow the analyzer to fold parts of the loop we aren't yielding.
tracker.undo_loop(loop_body, skipped_iterations);
diff --git a/src/stim/io/sparse_shot.cc b/src/stim/io/sparse_shot.cc
index bff7163e4..439ae0900 100644
--- a/src/stim/io/sparse_shot.cc
+++ b/src/stim/io/sparse_shot.cc
@@ -23,7 +23,8 @@ using namespace stim;
SparseShot::SparseShot() : hits(), obs_mask(0) {
}
-SparseShot::SparseShot(std::vector hits, simd_bits<64> obs_mask) : hits(std::move(hits)), obs_mask(std::move(obs_mask)) {
+SparseShot::SparseShot(std::vector hits, simd_bits<64> obs_mask)
+ : hits(std::move(hits)), obs_mask(std::move(obs_mask)) {
}
void SparseShot::clear() {
diff --git a/src/stim/io/sparse_shot.test.cc b/src/stim/io/sparse_shot.test.cc
index bcfe919bd..32df848b9 100644
--- a/src/stim/io/sparse_shot.test.cc
+++ b/src/stim/io/sparse_shot.test.cc
@@ -41,5 +41,7 @@ TEST(sparse_shot, equality) {
}
TEST(sparse_shot, str) {
- ASSERT_EQ((SparseShot{{1, 2, 3}, obs_mask(4)}.str()), "SparseShot{{1, 2, 3}, __1_____________________________________________________________}");
+ ASSERT_EQ(
+ (SparseShot{{1, 2, 3}, obs_mask(4)}.str()),
+ "SparseShot{{1, 2, 3}, __1_____________________________________________________________}");
}
diff --git a/src/stim/mem/bitword.h b/src/stim/mem/bitword.h
index de159bc3e..b0bce4636 100644
--- a/src/stim/mem/bitword.h
+++ b/src/stim/mem/bitword.h
@@ -15,7 +15,6 @@
*/
#include
-#include
#ifndef _STIM_MEM_BIT_WORD_H
#define _STIM_MEM_BIT_WORD_H
diff --git a/src/stim/mem/bitword_128_sse.h b/src/stim/mem/bitword_128_sse.h
index 3530e1f62..3164784ca 100644
--- a/src/stim/mem/bitword_128_sse.h
+++ b/src/stim/mem/bitword_128_sse.h
@@ -24,7 +24,7 @@
#include
#include
-#include "stim/mem/simd_word.h"
+#include "stim/mem/bitword.h"
#include "stim/mem/simd_util.h"
namespace stim {
@@ -89,7 +89,7 @@ struct bitword<128> {
return (bool)(words[0] | words[1]);
}
inline operator int() const { // NOLINT(hicpp-explicit-conversions)
- return (int64_t)*this;
+ return (int64_t) * this;
}
inline operator uint64_t() const { // NOLINT(hicpp-explicit-conversions)
auto words = to_u64_array();
diff --git a/src/stim/mem/bitword_256_avx.h b/src/stim/mem/bitword_256_avx.h
index 51a105618..fca2fe226 100644
--- a/src/stim/mem/bitword_256_avx.h
+++ b/src/stim/mem/bitword_256_avx.h
@@ -21,8 +21,8 @@
#include
#include
#include
-#include
#include
+#include
#include "stim/mem/bitword.h"
#include "stim/mem/simd_util.h"
@@ -88,7 +88,7 @@ struct bitword<256> {
return (bool)(words[0] | words[1] | words[2] | words[3]);
}
inline operator int() const { // NOLINT(hicpp-explicit-conversions)
- return (int64_t)*this;
+ return (int64_t) * this;
}
inline operator uint64_t() const { // NOLINT(hicpp-explicit-conversions)
auto words = to_u64_array();
diff --git a/src/stim/mem/bitword_64.h b/src/stim/mem/bitword_64.h
index 68b574827..50f537396 100644
--- a/src/stim/mem/bitword_64.h
+++ b/src/stim/mem/bitword_64.h
@@ -68,7 +68,7 @@ struct bitword<64> {
return (bool)(u64[0]);
}
inline operator int() const { // NOLINT(hicpp-explicit-conversions)
- return (int64_t)*this;
+ return (int64_t) * this;
}
inline operator uint64_t() const { // NOLINT(hicpp-explicit-conversions)
return u64[0];
diff --git a/src/stim/mem/simd_word.h b/src/stim/mem/simd_word.h
index 90c4b583f..10b095187 100644
--- a/src/stim/mem/simd_word.h
+++ b/src/stim/mem/simd_word.h
@@ -19,9 +19,9 @@
#ifndef _STIM_MEM_SIMD_WORD_H
#define _STIM_MEM_SIMD_WORD_H
-#include "stim/mem/bitword_64.h"
#include "stim/mem/bitword_128_sse.h"
#include "stim/mem/bitword_256_avx.h"
+#include "stim/mem/bitword_64.h"
namespace stim {
#if __AVX2__
diff --git a/src/stim/mem/simd_word.test.cc b/src/stim/mem/simd_word.test.cc
index 77c3db2c4..6fa3eeb22 100644
--- a/src/stim/mem/simd_word.test.cc
+++ b/src/stim/mem/simd_word.test.cc
@@ -76,10 +76,12 @@ TEST_EACH_WORD_SIZE_W(simd_word, integer_conversions, {
ASSERT_EQ((int64_t)(simd_word{(int64_t)23}), 23);
ASSERT_EQ((int64_t)(simd_word{(int64_t)-23}), -23);
if (W > 64) {
- ASSERT_THROW({
- uint64_t u = (uint64_t)(simd_word{(int64_t)-23});
- std::cerr << u;
- }, std::invalid_argument);
+ ASSERT_THROW(
+ {
+ uint64_t u = (uint64_t)(simd_word{(int64_t)-23});
+ std::cerr << u;
+ },
+ std::invalid_argument);
}
simd_word w0{(uint64_t)0};
diff --git a/src/stim/search/graphlike/algo.cc b/src/stim/search/graphlike/algo.cc
index d466ee036..96a5e234d 100644
--- a/src/stim/search/graphlike/algo.cc
+++ b/src/stim/search/graphlike/algo.cc
@@ -96,17 +96,25 @@ DetectorErrorModel stim::shortest_graphlike_undetectable_logical_error(
std::stringstream err_msg;
err_msg << "Failed to find any graphlike logical errors.";
if (graph.num_observables == 0) {
- err_msg << " Circuit defines no observables.";
+ err_msg << "\n WARNING: NO OBSERVABLES. The circuit or detector error model didn't define any observables, "
+ "making it vacuously impossible to find a logical error.";
}
if (graph.nodes.size() == 0) {
- err_msg << " Circuit defines no detectors.";
+ err_msg << "\n WARNING: NO DETECTORS. The circuit or detector error model didn't define any detectors.";
}
- bool edges = 0;
- for (const auto &n : graph.nodes) {
- edges |= ( n.edges.size() > 0 );
- }
- if ( !edges ) {
- err_msg << " Circuit defines no errors that can flip detectors or observables.";
+ if (model.count_errors() == 0) {
+ err_msg << "\n WARNING: NO ERRORS. The circuit or detector error model didn't include any errors, making it "
+ "vacuously impossible to find a logical error.";
+ } else {
+ bool edges = 0;
+ for (const auto &n : graph.nodes) {
+ edges |= n.edges.size() > 0;
+ }
+ if (!edges) {
+ err_msg << "\n WARNING: NO GRAPHLIKE ERRORS. Although the circuit or detector error model does define "
+ "some errors, none of them are graphlike (i.e. have at most two detection events), making it "
+ "vacuously impossible to find a graphlike logical error.";
+ }
}
throw std::invalid_argument(err_msg.str());
}
diff --git a/src/stim/search/graphlike/edge.h b/src/stim/search/graphlike/edge.h
index 375eb16c9..13a3e7aa5 100644
--- a/src/stim/search/graphlike/edge.h
+++ b/src/stim/search/graphlike/edge.h
@@ -20,6 +20,7 @@
#include
#include
#include
+
#include "stim/mem/simd_bits.h"
namespace stim {
diff --git a/src/stim/search/graphlike/graph.cc b/src/stim/search/graphlike/graph.cc
index 1808fec44..ce3a2b594 100644
--- a/src/stim/search/graphlike/graph.cc
+++ b/src/stim/search/graphlike/graph.cc
@@ -105,17 +105,21 @@ Graph Graph::from_dem(const DetectorErrorModel &model, bool ignore_ungraphlike_e
return result;
}
bool Graph::operator==(const Graph &other) const {
- return nodes == other.nodes && num_observables == other.num_observables && distance_1_error_mask == other.distance_1_error_mask;
+ return nodes == other.nodes && num_observables == other.num_observables &&
+ distance_1_error_mask == other.distance_1_error_mask;
}
bool Graph::operator!=(const Graph &other) const {
return !(*this == other);
}
-Graph::Graph(size_t node_count, size_t num_observables) : nodes(node_count), num_observables(num_observables), distance_1_error_mask(num_observables) {
+Graph::Graph(size_t node_count, size_t num_observables)
+ : nodes(node_count), num_observables(num_observables), distance_1_error_mask(num_observables) {
}
Graph::Graph(std::vector nodes, size_t num_observables, simd_bits<64> distance_1_error_mask)
- : nodes(std::move(nodes)), num_observables(num_observables), distance_1_error_mask(std::move(distance_1_error_mask)) {
+ : nodes(std::move(nodes)),
+ num_observables(num_observables),
+ distance_1_error_mask(std::move(distance_1_error_mask)) {
}
std::ostream &stim::impl_search_graphlike::operator<<(std::ostream &out, const Graph &v) {
diff --git a/src/stim/search/graphlike/search_state.cc b/src/stim/search/graphlike/search_state.cc
index 1eb291f45..fafb19498 100644
--- a/src/stim/search/graphlike/search_state.cc
+++ b/src/stim/search/graphlike/search_state.cc
@@ -29,7 +29,8 @@ std::string SearchState::str() const {
return result.str();
}
-SearchState::SearchState(size_t num_observables) : det_active(NO_NODE_INDEX), det_held(NO_NODE_INDEX), obs_mask(num_observables) {
+SearchState::SearchState(size_t num_observables)
+ : det_active(NO_NODE_INDEX), det_held(NO_NODE_INDEX), obs_mask(num_observables) {
}
SearchState::SearchState(uint64_t det_active, uint64_t det_held, simd_bits<64> obs_mask)
: det_active(det_active), det_held(det_held), obs_mask(std::move(obs_mask)) {
diff --git a/src/stim/search/graphlike/search_state.h b/src/stim/search/graphlike/search_state.h
index b7cfd9443..0e99343d3 100644
--- a/src/stim/search/graphlike/search_state.h
+++ b/src/stim/search/graphlike/search_state.h
@@ -25,9 +25,9 @@ namespace stim {
namespace impl_search_graphlike {
struct SearchState {
- uint64_t det_active; // The detection event being moved around in an attempt to remove it (or NO_NODE_INDEX).
- uint64_t det_held; // The detection event being left in the same place (or NO_NODE_INDEX).
- simd_bits<64> obs_mask; // The accumulated frame changes from moving the detection events around.
+ uint64_t det_active; // The detection event being moved around in an attempt to remove it (or NO_NODE_INDEX).
+ uint64_t det_held; // The detection event being left in the same place (or NO_NODE_INDEX).
+ simd_bits<64> obs_mask; // The accumulated frame changes from moving the detection events around.
SearchState() = delete;
SearchState(size_t num_observables);
diff --git a/src/stim/search/graphlike/search_state.test.cc b/src/stim/search/graphlike/search_state.test.cc
index 51e952283..c150e8bff 100644
--- a/src/stim/search/graphlike/search_state.test.cc
+++ b/src/stim/search/graphlike/search_state.test.cc
@@ -89,7 +89,8 @@ TEST(search_graphlike, DemAdjGraphSearchState_append_transition_as_error_instruc
error(1) D1 D3
)MODEL"));
- SearchState(1, 2, obs_mask(9)).append_transition_as_error_instruction_to(SearchState(1, NO_NODE_INDEX, obs_mask(9)), out);
+ SearchState(1, 2, obs_mask(9))
+ .append_transition_as_error_instruction_to(SearchState(1, NO_NODE_INDEX, obs_mask(9)), out);
ASSERT_EQ(out, DetectorErrorModel(R"MODEL(
error(1) L0 L3 L4
error(1) D1 D3
diff --git a/src/stim/search/hyper/algo.cc b/src/stim/search/hyper/algo.cc
index 1a39f6204..da1c00b27 100644
--- a/src/stim/search/hyper/algo.cc
+++ b/src/stim/search/hyper/algo.cc
@@ -105,19 +105,17 @@ DetectorErrorModel stim::find_undetectable_logical_error(
}
std::stringstream err_msg;
- err_msg << "Failed to find any graphlike logical errors.";
+ err_msg << "Failed to find any logical errors.";
if (graph.num_observables == 0) {
- err_msg << " Circuit defines no observables.";
+ err_msg << "\n WARNING: NO OBSERVABLES. The circuit or detector error model didn't define any observables, "
+ "making it vacuously impossible to find a logical error.";
}
if (graph.nodes.size() == 0) {
- err_msg << " Circuit defines no detectors.";
+ err_msg << "\n WARNING: NO DETECTORS. The circuit or detector error model didn't define any detectors.";
}
- bool edges = 0;
- for (const auto &n : graph.nodes) {
- edges |= ( n.edges.size() > 0 );
- }
- if ( !edges ) {
- err_msg << " Circuit defines no errors that can flip detectors or observables.";
+ if (model.count_errors() == 0) {
+ err_msg << "\n WARNING: NO ERRORS. The circuit or detector error model didn't include any errors, making it "
+ "vacuously impossible to find a logical error.";
}
throw std::invalid_argument(err_msg.str());
}
diff --git a/src/stim/search/hyper/edge.h b/src/stim/search/hyper/edge.h
index c7faf104e..50839f7ac 100644
--- a/src/stim/search/hyper/edge.h
+++ b/src/stim/search/hyper/edge.h
@@ -21,8 +21,8 @@
#include
#include
-#include "stim/mem/sparse_xor_vec.h"
#include "stim/mem/simd_bits.h"
+#include "stim/mem/sparse_xor_vec.h"
namespace stim {
diff --git a/src/stim/search/hyper/graph.cc b/src/stim/search/hyper/graph.cc
index fa9f5cb5e..cb80d2517 100644
--- a/src/stim/search/hyper/graph.cc
+++ b/src/stim/search/hyper/graph.cc
@@ -57,17 +57,21 @@ Graph Graph::from_dem(const DetectorErrorModel &model, size_t dont_explore_edges
return result;
}
bool Graph::operator==(const Graph &other) const {
- return nodes == other.nodes && num_observables == other.num_observables && distance_1_error_mask == other.distance_1_error_mask;
+ return nodes == other.nodes && num_observables == other.num_observables &&
+ distance_1_error_mask == other.distance_1_error_mask;
}
bool Graph::operator!=(const Graph &other) const {
return !(*this == other);
}
-Graph::Graph(size_t node_count, size_t num_observables) : nodes(node_count), num_observables(num_observables), distance_1_error_mask(simd_bits<64>(num_observables)) {
+Graph::Graph(size_t node_count, size_t num_observables)
+ : nodes(node_count), num_observables(num_observables), distance_1_error_mask(simd_bits<64>(num_observables)) {
}
Graph::Graph(std::vector nodes, size_t num_observables, simd_bits<64> distance_1_error_mask)
- : nodes(std::move(nodes)), num_observables(num_observables), distance_1_error_mask(std::move(distance_1_error_mask)) {
+ : nodes(std::move(nodes)),
+ num_observables(num_observables),
+ distance_1_error_mask(std::move(distance_1_error_mask)) {
}
std::ostream &stim::impl_search_hyper::operator<<(std::ostream &out, const Graph &v) {
diff --git a/src/stim/search/hyper/search_state.h b/src/stim/search/hyper/search_state.h
index 85c6ecd1d..491f80285 100644
--- a/src/stim/search/hyper/search_state.h
+++ b/src/stim/search/hyper/search_state.h
@@ -18,8 +18,8 @@
#define _STIM_SEARCH_HYPER_SEARCH_STATE_H
#include "stim/dem/detector_error_model.h"
-#include "stim/mem/sparse_xor_vec.h"
#include "stim/mem/simd_bits.h"
+#include "stim/mem/sparse_xor_vec.h"
namespace stim {
diff --git a/src/stim/search/hyper/search_state.test.cc b/src/stim/search/hyper/search_state.test.cc
index 53f56b72a..d941d4c7d 100644
--- a/src/stim/search/hyper/search_state.test.cc
+++ b/src/stim/search/hyper/search_state.test.cc
@@ -30,18 +30,21 @@ static simd_bits<64> obs_mask(uint64_t v) {
TEST(search_hyper_search_state, append_transition_as_error_instruction_to) {
DetectorErrorModel out;
- SearchState{{{1, 2}}, obs_mask(9)}.append_transition_as_error_instruction_to(SearchState{{{1, 2}}, obs_mask(16)}, out);
+ SearchState{{{1, 2}}, obs_mask(9)}.append_transition_as_error_instruction_to(
+ SearchState{{{1, 2}}, obs_mask(16)}, out);
ASSERT_EQ(out, DetectorErrorModel(R"MODEL(
error(1) L0 L3 L4
)MODEL"));
- SearchState{{{}}, obs_mask(9)}.append_transition_as_error_instruction_to(SearchState{{{1, 2, 4}}, obs_mask(16)}, out);
+ SearchState{{{}}, obs_mask(9)}.append_transition_as_error_instruction_to(
+ SearchState{{{1, 2, 4}}, obs_mask(16)}, out);
ASSERT_EQ(out, DetectorErrorModel(R"MODEL(
error(1) L0 L3 L4
error(1) D1 D2 D4 L0 L3 L4
)MODEL"));
- SearchState{{{1, 2}}, obs_mask(9)}.append_transition_as_error_instruction_to(SearchState{{{2, 3}}, obs_mask(9)}, out);
+ SearchState{{{1, 2}}, obs_mask(9)}.append_transition_as_error_instruction_to(
+ SearchState{{{2, 3}}, obs_mask(9)}, out);
ASSERT_EQ(out, DetectorErrorModel(R"MODEL(
error(1) L0 L3 L4
error(1) D1 D2 D4 L0 L3 L4
diff --git a/src/stim/simulators/dem_sampler.pybind.cc b/src/stim/simulators/dem_sampler.pybind.cc
index ecee562e2..112220389 100644
--- a/src/stim/simulators/dem_sampler.pybind.cc
+++ b/src/stim/simulators/dem_sampler.pybind.cc
@@ -38,7 +38,11 @@ RaiiFile optional_py_path_to_raii_file(const pybind11::object &obj, const char *
}
pybind11::object dem_sampler_py_sample(
- DemSampler &self, size_t shots, bool bit_packed, bool return_errors, pybind11::object &recorded_errors_to_replay) {
+ DemSampler &self,
+ size_t shots,
+ bool bit_packed,
+ bool return_errors,
+ pybind11::object &recorded_errors_to_replay) {
self.set_min_stripes(shots);
bool replay = !recorded_errors_to_replay.is_none();
diff --git a/src/stim/simulators/sparse_rev_frame_tracker.test.cc b/src/stim/simulators/sparse_rev_frame_tracker.test.cc
index dbd3fda62..0ff2c3f83 100644
--- a/src/stim/simulators/sparse_rev_frame_tracker.test.cc
+++ b/src/stim/simulators/sparse_rev_frame_tracker.test.cc
@@ -141,7 +141,6 @@ static std::vector qubit_targets(const std::vector &target
return result;
}
-
TEST_EACH_WORD_SIZE_W(SparseUnsignedRevFrameTracker, fuzz_all_unitary_gates_vs_tableau, {
auto &rng = SHARED_TEST_RNG();
for (const auto &gate : GATE_DATA.items) {
@@ -511,7 +510,6 @@ TEST_EACH_WORD_SIZE_W(SparseUnsignedRevFrameTracker, feedback_into_measurement,
ASSERT_EQ(actual, expected);
})
-
TEST(SparseUnsignedRevFrameTracker, undo_circuit_feedback) {
SparseUnsignedRevFrameTracker actual(20, 100, 10);
actual.undo_circuit(Circuit(R"CIRCUIT(
diff --git a/src/stim/simulators/tableau_simulator.h b/src/stim/simulators/tableau_simulator.h
index 82f13af83..49fdc6380 100644
--- a/src/stim/simulators/tableau_simulator.h
+++ b/src/stim/simulators/tableau_simulator.h
@@ -177,11 +177,11 @@ struct TableauSimulator {
/// Returns the expectation value of measuring the qubit in the Z basis.
int8_t peek_z(uint32_t target) const;
- /// Forces a desired X basis measurement result, or raises an exception if it was impossible.
+ /// Projects the system into a desired qubit X observable, or raises an exception if it was impossible.
void postselect_x(SpanRef targets, bool desired_result);
- /// Forces a desired Y basis measurement result, or raises an exception if it was impossible.
+ /// Projects the system into a desired qubit Y observable, or raises an exception if it was impossible.
void postselect_y(SpanRef targets, bool desired_result);
- /// Forces a desired Z basis measurement result, or raises an exception if it was impossible.
+ /// Projects the system into a desired qubit Z observable, or raises an exception if it was impossible.
void postselect_z(SpanRef targets, bool desired_result);
/// Applies all of the Pauli operations in the given PauliString to the simulator's state.
@@ -258,7 +258,11 @@ struct TableauSimulator {
/// 0: Observable will be random when measured.
int8_t peek_observable_expectation(const PauliString &observable) const;
+ /// Forces a desired measurement result, or raises an exception if it was impossible.
+ void postselect_observable(PauliStringRef observable, bool desired_result);
+
private:
+ uint32_t try_isolate_observable_to_qubit_z(PauliStringRef observable, bool undo);
void do_MXX_disjoint_controls_segment(const CircuitInstruction &inst);
void do_MYY_disjoint_controls_segment(const CircuitInstruction &inst);
void do_MZZ_disjoint_controls_segment(const CircuitInstruction &inst);
diff --git a/src/stim/simulators/tableau_simulator.inl b/src/stim/simulators/tableau_simulator.inl
index 0f4c996a4..3717a05ce 100644
--- a/src/stim/simulators/tableau_simulator.inl
+++ b/src/stim/simulators/tableau_simulator.inl
@@ -122,6 +122,85 @@ void TableauSimulator::postselect_helper(
}
}
+template
+uint32_t TableauSimulator::try_isolate_observable_to_qubit_z(PauliStringRef observable, bool undo) {
+ uint32_t pivot = UINT32_MAX;
+ for (uint32_t k = 0; k < observable.num_qubits; k++) {
+ uint8_t p = observable.xs[k] + observable.zs[k] * 2;
+ if (p) {
+ if (pivot == UINT32_MAX) {
+ pivot = k;
+ if (!undo) {
+ if (p == 1) {
+ inv_state.prepend_H_XZ(pivot);
+ } else if (p == 3) {
+ inv_state.prepend_H_YZ(pivot);
+ }
+ if (observable.sign) {
+ inv_state.prepend_X(pivot);
+ }
+ }
+ } else {
+ if (p == 1) {
+ inv_state.prepend_XCX(pivot, k);
+ } else if (p == 2) {
+ inv_state.prepend_XCZ(pivot, k);
+ } else if (p == 3) {
+ inv_state.prepend_XCY(pivot, k);
+ }
+ }
+ }
+ }
+ if (undo && pivot != UINT32_MAX) {
+ uint8_t p = observable.xs[pivot] + observable.zs[pivot] * 2;
+ if (observable.sign) {
+ inv_state.prepend_X(pivot);
+ }
+ if (p == 1) {
+ inv_state.prepend_H_XZ(pivot);
+ } else if (p == 3) {
+ inv_state.prepend_H_YZ(pivot);
+ }
+ }
+ return pivot;
+}
+
+template
+void TableauSimulator::postselect_observable(
+ PauliStringRef observable,
+ bool desired_result) {
+ ensure_large_enough_for_qubits(observable.num_qubits);
+
+ uint32_t pivot = try_isolate_observable_to_qubit_z(observable, false);
+ int8_t expected;
+ if (pivot != UINT32_MAX) {
+ expected = peek_z(pivot);
+ } else {
+ expected = observable.sign ? -1 : +1;
+ }
+ if (desired_result) {
+ expected *= -1;
+ }
+
+ if (expected != -1 && pivot != UINT32_MAX) {
+ GateTarget t{pivot};
+ postselect_z(&t, desired_result);
+ }
+ try_isolate_observable_to_qubit_z(observable, true);
+
+ if (expected == -1) {
+ std::stringstream msg;
+ msg << "It's impossible to postselect into the ";
+ msg << (desired_result ? "-1" : "+1");
+ msg << " eigenstate of ";
+ msg << observable;
+ msg << " because the system is deterministically in the ";
+ msg << (desired_result ? "+1" : "-1");
+ msg << " eigenstate.";
+ throw std::invalid_argument(msg.str());
+ }
+}
+
template
void TableauSimulator::postselect_x(SpanRef targets, bool desired_result) {
postselect_helper(targets, desired_result, GateType::H, "+", "-");
diff --git a/src/stim/simulators/tableau_simulator.pybind.cc b/src/stim/simulators/tableau_simulator.pybind.cc
index 963daa99b..0d4610030 100644
--- a/src/stim/simulators/tableau_simulator.pybind.cc
+++ b/src/stim/simulators/tableau_simulator.pybind.cc
@@ -1466,6 +1466,48 @@ void stim_pybind::pybind_tableau_simulator_methods(
)DOC")
.data());
+ c.def(
+ "postselect_observable",
+ [](TableauSimulator &self, const PyPauliString &observable, bool desired_value) {
+ if (observable.imag) {
+ throw std::invalid_argument(
+ "Observable isn't Hermitian; it has imaginary sign. Need observable.sign in [1, -1].");
+ }
+ self.postselect_observable(observable.value, desired_value);
+ },
+ pybind11::arg("observable"),
+ pybind11::kw_only(),
+ pybind11::arg("desired_value") = false,
+ clean_doc_string(R"DOC(
+ Projects into a desired observable, or raises an exception if it was impossible.
+
+ Postselecting an observable forces it to collapse to a specific eigenstate,
+ as if it was measured and that state was the result of the measurement.
+
+ Args:
+ observable: The observable to postselect, specified as a pauli string.
+ The pauli string's sign must be -1 or +1 (not -i or +i).
+ desired_value:
+ False (default): Postselect into the +1 eigenstate of the observable.
+ True: Postselect into the -1 eigenstate of the observable.
+
+ Raises:
+ ValueError:
+ The given observable had an imaginary sign.
+ OR
+ The postselection was impossible. The observable was in the opposite
+ eigenstate, so measuring it would never ever return the desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.postselect_observable(stim.PauliString("+XX"))
+ >>> s.postselect_observable(stim.PauliString("+ZZ"))
+ >>> s.peek_observable_expectation(stim.PauliString("+YY"))
+ -1
+ )DOC")
+ .data());
+
c.def(
"measure",
[](TableauSimulator &self, uint32_t target) {
@@ -1540,6 +1582,21 @@ void stim_pybind::pybind_tableau_simulator_methods(
orthogonal to the desired state, so it was literally
impossible for a measurement of the qubit to return the
desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.peek_x(0)
+ 0
+ >>> s.postselect_x(0, desired_value=False)
+ >>> s.peek_x(0)
+ 1
+ >>> s.h(0)
+ >>> s.peek_x(0)
+ 0
+ >>> s.postselect_x(0, desired_value=True)
+ >>> s.peek_x(0)
+ -1
)DOC")
.data());
@@ -1571,6 +1628,21 @@ void stim_pybind::pybind_tableau_simulator_methods(
orthogonal to the desired state, so it was literally
impossible for a measurement of the qubit to return the
desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.peek_y(0)
+ 0
+ >>> s.postselect_y(0, desired_value=False)
+ >>> s.peek_y(0)
+ 1
+ >>> s.reset_x(0)
+ >>> s.peek_y(0)
+ 0
+ >>> s.postselect_y(0, desired_value=True)
+ >>> s.peek_y(0)
+ -1
)DOC")
.data());
@@ -1602,6 +1674,22 @@ void stim_pybind::pybind_tableau_simulator_methods(
orthogonal to the desired state, so it was literally
impossible for a measurement of the qubit to return the
desired result.
+
+ Examples:
+ >>> import stim
+ >>> s = stim.TableauSimulator()
+ >>> s.h(0)
+ >>> s.peek_z(0)
+ 0
+ >>> s.postselect_z(0, desired_value=False)
+ >>> s.peek_z(0)
+ 1
+ >>> s.h(0)
+ >>> s.peek_z(0)
+ 0
+ >>> s.postselect_z(0, desired_value=True)
+ >>> s.peek_z(0)
+ -1
)DOC")
.data());
diff --git a/src/stim/simulators/tableau_simulator.test.cc b/src/stim/simulators/tableau_simulator.test.cc
index 81af6faff..c3bc4211d 100644
--- a/src/stim/simulators/tableau_simulator.test.cc
+++ b/src/stim/simulators/tableau_simulator.test.cc
@@ -2335,3 +2335,39 @@ TEST_EACH_WORD_SIZE_W(TableauSimulator, heralded_erase, {
ASSERT_EQ(sim.measurement_record.storage, (std::vector{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}));
ASSERT_NE(sim.inv_state, Tableau(14));
})
+
+TEST_EACH_WORD_SIZE_W(TableauSimulator, postselect_observable, {
+ TableauSimulator sim(SHARED_TEST_RNG(), 0);
+ sim.postselect_observable(PauliString("ZZ"), false);
+ sim.postselect_observable(PauliString("XX"), false);
+
+ auto initial_state = sim.inv_state;
+ ASSERT_THROW({ sim.postselect_observable(PauliString("YY"), false); }, std::invalid_argument);
+
+ sim.postselect_observable(PauliString("ZZ"), false);
+ ASSERT_EQ(sim.inv_state, initial_state);
+
+ sim.postselect_observable(PauliString("ZZ"), false);
+ ASSERT_EQ(sim.inv_state, initial_state);
+
+ ASSERT_THROW({ sim.postselect_observable(PauliString("ZZ"), true); }, std::invalid_argument);
+ ASSERT_EQ(sim.inv_state, initial_state);
+
+ ASSERT_THROW({ sim.postselect_observable(PauliString("ZZ"), true); }, std::invalid_argument);
+ ASSERT_EQ(sim.inv_state, initial_state);
+
+ sim.postselect_observable(PauliString("ZZ"), false);
+ ASSERT_EQ(sim.inv_state, initial_state);
+
+ sim.postselect_observable(PauliString("XX"), false);
+ ASSERT_EQ(sim.inv_state, initial_state);
+
+ sim.postselect_observable(PauliString("-YY"), false);
+ ASSERT_EQ(sim.inv_state, initial_state);
+
+ sim.postselect_observable(PauliString("YY"), true);
+ ASSERT_EQ(sim.inv_state, initial_state);
+
+ sim.postselect_observable(PauliString("XZ"), true);
+ ASSERT_NE(sim.inv_state, initial_state);
+})
diff --git a/src/stim/simulators/tableau_simulator_pybind_test.py b/src/stim/simulators/tableau_simulator_pybind_test.py
index df17afe26..067bc0e14 100644
--- a/src/stim/simulators/tableau_simulator_pybind_test.py
+++ b/src/stim/simulators/tableau_simulator_pybind_test.py
@@ -706,3 +706,35 @@ def test_bad_inverse_padding_issue_is_fixed():
sim.do(circuit)
stabs = sim.canonical_stabilizers()
assert stabs[-1] == stim.PauliString(466 * '_' + 'X')
+
+
+def test_postselect_observable():
+ sim = stim.TableauSimulator()
+ assert sim.peek_bloch(0) == stim.PauliString("+Z")
+
+ sim.postselect_observable(stim.PauliString("+X"))
+ assert sim.peek_bloch(0) == stim.PauliString("+X")
+
+ sim.postselect_observable(stim.PauliString("+Z"))
+ assert sim.peek_bloch(0) == stim.PauliString("+Z")
+
+ sim.postselect_observable(stim.PauliString("-X"))
+ assert sim.peek_bloch(0) == stim.PauliString("-X")
+
+ sim.postselect_observable(stim.PauliString("+Z"))
+ assert sim.peek_bloch(0) == stim.PauliString("+Z")
+
+ sim.postselect_observable(stim.PauliString("-X"), desired_value=True)
+ assert sim.peek_bloch(0) == stim.PauliString("+X")
+
+ with pytest.raises(ValueError, match="impossible"):
+ sim.postselect_observable(stim.PauliString("-X"))
+ assert sim.peek_bloch(0) == stim.PauliString("+X")
+
+ with pytest.raises(ValueError, match="imaginary sign"):
+ sim.postselect_observable(stim.PauliString("iZ"))
+ assert sim.peek_bloch(0) == stim.PauliString("+X")
+
+ sim.postselect_observable(stim.PauliString("+XX"))
+ sim.postselect_observable(stim.PauliString("+ZZ"))
+ assert sim.peek_observable_expectation(stim.PauliString("+YY")) == -1
diff --git a/src/stim/stabilizers/conversions.test.cc b/src/stim/stabilizers/conversions.test.cc
index 86a178ee9..7c6fb4ef7 100644
--- a/src/stim/stabilizers/conversions.test.cc
+++ b/src/stim/stabilizers/conversions.test.cc
@@ -337,8 +337,7 @@ TEST_EACH_WORD_SIZE_W(conversions, circuit_to_tableau_ignoring_gates, {
ASSERT_EQ(circuit_to_tableau(annotations, false, false, false), Tableau(1));
ASSERT_EQ(
- circuit_to_tableau(
- annotations + measure_reset + measure + reset + unitary + noise, true, true, true)
+ circuit_to_tableau(annotations + measure_reset + measure + reset + unitary + noise, true, true, true)
.num_qubits,
2);
})
@@ -418,8 +417,7 @@ TEST_EACH_WORD_SIZE_W(conversions, circuit_to_tableau, {
})
TEST_EACH_WORD_SIZE_W(conversions, circuit_to_output_state_vector, {
- ASSERT_EQ(
- circuit_to_output_state_vector(Circuit(""), false), (std::vector>{{1}}));
+ ASSERT_EQ(circuit_to_output_state_vector(Circuit(""), false), (std::vector>{{1}}));
ASSERT_EQ(
circuit_to_output_state_vector(Circuit("H 0 1"), false),
(std::vector>{{0.5}, {0.5}, {0.5}, {0.5}}));
@@ -475,8 +473,7 @@ TEST_EACH_WORD_SIZE_W(conversions, tableau_to_circuit, {
TEST_EACH_WORD_SIZE_W(conversions, unitary_to_tableau_vs_gate_data, {
for (const auto &gate : GATE_DATA.items) {
if (gate.flags & GATE_IS_UNITARY) {
- EXPECT_EQ(unitary_to_tableau(gate.unitary(), true), gate.tableau())
- << gate.name;
+ EXPECT_EQ(unitary_to_tableau(gate.unitary(), true), gate.tableau()) << gate.name;
}
}
})
@@ -508,31 +505,15 @@ TEST_EACH_WORD_SIZE_W(conversions, tableau_to_unitary_vs_gate_data, {
})
TEST_EACH_WORD_SIZE_W(conversions, unitary_vs_tableau_basic, {
- ASSERT_EQ(
- unitary_to_tableau(GATE_DATA.at("XCZ").unitary(), false),
- GATE_DATA.at("ZCX").tableau());
- ASSERT_EQ(
- unitary_to_tableau(GATE_DATA.at("XCZ").unitary(), true),
- GATE_DATA.at("XCZ").tableau());
- ASSERT_EQ(
- unitary_to_tableau(GATE_DATA.at("ZCX").unitary(), false),
- GATE_DATA.at("XCZ").tableau());
- ASSERT_EQ(
- unitary_to_tableau(GATE_DATA.at("ZCX").unitary(), true),
- GATE_DATA.at("ZCX").tableau());
-
- ASSERT_EQ(
- unitary_to_tableau(GATE_DATA.at("XCY").unitary(), false),
- GATE_DATA.at("YCX").tableau());
- ASSERT_EQ(
- unitary_to_tableau(GATE_DATA.at("XCY").unitary(), true),
- GATE_DATA.at("XCY").tableau());
- ASSERT_EQ(
- unitary_to_tableau(GATE_DATA.at("YCX").unitary(), false),
- GATE_DATA.at("XCY").tableau());
- ASSERT_EQ(
- unitary_to_tableau(GATE_DATA.at("YCX").unitary(), true),
- GATE_DATA.at("YCX").tableau());
+ ASSERT_EQ(unitary_to_tableau(GATE_DATA.at("XCZ").unitary(), false), GATE_DATA.at("ZCX").tableau());
+ ASSERT_EQ(unitary_to_tableau(GATE_DATA.at("XCZ").unitary(), true), GATE_DATA.at("XCZ").tableau());
+ ASSERT_EQ(unitary_to_tableau(GATE_DATA.at("ZCX").unitary(), false), GATE_DATA.at("XCZ").tableau());
+ ASSERT_EQ(unitary_to_tableau(GATE_DATA.at("ZCX").unitary(), true), GATE_DATA.at("ZCX").tableau());
+
+ ASSERT_EQ(unitary_to_tableau(GATE_DATA.at("XCY").unitary(), false), GATE_DATA.at("YCX").tableau());
+ ASSERT_EQ(unitary_to_tableau(GATE_DATA.at("XCY").unitary(), true), GATE_DATA.at("XCY").tableau());
+ ASSERT_EQ(unitary_to_tableau(GATE_DATA.at("YCX").unitary(), false), GATE_DATA.at("XCY").tableau());
+ ASSERT_EQ(unitary_to_tableau(GATE_DATA.at("YCX").unitary(), true), GATE_DATA.at("YCX").tableau());
})
TEST_EACH_WORD_SIZE_W(conversions, unitary_to_tableau_fuzz_vs_tableau_to_unitary, {
diff --git a/src/stim/stabilizers/pauli_string.h b/src/stim/stabilizers/pauli_string.h
index 5d67fbd1c..42afb0d33 100644
--- a/src/stim/stabilizers/pauli_string.h
+++ b/src/stim/stabilizers/pauli_string.h
@@ -80,6 +80,8 @@ struct PauliString {
/// Identity constructor.
explicit PauliString(size_t num_qubits);
+ /// Parse constructor.
+ explicit PauliString(const std::string &text);
/// Factory method for creating a PauliString whose Pauli entries are returned by a function.
static PauliString from_func(bool sign, size_t num_qubits, const std::function &func);
/// Factory method for creating a PauliString by parsing a string (e.g. "-XIIYZ").
diff --git a/src/stim/stabilizers/pauli_string.inl b/src/stim/stabilizers/pauli_string.inl
index c2f05ddbb..2b017ae95 100644
--- a/src/stim/stabilizers/pauli_string.inl
+++ b/src/stim/stabilizers/pauli_string.inl
@@ -36,6 +36,11 @@ template
PauliString::PauliString(size_t num_qubits) : num_qubits(num_qubits), sign(false), xs(num_qubits), zs(num_qubits) {
}
+template
+PauliString::PauliString(const std::string &text) : num_qubits(0), sign(false), xs(0), zs(0) {
+ *this = std::move(PauliString::from_str(text.c_str()));
+}
+
template
PauliString::PauliString(const PauliStringRef &other)
: num_qubits(other.num_qubits), sign((bool)other.sign), xs(other.xs), zs(other.zs) {
diff --git a/src/stim/stabilizers/tableau.h b/src/stim/stabilizers/tableau.h
index 5c8924517..0c728598e 100644
--- a/src/stim/stabilizers/tableau.h
+++ b/src/stim/stabilizers/tableau.h
@@ -176,7 +176,7 @@ struct Tableau {
void prepend_X(size_t q);
void prepend_Y(size_t q);
void prepend_Z(size_t q);
- void prepend_H_XZ(const size_t q);
+ void prepend_H_XZ(size_t q);
void prepend_H_YZ(size_t q);
void prepend_H_XY(size_t q);
void prepend_C_XYZ(size_t q);