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.

Files changed (62) hide show
  1. sinter/__init__.py +47 -0
  2. sinter/_collection/__init__.py +10 -0
  3. sinter/_collection/_collection.py +480 -0
  4. sinter/_collection/_collection_manager.py +581 -0
  5. sinter/_collection/_collection_manager_test.py +287 -0
  6. sinter/_collection/_collection_test.py +317 -0
  7. sinter/_collection/_collection_worker_loop.py +35 -0
  8. sinter/_collection/_collection_worker_state.py +259 -0
  9. sinter/_collection/_collection_worker_test.py +222 -0
  10. sinter/_collection/_mux_sampler.py +56 -0
  11. sinter/_collection/_printer.py +65 -0
  12. sinter/_collection/_sampler_ramp_throttled.py +66 -0
  13. sinter/_collection/_sampler_ramp_throttled_test.py +144 -0
  14. sinter/_command/__init__.py +0 -0
  15. sinter/_command/_main.py +39 -0
  16. sinter/_command/_main_collect.py +350 -0
  17. sinter/_command/_main_collect_test.py +482 -0
  18. sinter/_command/_main_combine.py +84 -0
  19. sinter/_command/_main_combine_test.py +153 -0
  20. sinter/_command/_main_plot.py +817 -0
  21. sinter/_command/_main_plot_test.py +445 -0
  22. sinter/_command/_main_predict.py +75 -0
  23. sinter/_command/_main_predict_test.py +36 -0
  24. sinter/_data/__init__.py +20 -0
  25. sinter/_data/_anon_task_stats.py +89 -0
  26. sinter/_data/_anon_task_stats_test.py +35 -0
  27. sinter/_data/_collection_options.py +106 -0
  28. sinter/_data/_collection_options_test.py +24 -0
  29. sinter/_data/_csv_out.py +74 -0
  30. sinter/_data/_existing_data.py +173 -0
  31. sinter/_data/_existing_data_test.py +41 -0
  32. sinter/_data/_task.py +311 -0
  33. sinter/_data/_task_stats.py +244 -0
  34. sinter/_data/_task_stats_test.py +140 -0
  35. sinter/_data/_task_test.py +38 -0
  36. sinter/_decoding/__init__.py +16 -0
  37. sinter/_decoding/_decoding.py +419 -0
  38. sinter/_decoding/_decoding_all_built_in_decoders.py +25 -0
  39. sinter/_decoding/_decoding_decoder_class.py +161 -0
  40. sinter/_decoding/_decoding_fusion_blossom.py +193 -0
  41. sinter/_decoding/_decoding_mwpf.py +302 -0
  42. sinter/_decoding/_decoding_pymatching.py +81 -0
  43. sinter/_decoding/_decoding_test.py +480 -0
  44. sinter/_decoding/_decoding_vacuous.py +38 -0
  45. sinter/_decoding/_perfectionist_sampler.py +38 -0
  46. sinter/_decoding/_sampler.py +72 -0
  47. sinter/_decoding/_stim_then_decode_sampler.py +222 -0
  48. sinter/_decoding/_stim_then_decode_sampler_test.py +192 -0
  49. sinter/_plotting.py +619 -0
  50. sinter/_plotting_test.py +108 -0
  51. sinter/_predict.py +381 -0
  52. sinter/_predict_test.py +227 -0
  53. sinter/_probability_util.py +519 -0
  54. sinter/_probability_util_test.py +281 -0
  55. sinter-1.15.0.data/data/README.md +332 -0
  56. sinter-1.15.0.data/data/readme_example_plot.png +0 -0
  57. sinter-1.15.0.data/data/requirements.txt +4 -0
  58. sinter-1.15.0.dist-info/METADATA +354 -0
  59. sinter-1.15.0.dist-info/RECORD +62 -0
  60. sinter-1.15.0.dist-info/WHEEL +5 -0
  61. sinter-1.15.0.dist-info/entry_points.txt +2 -0
  62. sinter-1.15.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,81 @@
