diff --git a/CMakeLists.txt b/CMakeLists.txt index 23e560f4..c448cc69 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,6 +132,16 @@ pybind11_add_module(tesseract_decoder MODULE ${TESSERACT_SRC_DIR}/tesseract.pybi target_compile_options(tesseract_decoder PRIVATE ${OPT_COPTS}) target_include_directories(tesseract_decoder PRIVATE ${TESSERACT_SRC_DIR}) target_link_libraries(tesseract_decoder PRIVATE common utils simplex tesseract_lib) +# Keep statically linked HiGHS symbols private to this extension on every +# supported Unix platform. Mach-O uses an explicit export list because it has +# no ELF-style --exclude-libs option; otherwise highspy can resolve symbols to +# Tesseract's private HiGHS copy when both extensions share a process. +if(APPLE) + target_link_options(tesseract_decoder PRIVATE + "-Wl,-exported_symbols_list,${TESSERACT_SRC_DIR}/tesseract_decoder.exports") +elseif(UNIX) + target_link_options(tesseract_decoder PRIVATE "-Wl,--exclude-libs,ALL") +endif() set_target_properties(tesseract_decoder PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/src LIBRARY_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/src diff --git a/src/BUILD b/src/BUILD index 1b1407bf..b40ca00c 100644 --- a/src/BUILD +++ b/src/BUILD @@ -105,6 +105,19 @@ pybind_extension( "tesseract.pybind.cc", ], copts = OPT_COPTS, + # Keep static-library symbols (notably HiGHS) private to this extension. + # Mach-O has no equivalent to ELF's --exclude-libs, so it needs an explicit + # export list; without it, highspy can see the linked HiGHS symbols. + additional_linker_inputs = ["tesseract_decoder.exports"], + linkopts = select({ + "@platforms//os:osx": [ + "-Wl,-exported_symbols_list,$(location :tesseract_decoder.exports)", + ], + # The ELF equivalent also hides symbols pulled from static archives. + "@platforms//os:linux": ["-Wl,--exclude-libs,ALL"], + # The default also covers Windows and non-ELF toolchains. + "//conditions:default": [], + }), deps = [ ":tesseract_decoder_pybind", ], diff --git a/src/common.cc b/src/common.cc index 654c68ad..3a0cce01 100644 --- a/src/common.cc +++ b/src/common.cc @@ -20,6 +20,8 @@ #include #include +namespace { + std::string vector_to_string(const std::vector& vec) { std::stringstream ss; ss << "["; @@ -34,6 +36,10 @@ std::string vector_to_string(const std::vector& vec) { return ss.str(); } +} // namespace + +namespace tesseract_decoder { + std::string common::Symptom::str() const { std::string s = "Symptom{detectors="; s += vector_to_string(detectors); @@ -255,3 +261,5 @@ stim::DetectorErrorModel common::dem_from_counts(const stim::DetectorErrorModel& } return out_dem; } + +} // namespace tesseract_decoder diff --git a/src/common.h b/src/common.h index 560246a8..3524cb34 100644 --- a/src/common.h +++ b/src/common.h @@ -18,6 +18,7 @@ #include "stim.h" +namespace tesseract_decoder { namespace common { // Represents the effect of an error @@ -113,5 +114,6 @@ stim::DetectorErrorModel dem_from_counts(const stim::DetectorErrorModel& orig_de double merge_weights(double a, double b); } // namespace common +} // namespace tesseract_decoder #endif diff --git a/src/common.pybind.h b/src/common.pybind.h index 693aae83..d3d5fa9e 100644 --- a/src/common.pybind.h +++ b/src/common.pybind.h @@ -26,6 +26,8 @@ namespace py = pybind11; +namespace tesseract_decoder { + void add_common_module(py::module& root) { auto m = root.def_submodule("common", "classes commonly used by the decoder"); @@ -217,4 +219,6 @@ void add_common_module(py::module& root) { )pbdoc"); } +} // namespace tesseract_decoder + #endif diff --git a/src/common.test.cc b/src/common.test.cc index 350853c8..70eebd97 100644 --- a/src/common.test.cc +++ b/src/common.test.cc @@ -17,6 +17,9 @@ #include "gtest/gtest.h" #include "stim.h" +namespace tesseract_decoder { +namespace { + TEST(common, ErrorsStructFromDemInstruction) { // Test a pathological DEM error instruction stim::DetectorErrorModel dem("error(0.1) D0 ^ D0 D1 L0 L1 L1"); @@ -194,3 +197,6 @@ TEST(CommonTest, merge_indistinguishable_errors_two_errors) { auto merged_dem4 = common::merge_indistinguishable_errors(dem4, error_index_map); ASSERT_NEAR(get_merged_probability(merged_dem4), expected_merged_p, 1e-9); } + +} // namespace +} // namespace tesseract_decoder diff --git a/src/simplex.cc b/src/simplex.cc index ec98dad2..9d746e8e 100644 --- a/src/simplex.cc +++ b/src/simplex.cc @@ -20,6 +20,8 @@ #include "io/HMPSIO.h" #include "utils.h" +namespace tesseract_decoder { + constexpr size_t T_COORD = 2; std::string SimplexConfig::str() { @@ -401,3 +403,5 @@ void SimplexDecoder::decode_shots(std::vector& shots, } SimplexDecoder::~SimplexDecoder() {} + +} // namespace tesseract_decoder diff --git a/src/simplex.h b/src/simplex.h index 4288f8ec..5929feb4 100644 --- a/src/simplex.h +++ b/src/simplex.h @@ -24,6 +24,8 @@ struct HighsModel; struct Highs; enum class HighsStatus; +namespace tesseract_decoder { + struct SimplexConfig { stim::DetectorErrorModel dem; bool parallelize = false; @@ -81,4 +83,6 @@ struct SimplexDecoder { void init_ilp(); }; +} // namespace tesseract_decoder + #endif // SIMPLEX_HPP diff --git a/src/simplex.pybind.h b/src/simplex.pybind.h index 27439b25..8648cbf2 100644 --- a/src/simplex.pybind.h +++ b/src/simplex.pybind.h @@ -27,7 +27,9 @@ namespace py = pybind11; +namespace tesseract_decoder { namespace { + // Helper function to compile the decoder. std::unique_ptr _compile_simplex_decoder_helper(const SimplexConfig& self) { return std::make_unique(self); @@ -41,19 +43,27 @@ SimplexConfig simplex_config_maker(py::object dem, bool parallelize = false, {input_dem, parallelize, window_length, window_slide_length, verbose, merge_errors}); } -}; // namespace +} // namespace void add_simplex_module(py::module& root) { auto m = root.def_submodule("simplex", "Module containing the SimplexDecoder and related methods"); - py::class_(m, "SimplexConfig", R"pbdoc( + auto py_simplex_config = py::class_(m, "SimplexConfig", R"pbdoc( Configuration object for the `SimplexDecoder`. This class holds all the parameters needed to initialize and configure a Simplex decoder instance, including the detector error model and decoding options. - )pbdoc") + )pbdoc"); + auto py_simplex_decoder = py::class_(m, "SimplexDecoder", R"pbdoc( + A class that implements the Simplex decoding algorithm. + + It can decode syndromes from a `stim.DetectorErrorModel` to predict + which observables have been flipped. + )pbdoc"); + + py_simplex_config .def(py::init(&simplex_config_maker), py::arg("dem"), py::arg("parallelize") = false, py::arg("window_length") = 0, py::arg("window_slide_length") = 0, py::arg("verbose") = false, py::arg("merge_errors") = true, R"pbdoc( @@ -101,12 +111,7 @@ void add_simplex_module(py::module& root) { settings. )pbdoc"); - py::class_(m, "SimplexDecoder", R"pbdoc( - A class that implements the Simplex decoding algorithm. - - It can decode syndromes from a `stim.DetectorErrorModel` to predict - which observables have been flipped. - )pbdoc") + py_simplex_decoder .def(py::init(), py::arg("config"), R"pbdoc( The constructor for the `SimplexDecoder` class. @@ -351,4 +356,7 @@ void add_simplex_module(py::module& root) { (num_shots, num_observables). )pbdoc"); } + +} // namespace tesseract_decoder + #endif diff --git a/src/simplex_main.cc b/src/simplex_main.cc index 7c8542a8..560284a5 100644 --- a/src/simplex_main.cc +++ b/src/simplex_main.cc @@ -24,6 +24,8 @@ #include "stim.h" #include "utils.h" +using namespace tesseract_decoder; + struct Args { std::string circuit_path; std::string dem_path; diff --git a/src/stim_utils.pybind.h b/src/stim_utils.pybind.h index ff638a79..a8a7ae4f 100644 --- a/src/stim_utils.pybind.h +++ b/src/stim_utils.pybind.h @@ -7,6 +7,8 @@ #include "stim.h" +namespace tesseract_decoder { + namespace { namespace py = pybind11; } @@ -66,4 +68,6 @@ void dem_setter(T& config, py::object dem) { config.dem = parse_py_object(dem); } +} // namespace tesseract_decoder + #endif diff --git a/src/tesseract.cc b/src/tesseract.cc index 0a180754..9ed11507 100644 --- a/src/tesseract.cc +++ b/src/tesseract.cc @@ -81,6 +81,8 @@ struct hash> { }; } // namespace std +namespace tesseract_decoder { + std::string TesseractConfig::str() { auto& config = *this; std::stringstream ss; @@ -733,3 +735,5 @@ void TesseractDecoder::build_sparse_d2e(const std::vector& detections) } } } + +} // namespace tesseract_decoder diff --git a/src/tesseract.h b/src/tesseract.h index 97b88eb4..d06e47e1 100644 --- a/src/tesseract.h +++ b/src/tesseract.h @@ -28,6 +28,8 @@ #include "utils.h" #include "visualization.h" +namespace tesseract_decoder { + constexpr size_t INF_DET_BEAM = std::numeric_limits::max(); constexpr int DEFAULT_DET_BEAM = 5; constexpr size_t DEFAULT_PQLIMIT = 200000; @@ -144,4 +146,6 @@ struct TesseractDecoder { const std::vector>& active_d2e); }; +} // namespace tesseract_decoder + #endif // TESSERACT_DECODER_H diff --git a/src/tesseract.perf.cc b/src/tesseract.perf.cc index a92dbe66..2de6928d 100644 --- a/src/tesseract.perf.cc +++ b/src/tesseract.perf.cc @@ -20,6 +20,8 @@ #include "stim.h" #include "utils.h" +using namespace tesseract_decoder; + constexpr uint64_t test_data_seed = 752024; template diff --git a/src/tesseract.pybind.cc b/src/tesseract.pybind.cc index 9f2808f4..f81486d0 100644 --- a/src/tesseract.pybind.cc +++ b/src/tesseract.pybind.cc @@ -25,6 +25,7 @@ #include "visualization.pybind.h" PYBIND11_MODULE(tesseract_decoder, tesseract) { + using namespace tesseract_decoder; py::module::import("stim"); add_common_module(tesseract); diff --git a/src/tesseract.pybind.h b/src/tesseract.pybind.h index 19a79fd9..2ebf892a 100644 --- a/src/tesseract.pybind.h +++ b/src/tesseract.pybind.h @@ -27,7 +27,9 @@ namespace py = pybind11; +namespace tesseract_decoder { namespace { + // Helper function to compile the decoder. std::unique_ptr _compile_tesseract_decoder_helper(const TesseractConfig& self) { return std::make_unique(self); @@ -79,12 +81,20 @@ void add_tesseract_module(py::module& root) { py::arg("num_detectors"), py::arg("sparsify_base_degree"), "Returns the suggested number of optional high-degree errors to reactivate per shot."); - py::class_(m, "TesseractConfig", R"pbdoc( + auto py_tesseract_config = py::class_(m, "TesseractConfig", R"pbdoc( Configuration object for the `TesseractDecoder`. This class holds all the parameters needed to initialize and configure a Tesseract decoder instance. - )pbdoc") + )pbdoc"); + auto py_tesseract_decoder = py::class_(m, "TesseractDecoder", R"pbdoc( + A class that implements the Tesseract decoding algorithm. + + It can decode syndromes from a `stim.DetectorErrorModel` to predict + which observables have been flipped. + )pbdoc"); + + py_tesseract_config .def(py::init<>(), R"pbdoc( Default constructor for TesseractConfig. Creates a new instance with default parameter values. @@ -239,12 +249,7 @@ void add_tesseract_module(py::module& root) { `TesseractConfig` object. )pbdoc"); - py::class_(m, "TesseractDecoder", R"pbdoc( - A class that implements the Tesseract decoding algorithm. - - It can decode syndromes from a `stim.DetectorErrorModel` to predict - which observables have been flipped. - )pbdoc") + py_tesseract_decoder .def(py::init(), py::arg("config"), R"pbdoc( The constructor for the `TesseractDecoder` class. @@ -522,4 +527,6 @@ void add_tesseract_module(py::module& root) { "visualization of the algorithm"); } +} // namespace tesseract_decoder + #endif diff --git a/src/tesseract.test.cc b/src/tesseract.test.cc index fd0d471e..4bda92d9 100644 --- a/src/tesseract.test.cc +++ b/src/tesseract.test.cc @@ -23,6 +23,9 @@ #include "stim.h" #include "utils.h" +namespace tesseract_decoder { +namespace { + constexpr uint64_t test_data_seed = 752024; bool simplex_test_compare(stim::DetectorErrorModel& dem, std::vector& shots) { @@ -559,3 +562,6 @@ TEST(tesseract, MoreThan64Observables) { ASSERT_EQ(flipped[i], i); } } + +} // namespace +} // namespace tesseract_decoder diff --git a/src/tesseract_decoder.exports b/src/tesseract_decoder.exports new file mode 100644 index 00000000..896702d0 --- /dev/null +++ b/src/tesseract_decoder.exports @@ -0,0 +1 @@ +_PyInit_tesseract_decoder diff --git a/src/tesseract_main.cc b/src/tesseract_main.cc index 4b3975fc..bbffc1b8 100644 --- a/src/tesseract_main.cc +++ b/src/tesseract_main.cc @@ -29,6 +29,8 @@ #include "tesseract.h" #include "utils.h" +using namespace tesseract_decoder; + struct Args { std::string circuit_path; std::string dem_path; diff --git a/src/tesseract_sinter_compat.pybind.h b/src/tesseract_sinter_compat.pybind.h index aecaa6b8..f118750b 100644 --- a/src/tesseract_sinter_compat.pybind.h +++ b/src/tesseract_sinter_compat.pybind.h @@ -25,6 +25,8 @@ namespace py = pybind11; +namespace tesseract_decoder { + // These are the classes that will be exposed to Python. struct TesseractSinterCompiledDecoder; struct TesseractSinterDecoder; @@ -495,3 +497,5 @@ void pybind_sinter_compat(py::module& root) { root.attr("TesseractSinterDecoder") = m.attr("TesseractSinterDecoder"); root.attr("make_tesseract_sinter_decoders_dict") = m.attr("make_tesseract_sinter_decoders_dict"); } + +} // namespace tesseract_decoder diff --git a/src/tesseract_trellis.cc b/src/tesseract_trellis.cc index afc91faa..034191b4 100644 --- a/src/tesseract_trellis.cc +++ b/src/tesseract_trellis.cc @@ -33,6 +33,8 @@ #include "utils.h" +namespace tesseract_decoder { + struct TesseractTrellisWideKernelBase { virtual ~TesseractTrellisWideKernelBase() = default; virtual void decode_shot(TesseractTrellisDecoder* decoder, @@ -1288,3 +1290,5 @@ void TesseractTrellisDecoder::decode_shots(std::vector& shots, obs_predicted[i] = decode(shots[i].hits); } } + +} // namespace tesseract_decoder diff --git a/src/tesseract_trellis.h b/src/tesseract_trellis.h index fec0a5ec..cea404a5 100644 --- a/src/tesseract_trellis.h +++ b/src/tesseract_trellis.h @@ -22,6 +22,8 @@ #include "common.h" #include "stim.h" +namespace tesseract_decoder { + struct TesseractTrellisWideKernelBase; enum class TesseractTrellisRankingMode { @@ -102,4 +104,6 @@ struct TesseractTrellisDecoder { std::vector kept_state_histogram_scratch; }; +} // namespace tesseract_decoder + #endif // TESSERACT_TRELLIS_DECODER_H diff --git a/src/tesseract_trellis.test.cc b/src/tesseract_trellis.test.cc index e375212b..e25f234a 100644 --- a/src/tesseract_trellis.test.cc +++ b/src/tesseract_trellis.test.cc @@ -22,6 +22,8 @@ #include "stim.h" +namespace tesseract_decoder { + TEST(TesseractTrellisDecoderTest, ComputesObservableProbabilityForAmbiguousSyndrome) { stim::DetectorErrorModel dem(R"DEM( error(0.1) D0 @@ -221,3 +223,5 @@ TEST(TesseractTrellisDecoderTest, RejectsMoreThanOneObservable) { EXPECT_NE(std::string(err.what()).find("supports at most one observable"), std::string::npos); } } + +} // namespace tesseract_decoder diff --git a/src/tesseract_trellis_main.cc b/src/tesseract_trellis_main.cc index e131f70d..11c89dc7 100644 --- a/src/tesseract_trellis_main.cc +++ b/src/tesseract_trellis_main.cc @@ -26,6 +26,8 @@ #include "tesseract_trellis.h" #include "utils.h" +using namespace tesseract_decoder; + namespace { TesseractTrellisRankingMode parse_ranking_mode(const std::string& value) { diff --git a/src/test_data.h b/src/test_data.h index 137f57e9..afe22bb7 100644 --- a/src/test_data.h +++ b/src/test_data.h @@ -19,6 +19,8 @@ #include "stim.h" +namespace tesseract_decoder { + std::vector get_small_test_circuits() { return {}; } @@ -27,4 +29,6 @@ std::vector get_large_test_circuits() { return {}; } +} // namespace tesseract_decoder + #endif // TESSERACT_TEST_DATA_H diff --git a/src/utils.cc b/src/utils.cc index a72a8ced..58659e02 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -27,6 +27,8 @@ #include "common.h" #include "stim.h" +namespace tesseract_decoder { + std::vector> get_detector_coords(const stim::DetectorErrorModel& dem) { std::vector> detector_coords; for (const stim::DemInstruction& instruction : common::flatten(dem).instructions) { @@ -279,3 +281,5 @@ uint64_t vector_to_u64_mask(const std::vector& v) { } return mask; } + +} // namespace tesseract_decoder diff --git a/src/utils.h b/src/utils.h index 3c5d6569..ad6932fb 100644 --- a/src/utils.h +++ b/src/utils.h @@ -29,6 +29,8 @@ #include "common.h" #include "stim.h" +namespace tesseract_decoder { + constexpr const double EPSILON = 1e-7; std::vector> get_detector_coords(const stim::DetectorErrorModel& dem); @@ -112,4 +114,6 @@ size_t parallel_for_shots_in_order(size_t num_shots, size_t num_threads, Process return shot; } +} // namespace tesseract_decoder + #endif // __TESSERACT_UTILS_H__ diff --git a/src/utils.pybind.h b/src/utils.pybind.h index 118b87e5..8ea9564e 100644 --- a/src/utils.pybind.h +++ b/src/utils.pybind.h @@ -23,6 +23,8 @@ namespace py = pybind11; +namespace tesseract_decoder { + void add_utils_module(py::module& root) { auto m = root.def_submodule("utils", "utility methods"); @@ -137,4 +139,7 @@ void add_utils_module(py::module& root) { // Not exposing sampling_from_dem and sample_shots because they depend on // stim::SparseShot which stim doesn't expose to python. } + +} // namespace tesseract_decoder + #endif diff --git a/src/visualization.cc b/src/visualization.cc index ecd78faa..c9c6f9bb 100644 --- a/src/visualization.cc +++ b/src/visualization.cc @@ -1,6 +1,8 @@ #include "visualization.h" +namespace tesseract_decoder { + void Visualizer::add_errors(const std::vector& errors) { for (auto& error : errors) { lines.push_back(error.str()); @@ -55,3 +57,5 @@ void Visualizer::write(const char* fpath) { fclose(fout); } + +} // namespace tesseract_decoder diff --git a/src/visualization.h b/src/visualization.h index 64d9ad5c..d3af2e9b 100644 --- a/src/visualization.h +++ b/src/visualization.h @@ -7,6 +7,8 @@ #include "common.h" +namespace tesseract_decoder { + struct Visualizer { void add_detector_coords(const std::vector>&); void add_errors(const std::vector&); @@ -19,4 +21,6 @@ struct Visualizer { std::list lines; }; +} // namespace tesseract_decoder + #endif diff --git a/src/visualization.pybind.h b/src/visualization.pybind.h index 820bf864..cdf1b671 100644 --- a/src/visualization.pybind.h +++ b/src/visualization.pybind.h @@ -8,9 +8,13 @@ namespace py = pybind11; +namespace tesseract_decoder { + void add_visualization_module(py::module& root) { auto m = root.def_submodule("viz", "Module containing the visualization tools"); - py::class_(m, "Visualizer") + py::class_(m, "Visualizer") .def(py::init<>()) - .def("write", &Visualizer::write, py::arg("fpath")); + .def("write", &tesseract_decoder::Visualizer::write, py::arg("fpath")); } + +} // namespace tesseract_decoder