Skip to content

Commit 8129e15

Browse files
Remove at most two errors per detector (#117)
Co-authored-by: Noureldin <[email protected]>
1 parent da3b5ba commit 8129e15

File tree

7 files changed

+54
-97
lines changed

7 files changed

+54
-97
lines changed

src/py/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ The `tesseract_decoder.tesseract` module provides the Tesseract decoder, which e
55

66
#### Class `tesseract.TesseractConfig`
77
This class holds the configuration parameters that control the behavior of the Tesseract decoder.
8-
* `TesseractConfig(dem: stim.DetectorErrorModel, det_beam: int = INF_DET_BEAM, beam_climbing: bool = False, no_revisit_dets: bool = False, at_most_two_errors_per_detector: bool = False, verbose: bool = False, pqlimit: int = sys.maxsize, det_orders: list[list[int]] = [], det_penalty: float = 0.0)`
8+
* `TesseractConfig(dem: stim.DetectorErrorModel, det_beam: int = INF_DET_BEAM, beam_climbing: bool = False, no_revisit_dets: bool = False, verbose: bool = False, pqlimit: int = sys.maxsize, det_orders: list[list[int]] = [], det_penalty: float = 0.0)`
99
* `__str__()`
1010

1111
Explanation of configuration arguments:
1212
* `dem`: This is a required argument that takes a `stim.DetectorErrorModel`. It provides the logical structure of the quantum error-correcting code, including the detectors and the relationships between them. This model is essential for the decoder to understand the syndrome and potential error locations.
1313
* `det_beam` - This integer value represents the beam search cutoff. It specifies a threshold for the number of "residual detection events" a node can have before it is pruned from the search. A lower `det_beam` value makes the search more aggressive, potentially sacrificing accuracy for speed. The default value `INF_DET_BEAM` means no beam cutoff is applied.
1414
* `beam_climbing` - A boolean flag that, when set to `True`, enables a heuristic called "beam climbing." This optimization causes the decoder to try different `det_beam` values (up to a maximum) to find a good decoding path. This can improve the decoder's chance of finding the most likely error, even with an initial narrow beam search.
1515
* `no_revisit_dets` - A boolean flag that, when `True`, activates a heuristic to prevent the decoder from revisiting nodes that have the same set of leftover detection events as a node it has already visited. This can help to reduce search redundancy and improve decoding speed.
16-
* `at_most_two_errors_per_detector` - This boolean flag is a specific constraint that assumes at most two errors can affect a given detector. This can be a useful optimization for certain types of codes and noise models, as it prunes the search space by making a stronger assumption about the error distribution.
16+
1717
* `verbose` - A boolean flag that, when `True`, enables verbose logging. This is useful for debugging and understanding the decoder's internal behavior, as it will print information about the search process.
1818
* `pqlimit` - An integer that sets a limit on the number of nodes in the priority queue. This can be used to constrain the memory usage of the decoder. The default value is `sys.maxsize`, which means the size is effectively unbounded.
1919
* `det_orders` - A list of lists of integers, where each inner list represents an ordering of the detectors. This is used for "ensemble reordering," an optimization that tries different detector orderings to improve the search's convergence. The default is an empty list, meaning a single, fixed ordering is used.

src/py/tesseract_test.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def test_create_tesseract_config():
5252
assert config.dem == _DETECTOR_ERROR_MODEL
5353
assert config.det_beam == 5
5454
assert config.no_revisit_dets is True
55-
assert config.at_most_two_errors_per_detector is False
55+
5656
assert config.verbose is False
5757
assert config.merge_errors is True
5858
assert config.pqlimit == 200000
@@ -71,7 +71,7 @@ def test_create_tesseract_config_with_dem():
7171
assert config.dem == _DETECTOR_ERROR_MODEL
7272
assert config.det_beam == 5
7373
assert config.no_revisit_dets is True
74-
assert config.at_most_two_errors_per_detector is False
74+
7575
assert config.verbose is False
7676
assert config.merge_errors is True
7777
assert config.pqlimit == 200000
@@ -94,7 +94,7 @@ def test_create_tesseract_config_with_dem_and_custom_args():
9494
assert config.dem == _DETECTOR_ERROR_MODEL
9595
assert config.det_beam == 100
9696
assert config.no_revisit_dets is True
97-
assert config.at_most_two_errors_per_detector is False
97+
9898
assert config.verbose is False
9999
assert config.merge_errors is False
100100
assert config.pqlimit == 200000
@@ -167,7 +167,7 @@ def test_create_tesseract_config_no_dem():
167167
assert config.dem == stim.DetectorErrorModel()
168168
assert config.det_beam == 5
169169
assert config.no_revisit_dets is True
170-
assert config.at_most_two_errors_per_detector is False
170+
171171
assert config.verbose is False
172172
assert config.merge_errors is True
173173
assert config.pqlimit == 200000
@@ -184,7 +184,7 @@ def test_create_tesseract_config_no_dem_with_custom_args():
184184
assert config.dem == stim.DetectorErrorModel()
185185
assert config.det_beam == 15
186186
assert config.no_revisit_dets is True
187-
assert config.at_most_two_errors_per_detector is False
187+
188188
assert config.verbose is True
189189
assert config.merge_errors is True
190190
assert config.pqlimit == 200000

src/tesseract.cc

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ std::string TesseractConfig::str() {
5757
ss << "dem=DetectorErrorModel_Object" << ", ";
5858
ss << "det_beam=" << config.det_beam << ", ";
5959
ss << "no_revisit_dets=" << config.no_revisit_dets << ", ";
60-
ss << "at_most_two_errors_per_detector=" << config.at_most_two_errors_per_detector << ", ";
60+
6161
ss << "verbose=" << config.verbose << ", ";
6262
ss << "merge_errors=" << config.merge_errors << ", ";
6363
ss << "pqlimit=" << config.pqlimit << ", ";
@@ -256,16 +256,11 @@ void TesseractDecoder::flip_detectors_and_block_errors(
256256

257257
for (int oei : d2e[min_detector]) {
258258
detector_cost_tuples[oei].error_blocked = 1;
259-
if (!config.at_most_two_errors_per_detector && oei == ei) break;
259+
if (oei == ei) break;
260260
}
261261

262262
for (int d : edets[ei]) {
263263
detectors[d] = !detectors[d];
264-
if (!detectors[d] && config.at_most_two_errors_per_detector) {
265-
for (int oei : d2e[d]) {
266-
detector_cost_tuples[oei].error_blocked = 1;
267-
}
268-
}
269264
}
270265
}
271266
}
@@ -398,12 +393,6 @@ void TesseractDecoder::decode_to_errors(const std::vector<uint64_t>& detections,
398393
}
399394
}
400395