1
+ from sinter._decoding._decoding_decoder_class import Decoder, CompiledDecoder
2
+
3
+
4
+ class PyMatchingCompiledDecoder(CompiledDecoder):
5
+ def __init__(self, matcher: 'pymatching.Matching'):
6
+ self.matcher = matcher
7
+
8
+ def decode_shots_bit_packed(
9
+ self,
10
+ *,
11
+ bit_packed_detection_event_data: 'np.ndarray',
12
+ ) -> 'np.ndarray':
13
+ return self.matcher.decode_batch(
14
+ shots=bit_packed_detection_event_data,
15
+ bit_packed_shots=True,
16
+ bit_packed_predictions=True,
17
+ return_weights=False,
18
+ )
19
+
20
+
21
+ class PyMatchingDecoder(Decoder):
22
+ """Use pymatching to predict observables from detection events."""
23
+
24
+ def compile_decoder_for_dem(self, *, dem: 'stim.DetectorErrorModel') -> CompiledDecoder:
25
+ try:
26
+ import pymatching
27
+ except ImportError as ex:
28
+ raise ImportError(
29
+ "The decoder 'pymatching' isn't installed\n"
30
+ "To fix this, install the python package 'pymatching' into your environment.\n"
31
+ "For example, if you are using pip, run `pip install pymatching`.\n"
32
+ ) from ex
33
+
34
+ return PyMatchingCompiledDecoder(pymatching.Matching.from_detector_error_model(dem))
35
+
36
+ def decode_via_files(self,
37
+ *,
38
+ num_shots: int,
39
+ num_dets: int,
40
+ num_obs: int,
41
+ dem_path: 'pathlib.Path',
42
+ dets_b8_in_path: 'pathlib.Path',
43
+ obs_predictions_b8_out_path: 'pathlib.Path',
44
+ tmp_dir: 'pathlib.Path',
45
+ ) -> None:
46
+ try:
47
+ import pymatching
48
+ except ImportError as ex:
49
+ raise ImportError(
50
+ "The decoder 'pymatching' isn't installed\n"
51
+ "To fix this, install the python package 'pymatching' into your environment.\n"
52
+ "For example, if you are using pip, run `pip install pymatching`.\n"
53
+ ) from ex
54
+
55
+ if num_dets == 0:
56
+ with open(obs_predictions_b8_out_path, 'wb') as f:
57
+ f.write(b'\0' * (num_obs * num_shots))
58
+ return
59
+
60
+ if not hasattr(pymatching, 'cli'):
61
+ raise ValueError("""
62
+ The installed version of pymatching has no `pymatching.cli` method.
63
+ sinter requires pymatching 2.1.0 or later.
64
+ If you're using pip to install packages, this can be fixed by running
65
+
66
+ ```
67
+ pip install "pymatching~=2.1" --upgrade
68
+ ```
69
+
70
+ """)
71
+
72
+ result = pymatching.cli(command_line_args=[
73
+ "predict",
74
+ "--dem", str(dem_path),
75
+ "--in", str(dets_b8_in_path),
76
+ "--in_format", "b8",
77
+ "--out", str(obs_predictions_b8_out_path),
78
+ "--out_format", "b8",
79
+ ])
80
+ if result:
81
+ raise ValueError("pymatching.cli returned a non-zero exit code")
@@ -0,0 +1,480 @@
1
+ import os
2
+
3
+ import pathlib
4
+ import tempfile
5
+ from typing import Dict, List, Optional, Tuple
6
+
7
+ import numpy as np
8
+ import pytest
9
+
10
+ import sinter
11
+ import stim
12
+
13
+ from sinter import CompiledDecoder
14
+ from sinter._collection import post_selection_mask_from_4th_coord
15
+ from sinter._decoding._decoding_all_built_in_decoders import BUILT_IN_DECODERS
16
+ from sinter._decoding._decoding import sample_decode
17
+ from sinter._decoding._decoding_vacuous import VacuousDecoder
18
+
19
+
20
+ def get_test_decoders() -> Tuple[List[str], Dict[str, sinter.Decoder]]:
21
+ available_decoders = list(BUILT_IN_DECODERS.keys())
22
+ custom_decoders = {}
23
+ try:
24
+ import pymatching
25
+ except ImportError:
26
+ available_decoders.remove('pymatching')
27
+ try:
28
+ import fusion_blossom
29
+ except ImportError:
30
+ available_decoders.remove('fusion_blossom')
31
+ try:
32
+ import mwpf
33
+ except ImportError:
34
+ available_decoders.remove('hypergraph_union_find')
35
+ available_decoders.remove('mw_parity_factor')
36
+
37
+ e = os.environ.get('SINTER_PYTEST_CUSTOM_DECODERS')
38
+ if e is not None:
39
+ for term in e.split(';'):
40
+ module, method = term.split(':')
41
+ for name, obj in getattr(__import__(module), method)().items():
42
+ custom_decoders[name] = obj
43
+ available_decoders.append(name)
44
+
45
+ available_decoders.append("also_vacuous")
46
+ custom_decoders["also_vacuous"] = VacuousDecoder()
47
+ return available_decoders, custom_decoders
48
+
49
+ TEST_DECODER_NAMES, TEST_CUSTOM_DECODERS = get_test_decoders()
50
+
51
+ DECODER_CASES = [
52
+ (decoder, force_streaming)
53
+ for decoder in TEST_DECODER_NAMES
54
+ for force_streaming in [None, True]
55
+ ]
56
+
57
+
58
+ @pytest.mark.parametrize('decoder,force_streaming', DECODER_CASES)
59
+ def test_decode_repetition_code(decoder: str, force_streaming: Optional[bool]):
60
+ circuit = stim.Circuit.generated('repetition_code:memory',
61
+ rounds=3,
62
+ distance=3,
63
+ after_clifford_depolarization=0.05)
64
+ result = sample_decode(
65
+ circuit_obj=circuit,
66
+ circuit_path=None,
67
+ dem_obj=circuit.detector_error_model(decompose_errors=True),
68
+ dem_path=None,
69
+ num_shots=1000,
70
+ decoder=decoder,
71
+ __private__unstable__force_decode_on_disk=force_streaming,
72
+ custom_decoders=TEST_CUSTOM_DECODERS,
73
+ )
74
+ assert result.discards == 0
75
+ if 'vacuous' not in decoder:
76
+ assert 1 <= result.errors <= 100
77
+ assert result.shots == 1000
78
+
79
+
80
+ @pytest.mark.parametrize('decoder,force_streaming', DECODER_CASES)
81
+ def test_decode_surface_code(decoder: str, force_streaming: Optional[bool]):
82
+ circuit = stim.Circuit.generated(
83
+ "surface_code:rotated_memory_x",
84
+ distance=3,
85
+ rounds=15,
86
+ after_clifford_depolarization=0.001,
87
+ )
88
+ stats = sample_decode(
89
+ num_shots=1000,
90
+ circuit_obj=circuit,
91
+ circuit_path=None,
92
+ dem_obj=circuit.detector_error_model(decompose_errors=True),
93
+ dem_path=None,
94
+ decoder=decoder,
95
+ __private__unstable__force_decode_on_disk=force_streaming,
96
+ custom_decoders=TEST_CUSTOM_DECODERS,
97
+ )
98
+ if 'vacuous' not in decoder:
99
+ assert 0 <= stats.errors <= 50
100
+
101
+
102
+ @pytest.mark.parametrize('decoder,force_streaming', DECODER_CASES)
103
+ def test_empty(decoder: str, force_streaming: Optional[bool]):
104
+ circuit = stim.Circuit()
105
+ result = sample_decode(
106
+ circuit_obj=circuit,
107
+ circuit_path=None,
108
+ dem_obj=circuit.detector_error_model(decompose_errors=True),
109
+ dem_path=None,
110
+ num_shots=1000,
111
+ decoder=decoder,
112
+ __private__unstable__force_decode_on_disk=force_streaming,
113
+ custom_decoders=TEST_CUSTOM_DECODERS,
114
+ )
115
+ assert result.discards == 0
116
+ assert result.shots == 1000
117
+ assert result.errors == 0
118
+
119
+
120
+ @pytest.mark.parametrize('decoder,force_streaming', DECODER_CASES)
121
+ def test_no_observables(decoder: str, force_streaming: Optional[bool]):
122
+ circuit = stim.Circuit("""
123
+ X_ERROR(0.1) 0
124
+ M 0
125
+ DETECTOR rec[-1]
126
+ """)
127
+ result = sample_decode(
128
+ circuit_obj=circuit,
129
+ circuit_path=None,
130
+ dem_obj=circuit.detector_error_model(decompose_errors=True),
131
+ dem_path=None,
132
+ num_shots=1000,
133
+ decoder=decoder,
134
+ __private__unstable__force_decode_on_disk=force_streaming,
135
+ custom_decoders=TEST_CUSTOM_DECODERS,
136
+ )
137
+ assert result.discards == 0
138
+ assert result.shots == 1000
139
+ assert result.errors == 0
140
+
141
+
142
+ @pytest.mark.parametrize('decoder,force_streaming', DECODER_CASES)
143
+ def test_invincible_observables(decoder: str, force_streaming: Optional[bool]):
144
+ circuit = stim.Circuit("""
145
+ X_ERROR(0.1) 0
146
+ M 0 1
147
+ DETECTOR rec[-2]
148
+ OBSERVABLE_INCLUDE(1) rec[-1]
149
+ """)
150
+ result = sample_decode(
151
+ circuit_obj=circuit,
152
+ circuit_path=None,
153
+ dem_obj=circuit.detector_error_model(decompose_errors=True),
154
+ dem_path=None,
155
+ num_shots=1000,
156
+ decoder=decoder,
157
+ __private__unstable__force_decode_on_disk=force_streaming,
158
+ custom_decoders=TEST_CUSTOM_DECODERS,
159
+ )
160
+ assert result.discards == 0
161
+ assert result.shots == 1000
162
+ assert result.errors == 0
163
+
164
+
165
+ @pytest.mark.parametrize('decoder,force_streaming,offset', [(a, b, c) for a, b in DECODER_CASES for c in range(8)])
166
+ def test_observable_offsets_mod8(decoder: str, force_streaming: bool, offset: int):
167
+ circuit = stim.Circuit("""
168
+ X_ERROR(0.1) 0
169
+ MR 0
170
+ DETECTOR rec[-1]
171
+ """) * (8 + offset) + stim.Circuit("""
172
+ X_ERROR(0.1) 0
173
+ MR 0
174
+ OBSERVABLE_INCLUDE(0) rec[-1]
175
+ """)
176
+ result = sample_decode(
177
+ circuit_obj=circuit,
178
+ circuit_path=None,
179
+ dem_obj=circuit.detector_error_model(decompose_errors=True),
180
+ dem_path=None,
181
+ num_shots=1000,
182
+ decoder=decoder,
183
+ __private__unstable__force_decode_on_disk=force_streaming,
184
+ custom_decoders=TEST_CUSTOM_DECODERS,
185
+ )
186
+ assert result.discards == 0
187
+ assert result.shots == 1000
188
+ assert 50 <= result.errors <= 150
189
+
190
+
191
+ @pytest.mark.parametrize('decoder,force_streaming', DECODER_CASES)
192
+ def test_no_detectors(decoder: str, force_streaming: Optional[bool]):
193
+ circuit = stim.Circuit("""
194
+ X_ERROR(0.1) 0
195
+ M 0
196
+ OBSERVABLE_INCLUDE(0) rec[-1]
197
+ """)
198
+ result = sample_decode(
199
+ circuit_obj=circuit,
200
+ circuit_path=None,
201
+ dem_obj=circuit.detector_error_model(decompose_errors=True),
202
+ dem_path=None,
203
+ num_shots=1000,
204
+ decoder=decoder,
205
+ __private__unstable__force_decode_on_disk=force_streaming,
206
+ custom_decoders=TEST_CUSTOM_DECODERS,
207
+ )
208
+ assert result.discards == 0
209
+ assert 50 <= result.errors <= 150
210
+
211
+
212
+ @pytest.mark.parametrize('decoder,force_streaming', DECODER_CASES)
213
+ def test_no_detectors_with_post_mask(decoder: str, force_streaming: Optional[bool]):
214
+ circuit = stim.Circuit("""
215
+ X_ERROR(0.1) 0
216
+ M 0
217
+ OBSERVABLE_INCLUDE(0) rec[-1]
218
+ """)
219
+ result = sample_decode(
220
+ circuit_obj=circuit,
221
+ circuit_path=None,
222
+ dem_obj=circuit.detector_error_model(decompose_errors=True),
223
+ dem_path=None,
224
+ post_mask=np.array([], dtype=np.uint8),
225
+ num_shots=1000,
226
+ decoder=decoder,
227
+ __private__unstable__force_decode_on_disk=force_streaming,
228
+ custom_decoders=TEST_CUSTOM_DECODERS,
229
+ )
230
+ assert result.discards == 0
231
+ assert 50 <= result.errors <= 150
232
+
233
+
234
+ @pytest.mark.parametrize('decoder,force_streaming', DECODER_CASES)
235
+ def test_post_selection(decoder: str, force_streaming: Optional[bool]):
236
+ circuit = stim.Circuit("""
237
+ X_ERROR(0.6) 0
238
+ M 0
239
+ DETECTOR(2, 0, 0, 1) rec[-1]
240
+ OBSERVABLE_INCLUDE(0) rec[-1]
241
+
242
+ X_ERROR(0.5) 1
243
+ M 1
244
+ DETECTOR(1, 0, 0) rec[-1]
245
+ OBSERVABLE_INCLUDE(0) rec[-1]
246
+
247
+ X_ERROR(0.1) 2
248
+ M 2
249
+ OBSERVABLE_INCLUDE(0) rec[-1]
250
+ """)
251
+ result = sample_decode(
252
+ circuit_obj=circuit,
253
+ circuit_path=None,
254
+ dem_obj=circuit.detector_error_model(decompose_errors=True),
255
+ dem_path=None,
256
+ post_mask=post_selection_mask_from_4th_coord(circuit),
257
+ num_shots=2000,
258
+ decoder=decoder,
259
+ __private__unstable__force_decode_on_disk=force_streaming,
260
+ custom_decoders=TEST_CUSTOM_DECODERS,
261
+ )
262
+ assert 1050 <= result.discards <= 1350
263
+ if 'vacuous' not in decoder:
264
+ assert 40 <= result.errors <= 160
265
+
266
+
267
+ @pytest.mark.parametrize('decoder,force_streaming', DECODER_CASES)
268
+ def test_observable_post_selection(decoder: str, force_streaming: Optional[bool]):
269
+ circuit = stim.Circuit("""
270
+ X_ERROR(0.1) 0
271
+ X_ERROR(0.2) 1
272
+ M 0 1
273
+ OBSERVABLE_INCLUDE(0) rec[-1]
274
+ OBSERVABLE_INCLUDE(1) rec[-1] rec[-2]
275
+ """)
276
+ result = sample_decode(
277
+ circuit_obj=circuit,
278
+ circuit_path=None,
279
+ dem_obj=circuit.detector_error_model(decompose_errors=True),
280
+ dem_path=None,
281
+ post_mask=None,
282
+ postselected_observable_mask=np.array([1], dtype=np.uint8),
283
+ num_shots=10000,
284
+ decoder=decoder,
285
+ __private__unstable__force_decode_on_disk=force_streaming,
286
+ custom_decoders=TEST_CUSTOM_DECODERS,
287
+ )
288
+ np.testing.assert_allclose(result.discards / result.shots, 0.2, atol=0.1)
289
+ if 'vacuous' not in decoder:
290
+ np.testing.assert_allclose(result.errors / (result.shots - result.discards), 0.1, atol=0.05)
291
+
292
+
293
+ @pytest.mark.parametrize('decoder,force_streaming', DECODER_CASES)
294
+ def test_error_splitting(decoder: str, force_streaming: Optional[bool]):
295
+ circuit = stim.Circuit("""
296
+ X_ERROR(0.1) 0
297
+ X_ERROR(0.2) 1
298
+ M 0 1
299
+ OBSERVABLE_INCLUDE(0) rec[-1]
300
+ OBSERVABLE_INCLUDE(1) rec[-1] rec[-2]
301
+ """)
302
+ result = sample_decode(
303
+ circuit_obj=circuit,
304
+ circuit_path=None,
305
+ dem_obj=circuit.detector_error_model(decompose_errors=True),
306
+ dem_path=None,
307
+ post_mask=None,
308
+ num_shots=10000,
309
+ decoder=decoder,
310
+ count_observable_error_combos=True,
311
+ __private__unstable__force_decode_on_disk=force_streaming,
312
+ custom_decoders=TEST_CUSTOM_DECODERS,
313
+ )
314
+ assert result.discards == 0
315
+ assert set(result.custom_counts.keys()) == {'obs_mistake_mask=E_', 'obs_mistake_mask=_E', 'obs_mistake_mask=EE'}
316
+ if 'vacuous' not in decoder:
317
+ np.testing.assert_allclose(result.errors / result.shots, 1 - 0.8 * 0.9, atol=0.05)
318
+ np.testing.assert_allclose(result.custom_counts['obs_mistake_mask=E_'] / result.shots, 0.1 * 0.2, atol=0.05)
319
+ np.testing.assert_allclose(result.custom_counts['obs_mistake_mask=_E'] / result.shots, 0.1 * 0.8, atol=0.05)
320
+ np.testing.assert_allclose(result.custom_counts['obs_mistake_mask=EE'] / result.shots, 0.9 * 0.2, atol=0.05)
321
+
322
+
323
+ @pytest.mark.parametrize('decoder,force_streaming', DECODER_CASES)
324
+ def test_detector_counting(decoder: str, force_streaming: Optional[bool]):
325
+ circuit = stim.Circuit("""
326
+ X_ERROR(0.1) 0
327
+ X_ERROR(0.2) 1
328
+ M 0 1
329
+ DETECTOR rec[-1]
330
+ DETECTOR rec[-2]
331
+ OBSERVABLE_INCLUDE(0) rec[-1]
332
+ OBSERVABLE_INCLUDE(1) rec[-1] rec[-2]
333
+ """)
334
+ result = sample_decode(
335
+ circuit_obj=circuit,
336
+ circuit_path=None,
337
+ dem_obj=circuit.detector_error_model(decompose_errors=True),
338
+ dem_path=None,
339
+ post_mask=None,
340
+ num_shots=10000,
341
+ decoder=decoder,
342
+ count_detection_events=True,
343
+ __private__unstable__force_decode_on_disk=force_streaming,
344
+ custom_decoders=TEST_CUSTOM_DECODERS,
345
+ )
346
+ assert result.discards == 0
347
+ assert result.custom_counts['detectors_checked'] == 20000
348
+ assert 0.3 * 10000 * 0.5 <= result.custom_counts['detection_events'] <= 0.3 * 10000 * 2.0
349
+ assert set(result.custom_counts.keys()) == {'detectors_checked', 'detection_events'}
350
+
351
+
352
+ @pytest.mark.parametrize('decoder,force_streaming', DECODER_CASES)
353
+ def test_decode_fails_correctly(decoder: str, force_streaming: Optional[bool]):
354
+ decoder_obj = BUILT_IN_DECODERS.get(decoder)
355
+ with tempfile.TemporaryDirectory() as d:
356
+ d = pathlib.Path(d)
357
+ circuit = stim.Circuit("""
358
+ REPEAT 9 {
359
+ MR(0.001) 0
360
+ DETECTOR rec[-1]
361
+ OBSERVABLE_INCLUDE(0) rec[-1]
362
+ }
363
+ """)
364
+ dem = circuit.detector_error_model()
365
+ circuit.to_file(d / 'circuit.stim')
366
+ dem.to_file(d / 'dem.dem')
367
+ with open(d / 'bad_dets.b8', 'wb') as f:
368
+ f.write(b'!')
369
+
370
+ if 'vacuous' not in decoder:
371
+ with pytest.raises(Exception):
372
+ decoder_obj.decode_via_files(
373
+ num_shots=1,
374
+ num_dets=dem.num_detectors,
375
+ num_obs=dem.num_observables,
376
+ dem_path=d / 'dem.dem',
377
+ dets_b8_in_path=d / 'bad_dets.b8',
378
+ obs_predictions_b8_out_path=d / 'predict.b8',
379
+ tmp_dir=d,
380
+ )
381
+
382
+
383
+ @pytest.mark.parametrize('decoder', TEST_DECODER_NAMES)
384
+ def test_full_scale(decoder: str):
385
+ result, = sinter.collect(
386
+ num_workers=2,
387
+ tasks=[sinter.Task(circuit=stim.Circuit())],
388
+ decoders=[decoder],
389
+ max_shots=1000,
390
+ custom_decoders=TEST_CUSTOM_DECODERS,
391
+ )
392
+ assert result.discards == 0
393
+ assert result.shots == 1000
394
+ assert result.errors == 0
395
+
396
+
397
+ def test_infer_decode_via_files_from_decode_from_compile_decoder_for_dem():
398
+ class IncompleteDecoder(sinter.Decoder):
399
+ pass
400
+
401
+ class WrongDecoder(sinter.Decoder, sinter.CompiledDecoder):
402
+ def compile_decoder_for_dem(
403
+ self,
404
+ *,
405
+ dem: stim.DetectorErrorModel,
406
+ ) -> CompiledDecoder:
407
+ return self
408
+ def decode_shots_bit_packed(
409
+ self,
410
+ *,
411
+ bit_packed_detection_event_data: np.ndarray,
412
+ ) -> np.ndarray:
413
+ return np.zeros(shape=5, dtype=np.bool_)
414
+
415
+ class TrivialCompiledDecoder(sinter.CompiledDecoder):
416
+ def __init__(self, num_obs: int):
417
+ self.num_obs = -(-num_obs // 8)
418
+
419
+ def decode_shots_bit_packed(
420
+ self,
421
+ *,
422
+ bit_packed_detection_event_data: np.ndarray,
423
+ ) -> np.ndarray:
424
+ return np.zeros(dtype=np.uint8, shape=(bit_packed_detection_event_data.shape[0], self.num_obs))
425
+
426
+ class TrivialDecoder(sinter.Decoder):
427
+ def compile_decoder_for_dem(
428
+ self,
429
+ *,
430
+ dem: stim.DetectorErrorModel,
431
+ ) -> CompiledDecoder:
432
+ return TrivialCompiledDecoder(num_obs=dem.num_observables)
433
+
434
+ circuit = stim.Circuit.generated("repetition_code:memory", distance=3, rounds=3)
435
+ dem = circuit.detector_error_model()
436
+
437
+ with tempfile.TemporaryDirectory() as d:
438
+ d = pathlib.Path(d)
439
+
440
+ circuit.compile_detector_sampler().sample_write(
441
+ shots=10,
442
+ filepath=d / 'dets.b8',
443
+ format='b8',
444
+ )
445
+
446
+ dem.to_file(d / 'dem.dem')
447
+
448
+ with pytest.raises(NotImplementedError, match='compile_decoder_for_dem'):
449
+ IncompleteDecoder().decode_via_files(
450
+ num_shots=10,
451
+ num_dets=dem.num_detectors,
452
+ num_obs=dem.num_observables,
453
+ dem_path=d / 'dem.dem',
454
+ dets_b8_in_path=d / 'dets.b8',
455
+ obs_predictions_b8_out_path=d / 'obs.b8',
456
+ tmp_dir=d,
457
+ )
458
+
459
+ with pytest.raises(ValueError, match='shape='):
460
+ WrongDecoder().decode_via_files(
461
+ num_shots=10,
462
+ num_dets=dem.num_detectors,
463
+ num_obs=dem.num_observables,
464
+ dem_path=d / 'dem.dem',
465
+ dets_b8_in_path=d / 'dets.b8',
466
+ obs_predictions_b8_out_path=d / 'obs.b8',
467
+ tmp_dir=d,
468
+ )
469
+
470
+ TrivialDecoder().decode_via_files(
471
+ num_shots=10,
472
+ num_dets=dem.num_detectors,
473
+ num_obs=dem.num_observables,
474
+ dem_path=d / 'dem.dem',
475
+ dets_b8_in_path=d / 'dets.b8',
476
+ obs_predictions_b8_out_path=d / 'obs.b8',
477
+ tmp_dir=d,
478
+ )
479
+ obs = np.fromfile(d / 'obs.b8', dtype=np.uint8, count=10)
480
+ np.testing.assert_array_equal(obs, [0] * 10)
@@ -0,0 +1,38 @@
1
+ import numpy as np
2
+
3
+ from sinter._decoding._decoding_decoder_class import Decoder, CompiledDecoder
4
+
5
+
6
+ class VacuousDecoder(Decoder):
7
+ """An example decoder that always predicts the observables aren't flipped.
8
+ """
9
+
10
+ def compile_decoder_for_dem(self, *, dem: 'stim.DetectorErrorModel') -> CompiledDecoder:
11
+ return VacuousCompiledDecoder(shape=(dem.num_observables + 7) // 8)
12
+
13
+ def decode_via_files(self,
14
+ *,
15
+ num_shots: int,
16
+ num_dets: int,
17
+ num_obs: int,
18
+ dem_path: 'pathlib.Path',
19
+ dets_b8_in_path: 'pathlib.Path',
20
+ obs_predictions_b8_out_path: 'pathlib.Path',
21
+ tmp_dir: 'pathlib.Path',
22
+ ) -> None:
23
+ with open(obs_predictions_b8_out_path, 'wb') as f:
24
+ f.write(b'\0' * (num_obs * num_shots))
25
+
26
+
27
+ class VacuousCompiledDecoder(CompiledDecoder):
28
+ """An example decoder that always predicts the observables aren't flipped.
29
+ """
30
+ def __init__(self, shape: int):
31
+ self.shape = shape
32
+
33
+ def decode_shots_bit_packed(
34
+ self,
35
+ *,
36
+ bit_packed_detection_event_data: 'np.ndarray',
37
+ ) -> 'np.ndarray':
38
+ return np.zeros(shape=(bit_packed_detection_event_data.shape[0], self.shape), dtype=np.uint8)
@@ -0,0 +1,38 @@
1
+ import time
2
+
3
+ import numpy as np
4
+
5
+ from sinter._data import Task, AnonTaskStats
6
+ from sinter._decoding._sampler import Sampler, CompiledSampler
7
+
8
+
9
+ class PerfectionistSampler(Sampler):
10
+ """Predicts obs aren't flipped. Discards shots with any detection events."""
11
+ def compiled_sampler_for_task(self, task: Task) -> CompiledSampler:
12
+ return CompiledPerfectionistSampler(task)
13
+
14
+
15
+ class CompiledPerfectionistSampler(CompiledSampler):
16
+ def __init__(self, task: Task):
17
+ self.stim_sampler = task.circuit.compile_detector_sampler()
18
+
19
+ def sample(self, max_shots: int) -> AnonTaskStats:
20
+ t0 = time.monotonic()
21
+ dets, obs = self.stim_sampler.sample(
22
+ shots=max_shots,
23
+ bit_packed=True,
24
+ separate_observables=True,
25
+ )
26
+ num_shots = dets.shape[0]
27
+ discards = np.any(dets, axis=1)
28
+ errors = np.any(obs, axis=1)
29
+ num_discards = np.count_nonzero(discards)
30
+ num_errors = np.count_nonzero(errors & ~discards)
31
+ t1 = time.monotonic()
32
+
33
+ return AnonTaskStats(
34
+ shots=num_shots,
35
+ errors=num_errors,
36
+ discards=num_discards,
37
+ seconds=t1 - t0,
38
+ )