diff --git a/src/py/README.md b/src/py/README.md index a7bc645..bc42efd 100644 --- a/src/py/README.md +++ b/src/py/README.md @@ -5,7 +5,7 @@ The `tesseract_decoder.tesseract` module provides the Tesseract decoder, which e #### Class `tesseract.TesseractConfig` This class holds the configuration parameters that control the behavior of the Tesseract decoder. -* `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)` +* `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)` * `__str__()` Explanation of configuration arguments: @@ -13,7 +13,7 @@ Explanation of configuration arguments: * `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. * `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. * `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. -* `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. + * `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. * `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. * `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. diff --git a/src/py/tesseract_test.py b/src/py/tesseract_test.py index 476d077..69b72e2 100644 --- a/src/py/tesseract_test.py +++ b/src/py/tesseract_test.py @@ -52,7 +52,7 @@ def test_create_tesseract_config(): assert config.dem == _DETECTOR_ERROR_MODEL assert config.det_beam == 5 assert config.no_revisit_dets is True - assert config.at_most_two_errors_per_detector is False + assert config.verbose is False assert config.merge_errors is True assert config.pqlimit == 200000 @@ -71,7 +71,7 @@ def test_create_tesseract_config_with_dem(): assert config.dem == _DETECTOR_ERROR_MODEL assert config.det_beam == 5 assert config.no_revisit_dets is True - assert config.at_most_two_errors_per_detector is False + assert config.verbose is False assert config.merge_errors is True assert config.pqlimit == 200000 @@ -94,7 +94,7 @@ def test_create_tesseract_config_with_dem_and_custom_args(): assert config.dem == _DETECTOR_ERROR_MODEL assert config.det_beam == 100 assert config.no_revisit_dets is True - assert config.at_most_two_errors_per_detector is False + assert config.verbose is False assert config.merge_errors is False assert config.pqlimit == 200000 @@ -167,7 +167,7 @@ def test_create_tesseract_config_no_dem(): assert config.dem == stim.DetectorErrorModel() assert config.det_beam == 5 assert config.no_revisit_dets is True - assert config.at_most_two_errors_per_detector is False + assert config.verbose is False assert config.merge_errors is True assert config.pqlimit == 200000 @@ -184,7 +184,7 @@ def test_create_tesseract_config_no_dem_with_custom_args(): assert config.dem == stim.DetectorErrorModel() assert config.det_beam == 15 assert config.no_revisit_dets is True - assert config.at_most_two_errors_per_detector is False + assert config.verbose is True assert config.merge_errors is True assert config.pqlimit == 200000 diff --git a/src/tesseract.cc b/src/tesseract.cc index ca44133..f3d2460 100644 --- a/src/tesseract.cc +++ b/src/tesseract.cc @@ -57,7 +57,7 @@ std::string TesseractConfig::str() { ss << "dem=DetectorErrorModel_Object" << ", "; ss << "det_beam=" << config.det_beam << ", "; ss << "no_revisit_dets=" << config.no_revisit_dets << ", "; - ss << "at_most_two_errors_per_detector=" << config.at_most_two_errors_per_detector << ", "; + ss << "verbose=" << config.verbose << ", "; ss << "merge_errors=" << config.merge_errors << ", "; ss << "pqlimit=" << config.pqlimit << ", "; @@ -256,16 +256,11 @@ void TesseractDecoder::flip_detectors_and_block_errors( for (int oei : d2e[min_detector]) { detector_cost_tuples[oei].error_blocked = 1; - if (!config.at_most_two_errors_per_detector && oei == ei) break; + if (oei == ei) break; } for (int d : edets[ei]) { detectors[d] = !detectors[d]; - if (!detectors[d] && config.at_most_two_errors_per_detector) { - for (int oei : d2e[d]) { - detector_cost_tuples[oei].error_blocked = 1; - } - } } } } @@ -398,12 +393,6 @@ void TesseractDecoder::decode_to_errors(const std::vector& detections, } } - if (config.at_most_two_errors_per_detector) { - for (int ei : d2e[min_detector]) { - next_detector_cost_tuples[ei].error_blocked = 1; - } - } - size_t prev_ei = std::numeric_limits::max(); std::vector detector_cost_cache(num_detectors, -1); @@ -415,11 +404,6 @@ void TesseractDecoder::decode_to_errors(const std::vector& detections, int fired = detectors[d] ? 1 : -1; for (int oei : d2e[d]) { next_detector_cost_tuples[oei].detectors_count += fired; - - if (config.at_most_two_errors_per_detector && - next_detector_cost_tuples[oei].error_blocked == 2) { - next_detector_cost_tuples[oei].error_blocked = 0; - } } } } @@ -440,17 +424,6 @@ void TesseractDecoder::decode_to_errors(const std::vector& detections, for (int oei : d2e[d]) { next_detector_cost_tuples[oei].detectors_count += fired; } - - if (!next_detectors[d] && config.at_most_two_errors_per_detector) { - for (int oei : d2e[d]) { - next_detector_cost_tuples[oei].error_blocked = - next_detector_cost_tuples[oei].error_blocked == 1 - ? 1 - : 2; // we store '2' value to indicate an error that was blocked due to the - // '--at-most-two-error-per-detector' heuristic, in order to revert it in - // the next decoding iteration - } - } } if (next_num_detectors > max_num_detectors) continue; diff --git a/src/tesseract.h b/src/tesseract.h index ead2b2a..62d6ed6 100644 --- a/src/tesseract.h +++ b/src/tesseract.h @@ -36,7 +36,7 @@ struct TesseractConfig { int det_beam = DEFAULT_DET_BEAM; bool beam_climbing = false; bool no_revisit_dets = true; - bool at_most_two_errors_per_detector = false; + bool verbose = false; bool merge_errors = true; size_t pqlimit = DEFAULT_PQLIMIT; diff --git a/src/tesseract.pybind.h b/src/tesseract.pybind.h index 65111f0..85603be 100644 --- a/src/tesseract.pybind.h +++ b/src/tesseract.pybind.h @@ -35,7 +35,7 @@ std::unique_ptr _compile_tesseract_decoder_helper(const Tesser TesseractConfig tesseract_config_maker_no_dem( int det_beam = INF_DET_BEAM, bool beam_climbing = false, bool no_revisit_dets = false, - bool at_most_two_errors_per_detector = false, bool verbose = false, bool merge_errors = true, + bool verbose = false, bool merge_errors = true, size_t pqlimit = std::numeric_limits::max(), std::vector> det_orders = std::vector>(), double det_penalty = 0.0, bool create_visualization = false) { @@ -43,15 +43,13 @@ TesseractConfig tesseract_config_maker_no_dem( if (det_orders.empty()) { det_orders = build_det_orders(empty_dem, 20, DetOrder::DetBFS, 2384753); } - return TesseractConfig({empty_dem, det_beam, beam_climbing, no_revisit_dets, - at_most_two_errors_per_detector, verbose, merge_errors, pqlimit, - det_orders, det_penalty, create_visualization}); + return TesseractConfig({empty_dem, det_beam, beam_climbing, no_revisit_dets, verbose, + merge_errors, pqlimit, det_orders, det_penalty, create_visualization}); } TesseractConfig tesseract_config_maker( py::object dem, int det_beam = INF_DET_BEAM, bool beam_climbing = false, - bool no_revisit_dets = false, bool at_most_two_errors_per_detector = false, - bool verbose = false, bool merge_errors = true, + bool no_revisit_dets = false, bool verbose = false, bool merge_errors = true, size_t pqlimit = std::numeric_limits::max(), std::vector> det_orders = std::vector>(), double det_penalty = 0.0, bool create_visualization = false) { @@ -59,9 +57,8 @@ TesseractConfig tesseract_config_maker( if (det_orders.empty()) { det_orders = build_det_orders(input_dem, 20, DetOrder::DetBFS, 2384753); } - return TesseractConfig({input_dem, det_beam, beam_climbing, no_revisit_dets, - at_most_two_errors_per_detector, verbose, merge_errors, pqlimit, - det_orders, det_penalty, create_visualization}); + return TesseractConfig({input_dem, det_beam, beam_climbing, no_revisit_dets, verbose, + merge_errors, pqlimit, det_orders, det_penalty, create_visualization}); } }; // namespace @@ -83,8 +80,7 @@ void add_tesseract_module(py::module& root) { )pbdoc") .def(py::init(&tesseract_config_maker_no_dem), py::arg("det_beam") = 5, py::arg("beam_climbing") = false, py::arg("no_revisit_dets") = true, - py::arg("at_most_two_errors_per_detector") = false, py::arg("verbose") = false, - py::arg("merge_errors") = true, py::arg("pqlimit") = 200000, + py::arg("verbose") = false, py::arg("merge_errors") = true, py::arg("pqlimit") = 200000, py::arg("det_orders") = std::vector>(), py::arg("det_penalty") = 0.0, py::arg("create_visualization") = false, R"pbdoc( @@ -99,9 +95,7 @@ void add_tesseract_module(py::module& root) { If True, enables a beam climbing heuristic. no_revisit_dets : bool, default=False If True, prevents the decoder from revisiting a syndrome pattern more than once. - at_most_two_errors_per_detector : bool, default=False - If True, an optimization is enabled that assumes at most two errors - are correlated with each detector. + verbose : bool, default=False If True, enables verbose logging from the decoder. merge_errors : bool, default=True @@ -118,8 +112,7 @@ void add_tesseract_module(py::module& root) { )pbdoc") .def(py::init(&tesseract_config_maker), py::arg("dem"), py::arg("det_beam") = 5, py::arg("beam_climbing") = false, py::arg("no_revisit_dets") = true, - py::arg("at_most_two_errors_per_detector") = false, py::arg("verbose") = false, - py::arg("merge_errors") = true, py::arg("pqlimit") = 200000, + py::arg("verbose") = false, py::arg("merge_errors") = true, py::arg("pqlimit") = 200000, py::arg("det_orders") = std::vector>(), py::arg("det_penalty") = 0.0, py::arg("create_visualization") = false, R"pbdoc( @@ -135,9 +128,7 @@ void add_tesseract_module(py::module& root) { If True, enables a beam climbing heuristic. no_revisit_dets : bool, default=False If True, prevents the decoder from revisiting a syndrome pattern more than once. - at_most_two_errors_per_detector : bool, default=False - If True, an optimization is enabled that assumes at most two errors - are correlated with each detector. + verbose : bool, default=False If True, enables verbose logging from the decoder. merge_errors : bool, default=True @@ -160,9 +151,7 @@ void add_tesseract_module(py::module& root) { "Whether to use a beam climbing heuristic.") .def_readwrite("no_revisit_dets", &TesseractConfig::no_revisit_dets, "Whether to prevent revisiting same syndrome patterns during decoding.") - .def_readwrite("at_most_two_errors_per_detector", - &TesseractConfig::at_most_two_errors_per_detector, - "Whether to assume at most two errors per detector for optimization.") + .def_readwrite("verbose", &TesseractConfig::verbose, "If True, the decoder will print verbose output.") .def_readwrite("merge_errors", &TesseractConfig::merge_errors, diff --git a/src/tesseract_main.cc b/src/tesseract_main.cc index 4785570..5b483b7 100644 --- a/src/tesseract_main.cc +++ b/src/tesseract_main.cc @@ -75,7 +75,7 @@ struct Args { double det_penalty = 0; bool beam_climbing = false; bool no_revisit_dets = false; - bool at_most_two_errors_per_detector = false; + size_t pqlimit; bool verbose = false; @@ -289,7 +289,7 @@ struct Args { config.det_penalty = det_penalty; config.beam_climbing = beam_climbing; config.no_revisit_dets = no_revisit_dets; - config.at_most_two_errors_per_detector = at_most_two_errors_per_detector; + config.pqlimit = pqlimit; config.verbose = verbose; } @@ -445,10 +445,7 @@ int main(int argc, char* argv[]) { .help("Use no-revisit-dets heuristic") .flag() .store_into(args.no_revisit_dets); - program.add_argument("--at-most-two-errors-per-detector") - .help("Use heuristic limitation of at most 2 errors per detector") - .flag() - .store_into(args.at_most_two_errors_per_detector); + program.add_argument("--pqlimit") .help("Maximum size of the priority queue (default = infinity)") .metavar("N") @@ -594,25 +591,24 @@ int main(int argc, char* argv[]) { bool print_final_stats = true; if (!args.stats_out_fname.empty()) { - nlohmann::json stats_json = { - {"circuit_path", args.circuit_path}, - {"dem_path", args.dem_path}, - {"max_errors", args.max_errors}, - {"sample_seed", args.sample_seed}, - {"at_most_two_errors_per_detector", args.at_most_two_errors_per_detector}, - {"det_beam", args.det_beam}, - {"det_penalty", args.det_penalty}, - {"beam_climbing", args.beam_climbing}, - {"no_revisit_dets", args.no_revisit_dets}, - {"pqlimit", args.pqlimit}, - {"num_det_orders", args.num_det_orders}, - {"det_order_seed", args.det_order_seed}, - {"total_time_seconds", total_time_seconds}, - {"num_errors", num_errors}, - {"num_low_confidence", num_low_confidence}, - {"num_shots", shot}, - {"num_threads", args.num_threads}, - {"sample_num_shots", args.sample_num_shots}}; + nlohmann::json stats_json = {{"circuit_path", args.circuit_path}, + {"dem_path", args.dem_path}, + {"max_errors", args.max_errors}, + {"sample_seed", args.sample_seed}, + + {"det_beam", args.det_beam}, + {"det_penalty", args.det_penalty}, + {"beam_climbing", args.beam_climbing}, + {"no_revisit_dets", args.no_revisit_dets}, + {"pqlimit", args.pqlimit}, + {"num_det_orders", args.num_det_orders}, + {"det_order_seed", args.det_order_seed}, + {"total_time_seconds", total_time_seconds}, + {"num_errors", num_errors}, + {"num_low_confidence", num_low_confidence}, + {"num_shots", shot}, + {"num_threads", args.num_threads}, + {"sample_num_shots", args.sample_num_shots}}; if (args.stats_out_fname == "-") { std::cout << stats_json << std::endl; diff --git a/src/tesseract_sinter_compat.pybind.h b/src/tesseract_sinter_compat.pybind.h index 623253d..6dcbcc7 100644 --- a/src/tesseract_sinter_compat.pybind.h +++ b/src/tesseract_sinter_compat.pybind.h @@ -292,14 +292,14 @@ void pybind_sinter_compat(py::module& root) { R"pbdoc(Checks if two TesseractSinterDecoder instances are not equal.)pbdoc") .def(py::pickle( [](const TesseractSinterDecoder& self) -> py::tuple { // __getstate__ - return py::make_tuple( - std::string(self.config.dem.str()), self.config.det_beam, self.config.beam_climbing, - self.config.no_revisit_dets, self.config.at_most_two_errors_per_detector, - self.config.verbose, self.config.merge_errors, self.config.pqlimit, - self.config.det_orders, self.config.det_penalty, self.config.create_visualization); + return py::make_tuple(std::string(self.config.dem.str()), self.config.det_beam, + self.config.beam_climbing, self.config.no_revisit_dets, + self.config.verbose, self.config.merge_errors, + self.config.pqlimit, self.config.det_orders, + self.config.det_penalty, self.config.create_visualization); }, [](py::tuple t) { // __setstate__ - if (t.size() != 11) { + if (t.size() != 10) { throw std::runtime_error("Invalid state for TesseractSinterDecoder!"); } TesseractConfig config; @@ -307,13 +307,12 @@ void pybind_sinter_compat(py::module& root) { config.det_beam = t[1].cast(); config.beam_climbing = t[2].cast(); config.no_revisit_dets = t[3].cast(); - config.at_most_two_errors_per_detector = t[4].cast(); - config.verbose = t[5].cast(); - config.merge_errors = t[6].cast(); - config.pqlimit = t[7].cast(); - config.det_orders = t[8].cast>>(); - config.det_penalty = t[9].cast(); - config.create_visualization = t[10].cast(); + config.verbose = t[4].cast(); + config.merge_errors = t[5].cast(); + config.pqlimit = t[6].cast(); + config.det_orders = t[7].cast>>(); + config.det_penalty = t[8].cast(); + config.create_visualization = t[9].cast(); return TesseractSinterDecoder(config); }));