401-
if (config.at_most_two_errors_per_detector) {
402-
for (int ei : d2e[min_detector]) {
403-
next_detector_cost_tuples[ei].error_blocked = 1;
404-
}
405-
}
406-
407396
size_t prev_ei = std::numeric_limits<size_t>::max();
408397
std::vector<double> detector_cost_cache(num_detectors, -1);
409398

@@ -415,11 +404,6 @@ void TesseractDecoder::decode_to_errors(const std::vector<uint64_t>& detections,
415404
int fired = detectors[d] ? 1 : -1;
416405
for (int oei : d2e[d]) {
417406
next_detector_cost_tuples[oei].detectors_count += fired;
418-
419-
if (config.at_most_two_errors_per_detector &&
420-
next_detector_cost_tuples[oei].error_blocked == 2) {
421-
next_detector_cost_tuples[oei].error_blocked = 0;
422-
}
423407
}
424408
}
425409
}
@@ -440,17 +424,6 @@ void TesseractDecoder::decode_to_errors(const std::vector<uint64_t>& detections,
440424
for (int oei : d2e[d]) {
441425
next_detector_cost_tuples[oei].detectors_count += fired;
442426
}
443-
444-
if (!next_detectors[d] && config.at_most_two_errors_per_detector) {
445-
for (int oei : d2e[d]) {
446-
next_detector_cost_tuples[oei].error_blocked =
447-
next_detector_cost_tuples[oei].error_blocked == 1
448-
? 1
449-
: 2; // we store '2' value to indicate an error that was blocked due to the
450-
// '--at-most-two-error-per-detector' heuristic, in order to revert it in
451-
// the next decoding iteration
452-
}
453-
}
454427
}
455428

