Skip to content

Commit d4ab871

Browse files
Test integration with Sinter, include Sinter API inside tesseract library, bind tesseract decoding config inside Sinter API
1 parent 655fccd commit d4ab871

File tree

8 files changed

+699
-56
lines changed

8 files changed

+699
-56
lines changed

src/BUILD

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ pybind_library(
7272
"simplex.pybind.h",
7373
"visualization.pybind.h",
7474
"tesseract.pybind.h",
75+
"tesseract_sinter_compat.pybind.h",
7576
],
7677
deps = [
7778
":libcommon",
@@ -91,20 +92,11 @@ pybind_extension(
9192
],
9293
)
9394

94-
pybind_extension(
95-
name = "tesseract_sinter_compat",
96-
srcs = ["tesseract_sinter_compat.pybind.cc"],
97-
deps = [
98-
":libtesseract",
99-
],
100-
)
101-
10295
py_library(
10396
name="lib_tesseract_decoder",
10497
imports=["src"],
10598
deps=[
10699
":tesseract_decoder",
107-
":tesseract_sinter_compat",
108100
],
109101
)
110102

src/py/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ py_test(
6565
deps = [
6666
"@pypi//pytest",
6767
"@pypi//stim",
68+
"@pypi//sinter",
6869
"//src:lib_tesseract_decoder",
6970
],
7071
)

src/py/requirements.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
stim
22
pytest
3+
sinter

src/py/requirements_lock.txt

Lines changed: 475 additions & 3 deletions
Large diffs are not rendered by default.

src/py/tesseract_sinter_compat_test.py

Lines changed: 179 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
import numpy as np
1818
import stim
1919
import shutil
20+
from sinter._decoding._decoding import sample_decode
2021

21-
from src import tesseract_sinter_compat as tesseract_module
22+
from src.tesseract_decoder import tesseract_sinter_compat as tesseract_module
23+
from src import tesseract_decoder
2224

2325

2426
def test_tesseract_sinter_obj_exists():
@@ -30,11 +32,12 @@ def test_tesseract_sinter_obj_exists():
3032
assert hasattr(decoder, 'compile_decoder_for_dem')
3133
assert hasattr(decoder, 'decode_via_files')
3234

33-
def test_compile_decoder_for_dem():
35+
@pytest.mark.parametrize("use_custom_config", [False, True])
36+
def test_compile_decoder_for_dem(use_custom_config):
3437
"""
35-
Test the 'compile_decoder_for_dem' method with a specific DEM.
38+
Test the 'compile_decoder_for_dem' method with and without a custom config.
3639
"""
37-
40+
3841
dem = stim.DetectorErrorModel("""
3942
detector(0, 0, 0) D0
4043
detector(0, 0, 1) D1
@@ -44,17 +47,26 @@ def test_compile_decoder_for_dem():
4447
error(0.1) D1 D2 L1
4548
error(0.1) D2 D3 L0
4649
""")
50+
51+
if use_custom_config:
52+
config = tesseract_decoder.tesseract.TesseractConfig()
53+
config.verbose = True
54+
decoder = tesseract_module.TesseractSinterDecoder(config=config)
55+
else:
56+
decoder = tesseract_module.TesseractSinterDecoder()
4757

48-
decoder = tesseract_module.TesseractSinterDecoder()
4958
compiled_decoder = decoder.compile_decoder_for_dem(dem=dem)
50-
59+
5160
assert compiled_decoder is not None
5261
assert hasattr(compiled_decoder, 'decode_shots_bit_packed')
53-
62+
5463
# Verify the detector and observable counts are correct
5564
assert compiled_decoder.num_detectors == dem.num_detectors
5665
assert compiled_decoder.num_observables == dem.num_observables
5766

