From 198ee0ab0a3d88701f591df506f0805511fa80a8 Mon Sep 17 00:00:00 2001 From: Matt McEwen Date: Tue, 3 Mar 2026 15:21:06 -0800 Subject: [PATCH 1/4] add pymatching-correlated --- doc/gates.md | 16 +- doc/python_api_reference_vDev.md | 819 +----------------- doc/sinter_api.md | 65 -- doc/stim.pyi | 643 +------------- doc/usage_command_line.md | 25 - glue/python/src/stim/__init__.pyi | 643 +------------- glue/sample/src/sinter/_decoding/_decoding.py | 8 +- .../_decoding_all_built_in_decoders.py | 1 + .../sinter/_decoding/_decoding_pymatching.py | 41 +- .../src/sinter/_decoding/_decoding_test.py | 4 +- 10 files changed, 152 insertions(+), 2113 deletions(-) diff --git a/doc/gates.md b/doc/gates.md index 7f9825026..3978e320a 100644 --- a/doc/gates.md +++ b/doc/gates.md @@ -3579,7 +3579,7 @@ Example: # Sample errors from the distribution 10% XX, 20% YZ, 70% II. # Apply independently to qubit pairs (1,2), (5,6), and (8,3) - PAULI_CHANNEL_2(0,0,0, 0,0.1,0,0, 0,0,0,0.2, 0,0,0,0) 1 2 5 6 8 3 + PAULI_CHANNEL_2(0,0,0, 0.1,0,0,0, 0,0,0,0.2, 0,0,0,0) 1 2 5 6 8 3 Pauli Mixture: @@ -4879,19 +4879,13 @@ detection event simulations and affect whether the observable is included in err makes it easier to benchmark all observables of a code, without having to introduce noiseless qubits entangled with the logical qubit to avoid the testing of the X observable anticommuting with the testing of the Z observable. -Unlike a `DETECTOR` instruction which provides a complete description of a detector by listing all its constituent -measurement records, an individual `OBSERVABLE_INCLUDE` instruction is not required to (and generally does not) fully -describe a logical observable. Instead, measurement records or Pauli targets are added to it incrementally. A logical -observable can be given both types of description: as a collection of Pauli targets and as a collection of measurement -record targets. - Parens Arguments: A non-negative integer specifying the index of the logical observable to add the measurement records to. Targets: - The measurement records or Pauli terms to add to the specified observable. + The measurement records to add to the specified observable. Example: @@ -4927,12 +4921,6 @@ Example: OBSERVABLE_INCLUDE(0) X0 X1 OBSERVABLE_INCLUDE(1) Z0 Z2 - # Stim circuit may include a description of an observable in terms of Pauli targets - # alongside a description in terms of measurement records. - OBSERVABLE_INCLUDE(0) Z0 Z1 - M 0 1 - OBSERVABLE_INCLUDE(0) rec[-2] rec[-1] - ### The 'QUBIT_COORDS' Instruction diff --git a/doc/python_api_reference_vDev.md b/doc/python_api_reference_vDev.md index 01ce7fe4b..e88766612 100644 --- a/doc/python_api_reference_vDev.md +++ b/doc/python_api_reference_vDev.md @@ -109,28 +109,6 @@ API references for stable versions are kept on the [stim github wiki](https://gi - [`stim.CircuitTargetsInsideInstruction.target_range_end`](#stim.CircuitTargetsInsideInstruction.target_range_end) - [`stim.CircuitTargetsInsideInstruction.target_range_start`](#stim.CircuitTargetsInsideInstruction.target_range_start) - [`stim.CircuitTargetsInsideInstruction.targets_in_range`](#stim.CircuitTargetsInsideInstruction.targets_in_range) -- [`stim.CliffordString`](#stim.CliffordString) - - [`stim.CliffordString.__add__`](#stim.CliffordString.__add__) - - [`stim.CliffordString.__eq__`](#stim.CliffordString.__eq__) - - [`stim.CliffordString.__getitem__`](#stim.CliffordString.__getitem__) - - [`stim.CliffordString.__iadd__`](#stim.CliffordString.__iadd__) - - [`stim.CliffordString.__imul__`](#stim.CliffordString.__imul__) - - [`stim.CliffordString.__init__`](#stim.CliffordString.__init__) - - [`stim.CliffordString.__ipow__`](#stim.CliffordString.__ipow__) - - [`stim.CliffordString.__len__`](#stim.CliffordString.__len__) - - [`stim.CliffordString.__mul__`](#stim.CliffordString.__mul__) - - [`stim.CliffordString.__ne__`](#stim.CliffordString.__ne__) - - [`stim.CliffordString.__pow__`](#stim.CliffordString.__pow__) - - [`stim.CliffordString.__repr__`](#stim.CliffordString.__repr__) - - [`stim.CliffordString.__rmul__`](#stim.CliffordString.__rmul__) - - [`stim.CliffordString.__setitem__`](#stim.CliffordString.__setitem__) - - [`stim.CliffordString.__str__`](#stim.CliffordString.__str__) - - [`stim.CliffordString.all_cliffords_string`](#stim.CliffordString.all_cliffords_string) - - [`stim.CliffordString.copy`](#stim.CliffordString.copy) - - [`stim.CliffordString.random`](#stim.CliffordString.random) - - [`stim.CliffordString.x_outputs`](#stim.CliffordString.x_outputs) - - [`stim.CliffordString.y_outputs`](#stim.CliffordString.y_outputs) - - [`stim.CliffordString.z_outputs`](#stim.CliffordString.z_outputs) - [`stim.CompiledDemSampler`](#stim.CompiledDemSampler) - [`stim.CompiledDemSampler.sample`](#stim.CompiledDemSampler.sample) - [`stim.CompiledDemSampler.sample_write`](#stim.CompiledDemSampler.sample_write) @@ -1686,7 +1664,7 @@ def detector_error_model( # (in class stim.Circuit) def diagram( self, - type: Literal["timeline-text", "timeline-svg", "timeline-svg-html", "timeline-3d", "timeline-3d-html", "detslice-text", "detslice-svg", "detslice-svg-html", "matchgraph-svg", "matchgraph-svg-html", "matchgraph-3d", "matchgraph-3d-html", "timeslice-svg", "timeslice-svg-html", "detslice-with-ops-svg", "detslice-with-ops-svg-html", "interactive", "interactive-html"] = 'timeline-text', + type: str = 'timeline-text', *, tick: Union[None, int, range] = None, filter_coords: Iterable[Union[Iterable[float], stim.DemTarget]] = ((),), @@ -5049,714 +5027,6 @@ def targets_in_range( """ ``` - -```python -# stim.CliffordString - -# (at top-level in the stim module) -class CliffordString: - """A tensor product of single qubit Clifford gates (e.g. "H \u2297 X \u2297 S"). - - Represents a collection of Clifford operations applied pairwise to a - collection of qubits. Ignores global phase. - - Examples: - >>> import stim - >>> stim.CliffordString("H,S,C_XYZ") * stim.CliffordString("H,H,H") - stim.CliffordString("I,C_ZYX,SQRT_X_DAG") - """ -``` - - -```python -# stim.CliffordString.__add__ - -# (in class stim.CliffordString) -def __add__( - self, - rhs: stim.CliffordString, -) -> stim.CliffordString: - """Concatenates two CliffordStrings. - - Args: - rhs: The suffix of the concatenation. - - Returns: - The concatenated Clifford string. - - Examples: - >>> import stim - >>> stim.CliffordString("I,X,H") + stim.CliffordString("Y,S") - stim.CliffordString("I,X,H,Y,S") - """ -``` - - -```python -# stim.CliffordString.__eq__ - -# (in class stim.CliffordString) -def __eq__( - self, - arg0: stim.CliffordString, -) -> bool: - """Determines if two Clifford strings have identical contents. - """ -``` - - -```python -# stim.CliffordString.__getitem__ - -# (in class stim.CliffordString) -@overload -def __getitem__( - self, - index_or_slice: int, -) -> stim.GateData: - pass -@overload -def __getitem__( - self, - index_or_slice: slice, -) -> stim.CliffordString: - pass -def __getitem__( - self, - index_or_slice: Union[int, slice], -) -> Union[stim.GateData, stim.CliffordString]: - """Returns a Clifford or substring from the CliffordString. - - Args: - index_or_slice: The index of the Clifford to return, or the slice - corresponding to the sub CliffordString to return. - - Returns: - The indexed Clifford (as a stim.GateData instance) or the sliced - CliffordString. - - Examples: - >>> import stim - >>> s = stim.CliffordString("I,X,Y,Z,H") - - >>> s[2] - stim.gate_data('Y') - - >>> s[-1] - stim.gate_data('H') - - >>> s[:-1] - stim.CliffordString("I,X,Y,Z") - - >>> s[::2] - stim.CliffordString("I,Y,H") - """ -``` - - -```python -# stim.CliffordString.__iadd__ - -# (in class stim.CliffordString) -def __iadd__( - self, - rhs: stim.CliffordString, -) -> stim.CliffordString: - """Mutates the CliffordString by concatenating onto it. - - Args: - rhs: The suffix to concatenate onto the target CliffordString. - - Returns: - The mutated Clifford string. - - Examples: - >>> import stim - >>> c = stim.CliffordString("I,X,H") - >>> alias = c - >>> alias += stim.CliffordString("Y,S") - >>> c - stim.CliffordString("I,X,H,Y,S") - """ -``` - - -```python -# stim.CliffordString.__imul__ - -# (in class stim.CliffordString) -def __imul__( - self, - rhs: Union[stim.CliffordString, int], -) -> stim.CliffordString: - """Inplace CliffordString multiplication. - - Mutates the CliffordString into itself multiplied by another CliffordString - (via pairwise Clifford multipliation) or by an integer (via repeating the - contents). - - Args: - rhs: Either a stim.CliffordString or an int. If rhs is a - stim.CliffordString, then the Cliffords from each string are multiplied - pairwise. If rhs is an int, it is the number of times to repeat the - Clifford string's contents. - - Returns: - The mutated Clifford string. - - Examples: - >>> import stim - - >>> c = stim.CliffordString("S,X,X") - >>> alias = c - >>> alias *= stim.CliffordString("S,Z,H,Z") - >>> c - stim.CliffordString("Z,Y,SQRT_Y,Z") - - >>> c = stim.CliffordString("I,X,H") - >>> alias = c - >>> alias *= 2 - >>> c - stim.CliffordString("I,X,H,I,X,H") - """ -``` - - -```python -# stim.CliffordString.__init__ - -# (in class stim.CliffordString) -def __init__( - self, - arg: Union[int, str, stim.CliffordString, stim.PauliString, stim.Circuit], - /, -) -> None: - """Initializes a stim.CliffordString from the given argument. - - Args: - arg [position-only]: This can be a variety of types, including: - int: initializes an identity Clifford string of the given length. - str: initializes by parsing a comma-separated list of gate names. - stim.CliffordString: initializes by copying the given Clifford string. - stim.PauliString: initializes by copying from the given Pauli string - (ignores the sign of the Pauli string). - stim.Circuit: initializes a CliffordString equivalent to the action - of the circuit (as long as the circuit only contains single qubit - unitary operations and annotations). - Iterable: initializes by interpreting each item as a Clifford. - Each item can be a single-qubit Clifford gate name (like "SQRT_X") - or stim.GateData instance. - - Examples: - >>> import stim - - >>> stim.CliffordString(5) - stim.CliffordString("I,I,I,I,I") - - >>> stim.CliffordString("X,Y,Z,SQRT_X") - stim.CliffordString("X,Y,Z,SQRT_X") - - >>> stim.CliffordString(["H", stim.gate_data("S")]) - stim.CliffordString("H,S") - - >>> stim.CliffordString(stim.PauliString("XYZ")) - stim.CliffordString("X,Y,Z") - - >>> stim.CliffordString(stim.CliffordString("X,Y,Z")) - stim.CliffordString("X,Y,Z") - - >>> stim.CliffordString(stim.Circuit(''' - ... H 0 1 2 - ... S 2 3 - ... TICK - ... S 3 - ... I 6 - ... ''')) - stim.CliffordString("H,H,C_ZYX,Z,I,I,I") - """ -``` - - -```python -# stim.CliffordString.__ipow__ - -# (in class stim.CliffordString) -def __ipow__( - self, - num_qubits: int, -) -> object: - """Mutates the CliffordString into itself raised to a power. - - Args: - power: The power to raise the CliffordString's Cliffords to. - This value can be negative (e.g. -1 inverts the string). - - Returns: - The mutated Clifford string. - - Examples: - >>> import stim - - >>> p = stim.CliffordString("I,X,H,S,C_XYZ") - >>> p **= 3 - >>> p - stim.CliffordString("I,X,H,S_DAG,I") - - >>> p **= 2 - >>> p - stim.CliffordString("I,I,I,Z,I") - - >>> alias = p - >>> alias **= 2 - >>> p - stim.CliffordString("I,I,I,I,I") - """ -``` - - -```python -# stim.CliffordString.__len__ - -# (in class stim.CliffordString) -def __len__( - self, -) -> int: - """Returns the number of Clifford operations in the string. - - Examples: - >>> import stim - >>> len(stim.CliffordString("I,X,Y,Z,H")) - 5 - """ -``` - - -```python -# stim.CliffordString.__mul__ - -# (in class stim.CliffordString) -def __mul__( - self, - rhs: Union[stim.CliffordString, int], -) -> stim.CliffordString: - """CliffordString multiplication. - - Args: - rhs: Either a stim.CliffordString or an int. If rhs is a - stim.CliffordString, then the Cliffords from each string are multiplied - pairwise. If rhs is an int, it is the number of times to repeat the - Clifford string's contents. - - Examples: - >>> import stim - - >>> stim.CliffordString("S,X,X") * stim.CliffordString("S,Z,H,Z") - stim.CliffordString("Z,Y,SQRT_Y,Z") - - >>> stim.CliffordString("I,X,H") * 3 - stim.CliffordString("I,X,H,I,X,H,I,X,H") - """ -``` - - -```python -# stim.CliffordString.__ne__ - -# (in class stim.CliffordString) -def __ne__( - self, - arg0: stim.CliffordString, -) -> bool: - """Determines if two Clifford strings have non-identical contents. - """ -``` - - -```python -# stim.CliffordString.__pow__ - -# (in class stim.CliffordString) -def __pow__( - self, - power: int, -) -> stim.CliffordString: - """Returns the CliffordString raised to a power. - - Args: - power: The power to raise the CliffordString's Cliffords to. - This value can be negative (e.g. -1 returns the inverse string). - - Returns: - The Clifford string raised to the power. - - Examples: - >>> import stim - - >>> p = stim.CliffordString("I,X,H,S,C_XYZ") - - >>> p**0 - stim.CliffordString("I,I,I,I,I") - - >>> p**1 - stim.CliffordString("I,X,H,S,C_XYZ") - - >>> p**12000001 - stim.CliffordString("I,X,H,S,C_XYZ") - - >>> p**2 - stim.CliffordString("I,I,I,Z,C_ZYX") - - >>> p**3 - stim.CliffordString("I,X,H,S_DAG,I") - - >>> p**-1 - stim.CliffordString("I,X,H,S_DAG,C_ZYX") - """ -``` - - -```python -# stim.CliffordString.__repr__ - -# (in class stim.CliffordString) -def __repr__( - self, -) -> str: - """Returns text that is a valid python expression evaluating to an equivalent `stim.CliffordString`. - """ -``` - - -```python -# stim.CliffordString.__rmul__ - -# (in class stim.CliffordString) -def __rmul__( - self, - lhs: int, -) -> stim.CliffordString: - """CliffordString left-multiplication. - - Args: - lhs: The number of times to repeat the Clifford string's contents. - - Returns: - The repeated Clifford string. - - Examples: - >>> import stim - - >>> 2 * stim.CliffordString("I,X,H") - stim.CliffordString("I,X,H,I,X,H") - - >>> 0 * stim.CliffordString("I,X,H") - stim.CliffordString("") - - >>> 5 * stim.CliffordString("I") - stim.CliffordString("I,I,I,I,I") - """ -``` - - -```python -# stim.CliffordString.__setitem__ - -# (in class stim.CliffordString) -def __setitem__( - self, - index_or_slice: Union[int, slice], - new_value: Union[str, stim.GateData, stim.CliffordString, stim.PauliString, stim.Tableau], -) -> None: - """Overwrites an indexed Clifford, or slice of Cliffords, with the given value. - - Args: - index_or_slice: The index of the Clifford to overwrite, or the slice - of Cliffords to overwrite. - new_value: Specifies the value to write into the Clifford string. This can - be set to a few different types of values: - - str: Name of the single qubit Clifford gate to write to the index or - broadcast over the slice. - - stim.GateData: The single qubit Clifford gate to write to the index - or broadcast over the slice. - - stim.Tableau: Must be a single qubit tableau. Specifies the single - qubit Clifford gate to write to the index or broadcast over the - slice. - - stim.CliffordString: String of Cliffords to write into the slice. - - Examples: - >>> import stim - >>> s = stim.CliffordString("I,I,I,I,I") - - >>> s[1] = 'H' - >>> s - stim.CliffordString("I,H,I,I,I") - - >>> s[2:] = 'SQRT_X' - >>> s - stim.CliffordString("I,H,SQRT_X,SQRT_X,SQRT_X") - - >>> s[0] = stim.gate_data('S_DAG').inverse - >>> s - stim.CliffordString("S,H,SQRT_X,SQRT_X,SQRT_X") - - >>> s[:] = 'I' - >>> s - stim.CliffordString("I,I,I,I,I") - - >>> s[::2] = stim.CliffordString("X,Y,Z") - >>> s - stim.CliffordString("X,I,Y,I,Z") - - >>> s[0] = stim.Tableau.from_named_gate("H") - >>> s - stim.CliffordString("H,I,Y,I,Z") - - >>> s[:] = stim.Tableau.from_named_gate("S") - >>> s - stim.CliffordString("S,S,S,S,S") - - >>> s[:4] = stim.PauliString("IXYZ") - >>> s - stim.CliffordString("I,X,Y,Z,S") - """ -``` - - -```python -# stim.CliffordString.__str__ - -# (in class stim.CliffordString) -def __str__( - self, -) -> str: - """Returns a string representation of the CliffordString's operations. - """ -``` - - -```python -# stim.CliffordString.all_cliffords_string - -# (in class stim.CliffordString) -@staticmethod -def all_cliffords_string( -) -> stim.CliffordString: - """Returns a stim.CliffordString containing each single qubit Clifford once. - - Useful for things like testing that a method works on every single Clifford. - - Examples: - >>> import stim - >>> cliffords = stim.CliffordString.all_cliffords_string() - >>> len(cliffords) - 24 - - >>> print(cliffords[:8]) - I,X,Y,Z,H_XY,S,S_DAG,H_NXY - - >>> print(cliffords[8:16]) - H,SQRT_Y_DAG,H_NXZ,SQRT_Y,H_YZ,H_NYZ,SQRT_X,SQRT_X_DAG - - >>> print(cliffords[16:]) - C_XYZ,C_XYNZ,C_NXYZ,C_XNYZ,C_ZYX,C_ZNYX,C_NZYX,C_ZYNX - """ -``` - - -```python -# stim.CliffordString.copy - -# (in class stim.CliffordString) -def copy( - self, -) -> stim.CliffordString: - """Returns a copy of the CliffordString. - - Returns: - The copy. - - Examples: - >>> import stim - >>> c = stim.CliffordString("H,X") - >>> alias = c - >>> copy = c.copy() - >>> c *= 5 - >>> alias - stim.CliffordString("H,X,H,X,H,X,H,X,H,X") - >>> copy - stim.CliffordString("H,X") - """ -``` - - -```python -# stim.CliffordString.random - -# (in class stim.CliffordString) -@staticmethod -def random( - num_qubits: int, -) -> stim.CliffordString: - """Samples a uniformly random CliffordString. - - Args: - num_qubits: The number of qubits the CliffordString should act upon. - - Examples: - >>> import stim - >>> p = stim.CliffordString.random(5) - >>> len(p) - 5 - - Returns: - The sampled Clifford string. - """ -``` - - -```python -# stim.CliffordString.x_outputs - -# (in class stim.CliffordString) -def x_outputs( - self, - *, - bit_packed_signs: bool = False, -) -> Tuple[stim.PauliString, np.ndarray]: - """Returns what each Clifford in the CliffordString conjugates an X input into. - - For example, H conjugates X into +Z and S_DAG conjugates X into -Y. - - Combined with `z_outputs`, the results of this method completely specify - the single qubit Clifford applied to each qubit. - - Args: - bit_packed_signs: Defaults to False. When False, the sign data is returned - in a numpy array with dtype `np.bool_`. When True, the dtype is instead - `np.uint8` and 8 bits are packed into each byte (in little endian - order). - - Returns: - A (paulis, signs) tuple. - - `paulis` has type stim.PauliString. Its sign is always positive. - - `signs` has type np.ndarray and an argument-dependent shape: - bit_packed_signs=False: - dtype=np.bool_ - shape=(num_qubits,) - bit_packed_signs=True: - dtype=np.uint8 - shape=(math.ceil(num_qubits / 8),) - - Examples: - >>> import stim - >>> x_paulis, x_signs = stim.CliffordString("I,Y,H,S").x_outputs() - >>> x_paulis - stim.PauliString("+XXZY") - >>> x_signs - array([False, True, False, False]) - - >>> stim.CliffordString("I,Y,H,S").x_outputs(bit_packed_signs=True)[1] - array([2], dtype=uint8) - """ -``` - - -```python -# stim.CliffordString.y_outputs - -# (in class stim.CliffordString) -def y_outputs( - self, - *, - bit_packed_signs: bool = False, -) -> Tuple[stim.PauliString, np.ndarray]: - """Returns what each Clifford in the CliffordString conjugates a Y input into. - - For example, H conjugates Y into -Y and S_DAG conjugates Y into +X. - - Args: - bit_packed_signs: Defaults to False. When False, the sign data is returned - in a numpy array with dtype `np.bool_`. When True, the dtype is instead - `np.uint8` and 8 bits are packed into each byte (in little endian - order). - - Returns: - A (paulis, signs) tuple. - - `paulis` has type stim.PauliString. Its sign is always positive. - - `signs` has type np.ndarray and an argument-dependent shape: - bit_packed_signs=False: - dtype=np.bool_ - shape=(num_qubits,) - bit_packed_signs=True: - dtype=np.uint8 - shape=(math.ceil(num_qubits / 8),) - - Examples: - >>> import stim - >>> y_paulis, y_signs = stim.CliffordString("I,X,H,S").y_outputs() - >>> y_paulis - stim.PauliString("+YYYX") - >>> y_signs - array([False, True, True, True]) - - >>> stim.CliffordString("I,X,H,S").y_outputs(bit_packed_signs=True)[1] - array([14], dtype=uint8) - """ -``` - - -```python -# stim.CliffordString.z_outputs - -# (in class stim.CliffordString) -def z_outputs( - self, - *, - bit_packed_signs: bool = False, -) -> Tuple[stim.PauliString, np.ndarray]: - """Returns what each Clifford in the CliffordString conjugates a Z input into. - - For example, H conjugates Z into +X and SQRT_X conjugates Z into -Y. - - Combined with `x_outputs`, the results of this method completely specify - the single qubit Clifford applied to each qubit. - - Args: - bit_packed_signs: Defaults to False. When False, the sign data is returned - in a numpy array with dtype `np.bool_`. When True, the dtype is instead - `np.uint8` and 8 bits are packed into each byte (in little endian - order). - - Returns: - A (paulis, signs) tuple. - - `paulis` has type stim.PauliString. Its sign is always positive. - - `signs` has type np.ndarray and an argument-dependent shape: - bit_packed_signs=False: - dtype=np.bool_ - shape=(num_qubits,) - bit_packed_signs=True: - dtype=np.uint8 - shape=(math.ceil(num_qubits / 8),) - - Examples: - >>> import stim - >>> z_paulis, z_signs = stim.CliffordString("I,Y,H,S").z_outputs() - >>> z_paulis - stim.PauliString("+ZZXZ") - >>> z_signs - array([False, True, False, False]) - - >>> stim.CliffordString("I,Y,H,S").z_outputs(bit_packed_signs=True)[1] - array([2], dtype=uint8) - """ -``` - ```python # stim.CompiledDemSampler @@ -5960,13 +5230,13 @@ def sample_write( shots: int, *, det_out_file: Union[None, str, pathlib.Path], - det_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + det_out_format: str = "01", obs_out_file: Union[None, str, pathlib.Path], - obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + obs_out_format: str = "01", err_out_file: Union[None, str, pathlib.Path] = None, - err_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + err_out_format: str = "01", replay_err_in_file: Union[None, str, pathlib.Path] = None, - replay_err_in_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + replay_err_in_format: str = "01", ) -> None: """Samples the detector error model and writes the results to disk. @@ -6227,9 +5497,9 @@ def sample_write( shots: int, *, filepath: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + format: 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]' = '01', obs_out_filepath: Optional[Union[str, pathlib.Path]] = None, - obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + obs_out_format: 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]' = '01', prepend_observables: bool = False, append_observables: bool = False, ) -> None: @@ -6440,8 +5710,8 @@ def sample_write( self, shots: int, *, - filepath: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + filepath: str, + format: str = '01', ) -> None: """Samples measurements from the circuit and writes them to a file. @@ -6568,7 +5838,7 @@ def convert( *, measurements: np.ndarray, sweep_bits: Optional[np.ndarray] = None, - separate_observables: Literal[True], + separate_observables: 'Literal[True]', append_observables: bool = False, bit_packed: bool = False, ) -> Tuple[np.ndarray, np.ndarray]: @@ -6667,15 +5937,15 @@ def convert( def convert_file( self, *, - measurements_filepath: Union[str, pathlib.Path], - measurements_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', - sweep_bits_filepath: Optional[Union[str, pathlib.Path]] = None, - sweep_bits_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', - detection_events_filepath: Union[str, pathlib.Path], - detection_events_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + measurements_filepath: str, + measurements_format: str = '01', + sweep_bits_filepath: str = None, + sweep_bits_format: str = '01', + detection_events_filepath: str, + detection_events_format: str = '01', append_observables: bool = False, - obs_out_filepath: Optional[Union[str, pathlib.Path]] = None, - obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + obs_out_filepath: str = None, + obs_out_format: str = '01', ) -> None: """Reads measurement data from a file and writes detection events to another file. @@ -8155,7 +7425,7 @@ def copy( # (in class stim.DetectorErrorModel) def diagram( self, - type: Literal["matchgraph-svg", "matchgraph-svg-html", "matchgraph-3d", "matchgraph-3d-html"] = 'matchgraph-svg', + type: str, ) -> Any: """Returns a diagram of the circuit, from a variety of options. @@ -10029,7 +9299,7 @@ def __init__( the string "X_ -> ZZ xor rec[-1]" will result in a flow with input pauli string "X_", output pauli string "ZZ", and measurement indices [-1]. - Args: + Arguments: arg [position-only]: Defaults to None. Must be specified by itself if used. str: Initializes a flow by parsing the given shorthand text. stim.Flow: Initializes a copy of the given flow. @@ -11794,7 +11064,7 @@ def __imul__( # (in class stim.PauliString) def __init__( self, - arg: Union[None, int, str, stim.PauliString, Iterable[Union[int, Literal["_", "I", "X", "Y", "Z"]]]] = None, + arg: Union[None, int, str, stim.PauliString, Iterable[Union[int, 'Literal["_", "I", "X", "Y", "Z"]']]] = None, /, ) -> None: """Initializes a stim.PauliString from the given argument. @@ -11807,7 +11077,7 @@ def __init__( pauli string is a series of integers seperated by '*' and prefixed by 'I', 'X', 'Y', or 'Z'. - Args: + Arguments: arg [position-only]: This can be a variety of types, including: None (default): initializes an empty Pauli string. int: initializes an identity Pauli string of the given length. @@ -11816,14 +11086,6 @@ def __init__( Iterable: initializes by interpreting each item as a Pauli. Each item can be a single-qubit Pauli string (like "X"), or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. - Dict[int, Union[int, str]]: initializes by interpreting keys as - the qubit index and values as the Pauli for that index. - Each value can be a single-qubit Pauli string (like "X"), - or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. - Dict[Union[int, str], Iterable[int]]: initializes by interpreting keys - as Pauli operators and values as the qubit indices for that Pauli. - Each key can be a single-qubit Pauli string (like "X"), - or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. Examples: >>> import stim @@ -11851,15 +11113,6 @@ def __init__( >>> stim.PauliString("X6*Y6") stim.PauliString("+i______Z") - - >>> stim.PauliString({0: "X", 2: "Y", 3: "X"}) - stim.PauliString("+X_YX") - - >>> stim.PauliString({0: "X", 2: 2, 3: 1}) - stim.PauliString("+X_YX") - - >>> stim.PauliString({"X": [1], 2: [4], "Z": [0, 3]}) - stim.PauliString("+ZX_ZY") """ ``` @@ -12414,7 +11667,7 @@ def from_numpy( def from_unitary_matrix( matrix: Iterable[Iterable[Union[int, float, complex]]], *, - endian: Literal["little", "big"] = 'little', + endian: str = 'little', unsigned: bool = False, ) -> stim.PauliString: """Creates a stim.PauliString from the unitary matrix of a Pauli group member. @@ -12753,7 +12006,7 @@ def to_tableau( def to_unitary_matrix( self, *, - endian: Literal["little", "big"], + endian: str, ) -> np.ndarray[np.complex64]: """Converts the pauli string into a unitary matrix. @@ -13556,7 +12809,7 @@ def from_stabilizers( def from_state_vector( state_vector: Iterable[float], *, - endian: Literal["little", "big"], + endian: str, ) -> stim.Tableau: """Creates a tableau representing the stabilizer state of the given state vector. @@ -13624,7 +12877,7 @@ def from_state_vector( def from_unitary_matrix( matrix: Iterable[Iterable[float]], *, - endian: Literal["little", "big"] = 'little', + endian: str = 'little', ) -> stim.Tableau: """Creates a tableau from the unitary matrix of a Clifford operation. @@ -14137,7 +13390,7 @@ def then( # (in class stim.Tableau) def to_circuit( self, - method: Literal["elimination", "graph_state"] = 'elimination', + method: 'Literal["elimination", "graph_state"]' = 'elimination', ) -> stim.Circuit: """Synthesizes a circuit that implements the tableau's Clifford operation. @@ -14532,7 +13785,7 @@ def to_stabilizers( def to_state_vector( self, *, - endian: Literal["little", "big"] = 'little', + endian: str = 'little', ) -> np.ndarray[np.complex64]: """Returns the state vector produced by applying the tableau to the |0..0> state. @@ -14591,7 +13844,7 @@ def to_state_vector( def to_unitary_matrix( self, *, - endian: Literal["little", "big"], + endian: str, ) -> np.ndarray[np.complex64]: """Converts the tableau into a unitary matrix. @@ -16733,7 +15986,7 @@ def set_state_from_state_vector( self, state_vector: Iterable[float], *, - endian: Literal["little", "big"], + endian: str, ) -> None: """Sets the simulator's state to a superposition specified by an amplitude vector. @@ -16914,7 +16167,7 @@ def sqrt_y_dag( def state_vector( self, *, - endian: Literal["little", "big"] = 'little', + endian: str = 'little', ) -> np.ndarray[np.complex64]: """Returns a wavefunction for the simulator's current state. @@ -17542,7 +16795,7 @@ def main( def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], + format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, @@ -17553,18 +16806,18 @@ def read_shot_data_file( def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], + format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, num_observables: int = 0, - separate_observables: Literal[True], + separate_observables: 'Literal[True]', ) -> Tuple[np.ndarray, np.ndarray]: pass def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], + format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, @@ -18026,7 +17279,7 @@ def write_shot_data_file( *, data: np.ndarray, path: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], + format: str, num_measurements: int = 0, num_detectors: int = 0, num_observables: int = 0, diff --git a/doc/sinter_api.md b/doc/sinter_api.md index ac7e10006..5cb64f821 100644 --- a/doc/sinter_api.md +++ b/doc/sinter_api.md @@ -42,7 +42,6 @@ API references for stable versions are kept on the [stim github wiki](https://gi - [`sinter.iter_collect`](#sinter.iter_collect) - [`sinter.log_binomial`](#sinter.log_binomial) - [`sinter.log_factorial`](#sinter.log_factorial) -- [`sinter.plot_custom`](#sinter.plot_custom) - [`sinter.plot_discard_rate`](#sinter.plot_discard_rate) - [`sinter.plot_error_rate`](#sinter.plot_error_rate) - [`sinter.post_selection_mask_from_4th_coord`](#sinter.post_selection_mask_from_4th_coord) @@ -1453,70 +1452,6 @@ def log_factorial( """ ``` - -```python -# sinter.plot_custom - -# (at top-level in the sinter module) -def plot_custom( - *, - ax: 'plt.Axes', - stats: 'Iterable[sinter.TaskStats]', - x_func: Callable[[sinter.TaskStats], Any], - y_func: Callable[[sinter.TaskStats], Union[sinter.Fit, float, int]], - group_func: Callable[[sinter.TaskStats], ~TCurveId] = lambda _: None, - point_label_func: Callable[[sinter.TaskStats], Any] = lambda _: None, - filter_func: Callable[[sinter.TaskStats], Any] = lambda _: True, - plot_args_func: Callable[[int, ~TCurveId, List[sinter.TaskStats]], Dict[str, Any]] = lambda index, group_key, group_stats: dict(), - line_fits: Optional[Tuple[Literal['linear', 'log', 'sqrt'], Literal['linear', 'log', 'sqrt']]] = None, -) -> None: - """Plots error rates in curves with uncertainty highlights. - - Args: - ax: The plt.Axes to plot onto. For example, the `ax` value from `fig, ax = plt.subplots(1, 1)`. - stats: The collected statistics to plot. - x_func: The X coordinate to use for each stat's data point. For example, this could be - `x_func=lambda stat: stat.json_metadata['physical_error_rate']`. - y_func: The Y value to use for each stat's data point. This can be a float or it can be a - sinter.Fit value, in which case the curve will follow the fit.best value and a - highlighted area will be shown from fit.low to fit.high. - group_func: Optional. When specified, multiple curves will be plotted instead of one curve. - The statistics are grouped into curves based on whether or not they get the same result - out of this function. For example, this could be `group_func=lambda stat: stat.decoder`. - If the result of the function is a dictionary, then optional keys in the dictionary will - also control the plotting of each curve. Available keys are: - 'label': the label added to the legend for the curve - 'color': the color used for plotting the curve - 'marker': the marker used for the curve - 'linestyle': the linestyle used for the curve - 'sort': the order in which the curves will be plotted and added to the legend - e.g. if two curves (with different resulting dictionaries from group_func) share the same - value for key 'marker', they will be plotted with the same marker. - Colors, markers and linestyles are assigned in order, sorted by the values for those keys. - point_label_func: Optional. Specifies text to draw next to data points. - filter_func: Optional. When specified, some curves will not be plotted. - The statistics are filtered and only plotted if filter_func(stat) returns True. - For example, `filter_func=lambda s: s.json_metadata['basis'] == 'x'` would plot only stats - where the saved metadata indicates the basis was 'x'. - plot_args_func: Optional. Specifies additional arguments to give the underlying calls to - `plot` and `fill_between` used to do the actual plotting. For example, this can be used - to specify markers and colors. Takes the index of the curve in sorted order and also a - curve_id (these will be 0 and None respectively if group_func is not specified). For example, - this could be: - - plot_args_func=lambda index, group_key, group_stats: { - 'color': ( - 'red' - if group_key == 'decoder=pymatching p=0.001' - else 'blue' - ), - } - line_fits: Defaults to None. Set this to a tuple (x_scale, y_scale) to include a dashed line - fit to every curve. The scales determine how to transform the coordinates before - performing the fit, and can be set to 'linear', 'sqrt', or 'log'. - """ -``` - ```python # sinter.plot_discard_rate diff --git a/doc/stim.pyi b/doc/stim.pyi index d71851394..6444b2cab 100644 --- a/doc/stim.pyi +++ b/doc/stim.pyi @@ -1049,7 +1049,7 @@ class Circuit: """ def diagram( self, - type: Literal["timeline-text", "timeline-svg", "timeline-svg-html", "timeline-3d", "timeline-3d-html", "detslice-text", "detslice-svg", "detslice-svg-html", "matchgraph-svg", "matchgraph-svg-html", "matchgraph-3d", "matchgraph-3d-html", "timeslice-svg", "timeslice-svg-html", "detslice-with-ops-svg", "detslice-with-ops-svg-html", "interactive", "interactive-html"] = 'timeline-text', + type: str = 'timeline-text', *, tick: Union[None, int, range] = None, filter_coords: Iterable[Union[Iterable[float], stim.DemTarget]] = ((),), @@ -3889,560 +3889,6 @@ class CircuitTargetsInsideInstruction: >>> loc.instruction_targets.targets_in_range [stim.GateTargetWithCoords(0, [])] """ -class CliffordString: - """A tensor product of single qubit Clifford gates (e.g. "H \u2297 X \u2297 S"). - - Represents a collection of Clifford operations applied pairwise to a - collection of qubits. Ignores global phase. - - Examples: - >>> import stim - >>> stim.CliffordString("H,S,C_XYZ") * stim.CliffordString("H,H,H") - stim.CliffordString("I,C_ZYX,SQRT_X_DAG") - """ - def __add__( - self, - rhs: stim.CliffordString, - ) -> stim.CliffordString: - """Concatenates two CliffordStrings. - - Args: - rhs: The suffix of the concatenation. - - Returns: - The concatenated Clifford string. - - Examples: - >>> import stim - >>> stim.CliffordString("I,X,H") + stim.CliffordString("Y,S") - stim.CliffordString("I,X,H,Y,S") - """ - def __eq__( - self, - arg0: stim.CliffordString, - ) -> bool: - """Determines if two Clifford strings have identical contents. - """ - @overload - def __getitem__( - self, - index_or_slice: int, - ) -> stim.GateData: - pass - @overload - def __getitem__( - self, - index_or_slice: slice, - ) -> stim.CliffordString: - pass - def __getitem__( - self, - index_or_slice: Union[int, slice], - ) -> Union[stim.GateData, stim.CliffordString]: - """Returns a Clifford or substring from the CliffordString. - - Args: - index_or_slice: The index of the Clifford to return, or the slice - corresponding to the sub CliffordString to return. - - Returns: - The indexed Clifford (as a stim.GateData instance) or the sliced - CliffordString. - - Examples: - >>> import stim - >>> s = stim.CliffordString("I,X,Y,Z,H") - - >>> s[2] - stim.gate_data('Y') - - >>> s[-1] - stim.gate_data('H') - - >>> s[:-1] - stim.CliffordString("I,X,Y,Z") - - >>> s[::2] - stim.CliffordString("I,Y,H") - """ - def __iadd__( - self, - rhs: stim.CliffordString, - ) -> stim.CliffordString: - """Mutates the CliffordString by concatenating onto it. - - Args: - rhs: The suffix to concatenate onto the target CliffordString. - - Returns: - The mutated Clifford string. - - Examples: - >>> import stim - >>> c = stim.CliffordString("I,X,H") - >>> alias = c - >>> alias += stim.CliffordString("Y,S") - >>> c - stim.CliffordString("I,X,H,Y,S") - """ - def __imul__( - self, - rhs: Union[stim.CliffordString, int], - ) -> stim.CliffordString: - """Inplace CliffordString multiplication. - - Mutates the CliffordString into itself multiplied by another CliffordString - (via pairwise Clifford multipliation) or by an integer (via repeating the - contents). - - Args: - rhs: Either a stim.CliffordString or an int. If rhs is a - stim.CliffordString, then the Cliffords from each string are multiplied - pairwise. If rhs is an int, it is the number of times to repeat the - Clifford string's contents. - - Returns: - The mutated Clifford string. - - Examples: - >>> import stim - - >>> c = stim.CliffordString("S,X,X") - >>> alias = c - >>> alias *= stim.CliffordString("S,Z,H,Z") - >>> c - stim.CliffordString("Z,Y,SQRT_Y,Z") - - >>> c = stim.CliffordString("I,X,H") - >>> alias = c - >>> alias *= 2 - >>> c - stim.CliffordString("I,X,H,I,X,H") - """ - def __init__( - self, - arg: Union[int, str, stim.CliffordString, stim.PauliString, stim.Circuit], - /, - ) -> None: - """Initializes a stim.CliffordString from the given argument. - - Args: - arg [position-only]: This can be a variety of types, including: - int: initializes an identity Clifford string of the given length. - str: initializes by parsing a comma-separated list of gate names. - stim.CliffordString: initializes by copying the given Clifford string. - stim.PauliString: initializes by copying from the given Pauli string - (ignores the sign of the Pauli string). - stim.Circuit: initializes a CliffordString equivalent to the action - of the circuit (as long as the circuit only contains single qubit - unitary operations and annotations). - Iterable: initializes by interpreting each item as a Clifford. - Each item can be a single-qubit Clifford gate name (like "SQRT_X") - or stim.GateData instance. - - Examples: - >>> import stim - - >>> stim.CliffordString(5) - stim.CliffordString("I,I,I,I,I") - - >>> stim.CliffordString("X,Y,Z,SQRT_X") - stim.CliffordString("X,Y,Z,SQRT_X") - - >>> stim.CliffordString(["H", stim.gate_data("S")]) - stim.CliffordString("H,S") - - >>> stim.CliffordString(stim.PauliString("XYZ")) - stim.CliffordString("X,Y,Z") - - >>> stim.CliffordString(stim.CliffordString("X,Y,Z")) - stim.CliffordString("X,Y,Z") - - >>> stim.CliffordString(stim.Circuit(''' - ... H 0 1 2 - ... S 2 3 - ... TICK - ... S 3 - ... I 6 - ... ''')) - stim.CliffordString("H,H,C_ZYX,Z,I,I,I") - """ - def __ipow__( - self, - num_qubits: int, - ) -> object: - """Mutates the CliffordString into itself raised to a power. - - Args: - power: The power to raise the CliffordString's Cliffords to. - This value can be negative (e.g. -1 inverts the string). - - Returns: - The mutated Clifford string. - - Examples: - >>> import stim - - >>> p = stim.CliffordString("I,X,H,S,C_XYZ") - >>> p **= 3 - >>> p - stim.CliffordString("I,X,H,S_DAG,I") - - >>> p **= 2 - >>> p - stim.CliffordString("I,I,I,Z,I") - - >>> alias = p - >>> alias **= 2 - >>> p - stim.CliffordString("I,I,I,I,I") - """ - def __len__( - self, - ) -> int: - """Returns the number of Clifford operations in the string. - - Examples: - >>> import stim - >>> len(stim.CliffordString("I,X,Y,Z,H")) - 5 - """ - def __mul__( - self, - rhs: Union[stim.CliffordString, int], - ) -> stim.CliffordString: - """CliffordString multiplication. - - Args: - rhs: Either a stim.CliffordString or an int. If rhs is a - stim.CliffordString, then the Cliffords from each string are multiplied - pairwise. If rhs is an int, it is the number of times to repeat the - Clifford string's contents. - - Examples: - >>> import stim - - >>> stim.CliffordString("S,X,X") * stim.CliffordString("S,Z,H,Z") - stim.CliffordString("Z,Y,SQRT_Y,Z") - - >>> stim.CliffordString("I,X,H") * 3 - stim.CliffordString("I,X,H,I,X,H,I,X,H") - """ - def __ne__( - self, - arg0: stim.CliffordString, - ) -> bool: - """Determines if two Clifford strings have non-identical contents. - """ - def __pow__( - self, - power: int, - ) -> stim.CliffordString: - """Returns the CliffordString raised to a power. - - Args: - power: The power to raise the CliffordString's Cliffords to. - This value can be negative (e.g. -1 returns the inverse string). - - Returns: - The Clifford string raised to the power. - - Examples: - >>> import stim - - >>> p = stim.CliffordString("I,X,H,S,C_XYZ") - - >>> p**0 - stim.CliffordString("I,I,I,I,I") - - >>> p**1 - stim.CliffordString("I,X,H,S,C_XYZ") - - >>> p**12000001 - stim.CliffordString("I,X,H,S,C_XYZ") - - >>> p**2 - stim.CliffordString("I,I,I,Z,C_ZYX") - - >>> p**3 - stim.CliffordString("I,X,H,S_DAG,I") - - >>> p**-1 - stim.CliffordString("I,X,H,S_DAG,C_ZYX") - """ - def __repr__( - self, - ) -> str: - """Returns text that is a valid python expression evaluating to an equivalent `stim.CliffordString`. - """ - def __rmul__( - self, - lhs: int, - ) -> stim.CliffordString: - """CliffordString left-multiplication. - - Args: - lhs: The number of times to repeat the Clifford string's contents. - - Returns: - The repeated Clifford string. - - Examples: - >>> import stim - - >>> 2 * stim.CliffordString("I,X,H") - stim.CliffordString("I,X,H,I,X,H") - - >>> 0 * stim.CliffordString("I,X,H") - stim.CliffordString("") - - >>> 5 * stim.CliffordString("I") - stim.CliffordString("I,I,I,I,I") - """ - def __setitem__( - self, - index_or_slice: Union[int, slice], - new_value: Union[str, stim.GateData, stim.CliffordString, stim.PauliString, stim.Tableau], - ) -> None: - """Overwrites an indexed Clifford, or slice of Cliffords, with the given value. - - Args: - index_or_slice: The index of the Clifford to overwrite, or the slice - of Cliffords to overwrite. - new_value: Specifies the value to write into the Clifford string. This can - be set to a few different types of values: - - str: Name of the single qubit Clifford gate to write to the index or - broadcast over the slice. - - stim.GateData: The single qubit Clifford gate to write to the index - or broadcast over the slice. - - stim.Tableau: Must be a single qubit tableau. Specifies the single - qubit Clifford gate to write to the index or broadcast over the - slice. - - stim.CliffordString: String of Cliffords to write into the slice. - - Examples: - >>> import stim - >>> s = stim.CliffordString("I,I,I,I,I") - - >>> s[1] = 'H' - >>> s - stim.CliffordString("I,H,I,I,I") - - >>> s[2:] = 'SQRT_X' - >>> s - stim.CliffordString("I,H,SQRT_X,SQRT_X,SQRT_X") - - >>> s[0] = stim.gate_data('S_DAG').inverse - >>> s - stim.CliffordString("S,H,SQRT_X,SQRT_X,SQRT_X") - - >>> s[:] = 'I' - >>> s - stim.CliffordString("I,I,I,I,I") - - >>> s[::2] = stim.CliffordString("X,Y,Z") - >>> s - stim.CliffordString("X,I,Y,I,Z") - - >>> s[0] = stim.Tableau.from_named_gate("H") - >>> s - stim.CliffordString("H,I,Y,I,Z") - - >>> s[:] = stim.Tableau.from_named_gate("S") - >>> s - stim.CliffordString("S,S,S,S,S") - - >>> s[:4] = stim.PauliString("IXYZ") - >>> s - stim.CliffordString("I,X,Y,Z,S") - """ - def __str__( - self, - ) -> str: - """Returns a string representation of the CliffordString's operations. - """ - @staticmethod - def all_cliffords_string( - ) -> stim.CliffordString: - """Returns a stim.CliffordString containing each single qubit Clifford once. - - Useful for things like testing that a method works on every single Clifford. - - Examples: - >>> import stim - >>> cliffords = stim.CliffordString.all_cliffords_string() - >>> len(cliffords) - 24 - - >>> print(cliffords[:8]) - I,X,Y,Z,H_XY,S,S_DAG,H_NXY - - >>> print(cliffords[8:16]) - H,SQRT_Y_DAG,H_NXZ,SQRT_Y,H_YZ,H_NYZ,SQRT_X,SQRT_X_DAG - - >>> print(cliffords[16:]) - C_XYZ,C_XYNZ,C_NXYZ,C_XNYZ,C_ZYX,C_ZNYX,C_NZYX,C_ZYNX - """ - def copy( - self, - ) -> stim.CliffordString: - """Returns a copy of the CliffordString. - - Returns: - The copy. - - Examples: - >>> import stim - >>> c = stim.CliffordString("H,X") - >>> alias = c - >>> copy = c.copy() - >>> c *= 5 - >>> alias - stim.CliffordString("H,X,H,X,H,X,H,X,H,X") - >>> copy - stim.CliffordString("H,X") - """ - @staticmethod - def random( - num_qubits: int, - ) -> stim.CliffordString: - """Samples a uniformly random CliffordString. - - Args: - num_qubits: The number of qubits the CliffordString should act upon. - - Examples: - >>> import stim - >>> p = stim.CliffordString.random(5) - >>> len(p) - 5 - - Returns: - The sampled Clifford string. - """ - def x_outputs( - self, - *, - bit_packed_signs: bool = False, - ) -> Tuple[stim.PauliString, np.ndarray]: - """Returns what each Clifford in the CliffordString conjugates an X input into. - - For example, H conjugates X into +Z and S_DAG conjugates X into -Y. - - Combined with `z_outputs`, the results of this method completely specify - the single qubit Clifford applied to each qubit. - - Args: - bit_packed_signs: Defaults to False. When False, the sign data is returned - in a numpy array with dtype `np.bool_`. When True, the dtype is instead - `np.uint8` and 8 bits are packed into each byte (in little endian - order). - - Returns: - A (paulis, signs) tuple. - - `paulis` has type stim.PauliString. Its sign is always positive. - - `signs` has type np.ndarray and an argument-dependent shape: - bit_packed_signs=False: - dtype=np.bool_ - shape=(num_qubits,) - bit_packed_signs=True: - dtype=np.uint8 - shape=(math.ceil(num_qubits / 8),) - - Examples: - >>> import stim - >>> x_paulis, x_signs = stim.CliffordString("I,Y,H,S").x_outputs() - >>> x_paulis - stim.PauliString("+XXZY") - >>> x_signs - array([False, True, False, False]) - - >>> stim.CliffordString("I,Y,H,S").x_outputs(bit_packed_signs=True)[1] - array([2], dtype=uint8) - """ - def y_outputs( - self, - *, - bit_packed_signs: bool = False, - ) -> Tuple[stim.PauliString, np.ndarray]: - """Returns what each Clifford in the CliffordString conjugates a Y input into. - - For example, H conjugates Y into -Y and S_DAG conjugates Y into +X. - - Args: - bit_packed_signs: Defaults to False. When False, the sign data is returned - in a numpy array with dtype `np.bool_`. When True, the dtype is instead - `np.uint8` and 8 bits are packed into each byte (in little endian - order). - - Returns: - A (paulis, signs) tuple. - - `paulis` has type stim.PauliString. Its sign is always positive. - - `signs` has type np.ndarray and an argument-dependent shape: - bit_packed_signs=False: - dtype=np.bool_ - shape=(num_qubits,) - bit_packed_signs=True: - dtype=np.uint8 - shape=(math.ceil(num_qubits / 8),) - - Examples: - >>> import stim - >>> y_paulis, y_signs = stim.CliffordString("I,X,H,S").y_outputs() - >>> y_paulis - stim.PauliString("+YYYX") - >>> y_signs - array([False, True, True, True]) - - >>> stim.CliffordString("I,X,H,S").y_outputs(bit_packed_signs=True)[1] - array([14], dtype=uint8) - """ - def z_outputs( - self, - *, - bit_packed_signs: bool = False, - ) -> Tuple[stim.PauliString, np.ndarray]: - """Returns what each Clifford in the CliffordString conjugates a Z input into. - - For example, H conjugates Z into +X and SQRT_X conjugates Z into -Y. - - Combined with `x_outputs`, the results of this method completely specify - the single qubit Clifford applied to each qubit. - - Args: - bit_packed_signs: Defaults to False. When False, the sign data is returned - in a numpy array with dtype `np.bool_`. When True, the dtype is instead - `np.uint8` and 8 bits are packed into each byte (in little endian - order). - - Returns: - A (paulis, signs) tuple. - - `paulis` has type stim.PauliString. Its sign is always positive. - - `signs` has type np.ndarray and an argument-dependent shape: - bit_packed_signs=False: - dtype=np.bool_ - shape=(num_qubits,) - bit_packed_signs=True: - dtype=np.uint8 - shape=(math.ceil(num_qubits / 8),) - - Examples: - >>> import stim - >>> z_paulis, z_signs = stim.CliffordString("I,Y,H,S").z_outputs() - >>> z_paulis - stim.PauliString("+ZZXZ") - >>> z_signs - array([False, True, False, False]) - - >>> stim.CliffordString("I,Y,H,S").z_outputs(bit_packed_signs=True)[1] - array([2], dtype=uint8) - """ class CompiledDemSampler: """A helper class for efficiently sampler from a detector error model. @@ -4627,13 +4073,13 @@ class CompiledDemSampler: shots: int, *, det_out_file: Union[None, str, pathlib.Path], - det_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + det_out_format: str = "01", obs_out_file: Union[None, str, pathlib.Path], - obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + obs_out_format: str = "01", err_out_file: Union[None, str, pathlib.Path] = None, - err_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + err_out_format: str = "01", replay_err_in_file: Union[None, str, pathlib.Path] = None, - replay_err_in_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + replay_err_in_format: str = "01", ) -> None: """Samples the detector error model and writes the results to disk. @@ -4887,9 +4333,9 @@ class CompiledDetectorSampler: shots: int, *, filepath: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + format: 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]' = '01', obs_out_filepath: Optional[Union[str, pathlib.Path]] = None, - obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + obs_out_format: 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]' = '01', prepend_observables: bool = False, append_observables: bool = False, ) -> None: @@ -5093,8 +4539,8 @@ class CompiledMeasurementSampler: self, shots: int, *, - filepath: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + filepath: str, + format: str = '01', ) -> None: """Samples measurements from the circuit and writes them to a file. @@ -5193,7 +4639,7 @@ class CompiledMeasurementsToDetectionEventsConverter: *, measurements: np.ndarray, sweep_bits: Optional[np.ndarray] = None, - separate_observables: Literal[True], + separate_observables: 'Literal[True]', append_observables: bool = False, bit_packed: bool = False, ) -> Tuple[np.ndarray, np.ndarray]: @@ -5285,15 +4731,15 @@ class CompiledMeasurementsToDetectionEventsConverter: def convert_file( self, *, - measurements_filepath: Union[str, pathlib.Path], - measurements_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', - sweep_bits_filepath: Optional[Union[str, pathlib.Path]] = None, - sweep_bits_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', - detection_events_filepath: Union[str, pathlib.Path], - detection_events_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + measurements_filepath: str, + measurements_format: str = '01', + sweep_bits_filepath: str = None, + sweep_bits_format: str = '01', + detection_events_filepath: str, + detection_events_format: str = '01', append_observables: bool = False, - obs_out_filepath: Optional[Union[str, pathlib.Path]] = None, - obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + obs_out_filepath: str = None, + obs_out_format: str = '01', ) -> None: """Reads measurement data from a file and writes detection events to another file. @@ -6388,7 +5834,7 @@ class DetectorErrorModel: """ def diagram( self, - type: Literal["matchgraph-svg", "matchgraph-svg-html", "matchgraph-3d", "matchgraph-3d-html"] = 'matchgraph-svg', + type: str, ) -> Any: """Returns a diagram of the circuit, from a variety of options. @@ -7982,7 +7428,7 @@ class Flow: the string "X_ -> ZZ xor rec[-1]" will result in a flow with input pauli string "X_", output pauli string "ZZ", and measurement indices [-1]. - Args: + Arguments: arg [position-only]: Defaults to None. Must be specified by itself if used. str: Initializes a flow by parsing the given shorthand text. stim.Flow: Initializes a copy of the given flow. @@ -9334,7 +8780,7 @@ class PauliString: """ def __init__( self, - arg: Union[None, int, str, stim.PauliString, Iterable[Union[int, Literal["_", "I", "X", "Y", "Z"]]]] = None, + arg: Union[None, int, str, stim.PauliString, Iterable[Union[int, 'Literal["_", "I", "X", "Y", "Z"]']]] = None, /, ) -> None: """Initializes a stim.PauliString from the given argument. @@ -9347,7 +8793,7 @@ class PauliString: pauli string is a series of integers seperated by '*' and prefixed by 'I', 'X', 'Y', or 'Z'. - Args: + Arguments: arg [position-only]: This can be a variety of types, including: None (default): initializes an empty Pauli string. int: initializes an identity Pauli string of the given length. @@ -9356,14 +8802,6 @@ class PauliString: Iterable: initializes by interpreting each item as a Pauli. Each item can be a single-qubit Pauli string (like "X"), or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. - Dict[int, Union[int, str]]: initializes by interpreting keys as - the qubit index and values as the Pauli for that index. - Each value can be a single-qubit Pauli string (like "X"), - or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. - Dict[Union[int, str], Iterable[int]]: initializes by interpreting keys - as Pauli operators and values as the qubit indices for that Pauli. - Each key can be a single-qubit Pauli string (like "X"), - or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. Examples: >>> import stim @@ -9391,15 +8829,6 @@ class PauliString: >>> stim.PauliString("X6*Y6") stim.PauliString("+i______Z") - - >>> stim.PauliString({0: "X", 2: "Y", 3: "X"}) - stim.PauliString("+X_YX") - - >>> stim.PauliString({0: "X", 2: 2, 3: 1}) - stim.PauliString("+X_YX") - - >>> stim.PauliString({"X": [1], 2: [4], "Z": [0, 3]}) - stim.PauliString("+ZX_ZY") """ def __itruediv__( self, @@ -9841,7 +9270,7 @@ class PauliString: def from_unitary_matrix( matrix: Iterable[Iterable[Union[int, float, complex]]], *, - endian: Literal["little", "big"] = 'little', + endian: str = 'little', unsigned: bool = False, ) -> stim.PauliString: """Creates a stim.PauliString from the unitary matrix of a Pauli group member. @@ -10131,7 +9560,7 @@ class PauliString: def to_unitary_matrix( self, *, - endian: Literal["little", "big"], + endian: str, ) -> np.ndarray[np.complex64]: """Converts the pauli string into a unitary matrix. @@ -10766,7 +10195,7 @@ class Tableau: def from_state_vector( state_vector: Iterable[float], *, - endian: Literal["little", "big"], + endian: str, ) -> stim.Tableau: """Creates a tableau representing the stabilizer state of the given state vector. @@ -10827,7 +10256,7 @@ class Tableau: def from_unitary_matrix( matrix: Iterable[Iterable[float]], *, - endian: Literal["little", "big"] = 'little', + endian: str = 'little', ) -> stim.Tableau: """Creates a tableau from the unitary matrix of a Clifford operation. @@ -11256,7 +10685,7 @@ class Tableau: """ def to_circuit( self, - method: Literal["elimination", "graph_state"] = 'elimination', + method: 'Literal["elimination", "graph_state"]' = 'elimination', ) -> stim.Circuit: """Synthesizes a circuit that implements the tableau's Clifford operation. @@ -11623,7 +11052,7 @@ class Tableau: def to_state_vector( self, *, - endian: Literal["little", "big"] = 'little', + endian: str = 'little', ) -> np.ndarray[np.complex64]: """Returns the state vector produced by applying the tableau to the |0..0> state. @@ -11675,7 +11104,7 @@ class Tableau: def to_unitary_matrix( self, *, - endian: Literal["little", "big"], + endian: str, ) -> np.ndarray[np.complex64]: """Converts the tableau into a unitary matrix. @@ -13397,7 +12826,7 @@ class TableauSimulator: self, state_vector: Iterable[float], *, - endian: Literal["little", "big"], + endian: str, ) -> None: """Sets the simulator's state to a superposition specified by an amplitude vector. @@ -13543,7 +12972,7 @@ class TableauSimulator: def state_vector( self, *, - endian: Literal["little", "big"] = 'little', + endian: str = 'little', ) -> np.ndarray[np.complex64]: """Returns a wavefunction for the simulator's current state. @@ -14038,7 +13467,7 @@ def main( def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], + format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, @@ -14049,18 +13478,18 @@ def read_shot_data_file( def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], + format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, num_observables: int = 0, - separate_observables: Literal[True], + separate_observables: 'Literal[True]', ) -> Tuple[np.ndarray, np.ndarray]: pass def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], + format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, @@ -14431,7 +13860,7 @@ def write_shot_data_file( *, data: np.ndarray, path: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], + format: str, num_measurements: int = 0, num_detectors: int = 0, num_observables: int = 0, diff --git a/doc/usage_command_line.md b/doc/usage_command_line.md index 2e5418e32..b5f107f60 100644 --- a/doc/usage_command_line.md +++ b/doc/usage_command_line.md @@ -1676,7 +1676,6 @@ SYNOPSIS [--out_format 01|b8|r8|ptb64|hits|dets] \ [--seed int] \ [--shots int] \ - [--skip_loop_folding] \ [--skip_reference_sample] DESCRIPTION @@ -1763,30 +1762,6 @@ OPTIONS Must be an integer between 0 and a quintillion (10^18). - --skip_loop_folding - Skips loop folding logic on the reference sample calculation. - - When this argument is specified, the reference sample (that is used - to convert measurement flip data from frame simulations into actual - measurement data) is generated by iterating through the entire - flattened circuit with no loop detection. - - Loop folding can enormously improve performance for circuits - containing REPEAT blocks with large repeat counts, by detecting - periodicity in loops and fast-forwarding across them when computing - the reference sample for the circuit. However, in some cases the - analysis is not able to detect the periodicity that is present. For - example, this has been observed in honeycomb code circuits. When - this happens, the folding-capable analysis is slower than simply - analyzing the flattened circuit without any specialized loop logic. - The `--skip_loop_folding` flag can be used to just analyze the - flattened circuit, bypassing this slowdown for circuits such as - honeycomb code circuits. - - By default, loop detection is enabled. Pass this flag to disable - it (when appropriate by use case). - - --skip_reference_sample Asserts the circuit can produce a noiseless sample that is just 0s. diff --git a/glue/python/src/stim/__init__.pyi b/glue/python/src/stim/__init__.pyi index d71851394..6444b2cab 100644 --- a/glue/python/src/stim/__init__.pyi +++ b/glue/python/src/stim/__init__.pyi @@ -1049,7 +1049,7 @@ class Circuit: """ def diagram( self, - type: Literal["timeline-text", "timeline-svg", "timeline-svg-html", "timeline-3d", "timeline-3d-html", "detslice-text", "detslice-svg", "detslice-svg-html", "matchgraph-svg", "matchgraph-svg-html", "matchgraph-3d", "matchgraph-3d-html", "timeslice-svg", "timeslice-svg-html", "detslice-with-ops-svg", "detslice-with-ops-svg-html", "interactive", "interactive-html"] = 'timeline-text', + type: str = 'timeline-text', *, tick: Union[None, int, range] = None, filter_coords: Iterable[Union[Iterable[float], stim.DemTarget]] = ((),), @@ -3889,560 +3889,6 @@ class CircuitTargetsInsideInstruction: >>> loc.instruction_targets.targets_in_range [stim.GateTargetWithCoords(0, [])] """ -class CliffordString: - """A tensor product of single qubit Clifford gates (e.g. "H \u2297 X \u2297 S"). - - Represents a collection of Clifford operations applied pairwise to a - collection of qubits. Ignores global phase. - - Examples: - >>> import stim - >>> stim.CliffordString("H,S,C_XYZ") * stim.CliffordString("H,H,H") - stim.CliffordString("I,C_ZYX,SQRT_X_DAG") - """ - def __add__( - self, - rhs: stim.CliffordString, - ) -> stim.CliffordString: - """Concatenates two CliffordStrings. - - Args: - rhs: The suffix of the concatenation. - - Returns: - The concatenated Clifford string. - - Examples: - >>> import stim - >>> stim.CliffordString("I,X,H") + stim.CliffordString("Y,S") - stim.CliffordString("I,X,H,Y,S") - """ - def __eq__( - self, - arg0: stim.CliffordString, - ) -> bool: - """Determines if two Clifford strings have identical contents. - """ - @overload - def __getitem__( - self, - index_or_slice: int, - ) -> stim.GateData: - pass - @overload - def __getitem__( - self, - index_or_slice: slice, - ) -> stim.CliffordString: - pass - def __getitem__( - self, - index_or_slice: Union[int, slice], - ) -> Union[stim.GateData, stim.CliffordString]: - """Returns a Clifford or substring from the CliffordString. - - Args: - index_or_slice: The index of the Clifford to return, or the slice - corresponding to the sub CliffordString to return. - - Returns: - The indexed Clifford (as a stim.GateData instance) or the sliced - CliffordString. - - Examples: - >>> import stim - >>> s = stim.CliffordString("I,X,Y,Z,H") - - >>> s[2] - stim.gate_data('Y') - - >>> s[-1] - stim.gate_data('H') - - >>> s[:-1] - stim.CliffordString("I,X,Y,Z") - - >>> s[::2] - stim.CliffordString("I,Y,H") - """ - def __iadd__( - self, - rhs: stim.CliffordString, - ) -> stim.CliffordString: - """Mutates the CliffordString by concatenating onto it. - - Args: - rhs: The suffix to concatenate onto the target CliffordString. - - Returns: - The mutated Clifford string. - - Examples: - >>> import stim - >>> c = stim.CliffordString("I,X,H") - >>> alias = c - >>> alias += stim.CliffordString("Y,S") - >>> c - stim.CliffordString("I,X,H,Y,S") - """ - def __imul__( - self, - rhs: Union[stim.CliffordString, int], - ) -> stim.CliffordString: - """Inplace CliffordString multiplication. - - Mutates the CliffordString into itself multiplied by another CliffordString - (via pairwise Clifford multipliation) or by an integer (via repeating the - contents). - - Args: - rhs: Either a stim.CliffordString or an int. If rhs is a - stim.CliffordString, then the Cliffords from each string are multiplied - pairwise. If rhs is an int, it is the number of times to repeat the - Clifford string's contents. - - Returns: - The mutated Clifford string. - - Examples: - >>> import stim - - >>> c = stim.CliffordString("S,X,X") - >>> alias = c - >>> alias *= stim.CliffordString("S,Z,H,Z") - >>> c - stim.CliffordString("Z,Y,SQRT_Y,Z") - - >>> c = stim.CliffordString("I,X,H") - >>> alias = c - >>> alias *= 2 - >>> c - stim.CliffordString("I,X,H,I,X,H") - """ - def __init__( - self, - arg: Union[int, str, stim.CliffordString, stim.PauliString, stim.Circuit], - /, - ) -> None: - """Initializes a stim.CliffordString from the given argument. - - Args: - arg [position-only]: This can be a variety of types, including: - int: initializes an identity Clifford string of the given length. - str: initializes by parsing a comma-separated list of gate names. - stim.CliffordString: initializes by copying the given Clifford string. - stim.PauliString: initializes by copying from the given Pauli string - (ignores the sign of the Pauli string). - stim.Circuit: initializes a CliffordString equivalent to the action - of the circuit (as long as the circuit only contains single qubit - unitary operations and annotations). - Iterable: initializes by interpreting each item as a Clifford. - Each item can be a single-qubit Clifford gate name (like "SQRT_X") - or stim.GateData instance. - - Examples: - >>> import stim - - >>> stim.CliffordString(5) - stim.CliffordString("I,I,I,I,I") - - >>> stim.CliffordString("X,Y,Z,SQRT_X") - stim.CliffordString("X,Y,Z,SQRT_X") - - >>> stim.CliffordString(["H", stim.gate_data("S")]) - stim.CliffordString("H,S") - - >>> stim.CliffordString(stim.PauliString("XYZ")) - stim.CliffordString("X,Y,Z") - - >>> stim.CliffordString(stim.CliffordString("X,Y,Z")) - stim.CliffordString("X,Y,Z") - - >>> stim.CliffordString(stim.Circuit(''' - ... H 0 1 2 - ... S 2 3 - ... TICK - ... S 3 - ... I 6 - ... ''')) - stim.CliffordString("H,H,C_ZYX,Z,I,I,I") - """ - def __ipow__( - self, - num_qubits: int, - ) -> object: - """Mutates the CliffordString into itself raised to a power. - - Args: - power: The power to raise the CliffordString's Cliffords to. - This value can be negative (e.g. -1 inverts the string). - - Returns: - The mutated Clifford string. - - Examples: - >>> import stim - - >>> p = stim.CliffordString("I,X,H,S,C_XYZ") - >>> p **= 3 - >>> p - stim.CliffordString("I,X,H,S_DAG,I") - - >>> p **= 2 - >>> p - stim.CliffordString("I,I,I,Z,I") - - >>> alias = p - >>> alias **= 2 - >>> p - stim.CliffordString("I,I,I,I,I") - """ - def __len__( - self, - ) -> int: - """Returns the number of Clifford operations in the string. - - Examples: - >>> import stim - >>> len(stim.CliffordString("I,X,Y,Z,H")) - 5 - """ - def __mul__( - self, - rhs: Union[stim.CliffordString, int], - ) -> stim.CliffordString: - """CliffordString multiplication. - - Args: - rhs: Either a stim.CliffordString or an int. If rhs is a - stim.CliffordString, then the Cliffords from each string are multiplied - pairwise. If rhs is an int, it is the number of times to repeat the - Clifford string's contents. - - Examples: - >>> import stim - - >>> stim.CliffordString("S,X,X") * stim.CliffordString("S,Z,H,Z") - stim.CliffordString("Z,Y,SQRT_Y,Z") - - >>> stim.CliffordString("I,X,H") * 3 - stim.CliffordString("I,X,H,I,X,H,I,X,H") - """ - def __ne__( - self, - arg0: stim.CliffordString, - ) -> bool: - """Determines if two Clifford strings have non-identical contents. - """ - def __pow__( - self, - power: int, - ) -> stim.CliffordString: - """Returns the CliffordString raised to a power. - - Args: - power: The power to raise the CliffordString's Cliffords to. - This value can be negative (e.g. -1 returns the inverse string). - - Returns: - The Clifford string raised to the power. - - Examples: - >>> import stim - - >>> p = stim.CliffordString("I,X,H,S,C_XYZ") - - >>> p**0 - stim.CliffordString("I,I,I,I,I") - - >>> p**1 - stim.CliffordString("I,X,H,S,C_XYZ") - - >>> p**12000001 - stim.CliffordString("I,X,H,S,C_XYZ") - - >>> p**2 - stim.CliffordString("I,I,I,Z,C_ZYX") - - >>> p**3 - stim.CliffordString("I,X,H,S_DAG,I") - - >>> p**-1 - stim.CliffordString("I,X,H,S_DAG,C_ZYX") - """ - def __repr__( - self, - ) -> str: - """Returns text that is a valid python expression evaluating to an equivalent `stim.CliffordString`. - """ - def __rmul__( - self, - lhs: int, - ) -> stim.CliffordString: - """CliffordString left-multiplication. - - Args: - lhs: The number of times to repeat the Clifford string's contents. - - Returns: - The repeated Clifford string. - - Examples: - >>> import stim - - >>> 2 * stim.CliffordString("I,X,H") - stim.CliffordString("I,X,H,I,X,H") - - >>> 0 * stim.CliffordString("I,X,H") - stim.CliffordString("") - - >>> 5 * stim.CliffordString("I") - stim.CliffordString("I,I,I,I,I") - """ - def __setitem__( - self, - index_or_slice: Union[int, slice], - new_value: Union[str, stim.GateData, stim.CliffordString, stim.PauliString, stim.Tableau], - ) -> None: - """Overwrites an indexed Clifford, or slice of Cliffords, with the given value. - - Args: - index_or_slice: The index of the Clifford to overwrite, or the slice - of Cliffords to overwrite. - new_value: Specifies the value to write into the Clifford string. This can - be set to a few different types of values: - - str: Name of the single qubit Clifford gate to write to the index or - broadcast over the slice. - - stim.GateData: The single qubit Clifford gate to write to the index - or broadcast over the slice. - - stim.Tableau: Must be a single qubit tableau. Specifies the single - qubit Clifford gate to write to the index or broadcast over the - slice. - - stim.CliffordString: String of Cliffords to write into the slice. - - Examples: - >>> import stim - >>> s = stim.CliffordString("I,I,I,I,I") - - >>> s[1] = 'H' - >>> s - stim.CliffordString("I,H,I,I,I") - - >>> s[2:] = 'SQRT_X' - >>> s - stim.CliffordString("I,H,SQRT_X,SQRT_X,SQRT_X") - - >>> s[0] = stim.gate_data('S_DAG').inverse - >>> s - stim.CliffordString("S,H,SQRT_X,SQRT_X,SQRT_X") - - >>> s[:] = 'I' - >>> s - stim.CliffordString("I,I,I,I,I") - - >>> s[::2] = stim.CliffordString("X,Y,Z") - >>> s - stim.CliffordString("X,I,Y,I,Z") - - >>> s[0] = stim.Tableau.from_named_gate("H") - >>> s - stim.CliffordString("H,I,Y,I,Z") - - >>> s[:] = stim.Tableau.from_named_gate("S") - >>> s - stim.CliffordString("S,S,S,S,S") - - >>> s[:4] = stim.PauliString("IXYZ") - >>> s - stim.CliffordString("I,X,Y,Z,S") - """ - def __str__( - self, - ) -> str: - """Returns a string representation of the CliffordString's operations. - """ - @staticmethod - def all_cliffords_string( - ) -> stim.CliffordString: - """Returns a stim.CliffordString containing each single qubit Clifford once. - - Useful for things like testing that a method works on every single Clifford. - - Examples: - >>> import stim - >>> cliffords = stim.CliffordString.all_cliffords_string() - >>> len(cliffords) - 24 - - >>> print(cliffords[:8]) - I,X,Y,Z,H_XY,S,S_DAG,H_NXY - - >>> print(cliffords[8:16]) - H,SQRT_Y_DAG,H_NXZ,SQRT_Y,H_YZ,H_NYZ,SQRT_X,SQRT_X_DAG - - >>> print(cliffords[16:]) - C_XYZ,C_XYNZ,C_NXYZ,C_XNYZ,C_ZYX,C_ZNYX,C_NZYX,C_ZYNX - """ - def copy( - self, - ) -> stim.CliffordString: - """Returns a copy of the CliffordString. - - Returns: - The copy. - - Examples: - >>> import stim - >>> c = stim.CliffordString("H,X") - >>> alias = c - >>> copy = c.copy() - >>> c *= 5 - >>> alias - stim.CliffordString("H,X,H,X,H,X,H,X,H,X") - >>> copy - stim.CliffordString("H,X") - """ - @staticmethod - def random( - num_qubits: int, - ) -> stim.CliffordString: - """Samples a uniformly random CliffordString. - - Args: - num_qubits: The number of qubits the CliffordString should act upon. - - Examples: - >>> import stim - >>> p = stim.CliffordString.random(5) - >>> len(p) - 5 - - Returns: - The sampled Clifford string. - """ - def x_outputs( - self, - *, - bit_packed_signs: bool = False, - ) -> Tuple[stim.PauliString, np.ndarray]: - """Returns what each Clifford in the CliffordString conjugates an X input into. - - For example, H conjugates X into +Z and S_DAG conjugates X into -Y. - - Combined with `z_outputs`, the results of this method completely specify - the single qubit Clifford applied to each qubit. - - Args: - bit_packed_signs: Defaults to False. When False, the sign data is returned - in a numpy array with dtype `np.bool_`. When True, the dtype is instead - `np.uint8` and 8 bits are packed into each byte (in little endian - order). - - Returns: - A (paulis, signs) tuple. - - `paulis` has type stim.PauliString. Its sign is always positive. - - `signs` has type np.ndarray and an argument-dependent shape: - bit_packed_signs=False: - dtype=np.bool_ - shape=(num_qubits,) - bit_packed_signs=True: - dtype=np.uint8 - shape=(math.ceil(num_qubits / 8),) - - Examples: - >>> import stim - >>> x_paulis, x_signs = stim.CliffordString("I,Y,H,S").x_outputs() - >>> x_paulis - stim.PauliString("+XXZY") - >>> x_signs - array([False, True, False, False]) - - >>> stim.CliffordString("I,Y,H,S").x_outputs(bit_packed_signs=True)[1] - array([2], dtype=uint8) - """ - def y_outputs( - self, - *, - bit_packed_signs: bool = False, - ) -> Tuple[stim.PauliString, np.ndarray]: - """Returns what each Clifford in the CliffordString conjugates a Y input into. - - For example, H conjugates Y into -Y and S_DAG conjugates Y into +X. - - Args: - bit_packed_signs: Defaults to False. When False, the sign data is returned - in a numpy array with dtype `np.bool_`. When True, the dtype is instead - `np.uint8` and 8 bits are packed into each byte (in little endian - order). - - Returns: - A (paulis, signs) tuple. - - `paulis` has type stim.PauliString. Its sign is always positive. - - `signs` has type np.ndarray and an argument-dependent shape: - bit_packed_signs=False: - dtype=np.bool_ - shape=(num_qubits,) - bit_packed_signs=True: - dtype=np.uint8 - shape=(math.ceil(num_qubits / 8),) - - Examples: - >>> import stim - >>> y_paulis, y_signs = stim.CliffordString("I,X,H,S").y_outputs() - >>> y_paulis - stim.PauliString("+YYYX") - >>> y_signs - array([False, True, True, True]) - - >>> stim.CliffordString("I,X,H,S").y_outputs(bit_packed_signs=True)[1] - array([14], dtype=uint8) - """ - def z_outputs( - self, - *, - bit_packed_signs: bool = False, - ) -> Tuple[stim.PauliString, np.ndarray]: - """Returns what each Clifford in the CliffordString conjugates a Z input into. - - For example, H conjugates Z into +X and SQRT_X conjugates Z into -Y. - - Combined with `x_outputs`, the results of this method completely specify - the single qubit Clifford applied to each qubit. - - Args: - bit_packed_signs: Defaults to False. When False, the sign data is returned - in a numpy array with dtype `np.bool_`. When True, the dtype is instead - `np.uint8` and 8 bits are packed into each byte (in little endian - order). - - Returns: - A (paulis, signs) tuple. - - `paulis` has type stim.PauliString. Its sign is always positive. - - `signs` has type np.ndarray and an argument-dependent shape: - bit_packed_signs=False: - dtype=np.bool_ - shape=(num_qubits,) - bit_packed_signs=True: - dtype=np.uint8 - shape=(math.ceil(num_qubits / 8),) - - Examples: - >>> import stim - >>> z_paulis, z_signs = stim.CliffordString("I,Y,H,S").z_outputs() - >>> z_paulis - stim.PauliString("+ZZXZ") - >>> z_signs - array([False, True, False, False]) - - >>> stim.CliffordString("I,Y,H,S").z_outputs(bit_packed_signs=True)[1] - array([2], dtype=uint8) - """ class CompiledDemSampler: """A helper class for efficiently sampler from a detector error model. @@ -4627,13 +4073,13 @@ class CompiledDemSampler: shots: int, *, det_out_file: Union[None, str, pathlib.Path], - det_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + det_out_format: str = "01", obs_out_file: Union[None, str, pathlib.Path], - obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + obs_out_format: str = "01", err_out_file: Union[None, str, pathlib.Path] = None, - err_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + err_out_format: str = "01", replay_err_in_file: Union[None, str, pathlib.Path] = None, - replay_err_in_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + replay_err_in_format: str = "01", ) -> None: """Samples the detector error model and writes the results to disk. @@ -4887,9 +4333,9 @@ class CompiledDetectorSampler: shots: int, *, filepath: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + format: 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]' = '01', obs_out_filepath: Optional[Union[str, pathlib.Path]] = None, - obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + obs_out_format: 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]' = '01', prepend_observables: bool = False, append_observables: bool = False, ) -> None: @@ -5093,8 +4539,8 @@ class CompiledMeasurementSampler: self, shots: int, *, - filepath: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + filepath: str, + format: str = '01', ) -> None: """Samples measurements from the circuit and writes them to a file. @@ -5193,7 +4639,7 @@ class CompiledMeasurementsToDetectionEventsConverter: *, measurements: np.ndarray, sweep_bits: Optional[np.ndarray] = None, - separate_observables: Literal[True], + separate_observables: 'Literal[True]', append_observables: bool = False, bit_packed: bool = False, ) -> Tuple[np.ndarray, np.ndarray]: @@ -5285,15 +4731,15 @@ class CompiledMeasurementsToDetectionEventsConverter: def convert_file( self, *, - measurements_filepath: Union[str, pathlib.Path], - measurements_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', - sweep_bits_filepath: Optional[Union[str, pathlib.Path]] = None, - sweep_bits_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', - detection_events_filepath: Union[str, pathlib.Path], - detection_events_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + measurements_filepath: str, + measurements_format: str = '01', + sweep_bits_filepath: str = None, + sweep_bits_format: str = '01', + detection_events_filepath: str, + detection_events_format: str = '01', append_observables: bool = False, - obs_out_filepath: Optional[Union[str, pathlib.Path]] = None, - obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + obs_out_filepath: str = None, + obs_out_format: str = '01', ) -> None: """Reads measurement data from a file and writes detection events to another file. @@ -6388,7 +5834,7 @@ class DetectorErrorModel: """ def diagram( self, - type: Literal["matchgraph-svg", "matchgraph-svg-html", "matchgraph-3d", "matchgraph-3d-html"] = 'matchgraph-svg', + type: str, ) -> Any: """Returns a diagram of the circuit, from a variety of options. @@ -7982,7 +7428,7 @@ class Flow: the string "X_ -> ZZ xor rec[-1]" will result in a flow with input pauli string "X_", output pauli string "ZZ", and measurement indices [-1]. - Args: + Arguments: arg [position-only]: Defaults to None. Must be specified by itself if used. str: Initializes a flow by parsing the given shorthand text. stim.Flow: Initializes a copy of the given flow. @@ -9334,7 +8780,7 @@ class PauliString: """ def __init__( self, - arg: Union[None, int, str, stim.PauliString, Iterable[Union[int, Literal["_", "I", "X", "Y", "Z"]]]] = None, + arg: Union[None, int, str, stim.PauliString, Iterable[Union[int, 'Literal["_", "I", "X", "Y", "Z"]']]] = None, /, ) -> None: """Initializes a stim.PauliString from the given argument. @@ -9347,7 +8793,7 @@ class PauliString: pauli string is a series of integers seperated by '*' and prefixed by 'I', 'X', 'Y', or 'Z'. - Args: + Arguments: arg [position-only]: This can be a variety of types, including: None (default): initializes an empty Pauli string. int: initializes an identity Pauli string of the given length. @@ -9356,14 +8802,6 @@ class PauliString: Iterable: initializes by interpreting each item as a Pauli. Each item can be a single-qubit Pauli string (like "X"), or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. - Dict[int, Union[int, str]]: initializes by interpreting keys as - the qubit index and values as the Pauli for that index. - Each value can be a single-qubit Pauli string (like "X"), - or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. - Dict[Union[int, str], Iterable[int]]: initializes by interpreting keys - as Pauli operators and values as the qubit indices for that Pauli. - Each key can be a single-qubit Pauli string (like "X"), - or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. Examples: >>> import stim @@ -9391,15 +8829,6 @@ class PauliString: >>> stim.PauliString("X6*Y6") stim.PauliString("+i______Z") - - >>> stim.PauliString({0: "X", 2: "Y", 3: "X"}) - stim.PauliString("+X_YX") - - >>> stim.PauliString({0: "X", 2: 2, 3: 1}) - stim.PauliString("+X_YX") - - >>> stim.PauliString({"X": [1], 2: [4], "Z": [0, 3]}) - stim.PauliString("+ZX_ZY") """ def __itruediv__( self, @@ -9841,7 +9270,7 @@ class PauliString: def from_unitary_matrix( matrix: Iterable[Iterable[Union[int, float, complex]]], *, - endian: Literal["little", "big"] = 'little', + endian: str = 'little', unsigned: bool = False, ) -> stim.PauliString: """Creates a stim.PauliString from the unitary matrix of a Pauli group member. @@ -10131,7 +9560,7 @@ class PauliString: def to_unitary_matrix( self, *, - endian: Literal["little", "big"], + endian: str, ) -> np.ndarray[np.complex64]: """Converts the pauli string into a unitary matrix. @@ -10766,7 +10195,7 @@ class Tableau: def from_state_vector( state_vector: Iterable[float], *, - endian: Literal["little", "big"], + endian: str, ) -> stim.Tableau: """Creates a tableau representing the stabilizer state of the given state vector. @@ -10827,7 +10256,7 @@ class Tableau: def from_unitary_matrix( matrix: Iterable[Iterable[float]], *, - endian: Literal["little", "big"] = 'little', + endian: str = 'little', ) -> stim.Tableau: """Creates a tableau from the unitary matrix of a Clifford operation. @@ -11256,7 +10685,7 @@ class Tableau: """ def to_circuit( self, - method: Literal["elimination", "graph_state"] = 'elimination', + method: 'Literal["elimination", "graph_state"]' = 'elimination', ) -> stim.Circuit: """Synthesizes a circuit that implements the tableau's Clifford operation. @@ -11623,7 +11052,7 @@ class Tableau: def to_state_vector( self, *, - endian: Literal["little", "big"] = 'little', + endian: str = 'little', ) -> np.ndarray[np.complex64]: """Returns the state vector produced by applying the tableau to the |0..0> state. @@ -11675,7 +11104,7 @@ class Tableau: def to_unitary_matrix( self, *, - endian: Literal["little", "big"], + endian: str, ) -> np.ndarray[np.complex64]: """Converts the tableau into a unitary matrix. @@ -13397,7 +12826,7 @@ class TableauSimulator: self, state_vector: Iterable[float], *, - endian: Literal["little", "big"], + endian: str, ) -> None: """Sets the simulator's state to a superposition specified by an amplitude vector. @@ -13543,7 +12972,7 @@ class TableauSimulator: def state_vector( self, *, - endian: Literal["little", "big"] = 'little', + endian: str = 'little', ) -> np.ndarray[np.complex64]: """Returns a wavefunction for the simulator's current state. @@ -14038,7 +13467,7 @@ def main( def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], + format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, @@ -14049,18 +13478,18 @@ def read_shot_data_file( def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], + format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, num_observables: int = 0, - separate_observables: Literal[True], + separate_observables: 'Literal[True]', ) -> Tuple[np.ndarray, np.ndarray]: pass def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], + format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, @@ -14431,7 +13860,7 @@ def write_shot_data_file( *, data: np.ndarray, path: Union[str, pathlib.Path], - format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], + format: str, num_measurements: int = 0, num_detectors: int = 0, num_observables: int = 0, diff --git a/glue/sample/src/sinter/_decoding/_decoding.py b/glue/sample/src/sinter/_decoding/_decoding.py index 1e54f87ef..e45aef72b 100644 --- a/glue/sample/src/sinter/_decoding/_decoding.py +++ b/glue/sample/src/sinter/_decoding/_decoding.py @@ -177,13 +177,7 @@ def sample_decode(*, were executed. The detection fraction is the ratio of these two numbers. num_shots: The number of sample shots to take from the circuit. - decoder: The name of the decoder to use. Allowed values are: - "pymatching": - Use pymatching min-weight-perfect-match decoder. - "internal": - Use internal decoder with uncorrelated decoding. - "internal_correlated": - Use internal decoder with correlated decoding. + decoder: The name of the decoder to use. For example, 'pymatching'. tmp_dir: An existing directory that is currently empty where temporary files can be written as part of performing decoding. If set to None, one is created using the tempfile package. diff --git a/glue/sample/src/sinter/_decoding/_decoding_all_built_in_decoders.py b/glue/sample/src/sinter/_decoding/_decoding_all_built_in_decoders.py index 92d8d49dd..93ffa584f 100644 --- a/glue/sample/src/sinter/_decoding/_decoding_all_built_in_decoders.py +++ b/glue/sample/src/sinter/_decoding/_decoding_all_built_in_decoders.py @@ -12,6 +12,7 @@ BUILT_IN_DECODERS: Dict[str, Decoder] = { 'vacuous': VacuousDecoder(), 'pymatching': PyMatchingDecoder(), + 'pymatching-correlated': PyMatchingDecoder(use_correlated_decoding=True), 'fusion_blossom': FusionBlossomDecoder(), # an implementation of (weighted) hypergraph UF decoder (https://arxiv.org/abs/2103.08049) 'hypergraph_union_find': HyperUFDecoder(), diff --git a/glue/sample/src/sinter/_decoding/_decoding_pymatching.py b/glue/sample/src/sinter/_decoding/_decoding_pymatching.py index b57bb32bc..ed3c118db 100644 --- a/glue/sample/src/sinter/_decoding/_decoding_pymatching.py +++ b/glue/sample/src/sinter/_decoding/_decoding_pymatching.py @@ -1,26 +1,45 @@ from sinter._decoding._decoding_decoder_class import Decoder, CompiledDecoder +def check_pymatching_version_for_correlated_decoding(pymatching): + v = pymatching.__version__.split('.') + if not (int(v[0]) >= 2 or int(v[1]) >= 3 or int(v[2]) >= 1): + raise ValueError( + "Pymatching version must be at least 2.3.1 for correlated decoding.\n" + f"Installed version: {pymatching.__version__}\n" + "To fix this, install a newer version of pymatching into your environment.\n" + "For example, if you are using pip, run `pip install pymatching --upgrade`.\n" + ) + + class PyMatchingCompiledDecoder(CompiledDecoder): - def __init__(self, matcher: 'pymatching.Matching'): + def __init__(self, matcher: 'pymatching.Matching', use_correlated_decoding: bool): self.matcher = matcher + self.use_correlated_decoding = use_correlated_decoding def decode_shots_bit_packed( self, *, bit_packed_detection_event_data: 'np.ndarray', ) -> 'np.ndarray': + kwargs = {} + if self.use_correlated_decoding: + kwargs['enable_correlations'] = True return self.matcher.decode_batch( shots=bit_packed_detection_event_data, bit_packed_shots=True, bit_packed_predictions=True, return_weights=False, + **kwargs, ) class PyMatchingDecoder(Decoder): """Use pymatching to predict observables from detection events.""" + def __init__(self, use_correlated_decoding: bool = False): + self.use_correlated_decoding = use_correlated_decoding + def compile_decoder_for_dem(self, *, dem: 'stim.DetectorErrorModel') -> CompiledDecoder: try: import pymatching @@ -31,7 +50,14 @@ def compile_decoder_for_dem(self, *, dem: 'stim.DetectorErrorModel') -> Compiled "For example, if you are using pip, run `pip install pymatching`.\n" ) from ex - return PyMatchingCompiledDecoder(pymatching.Matching.from_detector_error_model(dem)) + kwargs = {} + if self.use_correlated_decoding: + check_pymatching_version_for_correlated_decoding(pymatching) + kwargs['enable_correlations'] = True + return PyMatchingCompiledDecoder( + pymatching.Matching.from_detector_error_model(dem, **kwargs), + use_correlated_decoding=self.use_correlated_decoding, + ) def decode_via_files(self, *, @@ -60,7 +86,9 @@ def decode_via_files(self, if not hasattr(pymatching, 'cli'): raise ValueError(""" The installed version of pymatching has no `pymatching.cli` method. + sinter requires pymatching 2.1.0 or later. + If you're using pip to install packages, this can be fixed by running ``` @@ -69,13 +97,18 @@ def decode_via_files(self, """) - result = pymatching.cli(command_line_args=[ + args = [ "predict", "--dem", str(dem_path), "--in", str(dets_b8_in_path), "--in_format", "b8", "--out", str(obs_predictions_b8_out_path), "--out_format", "b8", - ]) + ] + if self.use_correlated_decoding: + check_pymatching_version_for_correlated_decoding(pymatching) + args.append("--enable_correlations") + + result = pymatching.cli(command_line_args=args) if result: raise ValueError("pymatching.cli returned a non-zero exit code") diff --git a/glue/sample/src/sinter/_decoding/_decoding_test.py b/glue/sample/src/sinter/_decoding/_decoding_test.py index cd4e28d0d..7dd08f379 100644 --- a/glue/sample/src/sinter/_decoding/_decoding_test.py +++ b/glue/sample/src/sinter/_decoding/_decoding_test.py @@ -233,6 +233,8 @@ def test_no_detectors_with_post_mask(decoder: str, force_streaming: Optional[boo @pytest.mark.parametrize('decoder,force_streaming', DECODER_CASES) def test_post_selection(decoder: str, force_streaming: Optional[bool]): + if decoder == 'pymatching-correlated': + pytest.skip("Correlated matching does not support error probabilities > 0.5 in from_detector_error_model") circuit = stim.Circuit(""" X_ERROR(0.6) 0 M 0 @@ -243,7 +245,7 @@ def test_post_selection(decoder: str, force_streaming: Optional[bool]): M 1 DETECTOR(1, 0, 0) rec[-1] OBSERVABLE_INCLUDE(0) rec[-1] - + X_ERROR(0.1) 2 M 2 OBSERVABLE_INCLUDE(0) rec[-1] From 1e8a50a45f5624eab452b84ce9359a07d511fe1c Mon Sep 17 00:00:00 2001 From: Matt McEwen Date: Tue, 3 Mar 2026 15:33:06 -0800 Subject: [PATCH 2/4] remove bad doc regen --- doc/gates.md | 16 +- doc/python_api_reference_vDev.md | 819 ++++++++++++++++++++++++++++-- doc/sinter_api.md | 65 +++ doc/stim.pyi | 643 +++++++++++++++++++++-- doc/usage_command_line.md | 25 + glue/python/src/stim/__init__.pyi | 643 +++++++++++++++++++++-- 6 files changed, 2101 insertions(+), 110 deletions(-) diff --git a/doc/gates.md b/doc/gates.md index 3978e320a..7f9825026 100644 --- a/doc/gates.md +++ b/doc/gates.md @@ -3579,7 +3579,7 @@ Example: # Sample errors from the distribution 10% XX, 20% YZ, 70% II. # Apply independently to qubit pairs (1,2), (5,6), and (8,3) - PAULI_CHANNEL_2(0,0,0, 0.1,0,0,0, 0,0,0,0.2, 0,0,0,0) 1 2 5 6 8 3 + PAULI_CHANNEL_2(0,0,0, 0,0.1,0,0, 0,0,0,0.2, 0,0,0,0) 1 2 5 6 8 3 Pauli Mixture: @@ -4879,13 +4879,19 @@ detection event simulations and affect whether the observable is included in err makes it easier to benchmark all observables of a code, without having to introduce noiseless qubits entangled with the logical qubit to avoid the testing of the X observable anticommuting with the testing of the Z observable. +Unlike a `DETECTOR` instruction which provides a complete description of a detector by listing all its constituent +measurement records, an individual `OBSERVABLE_INCLUDE` instruction is not required to (and generally does not) fully +describe a logical observable. Instead, measurement records or Pauli targets are added to it incrementally. A logical +observable can be given both types of description: as a collection of Pauli targets and as a collection of measurement +record targets. + Parens Arguments: A non-negative integer specifying the index of the logical observable to add the measurement records to. Targets: - The measurement records to add to the specified observable. + The measurement records or Pauli terms to add to the specified observable. Example: @@ -4921,6 +4927,12 @@ Example: OBSERVABLE_INCLUDE(0) X0 X1 OBSERVABLE_INCLUDE(1) Z0 Z2 + # Stim circuit may include a description of an observable in terms of Pauli targets + # alongside a description in terms of measurement records. + OBSERVABLE_INCLUDE(0) Z0 Z1 + M 0 1 + OBSERVABLE_INCLUDE(0) rec[-2] rec[-1] + ### The 'QUBIT_COORDS' Instruction diff --git a/doc/python_api_reference_vDev.md b/doc/python_api_reference_vDev.md index e88766612..01ce7fe4b 100644 --- a/doc/python_api_reference_vDev.md +++ b/doc/python_api_reference_vDev.md @@ -109,6 +109,28 @@ API references for stable versions are kept on the [stim github wiki](https://gi - [`stim.CircuitTargetsInsideInstruction.target_range_end`](#stim.CircuitTargetsInsideInstruction.target_range_end) - [`stim.CircuitTargetsInsideInstruction.target_range_start`](#stim.CircuitTargetsInsideInstruction.target_range_start) - [`stim.CircuitTargetsInsideInstruction.targets_in_range`](#stim.CircuitTargetsInsideInstruction.targets_in_range) +- [`stim.CliffordString`](#stim.CliffordString) + - [`stim.CliffordString.__add__`](#stim.CliffordString.__add__) + - [`stim.CliffordString.__eq__`](#stim.CliffordString.__eq__) + - [`stim.CliffordString.__getitem__`](#stim.CliffordString.__getitem__) + - [`stim.CliffordString.__iadd__`](#stim.CliffordString.__iadd__) + - [`stim.CliffordString.__imul__`](#stim.CliffordString.__imul__) + - [`stim.CliffordString.__init__`](#stim.CliffordString.__init__) + - [`stim.CliffordString.__ipow__`](#stim.CliffordString.__ipow__) + - [`stim.CliffordString.__len__`](#stim.CliffordString.__len__) + - [`stim.CliffordString.__mul__`](#stim.CliffordString.__mul__) + - [`stim.CliffordString.__ne__`](#stim.CliffordString.__ne__) + - [`stim.CliffordString.__pow__`](#stim.CliffordString.__pow__) + - [`stim.CliffordString.__repr__`](#stim.CliffordString.__repr__) + - [`stim.CliffordString.__rmul__`](#stim.CliffordString.__rmul__) + - [`stim.CliffordString.__setitem__`](#stim.CliffordString.__setitem__) + - [`stim.CliffordString.__str__`](#stim.CliffordString.__str__) + - [`stim.CliffordString.all_cliffords_string`](#stim.CliffordString.all_cliffords_string) + - [`stim.CliffordString.copy`](#stim.CliffordString.copy) + - [`stim.CliffordString.random`](#stim.CliffordString.random) + - [`stim.CliffordString.x_outputs`](#stim.CliffordString.x_outputs) + - [`stim.CliffordString.y_outputs`](#stim.CliffordString.y_outputs) + - [`stim.CliffordString.z_outputs`](#stim.CliffordString.z_outputs) - [`stim.CompiledDemSampler`](#stim.CompiledDemSampler) - [`stim.CompiledDemSampler.sample`](#stim.CompiledDemSampler.sample) - [`stim.CompiledDemSampler.sample_write`](#stim.CompiledDemSampler.sample_write) @@ -1664,7 +1686,7 @@ def detector_error_model( # (in class stim.Circuit) def diagram( self, - type: str = 'timeline-text', + type: Literal["timeline-text", "timeline-svg", "timeline-svg-html", "timeline-3d", "timeline-3d-html", "detslice-text", "detslice-svg", "detslice-svg-html", "matchgraph-svg", "matchgraph-svg-html", "matchgraph-3d", "matchgraph-3d-html", "timeslice-svg", "timeslice-svg-html", "detslice-with-ops-svg", "detslice-with-ops-svg-html", "interactive", "interactive-html"] = 'timeline-text', *, tick: Union[None, int, range] = None, filter_coords: Iterable[Union[Iterable[float], stim.DemTarget]] = ((),), @@ -5027,6 +5049,714 @@ def targets_in_range( """ ``` + +```python +# stim.CliffordString + +# (at top-level in the stim module) +class CliffordString: + """A tensor product of single qubit Clifford gates (e.g. "H \u2297 X \u2297 S"). + + Represents a collection of Clifford operations applied pairwise to a + collection of qubits. Ignores global phase. + + Examples: + >>> import stim + >>> stim.CliffordString("H,S,C_XYZ") * stim.CliffordString("H,H,H") + stim.CliffordString("I,C_ZYX,SQRT_X_DAG") + """ +``` + + +```python +# stim.CliffordString.__add__ + +# (in class stim.CliffordString) +def __add__( + self, + rhs: stim.CliffordString, +) -> stim.CliffordString: + """Concatenates two CliffordStrings. + + Args: + rhs: The suffix of the concatenation. + + Returns: + The concatenated Clifford string. + + Examples: + >>> import stim + >>> stim.CliffordString("I,X,H") + stim.CliffordString("Y,S") + stim.CliffordString("I,X,H,Y,S") + """ +``` + + +```python +# stim.CliffordString.__eq__ + +# (in class stim.CliffordString) +def __eq__( + self, + arg0: stim.CliffordString, +) -> bool: + """Determines if two Clifford strings have identical contents. + """ +``` + + +```python +# stim.CliffordString.__getitem__ + +# (in class stim.CliffordString) +@overload +def __getitem__( + self, + index_or_slice: int, +) -> stim.GateData: + pass +@overload +def __getitem__( + self, + index_or_slice: slice, +) -> stim.CliffordString: + pass +def __getitem__( + self, + index_or_slice: Union[int, slice], +) -> Union[stim.GateData, stim.CliffordString]: + """Returns a Clifford or substring from the CliffordString. + + Args: + index_or_slice: The index of the Clifford to return, or the slice + corresponding to the sub CliffordString to return. + + Returns: + The indexed Clifford (as a stim.GateData instance) or the sliced + CliffordString. + + Examples: + >>> import stim + >>> s = stim.CliffordString("I,X,Y,Z,H") + + >>> s[2] + stim.gate_data('Y') + + >>> s[-1] + stim.gate_data('H') + + >>> s[:-1] + stim.CliffordString("I,X,Y,Z") + + >>> s[::2] + stim.CliffordString("I,Y,H") + """ +``` + + +```python +# stim.CliffordString.__iadd__ + +# (in class stim.CliffordString) +def __iadd__( + self, + rhs: stim.CliffordString, +) -> stim.CliffordString: + """Mutates the CliffordString by concatenating onto it. + + Args: + rhs: The suffix to concatenate onto the target CliffordString. + + Returns: + The mutated Clifford string. + + Examples: + >>> import stim + >>> c = stim.CliffordString("I,X,H") + >>> alias = c + >>> alias += stim.CliffordString("Y,S") + >>> c + stim.CliffordString("I,X,H,Y,S") + """ +``` + + +```python +# stim.CliffordString.__imul__ + +# (in class stim.CliffordString) +def __imul__( + self, + rhs: Union[stim.CliffordString, int], +) -> stim.CliffordString: + """Inplace CliffordString multiplication. + + Mutates the CliffordString into itself multiplied by another CliffordString + (via pairwise Clifford multipliation) or by an integer (via repeating the + contents). + + Args: + rhs: Either a stim.CliffordString or an int. If rhs is a + stim.CliffordString, then the Cliffords from each string are multiplied + pairwise. If rhs is an int, it is the number of times to repeat the + Clifford string's contents. + + Returns: + The mutated Clifford string. + + Examples: + >>> import stim + + >>> c = stim.CliffordString("S,X,X") + >>> alias = c + >>> alias *= stim.CliffordString("S,Z,H,Z") + >>> c + stim.CliffordString("Z,Y,SQRT_Y,Z") + + >>> c = stim.CliffordString("I,X,H") + >>> alias = c + >>> alias *= 2 + >>> c + stim.CliffordString("I,X,H,I,X,H") + """ +``` + + +```python +# stim.CliffordString.__init__ + +# (in class stim.CliffordString) +def __init__( + self, + arg: Union[int, str, stim.CliffordString, stim.PauliString, stim.Circuit], + /, +) -> None: + """Initializes a stim.CliffordString from the given argument. + + Args: + arg [position-only]: This can be a variety of types, including: + int: initializes an identity Clifford string of the given length. + str: initializes by parsing a comma-separated list of gate names. + stim.CliffordString: initializes by copying the given Clifford string. + stim.PauliString: initializes by copying from the given Pauli string + (ignores the sign of the Pauli string). + stim.Circuit: initializes a CliffordString equivalent to the action + of the circuit (as long as the circuit only contains single qubit + unitary operations and annotations). + Iterable: initializes by interpreting each item as a Clifford. + Each item can be a single-qubit Clifford gate name (like "SQRT_X") + or stim.GateData instance. + + Examples: + >>> import stim + + >>> stim.CliffordString(5) + stim.CliffordString("I,I,I,I,I") + + >>> stim.CliffordString("X,Y,Z,SQRT_X") + stim.CliffordString("X,Y,Z,SQRT_X") + + >>> stim.CliffordString(["H", stim.gate_data("S")]) + stim.CliffordString("H,S") + + >>> stim.CliffordString(stim.PauliString("XYZ")) + stim.CliffordString("X,Y,Z") + + >>> stim.CliffordString(stim.CliffordString("X,Y,Z")) + stim.CliffordString("X,Y,Z") + + >>> stim.CliffordString(stim.Circuit(''' + ... H 0 1 2 + ... S 2 3 + ... TICK + ... S 3 + ... I 6 + ... ''')) + stim.CliffordString("H,H,C_ZYX,Z,I,I,I") + """ +``` + + +```python +# stim.CliffordString.__ipow__ + +# (in class stim.CliffordString) +def __ipow__( + self, + num_qubits: int, +) -> object: + """Mutates the CliffordString into itself raised to a power. + + Args: + power: The power to raise the CliffordString's Cliffords to. + This value can be negative (e.g. -1 inverts the string). + + Returns: + The mutated Clifford string. + + Examples: + >>> import stim + + >>> p = stim.CliffordString("I,X,H,S,C_XYZ") + >>> p **= 3 + >>> p + stim.CliffordString("I,X,H,S_DAG,I") + + >>> p **= 2 + >>> p + stim.CliffordString("I,I,I,Z,I") + + >>> alias = p + >>> alias **= 2 + >>> p + stim.CliffordString("I,I,I,I,I") + """ +``` + + +```python +# stim.CliffordString.__len__ + +# (in class stim.CliffordString) +def __len__( + self, +) -> int: + """Returns the number of Clifford operations in the string. + + Examples: + >>> import stim + >>> len(stim.CliffordString("I,X,Y,Z,H")) + 5 + """ +``` + + +```python +# stim.CliffordString.__mul__ + +# (in class stim.CliffordString) +def __mul__( + self, + rhs: Union[stim.CliffordString, int], +) -> stim.CliffordString: + """CliffordString multiplication. + + Args: + rhs: Either a stim.CliffordString or an int. If rhs is a + stim.CliffordString, then the Cliffords from each string are multiplied + pairwise. If rhs is an int, it is the number of times to repeat the + Clifford string's contents. + + Examples: + >>> import stim + + >>> stim.CliffordString("S,X,X") * stim.CliffordString("S,Z,H,Z") + stim.CliffordString("Z,Y,SQRT_Y,Z") + + >>> stim.CliffordString("I,X,H") * 3 + stim.CliffordString("I,X,H,I,X,H,I,X,H") + """ +``` + + +```python +# stim.CliffordString.__ne__ + +# (in class stim.CliffordString) +def __ne__( + self, + arg0: stim.CliffordString, +) -> bool: + """Determines if two Clifford strings have non-identical contents. + """ +``` + + +```python +# stim.CliffordString.__pow__ + +# (in class stim.CliffordString) +def __pow__( + self, + power: int, +) -> stim.CliffordString: + """Returns the CliffordString raised to a power. + + Args: + power: The power to raise the CliffordString's Cliffords to. + This value can be negative (e.g. -1 returns the inverse string). + + Returns: + The Clifford string raised to the power. + + Examples: + >>> import stim + + >>> p = stim.CliffordString("I,X,H,S,C_XYZ") + + >>> p**0 + stim.CliffordString("I,I,I,I,I") + + >>> p**1 + stim.CliffordString("I,X,H,S,C_XYZ") + + >>> p**12000001 + stim.CliffordString("I,X,H,S,C_XYZ") + + >>> p**2 + stim.CliffordString("I,I,I,Z,C_ZYX") + + >>> p**3 + stim.CliffordString("I,X,H,S_DAG,I") + + >>> p**-1 + stim.CliffordString("I,X,H,S_DAG,C_ZYX") + """ +``` + + +```python +# stim.CliffordString.__repr__ + +# (in class stim.CliffordString) +def __repr__( + self, +) -> str: + """Returns text that is a valid python expression evaluating to an equivalent `stim.CliffordString`. + """ +``` + + +```python +# stim.CliffordString.__rmul__ + +# (in class stim.CliffordString) +def __rmul__( + self, + lhs: int, +) -> stim.CliffordString: + """CliffordString left-multiplication. + + Args: + lhs: The number of times to repeat the Clifford string's contents. + + Returns: + The repeated Clifford string. + + Examples: + >>> import stim + + >>> 2 * stim.CliffordString("I,X,H") + stim.CliffordString("I,X,H,I,X,H") + + >>> 0 * stim.CliffordString("I,X,H") + stim.CliffordString("") + + >>> 5 * stim.CliffordString("I") + stim.CliffordString("I,I,I,I,I") + """ +``` + + +```python +# stim.CliffordString.__setitem__ + +# (in class stim.CliffordString) +def __setitem__( + self, + index_or_slice: Union[int, slice], + new_value: Union[str, stim.GateData, stim.CliffordString, stim.PauliString, stim.Tableau], +) -> None: + """Overwrites an indexed Clifford, or slice of Cliffords, with the given value. + + Args: + index_or_slice: The index of the Clifford to overwrite, or the slice + of Cliffords to overwrite. + new_value: Specifies the value to write into the Clifford string. This can + be set to a few different types of values: + - str: Name of the single qubit Clifford gate to write to the index or + broadcast over the slice. + - stim.GateData: The single qubit Clifford gate to write to the index + or broadcast over the slice. + - stim.Tableau: Must be a single qubit tableau. Specifies the single + qubit Clifford gate to write to the index or broadcast over the + slice. + - stim.CliffordString: String of Cliffords to write into the slice. + + Examples: + >>> import stim + >>> s = stim.CliffordString("I,I,I,I,I") + + >>> s[1] = 'H' + >>> s + stim.CliffordString("I,H,I,I,I") + + >>> s[2:] = 'SQRT_X' + >>> s + stim.CliffordString("I,H,SQRT_X,SQRT_X,SQRT_X") + + >>> s[0] = stim.gate_data('S_DAG').inverse + >>> s + stim.CliffordString("S,H,SQRT_X,SQRT_X,SQRT_X") + + >>> s[:] = 'I' + >>> s + stim.CliffordString("I,I,I,I,I") + + >>> s[::2] = stim.CliffordString("X,Y,Z") + >>> s + stim.CliffordString("X,I,Y,I,Z") + + >>> s[0] = stim.Tableau.from_named_gate("H") + >>> s + stim.CliffordString("H,I,Y,I,Z") + + >>> s[:] = stim.Tableau.from_named_gate("S") + >>> s + stim.CliffordString("S,S,S,S,S") + + >>> s[:4] = stim.PauliString("IXYZ") + >>> s + stim.CliffordString("I,X,Y,Z,S") + """ +``` + + +```python +# stim.CliffordString.__str__ + +# (in class stim.CliffordString) +def __str__( + self, +) -> str: + """Returns a string representation of the CliffordString's operations. + """ +``` + + +```python +# stim.CliffordString.all_cliffords_string + +# (in class stim.CliffordString) +@staticmethod +def all_cliffords_string( +) -> stim.CliffordString: + """Returns a stim.CliffordString containing each single qubit Clifford once. + + Useful for things like testing that a method works on every single Clifford. + + Examples: + >>> import stim + >>> cliffords = stim.CliffordString.all_cliffords_string() + >>> len(cliffords) + 24 + + >>> print(cliffords[:8]) + I,X,Y,Z,H_XY,S,S_DAG,H_NXY + + >>> print(cliffords[8:16]) + H,SQRT_Y_DAG,H_NXZ,SQRT_Y,H_YZ,H_NYZ,SQRT_X,SQRT_X_DAG + + >>> print(cliffords[16:]) + C_XYZ,C_XYNZ,C_NXYZ,C_XNYZ,C_ZYX,C_ZNYX,C_NZYX,C_ZYNX + """ +``` + + +```python +# stim.CliffordString.copy + +# (in class stim.CliffordString) +def copy( + self, +) -> stim.CliffordString: + """Returns a copy of the CliffordString. + + Returns: + The copy. + + Examples: + >>> import stim + >>> c = stim.CliffordString("H,X") + >>> alias = c + >>> copy = c.copy() + >>> c *= 5 + >>> alias + stim.CliffordString("H,X,H,X,H,X,H,X,H,X") + >>> copy + stim.CliffordString("H,X") + """ +``` + + +```python +# stim.CliffordString.random + +# (in class stim.CliffordString) +@staticmethod +def random( + num_qubits: int, +) -> stim.CliffordString: + """Samples a uniformly random CliffordString. + + Args: + num_qubits: The number of qubits the CliffordString should act upon. + + Examples: + >>> import stim + >>> p = stim.CliffordString.random(5) + >>> len(p) + 5 + + Returns: + The sampled Clifford string. + """ +``` + + +```python +# stim.CliffordString.x_outputs + +# (in class stim.CliffordString) +def x_outputs( + self, + *, + bit_packed_signs: bool = False, +) -> Tuple[stim.PauliString, np.ndarray]: + """Returns what each Clifford in the CliffordString conjugates an X input into. + + For example, H conjugates X into +Z and S_DAG conjugates X into -Y. + + Combined with `z_outputs`, the results of this method completely specify + the single qubit Clifford applied to each qubit. + + Args: + bit_packed_signs: Defaults to False. When False, the sign data is returned + in a numpy array with dtype `np.bool_`. When True, the dtype is instead + `np.uint8` and 8 bits are packed into each byte (in little endian + order). + + Returns: + A (paulis, signs) tuple. + + `paulis` has type stim.PauliString. Its sign is always positive. + + `signs` has type np.ndarray and an argument-dependent shape: + bit_packed_signs=False: + dtype=np.bool_ + shape=(num_qubits,) + bit_packed_signs=True: + dtype=np.uint8 + shape=(math.ceil(num_qubits / 8),) + + Examples: + >>> import stim + >>> x_paulis, x_signs = stim.CliffordString("I,Y,H,S").x_outputs() + >>> x_paulis + stim.PauliString("+XXZY") + >>> x_signs + array([False, True, False, False]) + + >>> stim.CliffordString("I,Y,H,S").x_outputs(bit_packed_signs=True)[1] + array([2], dtype=uint8) + """ +``` + + +```python +# stim.CliffordString.y_outputs + +# (in class stim.CliffordString) +def y_outputs( + self, + *, + bit_packed_signs: bool = False, +) -> Tuple[stim.PauliString, np.ndarray]: + """Returns what each Clifford in the CliffordString conjugates a Y input into. + + For example, H conjugates Y into -Y and S_DAG conjugates Y into +X. + + Args: + bit_packed_signs: Defaults to False. When False, the sign data is returned + in a numpy array with dtype `np.bool_`. When True, the dtype is instead + `np.uint8` and 8 bits are packed into each byte (in little endian + order). + + Returns: + A (paulis, signs) tuple. + + `paulis` has type stim.PauliString. Its sign is always positive. + + `signs` has type np.ndarray and an argument-dependent shape: + bit_packed_signs=False: + dtype=np.bool_ + shape=(num_qubits,) + bit_packed_signs=True: + dtype=np.uint8 + shape=(math.ceil(num_qubits / 8),) + + Examples: + >>> import stim + >>> y_paulis, y_signs = stim.CliffordString("I,X,H,S").y_outputs() + >>> y_paulis + stim.PauliString("+YYYX") + >>> y_signs + array([False, True, True, True]) + + >>> stim.CliffordString("I,X,H,S").y_outputs(bit_packed_signs=True)[1] + array([14], dtype=uint8) + """ +``` + + +```python +# stim.CliffordString.z_outputs + +# (in class stim.CliffordString) +def z_outputs( + self, + *, + bit_packed_signs: bool = False, +) -> Tuple[stim.PauliString, np.ndarray]: + """Returns what each Clifford in the CliffordString conjugates a Z input into. + + For example, H conjugates Z into +X and SQRT_X conjugates Z into -Y. + + Combined with `x_outputs`, the results of this method completely specify + the single qubit Clifford applied to each qubit. + + Args: + bit_packed_signs: Defaults to False. When False, the sign data is returned + in a numpy array with dtype `np.bool_`. When True, the dtype is instead + `np.uint8` and 8 bits are packed into each byte (in little endian + order). + + Returns: + A (paulis, signs) tuple. + + `paulis` has type stim.PauliString. Its sign is always positive. + + `signs` has type np.ndarray and an argument-dependent shape: + bit_packed_signs=False: + dtype=np.bool_ + shape=(num_qubits,) + bit_packed_signs=True: + dtype=np.uint8 + shape=(math.ceil(num_qubits / 8),) + + Examples: + >>> import stim + >>> z_paulis, z_signs = stim.CliffordString("I,Y,H,S").z_outputs() + >>> z_paulis + stim.PauliString("+ZZXZ") + >>> z_signs + array([False, True, False, False]) + + >>> stim.CliffordString("I,Y,H,S").z_outputs(bit_packed_signs=True)[1] + array([2], dtype=uint8) + """ +``` + ```python # stim.CompiledDemSampler @@ -5230,13 +5960,13 @@ def sample_write( shots: int, *, det_out_file: Union[None, str, pathlib.Path], - det_out_format: str = "01", + det_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', obs_out_file: Union[None, str, pathlib.Path], - obs_out_format: str = "01", + obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', err_out_file: Union[None, str, pathlib.Path] = None, - err_out_format: str = "01", + err_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', replay_err_in_file: Union[None, str, pathlib.Path] = None, - replay_err_in_format: str = "01", + replay_err_in_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', ) -> None: """Samples the detector error model and writes the results to disk. @@ -5497,9 +6227,9 @@ def sample_write( shots: int, *, filepath: Union[str, pathlib.Path], - format: 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]' = '01', + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', obs_out_filepath: Optional[Union[str, pathlib.Path]] = None, - obs_out_format: 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]' = '01', + obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', prepend_observables: bool = False, append_observables: bool = False, ) -> None: @@ -5710,8 +6440,8 @@ def sample_write( self, shots: int, *, - filepath: str, - format: str = '01', + filepath: Union[str, pathlib.Path], + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', ) -> None: """Samples measurements from the circuit and writes them to a file. @@ -5838,7 +6568,7 @@ def convert( *, measurements: np.ndarray, sweep_bits: Optional[np.ndarray] = None, - separate_observables: 'Literal[True]', + separate_observables: Literal[True], append_observables: bool = False, bit_packed: bool = False, ) -> Tuple[np.ndarray, np.ndarray]: @@ -5937,15 +6667,15 @@ def convert( def convert_file( self, *, - measurements_filepath: str, - measurements_format: str = '01', - sweep_bits_filepath: str = None, - sweep_bits_format: str = '01', - detection_events_filepath: str, - detection_events_format: str = '01', + measurements_filepath: Union[str, pathlib.Path], + measurements_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + sweep_bits_filepath: Optional[Union[str, pathlib.Path]] = None, + sweep_bits_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + detection_events_filepath: Union[str, pathlib.Path], + detection_events_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', append_observables: bool = False, - obs_out_filepath: str = None, - obs_out_format: str = '01', + obs_out_filepath: Optional[Union[str, pathlib.Path]] = None, + obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', ) -> None: """Reads measurement data from a file and writes detection events to another file. @@ -7425,7 +8155,7 @@ def copy( # (in class stim.DetectorErrorModel) def diagram( self, - type: str, + type: Literal["matchgraph-svg", "matchgraph-svg-html", "matchgraph-3d", "matchgraph-3d-html"] = 'matchgraph-svg', ) -> Any: """Returns a diagram of the circuit, from a variety of options. @@ -9299,7 +10029,7 @@ def __init__( the string "X_ -> ZZ xor rec[-1]" will result in a flow with input pauli string "X_", output pauli string "ZZ", and measurement indices [-1]. - Arguments: + Args: arg [position-only]: Defaults to None. Must be specified by itself if used. str: Initializes a flow by parsing the given shorthand text. stim.Flow: Initializes a copy of the given flow. @@ -11064,7 +11794,7 @@ def __imul__( # (in class stim.PauliString) def __init__( self, - arg: Union[None, int, str, stim.PauliString, Iterable[Union[int, 'Literal["_", "I", "X", "Y", "Z"]']]] = None, + arg: Union[None, int, str, stim.PauliString, Iterable[Union[int, Literal["_", "I", "X", "Y", "Z"]]]] = None, /, ) -> None: """Initializes a stim.PauliString from the given argument. @@ -11077,7 +11807,7 @@ def __init__( pauli string is a series of integers seperated by '*' and prefixed by 'I', 'X', 'Y', or 'Z'. - Arguments: + Args: arg [position-only]: This can be a variety of types, including: None (default): initializes an empty Pauli string. int: initializes an identity Pauli string of the given length. @@ -11086,6 +11816,14 @@ def __init__( Iterable: initializes by interpreting each item as a Pauli. Each item can be a single-qubit Pauli string (like "X"), or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. + Dict[int, Union[int, str]]: initializes by interpreting keys as + the qubit index and values as the Pauli for that index. + Each value can be a single-qubit Pauli string (like "X"), + or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. + Dict[Union[int, str], Iterable[int]]: initializes by interpreting keys + as Pauli operators and values as the qubit indices for that Pauli. + Each key can be a single-qubit Pauli string (like "X"), + or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. Examples: >>> import stim @@ -11113,6 +11851,15 @@ def __init__( >>> stim.PauliString("X6*Y6") stim.PauliString("+i______Z") + + >>> stim.PauliString({0: "X", 2: "Y", 3: "X"}) + stim.PauliString("+X_YX") + + >>> stim.PauliString({0: "X", 2: 2, 3: 1}) + stim.PauliString("+X_YX") + + >>> stim.PauliString({"X": [1], 2: [4], "Z": [0, 3]}) + stim.PauliString("+ZX_ZY") """ ``` @@ -11667,7 +12414,7 @@ def from_numpy( def from_unitary_matrix( matrix: Iterable[Iterable[Union[int, float, complex]]], *, - endian: str = 'little', + endian: Literal["little", "big"] = 'little', unsigned: bool = False, ) -> stim.PauliString: """Creates a stim.PauliString from the unitary matrix of a Pauli group member. @@ -12006,7 +12753,7 @@ def to_tableau( def to_unitary_matrix( self, *, - endian: str, + endian: Literal["little", "big"], ) -> np.ndarray[np.complex64]: """Converts the pauli string into a unitary matrix. @@ -12809,7 +13556,7 @@ def from_stabilizers( def from_state_vector( state_vector: Iterable[float], *, - endian: str, + endian: Literal["little", "big"], ) -> stim.Tableau: """Creates a tableau representing the stabilizer state of the given state vector. @@ -12877,7 +13624,7 @@ def from_state_vector( def from_unitary_matrix( matrix: Iterable[Iterable[float]], *, - endian: str = 'little', + endian: Literal["little", "big"] = 'little', ) -> stim.Tableau: """Creates a tableau from the unitary matrix of a Clifford operation. @@ -13390,7 +14137,7 @@ def then( # (in class stim.Tableau) def to_circuit( self, - method: 'Literal["elimination", "graph_state"]' = 'elimination', + method: Literal["elimination", "graph_state"] = 'elimination', ) -> stim.Circuit: """Synthesizes a circuit that implements the tableau's Clifford operation. @@ -13785,7 +14532,7 @@ def to_stabilizers( def to_state_vector( self, *, - endian: str = 'little', + endian: Literal["little", "big"] = 'little', ) -> np.ndarray[np.complex64]: """Returns the state vector produced by applying the tableau to the |0..0> state. @@ -13844,7 +14591,7 @@ def to_state_vector( def to_unitary_matrix( self, *, - endian: str, + endian: Literal["little", "big"], ) -> np.ndarray[np.complex64]: """Converts the tableau into a unitary matrix. @@ -15986,7 +16733,7 @@ def set_state_from_state_vector( self, state_vector: Iterable[float], *, - endian: str, + endian: Literal["little", "big"], ) -> None: """Sets the simulator's state to a superposition specified by an amplitude vector. @@ -16167,7 +16914,7 @@ def sqrt_y_dag( def state_vector( self, *, - endian: str = 'little', + endian: Literal["little", "big"] = 'little', ) -> np.ndarray[np.complex64]: """Returns a wavefunction for the simulator's current state. @@ -16795,7 +17542,7 @@ def main( def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, @@ -16806,18 +17553,18 @@ def read_shot_data_file( def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, num_observables: int = 0, - separate_observables: 'Literal[True]', + separate_observables: Literal[True], ) -> Tuple[np.ndarray, np.ndarray]: pass def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, @@ -17279,7 +18026,7 @@ def write_shot_data_file( *, data: np.ndarray, path: Union[str, pathlib.Path], - format: str, + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], num_measurements: int = 0, num_detectors: int = 0, num_observables: int = 0, diff --git a/doc/sinter_api.md b/doc/sinter_api.md index 5cb64f821..ac7e10006 100644 --- a/doc/sinter_api.md +++ b/doc/sinter_api.md @@ -42,6 +42,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi - [`sinter.iter_collect`](#sinter.iter_collect) - [`sinter.log_binomial`](#sinter.log_binomial) - [`sinter.log_factorial`](#sinter.log_factorial) +- [`sinter.plot_custom`](#sinter.plot_custom) - [`sinter.plot_discard_rate`](#sinter.plot_discard_rate) - [`sinter.plot_error_rate`](#sinter.plot_error_rate) - [`sinter.post_selection_mask_from_4th_coord`](#sinter.post_selection_mask_from_4th_coord) @@ -1452,6 +1453,70 @@ def log_factorial( """ ``` + +```python +# sinter.plot_custom + +# (at top-level in the sinter module) +def plot_custom( + *, + ax: 'plt.Axes', + stats: 'Iterable[sinter.TaskStats]', + x_func: Callable[[sinter.TaskStats], Any], + y_func: Callable[[sinter.TaskStats], Union[sinter.Fit, float, int]], + group_func: Callable[[sinter.TaskStats], ~TCurveId] = lambda _: None, + point_label_func: Callable[[sinter.TaskStats], Any] = lambda _: None, + filter_func: Callable[[sinter.TaskStats], Any] = lambda _: True, + plot_args_func: Callable[[int, ~TCurveId, List[sinter.TaskStats]], Dict[str, Any]] = lambda index, group_key, group_stats: dict(), + line_fits: Optional[Tuple[Literal['linear', 'log', 'sqrt'], Literal['linear', 'log', 'sqrt']]] = None, +) -> None: + """Plots error rates in curves with uncertainty highlights. + + Args: + ax: The plt.Axes to plot onto. For example, the `ax` value from `fig, ax = plt.subplots(1, 1)`. + stats: The collected statistics to plot. + x_func: The X coordinate to use for each stat's data point. For example, this could be + `x_func=lambda stat: stat.json_metadata['physical_error_rate']`. + y_func: The Y value to use for each stat's data point. This can be a float or it can be a + sinter.Fit value, in which case the curve will follow the fit.best value and a + highlighted area will be shown from fit.low to fit.high. + group_func: Optional. When specified, multiple curves will be plotted instead of one curve. + The statistics are grouped into curves based on whether or not they get the same result + out of this function. For example, this could be `group_func=lambda stat: stat.decoder`. + If the result of the function is a dictionary, then optional keys in the dictionary will + also control the plotting of each curve. Available keys are: + 'label': the label added to the legend for the curve + 'color': the color used for plotting the curve + 'marker': the marker used for the curve + 'linestyle': the linestyle used for the curve + 'sort': the order in which the curves will be plotted and added to the legend + e.g. if two curves (with different resulting dictionaries from group_func) share the same + value for key 'marker', they will be plotted with the same marker. + Colors, markers and linestyles are assigned in order, sorted by the values for those keys. + point_label_func: Optional. Specifies text to draw next to data points. + filter_func: Optional. When specified, some curves will not be plotted. + The statistics are filtered and only plotted if filter_func(stat) returns True. + For example, `filter_func=lambda s: s.json_metadata['basis'] == 'x'` would plot only stats + where the saved metadata indicates the basis was 'x'. + plot_args_func: Optional. Specifies additional arguments to give the underlying calls to + `plot` and `fill_between` used to do the actual plotting. For example, this can be used + to specify markers and colors. Takes the index of the curve in sorted order and also a + curve_id (these will be 0 and None respectively if group_func is not specified). For example, + this could be: + + plot_args_func=lambda index, group_key, group_stats: { + 'color': ( + 'red' + if group_key == 'decoder=pymatching p=0.001' + else 'blue' + ), + } + line_fits: Defaults to None. Set this to a tuple (x_scale, y_scale) to include a dashed line + fit to every curve. The scales determine how to transform the coordinates before + performing the fit, and can be set to 'linear', 'sqrt', or 'log'. + """ +``` + ```python # sinter.plot_discard_rate diff --git a/doc/stim.pyi b/doc/stim.pyi index 6444b2cab..d71851394 100644 --- a/doc/stim.pyi +++ b/doc/stim.pyi @@ -1049,7 +1049,7 @@ class Circuit: """ def diagram( self, - type: str = 'timeline-text', + type: Literal["timeline-text", "timeline-svg", "timeline-svg-html", "timeline-3d", "timeline-3d-html", "detslice-text", "detslice-svg", "detslice-svg-html", "matchgraph-svg", "matchgraph-svg-html", "matchgraph-3d", "matchgraph-3d-html", "timeslice-svg", "timeslice-svg-html", "detslice-with-ops-svg", "detslice-with-ops-svg-html", "interactive", "interactive-html"] = 'timeline-text', *, tick: Union[None, int, range] = None, filter_coords: Iterable[Union[Iterable[float], stim.DemTarget]] = ((),), @@ -3889,6 +3889,560 @@ class CircuitTargetsInsideInstruction: >>> loc.instruction_targets.targets_in_range [stim.GateTargetWithCoords(0, [])] """ +class CliffordString: + """A tensor product of single qubit Clifford gates (e.g. "H \u2297 X \u2297 S"). + + Represents a collection of Clifford operations applied pairwise to a + collection of qubits. Ignores global phase. + + Examples: + >>> import stim + >>> stim.CliffordString("H,S,C_XYZ") * stim.CliffordString("H,H,H") + stim.CliffordString("I,C_ZYX,SQRT_X_DAG") + """ + def __add__( + self, + rhs: stim.CliffordString, + ) -> stim.CliffordString: + """Concatenates two CliffordStrings. + + Args: + rhs: The suffix of the concatenation. + + Returns: + The concatenated Clifford string. + + Examples: + >>> import stim + >>> stim.CliffordString("I,X,H") + stim.CliffordString("Y,S") + stim.CliffordString("I,X,H,Y,S") + """ + def __eq__( + self, + arg0: stim.CliffordString, + ) -> bool: + """Determines if two Clifford strings have identical contents. + """ + @overload + def __getitem__( + self, + index_or_slice: int, + ) -> stim.GateData: + pass + @overload + def __getitem__( + self, + index_or_slice: slice, + ) -> stim.CliffordString: + pass + def __getitem__( + self, + index_or_slice: Union[int, slice], + ) -> Union[stim.GateData, stim.CliffordString]: + """Returns a Clifford or substring from the CliffordString. + + Args: + index_or_slice: The index of the Clifford to return, or the slice + corresponding to the sub CliffordString to return. + + Returns: + The indexed Clifford (as a stim.GateData instance) or the sliced + CliffordString. + + Examples: + >>> import stim + >>> s = stim.CliffordString("I,X,Y,Z,H") + + >>> s[2] + stim.gate_data('Y') + + >>> s[-1] + stim.gate_data('H') + + >>> s[:-1] + stim.CliffordString("I,X,Y,Z") + + >>> s[::2] + stim.CliffordString("I,Y,H") + """ + def __iadd__( + self, + rhs: stim.CliffordString, + ) -> stim.CliffordString: + """Mutates the CliffordString by concatenating onto it. + + Args: + rhs: The suffix to concatenate onto the target CliffordString. + + Returns: + The mutated Clifford string. + + Examples: + >>> import stim + >>> c = stim.CliffordString("I,X,H") + >>> alias = c + >>> alias += stim.CliffordString("Y,S") + >>> c + stim.CliffordString("I,X,H,Y,S") + """ + def __imul__( + self, + rhs: Union[stim.CliffordString, int], + ) -> stim.CliffordString: + """Inplace CliffordString multiplication. + + Mutates the CliffordString into itself multiplied by another CliffordString + (via pairwise Clifford multipliation) or by an integer (via repeating the + contents). + + Args: + rhs: Either a stim.CliffordString or an int. If rhs is a + stim.CliffordString, then the Cliffords from each string are multiplied + pairwise. If rhs is an int, it is the number of times to repeat the + Clifford string's contents. + + Returns: + The mutated Clifford string. + + Examples: + >>> import stim + + >>> c = stim.CliffordString("S,X,X") + >>> alias = c + >>> alias *= stim.CliffordString("S,Z,H,Z") + >>> c + stim.CliffordString("Z,Y,SQRT_Y,Z") + + >>> c = stim.CliffordString("I,X,H") + >>> alias = c + >>> alias *= 2 + >>> c + stim.CliffordString("I,X,H,I,X,H") + """ + def __init__( + self, + arg: Union[int, str, stim.CliffordString, stim.PauliString, stim.Circuit], + /, + ) -> None: + """Initializes a stim.CliffordString from the given argument. + + Args: + arg [position-only]: This can be a variety of types, including: + int: initializes an identity Clifford string of the given length. + str: initializes by parsing a comma-separated list of gate names. + stim.CliffordString: initializes by copying the given Clifford string. + stim.PauliString: initializes by copying from the given Pauli string + (ignores the sign of the Pauli string). + stim.Circuit: initializes a CliffordString equivalent to the action + of the circuit (as long as the circuit only contains single qubit + unitary operations and annotations). + Iterable: initializes by interpreting each item as a Clifford. + Each item can be a single-qubit Clifford gate name (like "SQRT_X") + or stim.GateData instance. + + Examples: + >>> import stim + + >>> stim.CliffordString(5) + stim.CliffordString("I,I,I,I,I") + + >>> stim.CliffordString("X,Y,Z,SQRT_X") + stim.CliffordString("X,Y,Z,SQRT_X") + + >>> stim.CliffordString(["H", stim.gate_data("S")]) + stim.CliffordString("H,S") + + >>> stim.CliffordString(stim.PauliString("XYZ")) + stim.CliffordString("X,Y,Z") + + >>> stim.CliffordString(stim.CliffordString("X,Y,Z")) + stim.CliffordString("X,Y,Z") + + >>> stim.CliffordString(stim.Circuit(''' + ... H 0 1 2 + ... S 2 3 + ... TICK + ... S 3 + ... I 6 + ... ''')) + stim.CliffordString("H,H,C_ZYX,Z,I,I,I") + """ + def __ipow__( + self, + num_qubits: int, + ) -> object: + """Mutates the CliffordString into itself raised to a power. + + Args: + power: The power to raise the CliffordString's Cliffords to. + This value can be negative (e.g. -1 inverts the string). + + Returns: + The mutated Clifford string. + + Examples: + >>> import stim + + >>> p = stim.CliffordString("I,X,H,S,C_XYZ") + >>> p **= 3 + >>> p + stim.CliffordString("I,X,H,S_DAG,I") + + >>> p **= 2 + >>> p + stim.CliffordString("I,I,I,Z,I") + + >>> alias = p + >>> alias **= 2 + >>> p + stim.CliffordString("I,I,I,I,I") + """ + def __len__( + self, + ) -> int: + """Returns the number of Clifford operations in the string. + + Examples: + >>> import stim + >>> len(stim.CliffordString("I,X,Y,Z,H")) + 5 + """ + def __mul__( + self, + rhs: Union[stim.CliffordString, int], + ) -> stim.CliffordString: + """CliffordString multiplication. + + Args: + rhs: Either a stim.CliffordString or an int. If rhs is a + stim.CliffordString, then the Cliffords from each string are multiplied + pairwise. If rhs is an int, it is the number of times to repeat the + Clifford string's contents. + + Examples: + >>> import stim + + >>> stim.CliffordString("S,X,X") * stim.CliffordString("S,Z,H,Z") + stim.CliffordString("Z,Y,SQRT_Y,Z") + + >>> stim.CliffordString("I,X,H") * 3 + stim.CliffordString("I,X,H,I,X,H,I,X,H") + """ + def __ne__( + self, + arg0: stim.CliffordString, + ) -> bool: + """Determines if two Clifford strings have non-identical contents. + """ + def __pow__( + self, + power: int, + ) -> stim.CliffordString: + """Returns the CliffordString raised to a power. + + Args: + power: The power to raise the CliffordString's Cliffords to. + This value can be negative (e.g. -1 returns the inverse string). + + Returns: + The Clifford string raised to the power. + + Examples: + >>> import stim + + >>> p = stim.CliffordString("I,X,H,S,C_XYZ") + + >>> p**0 + stim.CliffordString("I,I,I,I,I") + + >>> p**1 + stim.CliffordString("I,X,H,S,C_XYZ") + + >>> p**12000001 + stim.CliffordString("I,X,H,S,C_XYZ") + + >>> p**2 + stim.CliffordString("I,I,I,Z,C_ZYX") + + >>> p**3 + stim.CliffordString("I,X,H,S_DAG,I") + + >>> p**-1 + stim.CliffordString("I,X,H,S_DAG,C_ZYX") + """ + def __repr__( + self, + ) -> str: + """Returns text that is a valid python expression evaluating to an equivalent `stim.CliffordString`. + """ + def __rmul__( + self, + lhs: int, + ) -> stim.CliffordString: + """CliffordString left-multiplication. + + Args: + lhs: The number of times to repeat the Clifford string's contents. + + Returns: + The repeated Clifford string. + + Examples: + >>> import stim + + >>> 2 * stim.CliffordString("I,X,H") + stim.CliffordString("I,X,H,I,X,H") + + >>> 0 * stim.CliffordString("I,X,H") + stim.CliffordString("") + + >>> 5 * stim.CliffordString("I") + stim.CliffordString("I,I,I,I,I") + """ + def __setitem__( + self, + index_or_slice: Union[int, slice], + new_value: Union[str, stim.GateData, stim.CliffordString, stim.PauliString, stim.Tableau], + ) -> None: + """Overwrites an indexed Clifford, or slice of Cliffords, with the given value. + + Args: + index_or_slice: The index of the Clifford to overwrite, or the slice + of Cliffords to overwrite. + new_value: Specifies the value to write into the Clifford string. This can + be set to a few different types of values: + - str: Name of the single qubit Clifford gate to write to the index or + broadcast over the slice. + - stim.GateData: The single qubit Clifford gate to write to the index + or broadcast over the slice. + - stim.Tableau: Must be a single qubit tableau. Specifies the single + qubit Clifford gate to write to the index or broadcast over the + slice. + - stim.CliffordString: String of Cliffords to write into the slice. + + Examples: + >>> import stim + >>> s = stim.CliffordString("I,I,I,I,I") + + >>> s[1] = 'H' + >>> s + stim.CliffordString("I,H,I,I,I") + + >>> s[2:] = 'SQRT_X' + >>> s + stim.CliffordString("I,H,SQRT_X,SQRT_X,SQRT_X") + + >>> s[0] = stim.gate_data('S_DAG').inverse + >>> s + stim.CliffordString("S,H,SQRT_X,SQRT_X,SQRT_X") + + >>> s[:] = 'I' + >>> s + stim.CliffordString("I,I,I,I,I") + + >>> s[::2] = stim.CliffordString("X,Y,Z") + >>> s + stim.CliffordString("X,I,Y,I,Z") + + >>> s[0] = stim.Tableau.from_named_gate("H") + >>> s + stim.CliffordString("H,I,Y,I,Z") + + >>> s[:] = stim.Tableau.from_named_gate("S") + >>> s + stim.CliffordString("S,S,S,S,S") + + >>> s[:4] = stim.PauliString("IXYZ") + >>> s + stim.CliffordString("I,X,Y,Z,S") + """ + def __str__( + self, + ) -> str: + """Returns a string representation of the CliffordString's operations. + """ + @staticmethod + def all_cliffords_string( + ) -> stim.CliffordString: + """Returns a stim.CliffordString containing each single qubit Clifford once. + + Useful for things like testing that a method works on every single Clifford. + + Examples: + >>> import stim + >>> cliffords = stim.CliffordString.all_cliffords_string() + >>> len(cliffords) + 24 + + >>> print(cliffords[:8]) + I,X,Y,Z,H_XY,S,S_DAG,H_NXY + + >>> print(cliffords[8:16]) + H,SQRT_Y_DAG,H_NXZ,SQRT_Y,H_YZ,H_NYZ,SQRT_X,SQRT_X_DAG + + >>> print(cliffords[16:]) + C_XYZ,C_XYNZ,C_NXYZ,C_XNYZ,C_ZYX,C_ZNYX,C_NZYX,C_ZYNX + """ + def copy( + self, + ) -> stim.CliffordString: + """Returns a copy of the CliffordString. + + Returns: + The copy. + + Examples: + >>> import stim + >>> c = stim.CliffordString("H,X") + >>> alias = c + >>> copy = c.copy() + >>> c *= 5 + >>> alias + stim.CliffordString("H,X,H,X,H,X,H,X,H,X") + >>> copy + stim.CliffordString("H,X") + """ + @staticmethod + def random( + num_qubits: int, + ) -> stim.CliffordString: + """Samples a uniformly random CliffordString. + + Args: + num_qubits: The number of qubits the CliffordString should act upon. + + Examples: + >>> import stim + >>> p = stim.CliffordString.random(5) + >>> len(p) + 5 + + Returns: + The sampled Clifford string. + """ + def x_outputs( + self, + *, + bit_packed_signs: bool = False, + ) -> Tuple[stim.PauliString, np.ndarray]: + """Returns what each Clifford in the CliffordString conjugates an X input into. + + For example, H conjugates X into +Z and S_DAG conjugates X into -Y. + + Combined with `z_outputs`, the results of this method completely specify + the single qubit Clifford applied to each qubit. + + Args: + bit_packed_signs: Defaults to False. When False, the sign data is returned + in a numpy array with dtype `np.bool_`. When True, the dtype is instead + `np.uint8` and 8 bits are packed into each byte (in little endian + order). + + Returns: + A (paulis, signs) tuple. + + `paulis` has type stim.PauliString. Its sign is always positive. + + `signs` has type np.ndarray and an argument-dependent shape: + bit_packed_signs=False: + dtype=np.bool_ + shape=(num_qubits,) + bit_packed_signs=True: + dtype=np.uint8 + shape=(math.ceil(num_qubits / 8),) + + Examples: + >>> import stim + >>> x_paulis, x_signs = stim.CliffordString("I,Y,H,S").x_outputs() + >>> x_paulis + stim.PauliString("+XXZY") + >>> x_signs + array([False, True, False, False]) + + >>> stim.CliffordString("I,Y,H,S").x_outputs(bit_packed_signs=True)[1] + array([2], dtype=uint8) + """ + def y_outputs( + self, + *, + bit_packed_signs: bool = False, + ) -> Tuple[stim.PauliString, np.ndarray]: + """Returns what each Clifford in the CliffordString conjugates a Y input into. + + For example, H conjugates Y into -Y and S_DAG conjugates Y into +X. + + Args: + bit_packed_signs: Defaults to False. When False, the sign data is returned + in a numpy array with dtype `np.bool_`. When True, the dtype is instead + `np.uint8` and 8 bits are packed into each byte (in little endian + order). + + Returns: + A (paulis, signs) tuple. + + `paulis` has type stim.PauliString. Its sign is always positive. + + `signs` has type np.ndarray and an argument-dependent shape: + bit_packed_signs=False: + dtype=np.bool_ + shape=(num_qubits,) + bit_packed_signs=True: + dtype=np.uint8 + shape=(math.ceil(num_qubits / 8),) + + Examples: + >>> import stim + >>> y_paulis, y_signs = stim.CliffordString("I,X,H,S").y_outputs() + >>> y_paulis + stim.PauliString("+YYYX") + >>> y_signs + array([False, True, True, True]) + + >>> stim.CliffordString("I,X,H,S").y_outputs(bit_packed_signs=True)[1] + array([14], dtype=uint8) + """ + def z_outputs( + self, + *, + bit_packed_signs: bool = False, + ) -> Tuple[stim.PauliString, np.ndarray]: + """Returns what each Clifford in the CliffordString conjugates a Z input into. + + For example, H conjugates Z into +X and SQRT_X conjugates Z into -Y. + + Combined with `x_outputs`, the results of this method completely specify + the single qubit Clifford applied to each qubit. + + Args: + bit_packed_signs: Defaults to False. When False, the sign data is returned + in a numpy array with dtype `np.bool_`. When True, the dtype is instead + `np.uint8` and 8 bits are packed into each byte (in little endian + order). + + Returns: + A (paulis, signs) tuple. + + `paulis` has type stim.PauliString. Its sign is always positive. + + `signs` has type np.ndarray and an argument-dependent shape: + bit_packed_signs=False: + dtype=np.bool_ + shape=(num_qubits,) + bit_packed_signs=True: + dtype=np.uint8 + shape=(math.ceil(num_qubits / 8),) + + Examples: + >>> import stim + >>> z_paulis, z_signs = stim.CliffordString("I,Y,H,S").z_outputs() + >>> z_paulis + stim.PauliString("+ZZXZ") + >>> z_signs + array([False, True, False, False]) + + >>> stim.CliffordString("I,Y,H,S").z_outputs(bit_packed_signs=True)[1] + array([2], dtype=uint8) + """ class CompiledDemSampler: """A helper class for efficiently sampler from a detector error model. @@ -4073,13 +4627,13 @@ class CompiledDemSampler: shots: int, *, det_out_file: Union[None, str, pathlib.Path], - det_out_format: str = "01", + det_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', obs_out_file: Union[None, str, pathlib.Path], - obs_out_format: str = "01", + obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', err_out_file: Union[None, str, pathlib.Path] = None, - err_out_format: str = "01", + err_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', replay_err_in_file: Union[None, str, pathlib.Path] = None, - replay_err_in_format: str = "01", + replay_err_in_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', ) -> None: """Samples the detector error model and writes the results to disk. @@ -4333,9 +4887,9 @@ class CompiledDetectorSampler: shots: int, *, filepath: Union[str, pathlib.Path], - format: 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]' = '01', + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', obs_out_filepath: Optional[Union[str, pathlib.Path]] = None, - obs_out_format: 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]' = '01', + obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', prepend_observables: bool = False, append_observables: bool = False, ) -> None: @@ -4539,8 +5093,8 @@ class CompiledMeasurementSampler: self, shots: int, *, - filepath: str, - format: str = '01', + filepath: Union[str, pathlib.Path], + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', ) -> None: """Samples measurements from the circuit and writes them to a file. @@ -4639,7 +5193,7 @@ class CompiledMeasurementsToDetectionEventsConverter: *, measurements: np.ndarray, sweep_bits: Optional[np.ndarray] = None, - separate_observables: 'Literal[True]', + separate_observables: Literal[True], append_observables: bool = False, bit_packed: bool = False, ) -> Tuple[np.ndarray, np.ndarray]: @@ -4731,15 +5285,15 @@ class CompiledMeasurementsToDetectionEventsConverter: def convert_file( self, *, - measurements_filepath: str, - measurements_format: str = '01', - sweep_bits_filepath: str = None, - sweep_bits_format: str = '01', - detection_events_filepath: str, - detection_events_format: str = '01', + measurements_filepath: Union[str, pathlib.Path], + measurements_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + sweep_bits_filepath: Optional[Union[str, pathlib.Path]] = None, + sweep_bits_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + detection_events_filepath: Union[str, pathlib.Path], + detection_events_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', append_observables: bool = False, - obs_out_filepath: str = None, - obs_out_format: str = '01', + obs_out_filepath: Optional[Union[str, pathlib.Path]] = None, + obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', ) -> None: """Reads measurement data from a file and writes detection events to another file. @@ -5834,7 +6388,7 @@ class DetectorErrorModel: """ def diagram( self, - type: str, + type: Literal["matchgraph-svg", "matchgraph-svg-html", "matchgraph-3d", "matchgraph-3d-html"] = 'matchgraph-svg', ) -> Any: """Returns a diagram of the circuit, from a variety of options. @@ -7428,7 +7982,7 @@ class Flow: the string "X_ -> ZZ xor rec[-1]" will result in a flow with input pauli string "X_", output pauli string "ZZ", and measurement indices [-1]. - Arguments: + Args: arg [position-only]: Defaults to None. Must be specified by itself if used. str: Initializes a flow by parsing the given shorthand text. stim.Flow: Initializes a copy of the given flow. @@ -8780,7 +9334,7 @@ class PauliString: """ def __init__( self, - arg: Union[None, int, str, stim.PauliString, Iterable[Union[int, 'Literal["_", "I", "X", "Y", "Z"]']]] = None, + arg: Union[None, int, str, stim.PauliString, Iterable[Union[int, Literal["_", "I", "X", "Y", "Z"]]]] = None, /, ) -> None: """Initializes a stim.PauliString from the given argument. @@ -8793,7 +9347,7 @@ class PauliString: pauli string is a series of integers seperated by '*' and prefixed by 'I', 'X', 'Y', or 'Z'. - Arguments: + Args: arg [position-only]: This can be a variety of types, including: None (default): initializes an empty Pauli string. int: initializes an identity Pauli string of the given length. @@ -8802,6 +9356,14 @@ class PauliString: Iterable: initializes by interpreting each item as a Pauli. Each item can be a single-qubit Pauli string (like "X"), or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. + Dict[int, Union[int, str]]: initializes by interpreting keys as + the qubit index and values as the Pauli for that index. + Each value can be a single-qubit Pauli string (like "X"), + or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. + Dict[Union[int, str], Iterable[int]]: initializes by interpreting keys + as Pauli operators and values as the qubit indices for that Pauli. + Each key can be a single-qubit Pauli string (like "X"), + or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. Examples: >>> import stim @@ -8829,6 +9391,15 @@ class PauliString: >>> stim.PauliString("X6*Y6") stim.PauliString("+i______Z") + + >>> stim.PauliString({0: "X", 2: "Y", 3: "X"}) + stim.PauliString("+X_YX") + + >>> stim.PauliString({0: "X", 2: 2, 3: 1}) + stim.PauliString("+X_YX") + + >>> stim.PauliString({"X": [1], 2: [4], "Z": [0, 3]}) + stim.PauliString("+ZX_ZY") """ def __itruediv__( self, @@ -9270,7 +9841,7 @@ class PauliString: def from_unitary_matrix( matrix: Iterable[Iterable[Union[int, float, complex]]], *, - endian: str = 'little', + endian: Literal["little", "big"] = 'little', unsigned: bool = False, ) -> stim.PauliString: """Creates a stim.PauliString from the unitary matrix of a Pauli group member. @@ -9560,7 +10131,7 @@ class PauliString: def to_unitary_matrix( self, *, - endian: str, + endian: Literal["little", "big"], ) -> np.ndarray[np.complex64]: """Converts the pauli string into a unitary matrix. @@ -10195,7 +10766,7 @@ class Tableau: def from_state_vector( state_vector: Iterable[float], *, - endian: str, + endian: Literal["little", "big"], ) -> stim.Tableau: """Creates a tableau representing the stabilizer state of the given state vector. @@ -10256,7 +10827,7 @@ class Tableau: def from_unitary_matrix( matrix: Iterable[Iterable[float]], *, - endian: str = 'little', + endian: Literal["little", "big"] = 'little', ) -> stim.Tableau: """Creates a tableau from the unitary matrix of a Clifford operation. @@ -10685,7 +11256,7 @@ class Tableau: """ def to_circuit( self, - method: 'Literal["elimination", "graph_state"]' = 'elimination', + method: Literal["elimination", "graph_state"] = 'elimination', ) -> stim.Circuit: """Synthesizes a circuit that implements the tableau's Clifford operation. @@ -11052,7 +11623,7 @@ class Tableau: def to_state_vector( self, *, - endian: str = 'little', + endian: Literal["little", "big"] = 'little', ) -> np.ndarray[np.complex64]: """Returns the state vector produced by applying the tableau to the |0..0> state. @@ -11104,7 +11675,7 @@ class Tableau: def to_unitary_matrix( self, *, - endian: str, + endian: Literal["little", "big"], ) -> np.ndarray[np.complex64]: """Converts the tableau into a unitary matrix. @@ -12826,7 +13397,7 @@ class TableauSimulator: self, state_vector: Iterable[float], *, - endian: str, + endian: Literal["little", "big"], ) -> None: """Sets the simulator's state to a superposition specified by an amplitude vector. @@ -12972,7 +13543,7 @@ class TableauSimulator: def state_vector( self, *, - endian: str = 'little', + endian: Literal["little", "big"] = 'little', ) -> np.ndarray[np.complex64]: """Returns a wavefunction for the simulator's current state. @@ -13467,7 +14038,7 @@ def main( def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, @@ -13478,18 +14049,18 @@ def read_shot_data_file( def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, num_observables: int = 0, - separate_observables: 'Literal[True]', + separate_observables: Literal[True], ) -> Tuple[np.ndarray, np.ndarray]: pass def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, @@ -13860,7 +14431,7 @@ def write_shot_data_file( *, data: np.ndarray, path: Union[str, pathlib.Path], - format: str, + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], num_measurements: int = 0, num_detectors: int = 0, num_observables: int = 0, diff --git a/doc/usage_command_line.md b/doc/usage_command_line.md index b5f107f60..2e5418e32 100644 --- a/doc/usage_command_line.md +++ b/doc/usage_command_line.md @@ -1676,6 +1676,7 @@ SYNOPSIS [--out_format 01|b8|r8|ptb64|hits|dets] \ [--seed int] \ [--shots int] \ + [--skip_loop_folding] \ [--skip_reference_sample] DESCRIPTION @@ -1762,6 +1763,30 @@ OPTIONS Must be an integer between 0 and a quintillion (10^18). + --skip_loop_folding + Skips loop folding logic on the reference sample calculation. + + When this argument is specified, the reference sample (that is used + to convert measurement flip data from frame simulations into actual + measurement data) is generated by iterating through the entire + flattened circuit with no loop detection. + + Loop folding can enormously improve performance for circuits + containing REPEAT blocks with large repeat counts, by detecting + periodicity in loops and fast-forwarding across them when computing + the reference sample for the circuit. However, in some cases the + analysis is not able to detect the periodicity that is present. For + example, this has been observed in honeycomb code circuits. When + this happens, the folding-capable analysis is slower than simply + analyzing the flattened circuit without any specialized loop logic. + The `--skip_loop_folding` flag can be used to just analyze the + flattened circuit, bypassing this slowdown for circuits such as + honeycomb code circuits. + + By default, loop detection is enabled. Pass this flag to disable + it (when appropriate by use case). + + --skip_reference_sample Asserts the circuit can produce a noiseless sample that is just 0s. diff --git a/glue/python/src/stim/__init__.pyi b/glue/python/src/stim/__init__.pyi index 6444b2cab..d71851394 100644 --- a/glue/python/src/stim/__init__.pyi +++ b/glue/python/src/stim/__init__.pyi @@ -1049,7 +1049,7 @@ class Circuit: """ def diagram( self, - type: str = 'timeline-text', + type: Literal["timeline-text", "timeline-svg", "timeline-svg-html", "timeline-3d", "timeline-3d-html", "detslice-text", "detslice-svg", "detslice-svg-html", "matchgraph-svg", "matchgraph-svg-html", "matchgraph-3d", "matchgraph-3d-html", "timeslice-svg", "timeslice-svg-html", "detslice-with-ops-svg", "detslice-with-ops-svg-html", "interactive", "interactive-html"] = 'timeline-text', *, tick: Union[None, int, range] = None, filter_coords: Iterable[Union[Iterable[float], stim.DemTarget]] = ((),), @@ -3889,6 +3889,560 @@ class CircuitTargetsInsideInstruction: >>> loc.instruction_targets.targets_in_range [stim.GateTargetWithCoords(0, [])] """ +class CliffordString: + """A tensor product of single qubit Clifford gates (e.g. "H \u2297 X \u2297 S"). + + Represents a collection of Clifford operations applied pairwise to a + collection of qubits. Ignores global phase. + + Examples: + >>> import stim + >>> stim.CliffordString("H,S,C_XYZ") * stim.CliffordString("H,H,H") + stim.CliffordString("I,C_ZYX,SQRT_X_DAG") + """ + def __add__( + self, + rhs: stim.CliffordString, + ) -> stim.CliffordString: + """Concatenates two CliffordStrings. + + Args: + rhs: The suffix of the concatenation. + + Returns: + The concatenated Clifford string. + + Examples: + >>> import stim + >>> stim.CliffordString("I,X,H") + stim.CliffordString("Y,S") + stim.CliffordString("I,X,H,Y,S") + """ + def __eq__( + self, + arg0: stim.CliffordString, + ) -> bool: + """Determines if two Clifford strings have identical contents. + """ + @overload + def __getitem__( + self, + index_or_slice: int, + ) -> stim.GateData: + pass + @overload + def __getitem__( + self, + index_or_slice: slice, + ) -> stim.CliffordString: + pass + def __getitem__( + self, + index_or_slice: Union[int, slice], + ) -> Union[stim.GateData, stim.CliffordString]: + """Returns a Clifford or substring from the CliffordString. + + Args: + index_or_slice: The index of the Clifford to return, or the slice + corresponding to the sub CliffordString to return. + + Returns: + The indexed Clifford (as a stim.GateData instance) or the sliced + CliffordString. + + Examples: + >>> import stim + >>> s = stim.CliffordString("I,X,Y,Z,H") + + >>> s[2] + stim.gate_data('Y') + + >>> s[-1] + stim.gate_data('H') + + >>> s[:-1] + stim.CliffordString("I,X,Y,Z") + + >>> s[::2] + stim.CliffordString("I,Y,H") + """ + def __iadd__( + self, + rhs: stim.CliffordString, + ) -> stim.CliffordString: + """Mutates the CliffordString by concatenating onto it. + + Args: + rhs: The suffix to concatenate onto the target CliffordString. + + Returns: + The mutated Clifford string. + + Examples: + >>> import stim + >>> c = stim.CliffordString("I,X,H") + >>> alias = c + >>> alias += stim.CliffordString("Y,S") + >>> c + stim.CliffordString("I,X,H,Y,S") + """ + def __imul__( + self, + rhs: Union[stim.CliffordString, int], + ) -> stim.CliffordString: + """Inplace CliffordString multiplication. + + Mutates the CliffordString into itself multiplied by another CliffordString + (via pairwise Clifford multipliation) or by an integer (via repeating the + contents). + + Args: + rhs: Either a stim.CliffordString or an int. If rhs is a + stim.CliffordString, then the Cliffords from each string are multiplied + pairwise. If rhs is an int, it is the number of times to repeat the + Clifford string's contents. + + Returns: + The mutated Clifford string. + + Examples: + >>> import stim + + >>> c = stim.CliffordString("S,X,X") + >>> alias = c + >>> alias *= stim.CliffordString("S,Z,H,Z") + >>> c + stim.CliffordString("Z,Y,SQRT_Y,Z") + + >>> c = stim.CliffordString("I,X,H") + >>> alias = c + >>> alias *= 2 + >>> c + stim.CliffordString("I,X,H,I,X,H") + """ + def __init__( + self, + arg: Union[int, str, stim.CliffordString, stim.PauliString, stim.Circuit], + /, + ) -> None: + """Initializes a stim.CliffordString from the given argument. + + Args: + arg [position-only]: This can be a variety of types, including: + int: initializes an identity Clifford string of the given length. + str: initializes by parsing a comma-separated list of gate names. + stim.CliffordString: initializes by copying the given Clifford string. + stim.PauliString: initializes by copying from the given Pauli string + (ignores the sign of the Pauli string). + stim.Circuit: initializes a CliffordString equivalent to the action + of the circuit (as long as the circuit only contains single qubit + unitary operations and annotations). + Iterable: initializes by interpreting each item as a Clifford. + Each item can be a single-qubit Clifford gate name (like "SQRT_X") + or stim.GateData instance. + + Examples: + >>> import stim + + >>> stim.CliffordString(5) + stim.CliffordString("I,I,I,I,I") + + >>> stim.CliffordString("X,Y,Z,SQRT_X") + stim.CliffordString("X,Y,Z,SQRT_X") + + >>> stim.CliffordString(["H", stim.gate_data("S")]) + stim.CliffordString("H,S") + + >>> stim.CliffordString(stim.PauliString("XYZ")) + stim.CliffordString("X,Y,Z") + + >>> stim.CliffordString(stim.CliffordString("X,Y,Z")) + stim.CliffordString("X,Y,Z") + + >>> stim.CliffordString(stim.Circuit(''' + ... H 0 1 2 + ... S 2 3 + ... TICK + ... S 3 + ... I 6 + ... ''')) + stim.CliffordString("H,H,C_ZYX,Z,I,I,I") + """ + def __ipow__( + self, + num_qubits: int, + ) -> object: + """Mutates the CliffordString into itself raised to a power. + + Args: + power: The power to raise the CliffordString's Cliffords to. + This value can be negative (e.g. -1 inverts the string). + + Returns: + The mutated Clifford string. + + Examples: + >>> import stim + + >>> p = stim.CliffordString("I,X,H,S,C_XYZ") + >>> p **= 3 + >>> p + stim.CliffordString("I,X,H,S_DAG,I") + + >>> p **= 2 + >>> p + stim.CliffordString("I,I,I,Z,I") + + >>> alias = p + >>> alias **= 2 + >>> p + stim.CliffordString("I,I,I,I,I") + """ + def __len__( + self, + ) -> int: + """Returns the number of Clifford operations in the string. + + Examples: + >>> import stim + >>> len(stim.CliffordString("I,X,Y,Z,H")) + 5 + """ + def __mul__( + self, + rhs: Union[stim.CliffordString, int], + ) -> stim.CliffordString: + """CliffordString multiplication. + + Args: + rhs: Either a stim.CliffordString or an int. If rhs is a + stim.CliffordString, then the Cliffords from each string are multiplied + pairwise. If rhs is an int, it is the number of times to repeat the + Clifford string's contents. + + Examples: + >>> import stim + + >>> stim.CliffordString("S,X,X") * stim.CliffordString("S,Z,H,Z") + stim.CliffordString("Z,Y,SQRT_Y,Z") + + >>> stim.CliffordString("I,X,H") * 3 + stim.CliffordString("I,X,H,I,X,H,I,X,H") + """ + def __ne__( + self, + arg0: stim.CliffordString, + ) -> bool: + """Determines if two Clifford strings have non-identical contents. + """ + def __pow__( + self, + power: int, + ) -> stim.CliffordString: + """Returns the CliffordString raised to a power. + + Args: + power: The power to raise the CliffordString's Cliffords to. + This value can be negative (e.g. -1 returns the inverse string). + + Returns: + The Clifford string raised to the power. + + Examples: + >>> import stim + + >>> p = stim.CliffordString("I,X,H,S,C_XYZ") + + >>> p**0 + stim.CliffordString("I,I,I,I,I") + + >>> p**1 + stim.CliffordString("I,X,H,S,C_XYZ") + + >>> p**12000001 + stim.CliffordString("I,X,H,S,C_XYZ") + + >>> p**2 + stim.CliffordString("I,I,I,Z,C_ZYX") + + >>> p**3 + stim.CliffordString("I,X,H,S_DAG,I") + + >>> p**-1 + stim.CliffordString("I,X,H,S_DAG,C_ZYX") + """ + def __repr__( + self, + ) -> str: + """Returns text that is a valid python expression evaluating to an equivalent `stim.CliffordString`. + """ + def __rmul__( + self, + lhs: int, + ) -> stim.CliffordString: + """CliffordString left-multiplication. + + Args: + lhs: The number of times to repeat the Clifford string's contents. + + Returns: + The repeated Clifford string. + + Examples: + >>> import stim + + >>> 2 * stim.CliffordString("I,X,H") + stim.CliffordString("I,X,H,I,X,H") + + >>> 0 * stim.CliffordString("I,X,H") + stim.CliffordString("") + + >>> 5 * stim.CliffordString("I") + stim.CliffordString("I,I,I,I,I") + """ + def __setitem__( + self, + index_or_slice: Union[int, slice], + new_value: Union[str, stim.GateData, stim.CliffordString, stim.PauliString, stim.Tableau], + ) -> None: + """Overwrites an indexed Clifford, or slice of Cliffords, with the given value. + + Args: + index_or_slice: The index of the Clifford to overwrite, or the slice + of Cliffords to overwrite. + new_value: Specifies the value to write into the Clifford string. This can + be set to a few different types of values: + - str: Name of the single qubit Clifford gate to write to the index or + broadcast over the slice. + - stim.GateData: The single qubit Clifford gate to write to the index + or broadcast over the slice. + - stim.Tableau: Must be a single qubit tableau. Specifies the single + qubit Clifford gate to write to the index or broadcast over the + slice. + - stim.CliffordString: String of Cliffords to write into the slice. + + Examples: + >>> import stim + >>> s = stim.CliffordString("I,I,I,I,I") + + >>> s[1] = 'H' + >>> s + stim.CliffordString("I,H,I,I,I") + + >>> s[2:] = 'SQRT_X' + >>> s + stim.CliffordString("I,H,SQRT_X,SQRT_X,SQRT_X") + + >>> s[0] = stim.gate_data('S_DAG').inverse + >>> s + stim.CliffordString("S,H,SQRT_X,SQRT_X,SQRT_X") + + >>> s[:] = 'I' + >>> s + stim.CliffordString("I,I,I,I,I") + + >>> s[::2] = stim.CliffordString("X,Y,Z") + >>> s + stim.CliffordString("X,I,Y,I,Z") + + >>> s[0] = stim.Tableau.from_named_gate("H") + >>> s + stim.CliffordString("H,I,Y,I,Z") + + >>> s[:] = stim.Tableau.from_named_gate("S") + >>> s + stim.CliffordString("S,S,S,S,S") + + >>> s[:4] = stim.PauliString("IXYZ") + >>> s + stim.CliffordString("I,X,Y,Z,S") + """ + def __str__( + self, + ) -> str: + """Returns a string representation of the CliffordString's operations. + """ + @staticmethod + def all_cliffords_string( + ) -> stim.CliffordString: + """Returns a stim.CliffordString containing each single qubit Clifford once. + + Useful for things like testing that a method works on every single Clifford. + + Examples: + >>> import stim + >>> cliffords = stim.CliffordString.all_cliffords_string() + >>> len(cliffords) + 24 + + >>> print(cliffords[:8]) + I,X,Y,Z,H_XY,S,S_DAG,H_NXY + + >>> print(cliffords[8:16]) + H,SQRT_Y_DAG,H_NXZ,SQRT_Y,H_YZ,H_NYZ,SQRT_X,SQRT_X_DAG + + >>> print(cliffords[16:]) + C_XYZ,C_XYNZ,C_NXYZ,C_XNYZ,C_ZYX,C_ZNYX,C_NZYX,C_ZYNX + """ + def copy( + self, + ) -> stim.CliffordString: + """Returns a copy of the CliffordString. + + Returns: + The copy. + + Examples: + >>> import stim + >>> c = stim.CliffordString("H,X") + >>> alias = c + >>> copy = c.copy() + >>> c *= 5 + >>> alias + stim.CliffordString("H,X,H,X,H,X,H,X,H,X") + >>> copy + stim.CliffordString("H,X") + """ + @staticmethod + def random( + num_qubits: int, + ) -> stim.CliffordString: + """Samples a uniformly random CliffordString. + + Args: + num_qubits: The number of qubits the CliffordString should act upon. + + Examples: + >>> import stim + >>> p = stim.CliffordString.random(5) + >>> len(p) + 5 + + Returns: + The sampled Clifford string. + """ + def x_outputs( + self, + *, + bit_packed_signs: bool = False, + ) -> Tuple[stim.PauliString, np.ndarray]: + """Returns what each Clifford in the CliffordString conjugates an X input into. + + For example, H conjugates X into +Z and S_DAG conjugates X into -Y. + + Combined with `z_outputs`, the results of this method completely specify + the single qubit Clifford applied to each qubit. + + Args: + bit_packed_signs: Defaults to False. When False, the sign data is returned + in a numpy array with dtype `np.bool_`. When True, the dtype is instead + `np.uint8` and 8 bits are packed into each byte (in little endian + order). + + Returns: + A (paulis, signs) tuple. + + `paulis` has type stim.PauliString. Its sign is always positive. + + `signs` has type np.ndarray and an argument-dependent shape: + bit_packed_signs=False: + dtype=np.bool_ + shape=(num_qubits,) + bit_packed_signs=True: + dtype=np.uint8 + shape=(math.ceil(num_qubits / 8),) + + Examples: + >>> import stim + >>> x_paulis, x_signs = stim.CliffordString("I,Y,H,S").x_outputs() + >>> x_paulis + stim.PauliString("+XXZY") + >>> x_signs + array([False, True, False, False]) + + >>> stim.CliffordString("I,Y,H,S").x_outputs(bit_packed_signs=True)[1] + array([2], dtype=uint8) + """ + def y_outputs( + self, + *, + bit_packed_signs: bool = False, + ) -> Tuple[stim.PauliString, np.ndarray]: + """Returns what each Clifford in the CliffordString conjugates a Y input into. + + For example, H conjugates Y into -Y and S_DAG conjugates Y into +X. + + Args: + bit_packed_signs: Defaults to False. When False, the sign data is returned + in a numpy array with dtype `np.bool_`. When True, the dtype is instead + `np.uint8` and 8 bits are packed into each byte (in little endian + order). + + Returns: + A (paulis, signs) tuple. + + `paulis` has type stim.PauliString. Its sign is always positive. + + `signs` has type np.ndarray and an argument-dependent shape: + bit_packed_signs=False: + dtype=np.bool_ + shape=(num_qubits,) + bit_packed_signs=True: + dtype=np.uint8 + shape=(math.ceil(num_qubits / 8),) + + Examples: + >>> import stim + >>> y_paulis, y_signs = stim.CliffordString("I,X,H,S").y_outputs() + >>> y_paulis + stim.PauliString("+YYYX") + >>> y_signs + array([False, True, True, True]) + + >>> stim.CliffordString("I,X,H,S").y_outputs(bit_packed_signs=True)[1] + array([14], dtype=uint8) + """ + def z_outputs( + self, + *, + bit_packed_signs: bool = False, + ) -> Tuple[stim.PauliString, np.ndarray]: + """Returns what each Clifford in the CliffordString conjugates a Z input into. + + For example, H conjugates Z into +X and SQRT_X conjugates Z into -Y. + + Combined with `x_outputs`, the results of this method completely specify + the single qubit Clifford applied to each qubit. + + Args: + bit_packed_signs: Defaults to False. When False, the sign data is returned + in a numpy array with dtype `np.bool_`. When True, the dtype is instead + `np.uint8` and 8 bits are packed into each byte (in little endian + order). + + Returns: + A (paulis, signs) tuple. + + `paulis` has type stim.PauliString. Its sign is always positive. + + `signs` has type np.ndarray and an argument-dependent shape: + bit_packed_signs=False: + dtype=np.bool_ + shape=(num_qubits,) + bit_packed_signs=True: + dtype=np.uint8 + shape=(math.ceil(num_qubits / 8),) + + Examples: + >>> import stim + >>> z_paulis, z_signs = stim.CliffordString("I,Y,H,S").z_outputs() + >>> z_paulis + stim.PauliString("+ZZXZ") + >>> z_signs + array([False, True, False, False]) + + >>> stim.CliffordString("I,Y,H,S").z_outputs(bit_packed_signs=True)[1] + array([2], dtype=uint8) + """ class CompiledDemSampler: """A helper class for efficiently sampler from a detector error model. @@ -4073,13 +4627,13 @@ class CompiledDemSampler: shots: int, *, det_out_file: Union[None, str, pathlib.Path], - det_out_format: str = "01", + det_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', obs_out_file: Union[None, str, pathlib.Path], - obs_out_format: str = "01", + obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', err_out_file: Union[None, str, pathlib.Path] = None, - err_out_format: str = "01", + err_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', replay_err_in_file: Union[None, str, pathlib.Path] = None, - replay_err_in_format: str = "01", + replay_err_in_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', ) -> None: """Samples the detector error model and writes the results to disk. @@ -4333,9 +4887,9 @@ class CompiledDetectorSampler: shots: int, *, filepath: Union[str, pathlib.Path], - format: 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]' = '01', + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', obs_out_filepath: Optional[Union[str, pathlib.Path]] = None, - obs_out_format: 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]' = '01', + obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', prepend_observables: bool = False, append_observables: bool = False, ) -> None: @@ -4539,8 +5093,8 @@ class CompiledMeasurementSampler: self, shots: int, *, - filepath: str, - format: str = '01', + filepath: Union[str, pathlib.Path], + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', ) -> None: """Samples measurements from the circuit and writes them to a file. @@ -4639,7 +5193,7 @@ class CompiledMeasurementsToDetectionEventsConverter: *, measurements: np.ndarray, sweep_bits: Optional[np.ndarray] = None, - separate_observables: 'Literal[True]', + separate_observables: Literal[True], append_observables: bool = False, bit_packed: bool = False, ) -> Tuple[np.ndarray, np.ndarray]: @@ -4731,15 +5285,15 @@ class CompiledMeasurementsToDetectionEventsConverter: def convert_file( self, *, - measurements_filepath: str, - measurements_format: str = '01', - sweep_bits_filepath: str = None, - sweep_bits_format: str = '01', - detection_events_filepath: str, - detection_events_format: str = '01', + measurements_filepath: Union[str, pathlib.Path], + measurements_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + sweep_bits_filepath: Optional[Union[str, pathlib.Path]] = None, + sweep_bits_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', + detection_events_filepath: Union[str, pathlib.Path], + detection_events_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', append_observables: bool = False, - obs_out_filepath: str = None, - obs_out_format: str = '01', + obs_out_filepath: Optional[Union[str, pathlib.Path]] = None, + obs_out_format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"] = '01', ) -> None: """Reads measurement data from a file and writes detection events to another file. @@ -5834,7 +6388,7 @@ class DetectorErrorModel: """ def diagram( self, - type: str, + type: Literal["matchgraph-svg", "matchgraph-svg-html", "matchgraph-3d", "matchgraph-3d-html"] = 'matchgraph-svg', ) -> Any: """Returns a diagram of the circuit, from a variety of options. @@ -7428,7 +7982,7 @@ class Flow: the string "X_ -> ZZ xor rec[-1]" will result in a flow with input pauli string "X_", output pauli string "ZZ", and measurement indices [-1]. - Arguments: + Args: arg [position-only]: Defaults to None. Must be specified by itself if used. str: Initializes a flow by parsing the given shorthand text. stim.Flow: Initializes a copy of the given flow. @@ -8780,7 +9334,7 @@ class PauliString: """ def __init__( self, - arg: Union[None, int, str, stim.PauliString, Iterable[Union[int, 'Literal["_", "I", "X", "Y", "Z"]']]] = None, + arg: Union[None, int, str, stim.PauliString, Iterable[Union[int, Literal["_", "I", "X", "Y", "Z"]]]] = None, /, ) -> None: """Initializes a stim.PauliString from the given argument. @@ -8793,7 +9347,7 @@ class PauliString: pauli string is a series of integers seperated by '*' and prefixed by 'I', 'X', 'Y', or 'Z'. - Arguments: + Args: arg [position-only]: This can be a variety of types, including: None (default): initializes an empty Pauli string. int: initializes an identity Pauli string of the given length. @@ -8802,6 +9356,14 @@ class PauliString: Iterable: initializes by interpreting each item as a Pauli. Each item can be a single-qubit Pauli string (like "X"), or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. + Dict[int, Union[int, str]]: initializes by interpreting keys as + the qubit index and values as the Pauli for that index. + Each value can be a single-qubit Pauli string (like "X"), + or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. + Dict[Union[int, str], Iterable[int]]: initializes by interpreting keys + as Pauli operators and values as the qubit indices for that Pauli. + Each key can be a single-qubit Pauli string (like "X"), + or an integer. Integers use the convention 0=I, 1=X, 2=Y, 3=Z. Examples: >>> import stim @@ -8829,6 +9391,15 @@ class PauliString: >>> stim.PauliString("X6*Y6") stim.PauliString("+i______Z") + + >>> stim.PauliString({0: "X", 2: "Y", 3: "X"}) + stim.PauliString("+X_YX") + + >>> stim.PauliString({0: "X", 2: 2, 3: 1}) + stim.PauliString("+X_YX") + + >>> stim.PauliString({"X": [1], 2: [4], "Z": [0, 3]}) + stim.PauliString("+ZX_ZY") """ def __itruediv__( self, @@ -9270,7 +9841,7 @@ class PauliString: def from_unitary_matrix( matrix: Iterable[Iterable[Union[int, float, complex]]], *, - endian: str = 'little', + endian: Literal["little", "big"] = 'little', unsigned: bool = False, ) -> stim.PauliString: """Creates a stim.PauliString from the unitary matrix of a Pauli group member. @@ -9560,7 +10131,7 @@ class PauliString: def to_unitary_matrix( self, *, - endian: str, + endian: Literal["little", "big"], ) -> np.ndarray[np.complex64]: """Converts the pauli string into a unitary matrix. @@ -10195,7 +10766,7 @@ class Tableau: def from_state_vector( state_vector: Iterable[float], *, - endian: str, + endian: Literal["little", "big"], ) -> stim.Tableau: """Creates a tableau representing the stabilizer state of the given state vector. @@ -10256,7 +10827,7 @@ class Tableau: def from_unitary_matrix( matrix: Iterable[Iterable[float]], *, - endian: str = 'little', + endian: Literal["little", "big"] = 'little', ) -> stim.Tableau: """Creates a tableau from the unitary matrix of a Clifford operation. @@ -10685,7 +11256,7 @@ class Tableau: """ def to_circuit( self, - method: 'Literal["elimination", "graph_state"]' = 'elimination', + method: Literal["elimination", "graph_state"] = 'elimination', ) -> stim.Circuit: """Synthesizes a circuit that implements the tableau's Clifford operation. @@ -11052,7 +11623,7 @@ class Tableau: def to_state_vector( self, *, - endian: str = 'little', + endian: Literal["little", "big"] = 'little', ) -> np.ndarray[np.complex64]: """Returns the state vector produced by applying the tableau to the |0..0> state. @@ -11104,7 +11675,7 @@ class Tableau: def to_unitary_matrix( self, *, - endian: str, + endian: Literal["little", "big"], ) -> np.ndarray[np.complex64]: """Converts the tableau into a unitary matrix. @@ -12826,7 +13397,7 @@ class TableauSimulator: self, state_vector: Iterable[float], *, - endian: str, + endian: Literal["little", "big"], ) -> None: """Sets the simulator's state to a superposition specified by an amplitude vector. @@ -12972,7 +13543,7 @@ class TableauSimulator: def state_vector( self, *, - endian: str = 'little', + endian: Literal["little", "big"] = 'little', ) -> np.ndarray[np.complex64]: """Returns a wavefunction for the simulator's current state. @@ -13467,7 +14038,7 @@ def main( def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, @@ -13478,18 +14049,18 @@ def read_shot_data_file( def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, num_observables: int = 0, - separate_observables: 'Literal[True]', + separate_observables: Literal[True], ) -> Tuple[np.ndarray, np.ndarray]: pass def read_shot_data_file( *, path: Union[str, pathlib.Path], - format: Union[str, 'Literal["01", "b8", "r8", "ptb64", "hits", "dets"]'], + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], bit_packed: bool = False, num_measurements: int = 0, num_detectors: int = 0, @@ -13860,7 +14431,7 @@ def write_shot_data_file( *, data: np.ndarray, path: Union[str, pathlib.Path], - format: str, + format: Literal["01", "b8", "r8", "ptb64", "hits", "dets"], num_measurements: int = 0, num_detectors: int = 0, num_observables: int = 0, From 259e2199f24f6770c751703a8efcb6f66f8eba87 Mon Sep 17 00:00:00 2001 From: Matt McEwen <2858658+m-mcewen@users.noreply.github.com> Date: Thu, 5 Mar 2026 14:55:31 -0800 Subject: [PATCH 3/4] More robust version handling @Strilanc --- .../sinter/_decoding/_decoding_pymatching.py | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/glue/sample/src/sinter/_decoding/_decoding_pymatching.py b/glue/sample/src/sinter/_decoding/_decoding_pymatching.py index ed3c118db..ce933a523 100644 --- a/glue/sample/src/sinter/_decoding/_decoding_pymatching.py +++ b/glue/sample/src/sinter/_decoding/_decoding_pymatching.py @@ -3,13 +3,21 @@ def check_pymatching_version_for_correlated_decoding(pymatching): v = pymatching.__version__.split('.') - if not (int(v[0]) >= 2 or int(v[1]) >= 3 or int(v[2]) >= 1): - raise ValueError( - "Pymatching version must be at least 2.3.1 for correlated decoding.\n" - f"Installed version: {pymatching.__version__}\n" - "To fix this, install a newer version of pymatching into your environment.\n" - "For example, if you are using pip, run `pip install pymatching --upgrade`.\n" - ) + try: + a = int(v[0]) + b = int(v[1] + c = int(''.join(e for e in v[2] if e in '0123456789')) # In case dev version + except ValueError, IndexError: + return # Probably it's the future. + + if (a, b, c) < (2, 3, 1): + if not (int(v[0]) >= 2 or int(v[1]) >= 3 or int(v[2]) >= 1): + raise ValueError( + "Pymatching version must be at least 2.3.1 for correlated decoding.\n" + f"Installed version: {pymatching.__version__}\n" + "To fix this, install a newer version of pymatching into your environment.\n" + "For example, if you are using pip, run `pip install pymatching --upgrade`.\n" + ) class PyMatchingCompiledDecoder(CompiledDecoder): From a3dd4cbc1c4da84cdf31ad978f4f56b240cd642c Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Wed, 15 Apr 2026 18:50:20 -0700 Subject: [PATCH 4/4] Fix compile failure --- .../sinter/_collection/_collection_manager.py | 2 ++ .../sinter/_decoding/_decoding_pymatching.py | 17 ++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/glue/sample/src/sinter/_collection/_collection_manager.py b/glue/sample/src/sinter/_collection/_collection_manager.py index 19cba6bf8..637cd868b 100644 --- a/glue/sample/src/sinter/_collection/_collection_manager.py +++ b/glue/sample/src/sinter/_collection/_collection_manager.py @@ -227,6 +227,8 @@ def _compute_task_ids(self): shots_left = options.max_shots errors_left = options.max_errors + if shots_left is None: + raise ValueError("Didn't specify --max_shots.") if errors_left is None: errors_left = shots_left errors_left = min(errors_left, shots_left) diff --git a/glue/sample/src/sinter/_decoding/_decoding_pymatching.py b/glue/sample/src/sinter/_decoding/_decoding_pymatching.py index ce933a523..1473d037f 100644 --- a/glue/sample/src/sinter/_decoding/_decoding_pymatching.py +++ b/glue/sample/src/sinter/_decoding/_decoding_pymatching.py @@ -5,19 +5,18 @@ def check_pymatching_version_for_correlated_decoding(pymatching): v = pymatching.__version__.split('.') try: a = int(v[0]) - b = int(v[1] + b = int(v[1]) c = int(''.join(e for e in v[2] if e in '0123456789')) # In case dev version - except ValueError, IndexError: + except (ValueError, IndexError): return # Probably it's the future. if (a, b, c) < (2, 3, 1): - if not (int(v[0]) >= 2 or int(v[1]) >= 3 or int(v[2]) >= 1): - raise ValueError( - "Pymatching version must be at least 2.3.1 for correlated decoding.\n" - f"Installed version: {pymatching.__version__}\n" - "To fix this, install a newer version of pymatching into your environment.\n" - "For example, if you are using pip, run `pip install pymatching --upgrade`.\n" - ) + raise ValueError( + "PyMatching version must be at least 2.3.1 for correlated decoding.\n" + f"Installed version: {pymatching.__version__}\n" + "To fix this, install a newer version of pymatching into your environment.\n" + "For example, if you are using pip, run `pip install pymatching --upgrade`.\n" + ) class PyMatchingCompiledDecoder(CompiledDecoder):