456429
if (next_num_detectors > max_num_detectors) continue;

src/tesseract.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ struct TesseractConfig {
3636
int det_beam = DEFAULT_DET_BEAM;
3737
bool beam_climbing = false;
3838
bool no_revisit_dets = true;
39-
bool at_most_two_errors_per_detector = false;
39+
4040
bool verbose = false;
4141
bool merge_errors = true;
4242
size_t pqlimit = DEFAULT_PQLIMIT;

src/tesseract.pybind.h

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,33 +35,30 @@ std::unique_ptr<TesseractDecoder> _compile_tesseract_decoder_helper(const Tesser
3535

3636
TesseractConfig tesseract_config_maker_no_dem(
3737
int det_beam = INF_DET_BEAM, bool beam_climbing = false, bool no_revisit_dets = false,
38-
bool at_most_two_errors_per_detector = false, bool verbose = false, bool merge_errors = true,
38+
bool verbose = false, bool merge_errors = true,
3939
size_t pqlimit = std::numeric_limits<size_t>::max(),
4040
std::vector<std::vector<size_t>> det_orders = std::vector<std::vector<size_t>>(),
4141
double det_penalty = 0.0, bool create_visualization = false) {
4242
stim::DetectorErrorModel empty_dem;
4343
if (det_orders.empty()) {
4444
det_orders = build_det_orders(empty_dem, 20, DetOrder::DetBFS, 2384753);
4545
}
46-
return TesseractConfig({empty_dem, det_beam, beam_climbing, no_revisit_dets,
47-
at_most_two_errors_per_detector, verbose, merge_errors, pqlimit,
48-
det_orders, det_penalty, create_visualization});
46+
return TesseractConfig({empty_dem, det_beam, beam_climbing, no_revisit_dets, verbose,
47+
merge_errors, pqlimit, det_orders, det_penalty, create_visualization});
4948
}
5049

5150
TesseractConfig tesseract_config_maker(
5251
py::object dem, int det_beam = INF_DET_BEAM, bool beam_climbing = false,
53-
bool no_revisit_dets = false, bool at_most_two_errors_per_detector = false,
54-
bool verbose = false, bool merge_errors = true,
52+
bool no_revisit_dets = false, bool verbose = false, bool merge_errors = true,
5553
size_t pqlimit = std::numeric_limits<size_t>::max(),
5654
std::vector<std::vector<size_t>> det_orders = std::vector<std::vector<size_t>>(),
5755
double det_penalty = 0.0, bool create_visualization = false) {
5856
stim::DetectorErrorModel input_dem = parse_py_object<stim::DetectorErrorModel>(dem);
5957
if (det_orders.empty()) {
6058
det_orders = build_det_orders(input_dem, 20, DetOrder::DetBFS, 2384753);
6159
}
62-
return TesseractConfig({input_dem, det_beam, beam_climbing, no_revisit_dets,
63-
at_most_two_errors_per_detector, verbose, merge_errors, pqlimit,
64-
det_orders, det_penalty, create_visualization});
60+
return TesseractConfig({input_dem, det_beam, beam_climbing, no_revisit_dets, verbose,
61+
merge_errors, pqlimit, det_orders, det_penalty, create_visualization});
6562
}
6663

6764
}; // namespace
@@ -83,8 +80,7 @@ void add_tesseract_module(py::module& root) {
8380
)pbdoc")
8481
.def(py::init(&tesseract_config_maker_no_dem), py::arg("det_beam") = 5,
8582
py::arg("beam_climbing") = false, py::arg("no_revisit_dets") = true,
86-
py::arg("at_most_two_errors_per_detector") = false, py::arg("verbose") = false,
87-
py::arg("merge_errors") = true, py::arg("pqlimit") = 200000,
83+
py::arg("verbose") = false, py::arg("merge_errors") = true, py::arg("pqlimit") = 200000,
8884
py::arg("det_orders") = std::vector<std::vector<size_t>>(), py::arg("det_penalty") = 0.0,
8985
py::arg("create_visualization") = false,
9086
R"pbdoc(
@@ -99,9 +95,7 @@ void add_tesseract_module(py::module& root) {
9995
If True, enables a beam climbing heuristic.
10096
no_revisit_dets : bool, default=False
10197
If True, prevents the decoder from revisiting a syndrome pattern more than once.
102-
at_most_two_errors_per_detector : bool, default=False
103-
If True, an optimization is enabled that assumes at most two errors
104-
are correlated with each detector.
98+
10599
verbose : bool, default=False
106100
If True, enables verbose logging from the decoder.
107101
merge_errors : bool, default=True
@@ -118,8 +112,7 @@ void add_tesseract_module(py::module& root) {
118112
)pbdoc")
119113
.def(py::init(&tesseract_config_maker), py::arg("dem"), py::arg("det_beam") = 5,
120114
py::arg("beam_climbing") = false, py::arg("no_revisit_dets") = true,
121-
py::arg("at_most_two_errors_per_detector") = false, py::arg("verbose") = false,
122-
py::arg("merge_errors") = true, py::arg("pqlimit") = 200000,
115+
py::arg("verbose") = false, py::arg("merge_errors") = true, py::arg("pqlimit") = 200000,
123116
py::arg("det_orders") = std::vector<std::vector<size_t>>(), py::arg("det_penalty") = 0.0,
124117
py::arg("create_visualization") = false,
125118
R"pbdoc(
@@ -135,9 +128,7 @@ void add_tesseract_module(py::module& root) {
135128
If True, enables a beam climbing heuristic.
136129
no_revisit_dets : bool, default=False
137130
If True, prevents the decoder from revisiting a syndrome pattern more than once.
138-
at_most_two_errors_per_detector : bool, default=False
139-
If True, an optimization is enabled that assumes at most two errors
140-
are correlated with each detector.
131+
141132
verbose : bool, default=False
142133
If True, enables verbose logging from the decoder.
143134
merge_errors : bool, default=True
@@ -160,9 +151,7 @@ void add_tesseract_module(py::module& root) {
160151
"Whether to use a beam climbing heuristic.")
161152
.def_readwrite("no_revisit_dets", &TesseractConfig::no_revisit_dets,
162153
"Whether to prevent revisiting same syndrome patterns during decoding.")
163-
.def_readwrite("at_most_two_errors_per_detector",
164-
&TesseractConfig::at_most_two_errors_per_detector,
165-
"Whether to assume at most two errors per detector for optimization.")
154+
166155
.def_readwrite("verbose", &TesseractConfig::verbose,
167156
"If True, the decoder will print verbose output.")
168157
.def_readwrite("merge_errors", &TesseractConfig::merge_errors,

src/tesseract_main.cc

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ struct Args {
7575
double det_penalty = 0;
7676
bool beam_climbing = false;
7777
bool no_revisit_dets = false;
78-
bool at_most_two_errors_per_detector = false;
78+
7979
size_t pqlimit;
8080

8181
bool verbose = false;
@@ -289,7 +289,7 @@ struct Args {
289289
config.det_penalty = det_penalty;
290290
config.beam_climbing = beam_climbing;
291291
config.no_revisit_dets = no_revisit_dets;
292-
config.at_most_two_errors_per_detector = at_most_two_errors_per_detector;
292+
293293
config.pqlimit = pqlimit;
294294
config.verbose = verbose;
295295
}
@@ -445,10 +445,7 @@ int main(int argc, char* argv[]) {
445445
.help("Use no-revisit-dets heuristic")
446446
.flag()
447447
.store_into(args.no_revisit_dets);
448-
program.add_argument("--at-most-two-errors-per-detector")
449-
.help("Use heuristic limitation of at most 2 errors per detector")
450-
.flag()
451-
.store_into(args.at_most_two_errors_per_detector);
448+
452449
program.add_argument("--pqlimit")
453450
.help("Maximum size of the priority queue (default = infinity)")
454451
.metavar("N")
@@ -594,25 +591,24 @@ int main(int argc, char* argv[]) {
594591

595592
bool print_final_stats = true;
596593
if (!args.stats_out_fname.empty()) {
597-
nlohmann::json stats_json = {
598-
{"circuit_path", args.circuit_path},
599-
{"dem_path", args.dem_path},
600-
{"max_errors", args.max_errors},
601-
{"sample_seed", args.sample_seed},
602-
{"at_most_two_errors_per_detector", args.at_most_two_errors_per_detector},
603-
{"det_beam", args.det_beam},
604-
{"det_penalty", args.det_penalty},
605-
{"beam_climbing", args.beam_climbing},
606-
{"no_revisit_dets", args.no_revisit_dets},
607-
{"pqlimit", args.pqlimit},
608-
{"num_det_orders", args.num_det_orders},
609-
{"det_order_seed", args.det_order_seed},
610-
{"total_time_seconds", total_time_seconds},
611-
{"num_errors", num_errors},
612-
{"num_low_confidence", num_low_confidence},
613-
{"num_shots", shot},
614-
{"num_threads", args.num_threads},
615-
{"sample_num_shots", args.sample_num_shots}};
594+
nlohmann::json stats_json = {{"circuit_path", args.circuit_path},
595+
{"dem_path", args.dem_path},
596+
{"max_errors", args.max_errors},
597+
{"sample_seed", args.sample_seed},
598+
599+
{"det_beam", args.det_beam},
600+
{"det_penalty", args.det_penalty},
601+
{"beam_climbing", args.beam_climbing},
602+
{"no_revisit_dets", args.no_revisit_dets},
603+
{"pqlimit", args.pqlimit},
604+
{"num_det_orders", args.num_det_orders},
605+
{"det_order_seed", args.det_order_seed},
606+
{"total_time_seconds", total_time_seconds},
607+
{"num_errors", num_errors},
608+
{"num_low_confidence", num_low_confidence},
609+
{"num_shots", shot},
610+
{"num_threads", args.num_threads},
611+
{"sample_num_shots", args.sample_num_shots}};
616612

617613
if (args.stats_out_fname == "-") {
618614
std::cout << stats_json << std::endl;

src/tesseract_sinter_compat.pybind.h

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -292,28 +292,27 @@ void pybind_sinter_compat(py::module& root) {
292292
R"pbdoc(Checks if two TesseractSinterDecoder instances are not equal.)pbdoc")
293293
.def(py::pickle(
294294
[](const TesseractSinterDecoder& self) -> py::tuple { // __getstate__
295-
return py::make_tuple(
296-
std::string(self.config.dem.str()), self.config.det_beam, self.config.beam_climbing,
297-
self.config.no_revisit_dets, self.config.at_most_two_errors_per_detector,
298-
self.config.verbose, self.config.merge_errors, self.config.pqlimit,
299-
self.config.det_orders, self.config.det_penalty, self.config.create_visualization);
295+
return py::make_tuple(std::string(self.config.dem.str()), self.config.det_beam,
296+
self.config.beam_climbing, self.config.no_revisit_dets,
297+
self.config.verbose, self.config.merge_errors,
298+
self.config.pqlimit, self.config.det_orders,
299+
self.config.det_penalty, self.config.create_visualization);
300300
},
301301
[](py::tuple t) { // __setstate__
302-
if (t.size() != 11) {
302+
if (t.size() != 10) {
303303
throw std::runtime_error("Invalid state for TesseractSinterDecoder!");
304304
}
305305
TesseractConfig config;
306306
config.dem = stim::DetectorErrorModel(t[0].cast<std::string>());
307307
config.det_beam = t[1].cast<int>();
308308
config.beam_climbing = t[2].cast<bool>();
309309
config.no_revisit_dets = t[3].cast<bool>();
310-
config.at_most_two_errors_per_detector = t[4].cast<bool>();
311-
config.verbose = t[5].cast<bool>();
312-
config.merge_errors = t[6].cast<bool>();
313-
config.pqlimit = t[7].cast<size_t>();
314-
config.det_orders = t[8].cast<std::vector<std::vector<size_t>>>();
315-
config.det_penalty = t[9].cast<double>();
316-
config.create_visualization = t[10].cast<bool>();
310+
config.verbose = t[4].cast<bool>();
311+
config.merge_errors = t[5].cast<bool>();
312+
config.pqlimit = t[6].cast<size_t>();
313+
config.det_orders = t[7].cast<std::vector<std::vector<size_t>>>();
314+
config.det_penalty = t[8].cast<double>();
315+
config.create_visualization = t[9].cast<bool>();
317316
return TesseractSinterDecoder(config);
318317
}));
319318

0 commit comments

Comments
 (0)