Skip to content

Commit b1de042

Browse files
authored
Add support for more types to stim.CliffordString operations (#1032)
- Add `stim.CliffordString.copy` - Add `stim.Circuit` argument support to `stim.CliffordString.__init__` - Add `stim.PauliString` value support to `stim.CliffordString.__setitem__` - Add `stim.Tableau` value support to `stim.CliffordString.__setitem__`
1 parent 5cebcb4 commit b1de042

7 files changed

Lines changed: 334 additions & 43 deletions

File tree

doc/python_api_reference_vDev.md

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
126126
- [`stim.CliffordString.__setitem__`](#stim.CliffordString.__setitem__)
127127
- [`stim.CliffordString.__str__`](#stim.CliffordString.__str__)
128128
- [`stim.CliffordString.all_cliffords_string`](#stim.CliffordString.all_cliffords_string)
129+
- [`stim.CliffordString.copy`](#stim.CliffordString.copy)
129130
- [`stim.CliffordString.random`](#stim.CliffordString.random)
130131
- [`stim.CliffordString.x_outputs`](#stim.CliffordString.x_outputs)
131132
- [`stim.CliffordString.y_outputs`](#stim.CliffordString.y_outputs)
@@ -5227,7 +5228,7 @@ def __imul__(
52275228
# (in class stim.CliffordString)
52285229
def __init__(
52295230
self,
5230-
arg: Union[int, str, stim.CliffordString, stim.PauliString],
5231+
arg: Union[int, str, stim.CliffordString, stim.PauliString, stim.Circuit],
52315232
/,
52325233
) -> None:
52335234
"""Initializes a stim.CliffordString from the given argument.
@@ -5239,6 +5240,9 @@ def __init__(
52395240
stim.CliffordString: initializes by copying the given Clifford string.
52405241
stim.PauliString: initializes by copying from the given Pauli string
52415242
(ignores the sign of the Pauli string).
5243+
stim.Circuit: initializes a CliffordString equivalent to the action
5244+
of the circuit (as long as the circuit only contains single qubit
5245+
unitary operations and annotations).
52425246
Iterable: initializes by interpreting each item as a Clifford.
52435247
Each item can be a single-qubit Clifford gate name (like "SQRT_X")
52445248
or stim.GateData instance.
@@ -5260,6 +5264,15 @@ def __init__(
52605264

52615265
>>> stim.CliffordString(stim.CliffordString("X,Y,Z"))
52625266
stim.CliffordString("X,Y,Z")
5267+
5268+
>>> stim.CliffordString(stim.Circuit('''
5269+
... H 0 1 2
5270+
... S 2 3
5271+
... TICK
5272+
... S 3
5273+
... I 6
5274+
... '''))
5275+
stim.CliffordString("H,H,C_ZYX,Z,I,I,I")
52635276
"""
52645277
```
52655278

@@ -5452,7 +5465,7 @@ def __rmul__(
54525465
def __setitem__(
54535466
self,
54545467
index_or_slice: Union[int, slice],
5455-
new_value: Union[str, stim.GateData, stim.CliffordString],
5468+
new_value: Union[str, stim.GateData, stim.CliffordString, stim.PauliString, stim.Tableau],
54565469
) -> None:
54575470
"""Overwrites an indexed Clifford, or slice of Cliffords, with the given value.
54585471

@@ -5465,7 +5478,10 @@ def __setitem__(
54655478
broadcast over the slice.
54665479
- stim.GateData: The single qubit Clifford gate to write to the index
54675480
or broadcast over the slice.
5468-
- stim.CliffordString: Values to write into the slice.
5481+
- stim.Tableau: Must be a single qubit tableau. Specifies the single
5482+
qubit Clifford gate to write to the index or broadcast over the
5483+
slice.
5484+
- stim.CliffordString: String of Cliffords to write into the slice.
54695485

54705486
Examples:
54715487
>>> import stim
@@ -5490,6 +5506,18 @@ def __setitem__(
54905506
>>> s[::2] = stim.CliffordString("X,Y,Z")
54915507
>>> s
54925508
stim.CliffordString("X,I,Y,I,Z")
5509+
5510+
>>> s[0] = stim.Tableau.from_named_gate("H")
5511+
>>> s
5512+
stim.CliffordString("H,I,Y,I,Z")
5513+
5514+
>>> s[:] = stim.Tableau.from_named_gate("S")
5515+
>>> s
5516+
stim.CliffordString("S,S,S,S,S")
5517+
5518+
>>> s[:4] = stim.PauliString("IXYZ")
5519+
>>> s
5520+
stim.CliffordString("I,X,Y,Z,S")
54935521
"""
54945522
```
54955523

@@ -5534,6 +5562,32 @@ def all_cliffords_string(
55345562
"""
55355563
```
55365564

5565+
<a name="stim.CliffordString.copy"></a>
5566+
```python
5567+
# stim.CliffordString.copy
5568+
5569+
# (in class stim.CliffordString)
5570+
def copy(
5571+
self,
5572+
) -> stim.CliffordString:
5573+
"""Returns a copy of the CliffordString.
5574+
5575+
Returns:
5576+
The copy.
5577+
5578+
Examples:
5579+
>>> import stim
5580+
>>> c = stim.CliffordString("H,X")
5581+
>>> alias = c
5582+
>>> copy = c.copy()
5583+
>>> c *= 5
5584+
>>> alias
5585+
stim.CliffordString("H,X,H,X,H,X,H,X,H,X")
5586+
>>> copy
5587+
stim.CliffordString("H,X")
5588+
"""
5589+
```
5590+
55375591
<a name="stim.CliffordString.random"></a>
55385592
```python
55395593
# stim.CliffordString.random

doc/stim.pyi

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4021,7 +4021,7 @@ class CliffordString:
40214021
"""
40224022
def __init__(
40234023
self,
4024-
arg: Union[int, str, stim.CliffordString, stim.PauliString],
4024+
arg: Union[int, str, stim.CliffordString, stim.PauliString, stim.Circuit],
40254025
/,
40264026
) -> None:
40274027
"""Initializes a stim.CliffordString from the given argument.
@@ -4033,6 +4033,9 @@ class CliffordString:
40334033
stim.CliffordString: initializes by copying the given Clifford string.
40344034
stim.PauliString: initializes by copying from the given Pauli string
40354035
(ignores the sign of the Pauli string).
4036+
stim.Circuit: initializes a CliffordString equivalent to the action
4037+
of the circuit (as long as the circuit only contains single qubit
4038+
unitary operations and annotations).
40364039
Iterable: initializes by interpreting each item as a Clifford.
40374040
Each item can be a single-qubit Clifford gate name (like "SQRT_X")
40384041
or stim.GateData instance.
@@ -4054,6 +4057,15 @@ class CliffordString:
40544057

40554058
>>> stim.CliffordString(stim.CliffordString("X,Y,Z"))
40564059
stim.CliffordString("X,Y,Z")
4060+
4061+
>>> stim.CliffordString(stim.Circuit('''
4062+
... H 0 1 2
4063+
... S 2 3
4064+
... TICK
4065+
... S 3
4066+
... I 6
4067+
... '''))
4068+
stim.CliffordString("H,H,C_ZYX,Z,I,I,I")
40574069
"""
40584070
def __ipow__(
40594071
self,
@@ -4190,7 +4202,7 @@ class CliffordString:
41904202
def __setitem__(
41914203
self,
41924204
index_or_slice: Union[int, slice],
4193-
new_value: Union[str, stim.GateData, stim.CliffordString],
4205+
new_value: Union[str, stim.GateData, stim.CliffordString, stim.PauliString, stim.Tableau],
41944206
) -> None:
41954207
"""Overwrites an indexed Clifford, or slice of Cliffords, with the given value.
41964208

@@ -4203,7 +4215,10 @@ class CliffordString:
42034215
broadcast over the slice.
42044216
- stim.GateData: The single qubit Clifford gate to write to the index
42054217
or broadcast over the slice.
4206-
- stim.CliffordString: Values to write into the slice.
4218+
- stim.Tableau: Must be a single qubit tableau. Specifies the single
4219+
qubit Clifford gate to write to the index or broadcast over the
4220+
slice.
4221+
- stim.CliffordString: String of Cliffords to write into the slice.
42074222

42084223
Examples:
42094224
>>> import stim
@@ -4228,6 +4243,18 @@ class CliffordString:
42284243
>>> s[::2] = stim.CliffordString("X,Y,Z")
42294244
>>> s
42304245
stim.CliffordString("X,I,Y,I,Z")
4246+
4247+
>>> s[0] = stim.Tableau.from_named_gate("H")
4248+
>>> s
4249+
stim.CliffordString("H,I,Y,I,Z")
4250+
4251+
>>> s[:] = stim.Tableau.from_named_gate("S")
4252+
>>> s
4253+
stim.CliffordString("S,S,S,S,S")
4254+
4255+
>>> s[:4] = stim.PauliString("IXYZ")
4256+
>>> s
4257+
stim.CliffordString("I,X,Y,Z,S")
42314258
"""
42324259
def __str__(
42334260
self,
@@ -4256,6 +4283,25 @@ class CliffordString:
42564283
>>> print(cliffords[16:])
42574284
C_XYZ,C_XYNZ,C_NXYZ,C_XNYZ,C_ZYX,C_ZNYX,C_NZYX,C_ZYNX
42584285
"""
4286+
def copy(
4287+
self,
4288+
) -> stim.CliffordString:
4289+
"""Returns a copy of the CliffordString.
4290+
4291+
Returns:
4292+
The copy.
4293+
4294+
Examples:
4295+
>>> import stim
4296+
>>> c = stim.CliffordString("H,X")
4297+
>>> alias = c
4298+
>>> copy = c.copy()
4299+
>>> c *= 5
4300+
>>> alias
4301+
stim.CliffordString("H,X,H,X,H,X,H,X,H,X")
4302+
>>> copy
4303+
stim.CliffordString("H,X")
4304+
"""
42594305
@staticmethod
42604306
def random(
42614307
num_qubits: int,

glue/python/src/stim/__init__.pyi

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4021,7 +4021,7 @@ class CliffordString:
40214021
"""
40224022
def __init__(
40234023
self,
4024-
arg: Union[int, str, stim.CliffordString, stim.PauliString],
4024+
arg: Union[int, str, stim.CliffordString, stim.PauliString, stim.Circuit],
40254025
/,
40264026
) -> None:
40274027
"""Initializes a stim.CliffordString from the given argument.
@@ -4033,6 +4033,9 @@ class CliffordString:
40334033
stim.CliffordString: initializes by copying the given Clifford string.
40344034
stim.PauliString: initializes by copying from the given Pauli string
40354035
(ignores the sign of the Pauli string).
4036+
stim.Circuit: initializes a CliffordString equivalent to the action
4037+
of the circuit (as long as the circuit only contains single qubit
4038+
unitary operations and annotations).
40364039
Iterable: initializes by interpreting each item as a Clifford.
40374040
Each item can be a single-qubit Clifford gate name (like "SQRT_X")
40384041
or stim.GateData instance.
@@ -4054,6 +4057,15 @@ class CliffordString:
40544057

40554058
>>> stim.CliffordString(stim.CliffordString("X,Y,Z"))
40564059
stim.CliffordString("X,Y,Z")
4060+
4061+
>>> stim.CliffordString(stim.Circuit('''
4062+
... H 0 1 2
4063+
... S 2 3
4064+
... TICK
4065+
... S 3
4066+
... I 6
4067+
... '''))
4068+
stim.CliffordString("H,H,C_ZYX,Z,I,I,I")
40574069
"""
40584070
def __ipow__(
40594071
self,
@@ -4190,7 +4202,7 @@ class CliffordString:
41904202
def __setitem__(
41914203
self,
41924204
index_or_slice: Union[int, slice],
4193-
new_value: Union[str, stim.GateData, stim.CliffordString],
4205+
new_value: Union[str, stim.GateData, stim.CliffordString, stim.PauliString, stim.Tableau],
41944206
) -> None:
41954207
"""Overwrites an indexed Clifford, or slice of Cliffords, with the given value.
41964208

@@ -4203,7 +4215,10 @@ class CliffordString:
42034215
broadcast over the slice.
42044216
- stim.GateData: The single qubit Clifford gate to write to the index
42054217
or broadcast over the slice.
4206-
- stim.CliffordString: Values to write into the slice.
4218+
- stim.Tableau: Must be a single qubit tableau. Specifies the single
4219+
qubit Clifford gate to write to the index or broadcast over the
4220+
slice.
4221+
- stim.CliffordString: String of Cliffords to write into the slice.
42074222

42084223
Examples:
42094224
>>> import stim
@@ -4228,6 +4243,18 @@ class CliffordString:
42284243
>>> s[::2] = stim.CliffordString("X,Y,Z")
42294244
>>> s
42304245
stim.CliffordString("X,I,Y,I,Z")
4246+
4247+
>>> s[0] = stim.Tableau.from_named_gate("H")
4248+
>>> s
4249+
stim.CliffordString("H,I,Y,I,Z")
4250+
4251+
>>> s[:] = stim.Tableau.from_named_gate("S")
4252+
>>> s
4253+
stim.CliffordString("S,S,S,S,S")
4254+
4255+
>>> s[:4] = stim.PauliString("IXYZ")
4256+
>>> s
4257+
stim.CliffordString("I,X,Y,Z,S")
42314258
"""
42324259
def __str__(
42334260
self,
@@ -4256,6 +4283,25 @@ class CliffordString:
42564283
>>> print(cliffords[16:])
42574284
C_XYZ,C_XYNZ,C_NXYZ,C_XNYZ,C_ZYX,C_ZNYX,C_NZYX,C_ZYNX
42584285
"""
4286+
def copy(
4287+
self,
4288+
) -> stim.CliffordString:
4289+
"""Returns a copy of the CliffordString.
4290+
4291+
Returns:
4292+
The copy.
4293+
4294+
Examples:
4295+
>>> import stim
4296+
>>> c = stim.CliffordString("H,X")
4297+
>>> alias = c
4298+
>>> copy = c.copy()
4299+
>>> c *= 5
4300+
>>> alias
4301+
stim.CliffordString("H,X,H,X,H,X,H,X,H,X")
4302+
>>> copy
4303+
stim.CliffordString("H,X")
4304+
"""
42594305
@staticmethod
42604306
def random(
42614307
num_qubits: int,

src/stim/py/base.pybind.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ bool stim_pybind::normalize_index_or_slice(
4949
"Index " + std::to_string(pybind11::cast<pybind11::ssize_t>(index_or_slice)) +
5050
" not in range for sequence of length " + std::to_string(length) + ".");
5151
}
52+
*step = 0;
53+
*slice_length = 1;
5254
return false;
5355
} catch (const pybind11::cast_error &) {
5456
}

src/stim/py/base.pybind.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,50 @@ namespace stim_pybind {
3030

3131
std::mt19937_64 make_py_seeded_rng(const pybind11::object &seed);
3232
stim::SampleFormat format_to_enum(std::string_view format);
33+
34+
/// Converts a python index or slice into a form that's easier to consume.
35+
///
36+
/// In particular, replaces negative indices with non-negative indices.
37+
/// When the object is an integer, it is treated as a slice over a single
38+
/// index. The boolean returned from the method can be used to distinguish
39+
/// a single-index slice from an integer index.
40+
///
41+
/// The result can be consumed as follows:
42+
///
43+
/// pybind11::ssize_t slice_start;
44+
/// pybind11::ssize_t slice_step;
45+
/// pybind11::ssize_t slice_length;
46+
/// bool was_slice = normalize_index_or_slice(
47+
/// obj,
48+
/// collection_length,
49+
/// &slice_start,
50+
/// &slice_step,
51+
/// &slice_length)
52+
/// for (size_t k = 0; k < (size_t)slice_length; k++) {
53+
/// size_t target_k = index + step * k;
54+
/// auto &target = collection[slice_start + slice_step*k];
55+
/// ...
56+
/// }
57+
///
58+
/// Args:
59+
/// index_or_slice: An int or slice object.
60+
/// length: The length of the collection being indexed or sliced.
61+
/// start: Output address for the offset to use when looping.
62+
/// The value written to this pointer will be non-negative
63+
// (unless an exception is thrown).
64+
/// step: Output address for the stride to use when looping.
65+
/// The value written to this pointer may be negative.
66+
/// slice_length: Output address for how many iterations to run the loop.
67+
/// The value written to this pointer will be non-negative
68+
// (unless an exception is thrown).
69+
///
70+
/// Returns:
71+
/// True: the given object was a slice.
72+
/// False: the given object was an integer index.
73+
///
74+
/// Raises:
75+
/// invalid_argument: The given object was not a valid slice or index for
76+
/// the given collection length;
3377
bool normalize_index_or_slice(
3478
const pybind11::object &index_or_slice,
3579
size_t length,

0 commit comments

Comments
 (0)