sinter 1.15.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sinter might be problematic. Click here for more details.
- sinter/__init__.py +47 -0
- sinter/_collection/__init__.py +10 -0
- sinter/_collection/_collection.py +480 -0
- sinter/_collection/_collection_manager.py +581 -0
- sinter/_collection/_collection_manager_test.py +287 -0
- sinter/_collection/_collection_test.py +317 -0
- sinter/_collection/_collection_worker_loop.py +35 -0
- sinter/_collection/_collection_worker_state.py +259 -0
- sinter/_collection/_collection_worker_test.py +222 -0
- sinter/_collection/_mux_sampler.py +56 -0
- sinter/_collection/_printer.py +65 -0
- sinter/_collection/_sampler_ramp_throttled.py +66 -0
- sinter/_collection/_sampler_ramp_throttled_test.py +144 -0
- sinter/_command/__init__.py +0 -0
- sinter/_command/_main.py +39 -0
- sinter/_command/_main_collect.py +350 -0
- sinter/_command/_main_collect_test.py +482 -0
- sinter/_command/_main_combine.py +84 -0
- sinter/_command/_main_combine_test.py +153 -0
- sinter/_command/_main_plot.py +817 -0
- sinter/_command/_main_plot_test.py +445 -0
- sinter/_command/_main_predict.py +75 -0
- sinter/_command/_main_predict_test.py +36 -0
- sinter/_data/__init__.py +20 -0
- sinter/_data/_anon_task_stats.py +89 -0
- sinter/_data/_anon_task_stats_test.py +35 -0
- sinter/_data/_collection_options.py +106 -0
- sinter/_data/_collection_options_test.py +24 -0
- sinter/_data/_csv_out.py +74 -0
- sinter/_data/_existing_data.py +173 -0
- sinter/_data/_existing_data_test.py +41 -0
- sinter/_data/_task.py +311 -0
- sinter/_data/_task_stats.py +244 -0
- sinter/_data/_task_stats_test.py +140 -0
- sinter/_data/_task_test.py +38 -0
- sinter/_decoding/__init__.py +16 -0
- sinter/_decoding/_decoding.py +419 -0
- sinter/_decoding/_decoding_all_built_in_decoders.py +25 -0
- sinter/_decoding/_decoding_decoder_class.py +161 -0
- sinter/_decoding/_decoding_fusion_blossom.py +193 -0
- sinter/_decoding/_decoding_mwpf.py +302 -0
- sinter/_decoding/_decoding_pymatching.py +81 -0
- sinter/_decoding/_decoding_test.py +480 -0
- sinter/_decoding/_decoding_vacuous.py +38 -0
- sinter/_decoding/_perfectionist_sampler.py +38 -0
- sinter/_decoding/_sampler.py +72 -0
- sinter/_decoding/_stim_then_decode_sampler.py +222 -0
- sinter/_decoding/_stim_then_decode_sampler_test.py +192 -0
- sinter/_plotting.py +619 -0
- sinter/_plotting_test.py +108 -0
- sinter/_predict.py +381 -0
- sinter/_predict_test.py +227 -0
- sinter/_probability_util.py +519 -0
- sinter/_probability_util_test.py +281 -0
- sinter-1.15.0.data/data/README.md +332 -0
- sinter-1.15.0.data/data/readme_example_plot.png +0 -0
- sinter-1.15.0.data/data/requirements.txt +4 -0
- sinter-1.15.0.dist-info/METADATA +354 -0
- sinter-1.15.0.dist-info/RECORD +62 -0
- sinter-1.15.0.dist-info/WHEEL +5 -0
- sinter-1.15.0.dist-info/entry_points.txt +2 -0
- sinter-1.15.0.dist-info/top_level.txt +1 -0
sinter/_plotting_test.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import io
|
|
2
|
+
|
|
3
|
+
from matplotlib import pyplot as plt
|
|
4
|
+
|
|
5
|
+
import sinter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_better_sorted_str_terms():
|
|
9
|
+
f = sinter.better_sorted_str_terms
|
|
10
|
+
assert f('everyone et al. 2020') == ('everyone et al', '.', ' ', 2020)
|
|
11
|
+
assert f('a') == ('a',)
|
|
12
|
+
assert f('abc') == ('abc',)
|
|
13
|
+
assert f('a1b2') == ('a', 1, 'b', 2)
|
|
14
|
+
assert f('a1.5b2') == ('a', 1.5, 'b', 2)
|
|
15
|
+
assert f('a1.5.3b2') == ('a', (1, 5, 3), 'b', 2)
|
|
16
|
+
assert f(1) < f(None)
|
|
17
|
+
assert f(1) < f('2')
|
|
18
|
+
assert f('2') > f(1)
|
|
19
|
+
assert sorted([
|
|
20
|
+
"planar d=10 r=30",
|
|
21
|
+
"planar d=16 r=36",
|
|
22
|
+
"planar d=4 r=12",
|
|
23
|
+
"toric d=10 r=30",
|
|
24
|
+
"toric d=18 r=54",
|
|
25
|
+
], key=f) == [
|
|
26
|
+
"planar d=4 r=12",
|
|
27
|
+
"planar d=10 r=30",
|
|
28
|
+
"planar d=16 r=36",
|
|
29
|
+
"toric d=10 r=30",
|
|
30
|
+
"toric d=18 r=54",
|
|
31
|
+
]
|
|
32
|
+
assert sorted([
|
|
33
|
+
"a1",
|
|
34
|
+
"1a",
|
|
35
|
+
], key=f) == [
|
|
36
|
+
"1a",
|
|
37
|
+
"a1",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_plotting_does_not_crash():
|
|
42
|
+
data = io.StringIO()
|
|
43
|
+
data.write(""" shots,errors,discards,seconds,decoder,strong_id,json_metadata
|
|
44
|
+
1000000, 837,0,36.6,pymatching,9f7e20c54fec45b6aef7491b774dd5c0a3b9a005aa82faf5b9c051d6e40d60a9,"{""d"":3,""p"":0.001}"
|
|
45
|
+
53498,1099,0,6.52,pymatching,3f40432443a99b933fb548b831fb54e7e245d9d73a35c03ea5a2fb2ce270f8c8,"{""d"":3,""p"":0.005}"
|
|
46
|
+
16269,1023,0,3.23,pymatching,17b2e0c99560d20307204494ac50e31b33e50721b4ebae99d9e3577ae7248874,"{""d"":3,""p"":0.01}"
|
|
47
|
+
1000000, 151,0,77.3,pymatching,e179a18739201250371ffaae0197d8fa19d26b58dfc2942f9f1c85568645387a,"{""d"":5,""p"":0.001}"
|
|
48
|
+
11363,1068,0,12.5,pymatching,a4dec28934a033215ff1389651a26114ecc22016a6e122008830cf7dd04ba5ad,"{""d"":5,""p"":0.01}"
|
|
49
|
+
61569,1001,0,24.5,pymatching,2fefcc356752482fb4c6d912c228f6d18762f5752796c668b6abeb7775f5de92,"{""d"":5,""p"":0.005}"
|
|
50
|
+
""")
|
|
51
|
+
data.seek(0)
|
|
52
|
+
stats = sinter.stats_from_csv_files(data)
|
|
53
|
+
|
|
54
|
+
fig, ax = plt.subplots(1, 1)
|
|
55
|
+
sinter.plot_error_rate(
|
|
56
|
+
ax=ax,
|
|
57
|
+
stats=stats,
|
|
58
|
+
group_func=lambda e: f"Rotated Surface Code d={e.json_metadata['d']}",
|
|
59
|
+
x_func=lambda e: e.json_metadata['p'],
|
|
60
|
+
plot_args_func=lambda k, e: {'marker': "ov*sp^<>8PhH+xXDd|"[k]},
|
|
61
|
+
)
|
|
62
|
+
sinter.plot_error_rate(
|
|
63
|
+
ax=ax,
|
|
64
|
+
stats=stats,
|
|
65
|
+
group_func=lambda e: f"Rotated Surface Code d={e.json_metadata['d']}",
|
|
66
|
+
x_func=lambda e: e.json_metadata['p'],
|
|
67
|
+
plot_args_func=lambda k, e: {'marker': "ov*sp^<>8PhH+xXDd|"[k]},
|
|
68
|
+
failure_units_per_shot_func=lambda stats: stats.json_metadata['d'] * 3,
|
|
69
|
+
)
|
|
70
|
+
sinter.plot_error_rate(
|
|
71
|
+
ax=ax,
|
|
72
|
+
stats=stats,
|
|
73
|
+
group_func=lambda e: f"Rotated Surface Code d={e.json_metadata['d']}",
|
|
74
|
+
x_func=lambda e: e.json_metadata['p'],
|
|
75
|
+
plot_args_func=lambda k, e: {'marker': "ov*sp^<>8PhH+xXDd|"[k]},
|
|
76
|
+
failure_units_per_shot_func=lambda stats: stats.json_metadata['d'] * 3,
|
|
77
|
+
highlight_max_likelihood_factor=None,
|
|
78
|
+
)
|
|
79
|
+
sinter.plot_discard_rate(
|
|
80
|
+
ax=ax,
|
|
81
|
+
stats=stats,
|
|
82
|
+
group_func=lambda e: f"Rotated Surface Code d={e.json_metadata['d']}",
|
|
83
|
+
x_func=lambda e: e.json_metadata['p'],
|
|
84
|
+
plot_args_func=lambda k, e: {'marker': "ov*sp^<>8PhH+xXDd|"[k]},
|
|
85
|
+
)
|
|
86
|
+
sinter.plot_discard_rate(
|
|
87
|
+
ax=ax,
|
|
88
|
+
stats=stats,
|
|
89
|
+
group_func=lambda e: f"Rotated Surface Code d={e.json_metadata['d']}",
|
|
90
|
+
x_func=lambda e: e.json_metadata['p'],
|
|
91
|
+
plot_args_func=lambda k, e: {'marker': "ov*sp^<>8PhH+xXDd|"[k]},
|
|
92
|
+
highlight_max_likelihood_factor=None,
|
|
93
|
+
)
|
|
94
|
+
sinter.plot_discard_rate(
|
|
95
|
+
ax=ax,
|
|
96
|
+
stats=stats,
|
|
97
|
+
group_func=lambda e: f"Rotated Surface Code d={e.json_metadata['d']}",
|
|
98
|
+
x_func=lambda e: e.json_metadata['p'],
|
|
99
|
+
plot_args_func=lambda k, e: {'marker': "ov*sp^<>8PhH+xXDd|"[k]},
|
|
100
|
+
failure_units_per_shot_func=lambda stats: stats.json_metadata['d'] * 3,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_group_by():
|
|
105
|
+
assert sinter.group_by([1, 2, 3], key=lambda i: i == 2) == {False: [1, 3], True: [2]}
|
|
106
|
+
assert sinter.group_by(range(10), key=lambda i: i % 3) == {0: [0, 3, 6, 9], 1: [1, 4, 7], 2: [2, 5, 8]}
|
|
107
|
+
assert sinter.group_by([], key=lambda i: 0) == {}
|
|
108
|
+
assert sinter.group_by([1, 2, 3, 1], key=lambda i: 0) == {0: [1, 2, 3, 1]}
|
sinter/_predict.py
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import os.path
|
|
2
|
+
import pathlib
|
|
3
|
+
|
|
4
|
+
import math
|
|
5
|
+
import numpy as np
|
|
6
|
+
import stim
|
|
7
|
+
import tempfile
|
|
8
|
+
from typing import Optional, Union, Dict, TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from sinter._collection import post_selection_mask_from_4th_coord
|
|
11
|
+
from sinter._decoding import Decoder, BUILT_IN_DECODERS, streaming_post_select
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
import sinter
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _converted_on_disk(
|
|
18
|
+
in_path: pathlib.Path,
|
|
19
|
+
out_path: pathlib.Path,
|
|
20
|
+
num_dets: int,
|
|
21
|
+
num_obs: int,
|
|
22
|
+
in_format: str,
|
|
23
|
+
out_format: str) -> pathlib.Path:
|
|
24
|
+
if in_format == out_format:
|
|
25
|
+
return in_path
|
|
26
|
+
raw = stim.read_shot_data_file(
|
|
27
|
+
path=str(in_path),
|
|
28
|
+
format=in_format,
|
|
29
|
+
bit_pack=True,
|
|
30
|
+
num_detectors=num_dets,
|
|
31
|
+
num_observables=num_obs,
|
|
32
|
+
)
|
|
33
|
+
stim.write_shot_data_file(
|
|
34
|
+
data=raw,
|
|
35
|
+
path=str(out_path),
|
|
36
|
+
format=out_format,
|
|
37
|
+
num_detectors=num_dets,
|
|
38
|
+
num_observables=num_obs,
|
|
39
|
+
)
|
|
40
|
+
return out_path
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def predict_on_disk(
|
|
44
|
+
*,
|
|
45
|
+
decoder: str,
|
|
46
|
+
dem_path: Union[str, pathlib.Path],
|
|
47
|
+
dets_path: Union[str, pathlib.Path],
|
|
48
|
+
dets_format: str,
|
|
49
|
+
obs_out_path: Union[str, pathlib.Path],
|
|
50
|
+
obs_out_format: str,
|
|
51
|
+
postselect_detectors_with_non_zero_4th_coord: bool = False,
|
|
52
|
+
discards_out_path: Optional[Union[str, pathlib.Path]] = None,
|
|
53
|
+
discards_out_format: Optional[str] = None,
|
|
54
|
+
custom_decoders: Dict[str, 'sinter.Decoder'] = None,
|
|
55
|
+
) -> None:
|
|
56
|
+
"""Performs decoding and postselection on disk.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
decoder: The decoder to use for decoding.
|
|
60
|
+
dem_path: The detector error model to use to configure the decoder.
|
|
61
|
+
dets_path: Where the detection event data is stored on disk.
|
|
62
|
+
dets_format: The format the detection event data is stored in (e.g. '01' or 'b8').
|
|
63
|
+
obs_out_path: Where to write predicted observable flip data on disk.
|
|
64
|
+
Note that the predicted observable flip data will not included data from shots discarded by postselection.
|
|
65
|
+
Use the data in discards_out_path to determine which shots were discarded.
|
|
66
|
+
obs_out_format: The format to write the observable flip data in (e.g. '01' or 'b8').
|
|
67
|
+
postselect_detectors_with_non_zero_4th_coord: Activates postselection. Detectors that have a non-zero 4th
|
|
68
|
+
coordinate will be postselected. Any shot where a postselected detector fires will be discarded.
|
|
69
|
+
Requires specifying discards_out_path, for indicating which shots were discarded.
|
|
70
|
+
discards_out_path: Only used if postselection is being used. Where to write discard data on disk.
|
|
71
|
+
discards_out_format: The format to write discard data in (e.g. '01' or 'b8').
|
|
72
|
+
custom_decoders: Custom decoders that can be used if requested by name.
|
|
73
|
+
"""
|
|
74
|
+
if (discards_out_path is not None) != (discards_out_format is not None):
|
|
75
|
+
raise ValueError('(discards_out_path is not None) != (discards_out_format is not None)')
|
|
76
|
+
if (discards_out_path is not None) != postselect_detectors_with_non_zero_4th_coord:
|
|
77
|
+
raise ValueError('(discards_out_path is not None) != postselect_detectors_with_non_zero_4th_coord')
|
|
78
|
+
|
|
79
|
+
dem_path = pathlib.Path(dem_path)
|
|
80
|
+
dets_path = pathlib.Path(dets_path)
|
|
81
|
+
obs_out_path = pathlib.Path(obs_out_path)
|
|
82
|
+
if discards_out_path is not None:
|
|
83
|
+
discards_out_path = pathlib.Path(discards_out_path)
|
|
84
|
+
|
|
85
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
86
|
+
tmp_dir = pathlib.Path(tmp_dir)
|
|
87
|
+
decode_obj: Optional[Decoder] = None
|
|
88
|
+
if custom_decoders is not None:
|
|
89
|
+
decode_obj = custom_decoders.get(decoder)
|
|
90
|
+
if decode_obj is None:
|
|
91
|
+
decode_obj = BUILT_IN_DECODERS.get(decoder)
|
|
92
|
+
if decode_obj is None:
|
|
93
|
+
raise NotImplementedError(f"Unrecognized decoder: {decoder!r}")
|
|
94
|
+
|
|
95
|
+
with open(dem_path) as f:
|
|
96
|
+
dem = stim.DetectorErrorModel(f.read())
|
|
97
|
+
|
|
98
|
+
num_dets = dem.num_detectors
|
|
99
|
+
num_det_bytes = math.ceil(num_dets / 8)
|
|
100
|
+
num_obs = dem.num_observables
|
|
101
|
+
|
|
102
|
+
dets_b8_path = _converted_on_disk(
|
|
103
|
+
in_path=dets_path,
|
|
104
|
+
out_path=tmp_dir / 'sinter_dets.b8',
|
|
105
|
+
in_format=dets_format,
|
|
106
|
+
out_format='b8',
|
|
107
|
+
num_dets=num_dets,
|
|
108
|
+
num_obs=0)
|
|
109
|
+
if num_det_bytes == 0:
|
|
110
|
+
raise NotImplementedError("Don't know how many shots there are, because num_det_bytes=0.")
|
|
111
|
+
num_shots = os.path.getsize(dets_b8_path) // num_det_bytes
|
|
112
|
+
|
|
113
|
+
if discards_out_path is not None:
|
|
114
|
+
if discards_out_format == 'b8':
|
|
115
|
+
discards_b8_path = discards_out_path
|
|
116
|
+
else:
|
|
117
|
+
discards_b8_path = tmp_dir / 'sinter_discards.b8'
|
|
118
|
+
post_selection_mask = np.zeros(dtype=np.uint8, shape=math.ceil(num_dets / 8))
|
|
119
|
+
if postselect_detectors_with_non_zero_4th_coord:
|
|
120
|
+
post_selection_mask = post_selection_mask_from_4th_coord(dem)
|
|
121
|
+
kept_dets_b8_path = tmp_dir / 'sinter_dets.kept.b8'
|
|
122
|
+
num_discards = streaming_post_select(
|
|
123
|
+
num_shots=num_shots,
|
|
124
|
+
num_dets=num_dets,
|
|
125
|
+
num_obs=num_obs,
|
|
126
|
+
dets_in_b8=dets_b8_path,
|
|
127
|
+
obs_in_b8=None,
|
|
128
|
+
obs_out_b8=None,
|
|
129
|
+
discards_out_b8=discards_b8_path,
|
|
130
|
+
dets_out_b8=kept_dets_b8_path,
|
|
131
|
+
post_mask=post_selection_mask,
|
|
132
|
+
)
|
|
133
|
+
assert discards_out_format is not None
|
|
134
|
+
_converted_on_disk(
|
|
135
|
+
in_path=discards_b8_path,
|
|
136
|
+
out_path=discards_out_path,
|
|
137
|
+
out_format=discards_out_format,
|
|
138
|
+
in_format='b8',
|
|
139
|
+
num_dets=1,
|
|
140
|
+
num_obs=0,
|
|
141
|
+
)
|
|
142
|
+
num_kept_shots = num_shots - num_discards
|
|
143
|
+
else:
|
|
144
|
+
kept_dets_b8_path = dets_b8_path
|
|
145
|
+
num_kept_shots = num_shots
|
|
146
|
+
if postselect_detectors_with_non_zero_4th_coord:
|
|
147
|
+
raise ValueError('postselect_detectors_with_non_zero_4th_coord and discards_out_path is None')
|
|
148
|
+
|
|
149
|
+
if obs_out_format != 'b8':
|
|
150
|
+
obs_inter = tmp_dir / 'sinter_obs_inter.b8'
|
|
151
|
+
else:
|
|
152
|
+
obs_inter = obs_out_path
|
|
153
|
+
decode_obj.decode_via_files(
|
|
154
|
+
num_shots=num_kept_shots,
|
|
155
|
+
num_dets=num_dets,
|
|
156
|
+
num_obs=num_obs,
|
|
157
|
+
dem_path=dem_path,
|
|
158
|
+
dets_b8_in_path=kept_dets_b8_path,
|
|
159
|
+
obs_predictions_b8_out_path=obs_inter,
|
|
160
|
+
tmp_dir=tmp_dir,
|
|
161
|
+
)
|
|
162
|
+
_converted_on_disk(
|
|
163
|
+
in_path=obs_inter,
|
|
164
|
+
out_path=obs_out_path,
|
|
165
|
+
out_format=obs_out_format,
|
|
166
|
+
in_format='b8',
|
|
167
|
+
num_dets=0,
|
|
168
|
+
num_obs=num_obs,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def predict_discards_bit_packed(
|
|
173
|
+
*,
|
|
174
|
+
dem: stim.DetectorErrorModel,
|
|
175
|
+
dets_bit_packed: np.ndarray,
|
|
176
|
+
postselect_detectors_with_non_zero_4th_coord: bool,
|
|
177
|
+
) -> np.ndarray:
|
|
178
|
+
"""Determines which shots to discard due to postselected detectors firing.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
dem: The detector error model the detector data applies to.
|
|
182
|
+
This is also where coordinate data is read from, in order to
|
|
183
|
+
determine which detectors to postselect as not having fired.
|
|
184
|
+
dets_bit_packed: A uint8 numpy array with shape
|
|
185
|
+
(num_shots, math.ceil(num_dets / 8)). Contains bit packed detection
|
|
186
|
+
event data.
|
|
187
|
+
postselect_detectors_with_non_zero_4th_coord: Determines how
|
|
188
|
+
postselection is done. Currently, this is the only option so it has
|
|
189
|
+
to be set to True. Any detector from the detector error model that
|
|
190
|
+
specifies coordinate data with at least four coordinates where the
|
|
191
|
+
fourth coordinate (coord index 3) is non-zero will be postselected.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
A numpy bool_ array with shape (num_shots,) where False means not discarded and
|
|
195
|
+
True means yes discarded.
|
|
196
|
+
"""
|
|
197
|
+
if not postselect_detectors_with_non_zero_4th_coord:
|
|
198
|
+
raise ValueError("not postselect_detectors_with_non_zero_4th_coord")
|
|
199
|
+
num_dets = dem.num_detectors
|
|
200
|
+
nb = math.ceil(num_dets / 8)
|
|
201
|
+
if len(dets_bit_packed.shape) != 2:
|
|
202
|
+
raise ValueError(f'len(dets_data_bit_packed.shape={dets_bit_packed.shape}) != 2')
|
|
203
|
+
if dets_bit_packed.shape[1] != nb:
|
|
204
|
+
raise ValueError(f'dets_data_bit_packed.shape[1]={dets_bit_packed.shape[1]} != math.ceil(dem.num_detectors={dem.num_detectors} / 8)')
|
|
205
|
+
if dets_bit_packed.dtype != np.uint8:
|
|
206
|
+
raise ValueError(f'dets_data_bit_packed.dtype={dets_bit_packed.dtype} != np.uint8')
|
|
207
|
+
|
|
208
|
+
post_selection_mask = np.zeros(dtype=np.uint8, shape=nb)
|
|
209
|
+
if postselect_detectors_with_non_zero_4th_coord:
|
|
210
|
+
for k, coord in dem.get_detector_coordinates().items():
|
|
211
|
+
if len(coord) >= 4 and coord[3]:
|
|
212
|
+
post_selection_mask[k // 8] |= 1 << (k % 8)
|
|
213
|
+
return np.any(dets_bit_packed & post_selection_mask, axis=1)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def predict_observables(
|
|
217
|
+
*,
|
|
218
|
+
dem: stim.DetectorErrorModel,
|
|
219
|
+
dets: np.ndarray,
|
|
220
|
+
decoder: str,
|
|
221
|
+
bit_pack_result: bool = False,
|
|
222
|
+
custom_decoders: Optional[Dict[str, 'sinter.Decoder']] = None,
|
|
223
|
+
) -> np.ndarray:
|
|
224
|
+
"""Predicts which observables were flipped based on detection event data.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
dem: The detector error model the detector data applies to.
|
|
228
|
+
This is also where coordinate data is read from, in order to
|
|
229
|
+
determine which detectors to postselect as not having fired.
|
|
230
|
+
dets: The detection event data. Can be bit packed or not bit packed.
|
|
231
|
+
If dtype=np.bool_ then shape=(num_shots, num_detectors)
|
|
232
|
+
If dtype=np.uint8 then shape=(num_shots, math.ceil(num_detectors/8))
|
|
233
|
+
decoder: The decoder to use for decoding, e.g. "pymatching".
|
|
234
|
+
bit_pack_result: Defaults to False. Determines if the result is bit packed
|
|
235
|
+
or not.
|
|
236
|
+
custom_decoders: Custom decoders that can be used if requested by name.
|
|
237
|
+
If not specified, only decoders built into sinter, such as
|
|
238
|
+
'pymatching' and 'fusion_blossom', can be used.
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
If bit_packed_result=False (default):
|
|
242
|
+
dtype=np.bool_
|
|
243
|
+
shape=(num_shots, num_observables)
|
|
244
|
+
If bit_packed_result=True:
|
|
245
|
+
dtype=np.uint8
|
|
246
|
+
shape=(num_shots, math.ceil(num_observables / 8))
|
|
247
|
+
|
|
248
|
+
Examples:
|
|
249
|
+
>>> import numpy as np
|
|
250
|
+
>>> import sinter
|
|
251
|
+
>>> import stim
|
|
252
|
+
>>> dem = stim.DetectorErrorModel('''
|
|
253
|
+
... error(0.1) D0 L0
|
|
254
|
+
... error(0.1) D0 D1
|
|
255
|
+
... error(0.1) D1
|
|
256
|
+
... ''')
|
|
257
|
+
>>> sinter.predict_observables(
|
|
258
|
+
... dem=dem,
|
|
259
|
+
... dets=np.array([
|
|
260
|
+
... [False, False],
|
|
261
|
+
... [True, False],
|
|
262
|
+
... [False, True],
|
|
263
|
+
... [True, True],
|
|
264
|
+
... ], dtype=np.bool_),
|
|
265
|
+
... decoder='vacuous', # try replacing with 'pymatching'
|
|
266
|
+
... bit_pack_result=False,
|
|
267
|
+
... )
|
|
268
|
+
array([[False],
|
|
269
|
+
[False],
|
|
270
|
+
[False],
|
|
271
|
+
[False]])
|
|
272
|
+
"""
|
|
273
|
+
|
|
274
|
+
if dets.dtype == np.bool_:
|
|
275
|
+
dets = np.packbits(dets, axis=1, bitorder='little')
|
|
276
|
+
result = predict_observables_bit_packed(
|
|
277
|
+
dem=dem,
|
|
278
|
+
dets_bit_packed=dets,
|
|
279
|
+
decoder=decoder,
|
|
280
|
+
custom_decoders=custom_decoders,
|
|
281
|
+
)
|
|
282
|
+
if not bit_pack_result:
|
|
283
|
+
return np.unpackbits(result, axis=1, bitorder='little', count=dem.num_observables).astype(np.bool_)
|
|
284
|
+
return result
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def predict_observables_bit_packed(
|
|
288
|
+
*,
|
|
289
|
+
dem: stim.DetectorErrorModel,
|
|
290
|
+
dets_bit_packed: np.ndarray,
|
|
291
|
+
decoder: str,
|
|
292
|
+
custom_decoders: Optional[Dict[str, 'sinter.Decoder']] = None,
|
|
293
|
+
) -> np.ndarray:
|
|
294
|
+
"""Predicts which observables were flipped based on detection event data.
|
|
295
|
+
|
|
296
|
+
This method predates `sinter.predict_observables` gaining optional bit
|
|
297
|
+
packing arguments.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
dem: The detector error model the detector data applies to.
|
|
301
|
+
This is also where coordinate data is read from, in order to
|
|
302
|
+
determine which detectors to postselect as not having fired.
|
|
303
|
+
dets_bit_packed: A uint8 numpy array with shape
|
|
304
|
+
(num_shots, math.ceil(num_dets / 8)). Contains bit packed detection
|
|
305
|
+
event data.
|
|
306
|
+
decoder: The decoder to use for decoding, e.g. "pymatching".
|
|
307
|
+
custom_decoders: Custom decoders that can be used if requested by name.
|
|
308
|
+
If not specified, only decoders built into sinter, such as
|
|
309
|
+
'pymatching' and 'fusion_blossom', can be used.
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
A numpy uint8 array with shape (num_shots, math.ceil(num_obs / 8)).
|
|
313
|
+
Contains bit packed observable prediction data.
|
|
314
|
+
|
|
315
|
+
Examples:
|
|
316
|
+
>>> import numpy as np
|
|
317
|
+
>>> import sinter
|
|
318
|
+
>>> import stim
|
|
319
|
+
>>> dem = stim.DetectorErrorModel('''
|
|
320
|
+
... error(0.1) D0 L0
|
|
321
|
+
... error(0.1) D0 D1
|
|
322
|
+
... error(0.1) D1
|
|
323
|
+
... ''')
|
|
324
|
+
>>> sinter.predict_observables_bit_packed(
|
|
325
|
+
... dem=dem,
|
|
326
|
+
... dets_bit_packed=np.array([
|
|
327
|
+
... [0b00],
|
|
328
|
+
... [0b01],
|
|
329
|
+
... [0b10],
|
|
330
|
+
... [0b11],
|
|
331
|
+
... ], dtype=np.uint8),
|
|
332
|
+
... decoder='vacuous', # try replacing with 'pymatching'
|
|
333
|
+
... )
|
|
334
|
+
array([[0],
|
|
335
|
+
[0],
|
|
336
|
+
[0],
|
|
337
|
+
[0]], dtype=uint8)
|
|
338
|
+
"""
|
|
339
|
+
|
|
340
|
+
decode_obj: Optional[Decoder] = None
|
|
341
|
+
if custom_decoders is not None:
|
|
342
|
+
decode_obj = custom_decoders.get(decoder)
|
|
343
|
+
if decode_obj is None:
|
|
344
|
+
decode_obj = BUILT_IN_DECODERS.get(decoder)
|
|
345
|
+
if decode_obj is None:
|
|
346
|
+
raise NotImplementedError(f"Unrecognized decoder: {decoder!r}")
|
|
347
|
+
|
|
348
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
349
|
+
tmp_dir = pathlib.Path(tmp_dir)
|
|
350
|
+
dets_b8_path = tmp_dir / 'sinter_dets.b8'
|
|
351
|
+
pred_b8_path = tmp_dir / 'sinter_predictions.b8'
|
|
352
|
+
dem_path = tmp_dir / 'dem.dem'
|
|
353
|
+
dem.to_file(dem_path)
|
|
354
|
+
num_dets = dem.num_detectors
|
|
355
|
+
num_obs = dem.num_observables
|
|
356
|
+
|
|
357
|
+
stim.write_shot_data_file(
|
|
358
|
+
data=dets_bit_packed,
|
|
359
|
+
path=str(dets_b8_path),
|
|
360
|
+
format='b8',
|
|
361
|
+
num_detectors=num_dets,
|
|
362
|
+
num_observables=0,
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
decode_obj.decode_via_files(
|
|
366
|
+
num_shots=dets_bit_packed.shape[0],
|
|
367
|
+
num_dets=num_dets,
|
|
368
|
+
num_obs=num_obs,
|
|
369
|
+
dem_path=dem_path,
|
|
370
|
+
dets_b8_in_path=dets_b8_path,
|
|
371
|
+
obs_predictions_b8_out_path=pred_b8_path,
|
|
372
|
+
tmp_dir=tmp_dir,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
return stim.read_shot_data_file(
|
|
376
|
+
path=str(pred_b8_path),
|
|
377
|
+
format='b8',
|
|
378
|
+
bit_pack=True,
|
|
379
|
+
num_detectors=0,
|
|
380
|
+
num_observables=num_obs,
|
|
381
|
+
)
|