67+
# Verify the config was correctly applied
68+
assert compiled_decoder.decoder.config.verbose == use_custom_config
69+
5870
def test_decode_shots_bit_packed():
5971
"""
6072
Tests the 'decode_shots_bit_packed' method with a specific DEM and detection event.
@@ -176,7 +188,8 @@ def test_decode_via_files_sanity_check():
176188
if temp_dir.exists():
177189
shutil.rmtree(temp_dir)
178190

179-
def test_decode_via_files():
191+
@pytest.mark.parametrize("use_custom_config", [False, True])
192+
def test_decode_via_files(use_custom_config):
180193
"""
181194
Tests the 'decode_via_files' method with a specific DEM and detection event.
182195
"""
@@ -222,7 +235,14 @@ def test_decode_via_files():
222235
with open(dets_in_path, 'wb') as f:
223236
f.write(detection_events_np.tobytes())
224237

225-
tesseract_module.TesseractSinterDecoder().decode_via_files(
238+
if use_custom_config:
239+
config = tesseract_decoder.tesseract.TesseractConfig()
240+
config.verbose = True
241+
decoder = tesseract_module.TesseractSinterDecoder(config=config)
242+
else:
243+
decoder = tesseract_module.TesseractSinterDecoder()
244+
245+
decoder.decode_via_files(
226246
num_shots=num_shots,
227247
num_dets=num_detectors,
228248
num_obs=dem.num_observables,
@@ -248,6 +268,9 @@ def test_decode_via_files():
248268
# Clean up temporary files
249269
if temp_dir.exists():
250270
shutil.rmtree(temp_dir)
271+
272+
assert decoder.config.verbose == use_custom_config
273+
251274

252275
def test_decode_via_files_multi_shot():
253276
"""
@@ -325,5 +348,152 @@ def test_decode_via_files_multi_shot():
325348
if temp_dir.exists():
326349
shutil.rmtree(temp_dir)
327350

351+
def construct_tesseract_decoder_for_sinter():
352+
return {"tesseract": tesseract_module.TesseractSinterDecoder()}
353+
354+
355+
def test_sinter_decode_repetition_code():
356+
"""
357+
Tests the 'tesseract' decoder on a repetition code circuit.
358+
"""
359+
circuit = stim.Circuit.generated('repetition_code:memory',
360+
rounds=3,
361+
distance=3,
362+
after_clifford_depolarization=0.05)
363+
364+
result = sample_decode(
365+
circuit_obj=circuit,
366+
circuit_path=None,
367+
dem_obj=circuit.detector_error_model(decompose_errors=True),
368+
dem_path=None,
369+
num_shots=1000,
370+
decoder="tesseract",
371+
custom_decoders=construct_tesseract_decoder_for_sinter(),
372+
)
373+
assert result.discards == 0
374+
assert 0 <= result.errors <= 100
375+
assert result.shots == 1000
376+
377+
378+
def test_sinter_decode_surface_code():
379+
"""
380+
Tests the 'tesseract' decoder on a more complex surface code circuit.
381+
"""
382+
circuit = stim.Circuit.generated(
383+
"surface_code:rotated_memory_x",
384+
distance=3,
385+
rounds=15,
386+
after_clifford_depolarization=0.001,
387+
)
388+
result = sample_decode(
389+
num_shots=1000,
390+
circuit_obj=circuit,
391+
circuit_path=None,
392+
dem_obj=circuit.detector_error_model(decompose_errors=True),
393+
dem_path=None,
394+
decoder="tesseract",
395+
custom_decoders=construct_tesseract_decoder_for_sinter(),
396+
)
397+
assert result.discards == 0
398+
assert 0 <= result.errors <= 50
399+
assert result.shots == 1000
400+
401+
def test_sinter_empty():
402+
"""
403+
Tests the 'tesseract' decoder on an empty circuit.
404+
"""
405+
circuit = stim.Circuit()
406+
result = sample_decode(
407+
circuit_obj=circuit,
408+
circuit_path=None,
409+
dem_obj=circuit.detector_error_model(decompose_errors=True),
410+
dem_path=None,
411+
num_shots=1000,
412+
decoder="tesseract",
413+
custom_decoders=construct_tesseract_decoder_for_sinter(),
414+
)
415+
assert result.discards == 0
416+
assert result.shots == 1000
417+
assert result.errors == 0
418+
419+
def test_sinter_no_observables():
420+
"""
421+
Tests the decoder on a circuit with detectors but no logical observables.
422+
"""
423+
circuit = stim.Circuit("""
424+
X_ERROR(0.1) 0
425+
M 0
426+
DETECTOR rec[-1]
427+
""")
428+
result = sample_decode(
429+
circuit_obj=circuit,
430+
circuit_path=None,
431+
dem_obj=circuit.detector_error_model(decompose_errors=True),
432+
dem_path=None,
433+
num_shots=1000,
434+
decoder="tesseract",
435+
custom_decoders=construct_tesseract_decoder_for_sinter(),
436+
)
437+
assert result.discards == 0
438+
assert result.shots == 1000
439+
assert result.errors == 0
440+
441+
def test_sinter_invincible_observables():
442+
"""
443+
Tests the decoder on a circuit where an observable is not affected by errors.
444+
"""
445+
circuit = stim.Circuit("""
446+
X_ERROR(0.1) 0
447+
M 0 1
448+
DETECTOR rec[-2]
449+
OBSERVABLE_INCLUDE(1) rec[-1]
450+
""")
451+
result = sample_decode(
452+
circuit_obj=circuit,
453+
circuit_path=None,
454+
dem_obj=circuit.detector_error_model(decompose_errors=True),
455+
dem_path=None,
456+
num_shots=1000,
457+
decoder="tesseract",
458+
custom_decoders=construct_tesseract_decoder_for_sinter(),
459+
)
460+
assert result.discards == 0
461+
assert result.shots == 1000
462+
assert result.errors == 0
463+
464+
465+
466+
def test_sinter_detector_counting():
467+
"""
468+
Tests 'that the decoder's detector count is correctly reported via Sinter'.
469+
"""
470+
circuit = stim.Circuit("""
471+
X_ERROR(0.1) 0
472+
X_ERROR(0.2) 1
473+
M 0 1
474+
DETECTOR rec[-1]
475+
DETECTOR rec[-2]
476+
OBSERVABLE_INCLUDE(0) rec[-1]
477+
OBSERVABLE_INCLUDE(1) rec[-1] rec[-2]
478+
""")
479+
result = sample_decode(
480+
circuit_obj=circuit,
481+
circuit_path=None,
482+
dem_obj=circuit.detector_error_model(decompose_errors=True),
483+
dem_path=None,
484+
post_mask=None,
485+
num_shots=10000,
486+
decoder="tesseract",
487+
count_detection_events=True,
488+
custom_decoders=construct_tesseract_decoder_for_sinter(),
489+
)
490+
assert result.discards == 0
491+
assert result.custom_counts['detectors_checked'] == 20000
492+
assert 0.3 * 10000 * 0.5 <= result.custom_counts['detection_events'] <= 0.3 * 10000 * 2.0
493+
assert set(result.custom_counts.keys()) == {'detectors_checked', 'detection_events'}
494+
495+
496+
497+
328498
if __name__ == "__main__":
329499
raise SystemExit(pytest.main([__file__]))

src/tesseract.pybind.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "common.pybind.h"
2121
#include "pybind11/detail/common.h"
2222
#include "simplex.pybind.h"
23+
#include "tesseract_sinter_compat.pybind.h"
2324
#include "utils.pybind.h"
2425
#include "visualization.pybind.h"
2526

@@ -31,6 +32,7 @@ PYBIND11_MODULE(tesseract_decoder, tesseract) {
3132
add_simplex_module(tesseract);
3233
add_tesseract_module(tesseract);
3334
add_visualization_module(tesseract);
35+
pybind_sinter_compat(tesseract);
3436

3537
// Adds a context manager to the python library that can be used to redirect C++'s stdout/stderr
3638
// to python's stdout/stderr at run time like

src/tesseract.pybind.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ void add_tesseract_module(py::module& root) {
5656
5757
This class holds all the parameters needed to initialize and configure a
5858
Tesseract decoder instance.
59+
)pbdoc")
60+
.def(py::init<>(), R"pbdoc(
61+
Default constructor for TesseractConfig.
62+
Creates a new instance with default parameter values.
5963
)pbdoc")
6064
.def(py::init(&tesseract_config_maker), py::arg("dem"), py::arg("det_beam") = INF_DET_BEAM,
6165
py::arg("beam_climbing") = false, py::arg("no_revisit_dets") = false,

0 commit comments

Comments
 (0)