├── .clang-format ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── BUILD ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── WORKSPACE ├── assets ├── ablated_stabilizers.png ├── cmp_transit_from_prev.png ├── color2surface.png ├── color_code_layouts.png ├── color_code_layouts.svg ├── example_boundary_match.png ├── generated │ ├── cmp_transit.png │ ├── compare_X.png │ ├── compare_XZ.png │ ├── compare_Z.png │ ├── midout_error_X.png │ ├── midout_error_XZ.png │ ├── midout_error_Z.png │ ├── midout_footprint_X.png │ ├── midout_footprint_XZ.png │ ├── midout_footprint_Z.png │ ├── phenom.png │ ├── prev_work_comparison_0.0001.png │ ├── prev_work_comparison_0.0002.png │ ├── prev_work_comparison_0.0003.png │ ├── prev_work_comparison_0.0005.png │ ├── prev_work_comparison_0.001.png │ ├── prev_work_comparison_0.002.png │ ├── stats-xz-combo.csv │ ├── superdense_error_X.png │ ├── superdense_error_XZ.png │ ├── superdense_error_Z.png │ ├── superdense_footprint_X.png │ ├── superdense_footprint_XZ.png │ ├── superdense_footprint_Z.png │ ├── timing_d.png │ ├── timing_d_full.png │ ├── timing_r.png │ ├── timing_r_full.png │ ├── timing_rq.png │ ├── timing_rq_full.png │ └── toric.png ├── midout-detslice-cycle.png ├── midout-detslice-cycle.svg ├── midout_detectors.svg ├── midout_detectors_2.svg ├── multiplexing.png ├── perf.png ├── pyramid_code.png ├── pyramid_code.svg ├── solved_euclidean_problem.png ├── stats.csv ├── superdense-detslice-cycle.png ├── superdense-detslice-cycle.svg ├── superdense_detectors.svg ├── superdense_detectors_2.svg └── timing_stats.csv ├── doc ├── chromobius.pyi ├── chromobius_api_reference.md ├── developers.md └── getting_started.ipynb ├── file_lists ├── perf_files ├── pybind_files ├── source_files_no_main └── test_files ├── pyproject.toml ├── requirements.txt ├── setup.py ├── src ├── chromobius.h ├── chromobius │ ├── commands │ │ ├── main_all.cc │ │ ├── main_all.h │ │ ├── main_all.test.cc │ │ ├── main_all.test.h │ │ ├── main_benchmark.cc │ │ ├── main_benchmark.h │ │ ├── main_benchmark.test.cc │ │ ├── main_describe_decoder.cc │ │ ├── main_describe_decoder.h │ │ ├── main_describe_decoder.test.cc │ │ ├── main_predict.cc │ │ ├── main_predict.h │ │ └── main_predict.test.cc │ ├── datatypes │ │ ├── atomic_error.cc │ │ ├── atomic_error.h │ │ ├── atomic_error.perf.cc │ │ ├── atomic_error.test.cc │ │ ├── color_basis.cc │ │ ├── color_basis.h │ │ ├── color_basis.test.cc │ │ ├── conf.h │ │ ├── rgb_edge.cc │ │ ├── rgb_edge.h │ │ ├── rgb_edge.test.cc │ │ ├── stim_integration.cc │ │ ├── stim_integration.h │ │ ├── stim_integration.test.cc │ │ ├── xor_vec.h │ │ └── xor_vec.test.cc │ ├── decode │ │ ├── decoder.cc │ │ ├── decoder.h │ │ ├── decoder.perf.cc │ │ ├── decoder.test.cc │ │ ├── decoder_integration.test.cc │ │ ├── matcher_interface.h │ │ ├── pymatcher.cc │ │ └── pymatcher.h │ ├── graph │ │ ├── charge_graph.cc │ │ ├── charge_graph.h │ │ ├── charge_graph.test.cc │ │ ├── choose_rgb_reps.cc │ │ ├── choose_rgb_reps.h │ │ ├── choose_rgb_reps.test.cc │ │ ├── collect_atomic_errors.cc │ │ ├── collect_atomic_errors.h │ │ ├── collect_composite_errors.cc │ │ ├── collect_composite_errors.h │ │ ├── collect_nodes.cc │ │ ├── collect_nodes.h │ │ ├── drag_graph.cc │ │ ├── drag_graph.h │ │ ├── drag_graph.test.cc │ │ ├── euler_tours.cc │ │ ├── euler_tours.h │ │ ├── euler_tours.perf.cc │ │ └── euler_tours.test.cc │ ├── pybind │ │ ├── chromobius.pybind.cc │ │ ├── chromobius_pybind_test.py │ │ ├── sinter_compat.pybind.cc │ │ ├── sinter_compat.pybind.h │ │ └── sinter_compat_pybind_test.py │ ├── test_util.test.cc │ ├── test_util.test.h │ └── util.perf.h ├── clorco │ ├── __init__.py │ ├── _circuits_vs_chromobius_test.py │ ├── _make_circuit.py │ ├── _make_circuit_params.py │ ├── _make_circuit_test.py │ ├── color2surface_code │ │ ├── __init__.py │ │ ├── _color2surface_layouts.py │ │ ├── _color2surface_layouts_test.py │ │ ├── _draw_mobius_graph.py │ │ └── _keyed_constructions.py │ ├── color_code │ │ ├── __init__.py │ │ ├── _color_code_layouts.py │ │ ├── _color_code_layouts_test.py │ │ ├── _keyed_constructions.py │ │ ├── _midout_planar_color_code_circuits.py │ │ ├── _midout_planar_color_code_circuits_test.py │ │ ├── _mxyz_color_codes.py │ │ ├── _mxyz_color_codes_test.py │ │ ├── _superdense_planar_color_code_circuits.py │ │ ├── _superdense_planar_color_code_circuits_test.py │ │ ├── _toric_color_code_circuits.py │ │ └── _toric_color_code_circuits_test.py │ ├── pyramid_code │ │ ├── __init__.py │ │ ├── _keyed_constructions.py │ │ ├── _pyramid_code_layouts.py │ │ └── _pyramid_code_layouts_test.py │ ├── rep_code │ │ ├── __init__.py │ │ ├── _keyed_constructions.py │ │ ├── _keyed_constructions_test.py │ │ ├── _rep_code_circuits.py │ │ ├── _rep_code_circuits_test.py │ │ ├── _rep_code_layouts.py │ │ └── _rep_code_layouts_test.py │ └── surface_code │ │ ├── __init__.py │ │ ├── _keyed_constructions.py │ │ ├── _surface_code_chunks.py │ │ ├── _surface_code_chunks_test.py │ │ ├── _surface_code_layouts.py │ │ ├── _surface_code_layouts_test.py │ │ ├── _surface_code_patches.py │ │ ├── _transversal_cnot.py │ │ ├── _transversal_cnot_test.py │ │ ├── _xz_surface_code_memory_circuits.py │ │ └── _xz_surface_code_memory_circuits_test.py ├── gen │ ├── __init__.py │ ├── _circuit_util.py │ ├── _circuit_util_test.py │ ├── _core │ │ ├── __init__.py │ │ ├── _builder.py │ │ ├── _builder_test.py │ │ ├── _keyed_pauli_string.py │ │ ├── _measurement_tracker.py │ │ ├── _noise.py │ │ ├── _noise_test.py │ │ ├── _patch.py │ │ ├── _patch_test.py │ │ ├── _pauli_string.py │ │ ├── _pauli_string_test.py │ │ ├── _stabilizer_code.py │ │ ├── _stabilizer_code_test.py │ │ ├── _tile.py │ │ ├── _tile_test.py │ │ ├── _util.py │ │ └── _util_test.py │ ├── _flows │ │ ├── __init__.py │ │ ├── _chunk.py │ │ ├── _chunk_interface.py │ │ ├── _chunk_loop.py │ │ ├── _chunk_reflow.py │ │ ├── _chunk_test.py │ │ ├── _circuit_util.py │ │ ├── _flow.py │ │ ├── _flow_test.py │ │ ├── _flow_util.py │ │ ├── _flow_util_test.py │ │ └── _test_util.py │ ├── _layers │ │ ├── __init__.py │ │ ├── _data.py │ │ ├── _det_obs_annotation_layer.py │ │ ├── _empty_layer.py │ │ ├── _feedback_layer.py │ │ ├── _feedback_layer_test.py │ │ ├── _interact_layer.py │ │ ├── _interact_swap_layer.py │ │ ├── _iswap_layer.py │ │ ├── _layer.py │ │ ├── _layer_circuit.py │ │ ├── _layer_circuit_test.py │ │ ├── _loop_layer.py │ │ ├── _measure_layer.py │ │ ├── _mpp_layer.py │ │ ├── _noise_layer.py │ │ ├── _qubit_coord_annotation_layer.py │ │ ├── _reset_layer.py │ │ ├── _rotation_layer.py │ │ ├── _shift_coord_annotation_layer.py │ │ ├── _sqrt_pp_layer.py │ │ ├── _swap_layer.py │ │ ├── _transpile.py │ │ └── _transpile_test.py │ ├── _util.py │ ├── _util_test.py │ ├── _viz_circuit_html.py │ ├── _viz_gltf_3d.py │ ├── _viz_gltf_3d_test.py │ ├── _viz_patch_svg.py │ └── _viz_patch_svg_test.py ├── main.cc └── main.perf.cc ├── test_data ├── color2surface_d5_transit_p100.stim ├── color2surface_d7_phenom_r7_p100.stim ├── fix_check_1.stim ├── midout488_color_code_d9_r33_p1000.stim ├── midout_color_code_d25_r100_p1000.stim ├── midout_color_code_d5_r10_p1000.stim ├── midout_color_code_d9_r36_p1000.stim ├── old_fail_to_configure_cases │ ├── detector_reorder_superdense_round.stim │ ├── detector_unreorder_superdense_round.stim │ └── hard_config.stim ├── phenom_color_code_d5_r5_p1000.stim ├── phenom_color_code_d5_r5_p1000_with_ignored.stim ├── rep_code_d9_transit_p10.stim ├── rep_code_rbrrr_d9_transit_p10.stim ├── rep_code_rg_d9_transit_p10.stim ├── superdense_color_code_d5_r20_p1000.stim ├── surface_code_d5_r5_p1000.stim └── toric_superdense_color_code_epr_d12_r5_p1000.stim └── tools ├── doctest_proper.py ├── fuse_xz_data ├── gen_chromobius_api_reference.py ├── gen_chromobius_stub_file.py ├── gen_circuits ├── gen_timing_stats ├── overwrite_dev_versions_with_date.py ├── plot_comparison ├── plot_stats_timing ├── regen_docs.sh ├── regen_file_lists.sh ├── regen_test_data.sh ├── sinter_plot_print ├── step1_generate_circuits.sh ├── step2_collect_stats.sh ├── step3_plot_stats.sh └── util_gen_stub_file.py /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | IndentWidth: 4 5 | ColumnLimit: 120 6 | AlignAfterOpenBracket: AlwaysBreak 7 | AllowShortBlocksOnASingleLine: Never 8 | AllowShortFunctionsOnASingleLine: None 9 | AllowShortIfStatementsOnASingleLine: Never 10 | AllowShortLoopsOnASingleLine: false 11 | AllowShortLambdasOnASingleLine: None 12 | BinPackArguments: false 13 | BinPackParameters: false 14 | 15 | IncludeCategories: 16 | - Regex: '^<.*>$' 17 | Priority: 1 18 | - Regex: '^"gtest/.*"$' 19 | Priority: 2 20 | - Regex: '^".*"$' 21 | Priority: 3 22 | ... 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .pytest_cache 3 | out 4 | __pycache__ 5 | cmake-build-debug 6 | .cmake 7 | CMakeFiles 8 | CMakeCache.txt 9 | .ninja_deps 10 | .ninja_log 11 | Testing 12 | _deps 13 | build.ninja 14 | cmake_install.cmake 15 | perf.data 16 | perf.data.old 17 | lib 18 | bazel-bin 19 | bazel-out 20 | bazel-testlogs 21 | bazel-* 22 | python_build_chromobius 23 | chromobius.egg-info 24 | .clwb 25 | wheelhouse 26 | build 27 | package_data 28 | .eggs 29 | dist 30 | MODULE.bazel 31 | MODULE.bazel.lock 32 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | load("@rules_python//python:packaging.bzl", "py_wheel") 4 | 5 | SOURCE_FILES_NO_MAIN = glob( 6 | [ 7 | "src/**/*.cc", 8 | "src/**/*.h", 9 | "src/**/*.inl", 10 | ], 11 | exclude = glob([ 12 | "src/**/*.test.cc", 13 | "src/**/*.test.h", 14 | "src/**/*.perf.cc", 15 | "src/**/*.perf.h", 16 | "src/**/*.pybind.cc", 17 | "src/**/*.pybind.h", 18 | "src/**/main.cc", 19 | ]), 20 | ) 21 | 22 | TEST_FILES = glob( 23 | [ 24 | "src/**/*.test.cc", 25 | "src/**/*.test.h", 26 | ], 27 | ) 28 | 29 | PERF_FILES = glob( 30 | [ 31 | "src/**/*.perf.cc", 32 | "src/**/*.perf.h", 33 | ], 34 | ) 35 | 36 | PYBIND_FILES = glob( 37 | [ 38 | "src/**/*.pybind.cc", 39 | "src/**/*.pybind.h", 40 | ], 41 | ) 42 | 43 | cc_binary( 44 | name = "chromobius", 45 | srcs = SOURCE_FILES_NO_MAIN + glob(["src/**/main.cc"]), 46 | copts = [ 47 | "-std=c++20", 48 | "-O3", 49 | "-DNDEBUG", 50 | ], 51 | includes = ["src/"], 52 | deps = [ 53 | "@pymatching//:libpymatching", 54 | "@stim//:stim_lib", 55 | ], 56 | ) 57 | 58 | cc_binary( 59 | name = "chromobius_perf", 60 | srcs = SOURCE_FILES_NO_MAIN + PERF_FILES, 61 | copts = [ 62 | "-O3", 63 | "-std=c++20", 64 | "-DNDEBUG", 65 | ], 66 | data = glob(["test_data/**"]), 67 | includes = ["src/"], 68 | deps = [ 69 | "@pymatching//:libpymatching", 70 | "@stim//:stim_lib", 71 | ], 72 | ) 73 | 74 | cc_test( 75 | name = "chromobius_test", 76 | srcs = SOURCE_FILES_NO_MAIN + TEST_FILES, 77 | copts = [ 78 | "-std=c++20", 79 | ], 80 | data = glob(["test_data/**"]), 81 | includes = ["src/"], 82 | deps = [ 83 | "@gtest", 84 | "@gtest//:gtest_main", 85 | "@pymatching//:libpymatching", 86 | "@stim//:stim_lib", 87 | ], 88 | ) 89 | 90 | cc_binary( 91 | name = "chromobius.so", 92 | srcs = SOURCE_FILES_NO_MAIN + PYBIND_FILES, 93 | copts = [ 94 | "-O3", 95 | "-std=c++20", 96 | "-fvisibility=hidden", 97 | "-DNDEBUG", 98 | "-DCHROMOBIUS_VERSION_INFO=0.0.dev0", 99 | ], 100 | includes = ["src/"], 101 | linkshared = 1, 102 | deps = [ 103 | "@pybind11", 104 | "@pymatching//:libpymatching", 105 | "@stim//:stim_lib", 106 | ], 107 | ) 108 | 109 | genrule( 110 | name = "chromobius_wheel_files", 111 | srcs = ["doc/chromobius.pyi"], 112 | outs = ["chromobius.pyi"], 113 | cmd = "cp $(location doc/chromobius.pyi) $@", 114 | ) 115 | 116 | py_wheel( 117 | name = "chromobius_dev_wheel", 118 | distribution = "chromobius", 119 | requires = [ 120 | "numpy", 121 | "stim", 122 | ], 123 | version = "0.0.dev0", 124 | deps = [ 125 | ":chromobius.so", 126 | ":chromobius_wheel_files", 127 | ], 128 | ) 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | We'd love to accept your patches and contributions to this project. 4 | 5 | ## Before you begin 6 | 7 | ### Sign our Contributor License Agreement 8 | 9 | Contributions to this project must be accompanied by a 10 | [Contributor License Agreement](https://cla.developers.google.com/about) (CLA). 11 | You (or your employer) retain the copyright to your contribution; this simply 12 | gives us permission to use and redistribute your contributions as part of the 13 | project. 14 | 15 | If you or your current employer have already signed the Google CLA (even if it 16 | was for a different project), you probably don't need to do it again. 17 | 18 | Visit to see your current agreements or to 19 | sign a new one. 20 | 21 | ### Review our community guidelines 22 | 23 | This project follows 24 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). 25 | 26 | ## Contribution process 27 | 28 | ### Code reviews 29 | 30 | All submissions, including submissions by project members, require review. We 31 | use GitHub pull requests for this purpose. Consult 32 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 33 | information on using pull requests. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chromobius: color code decoder 2 | 3 | Chromobius is an implementation of a ["mobius decoder"](https://arxiv.org/abs/2108.11395), which approximates the color code decoding problem as a minimum weight matching problem. 4 | Chromobius uses [PyMatching](https://github.com/oscarhiggott/PyMatching/) to solve the minimum weight matching problem. 5 | 6 | See ((((the paper "New circuits and an open source decoder for the color code")))) for more details on how Chromobius works. 7 | 8 | ## How to use Chromobius 9 | 10 | See the [**getting started notebook**](doc/getting_started.ipynb). 11 | 12 | Also see the [python api reference](doc/chromobius_api_reference.md). 13 | 14 | Programmers who want to edit and build Chromobius can check the [developer documentation](doc/developers.md). 15 | 16 | ## Example Snippets 17 | 18 | ### Decoding a shot with Chromobius 19 | 20 | From python: 21 | 22 | ```python 23 | import stim 24 | import chromobius 25 | import numpy as np 26 | 27 | def count_mistakes(circuit: stim.Circuit, shots: int) -> int: 28 | # Sample the circuit. 29 | dets, actual_obs_flips = circuit.compile_detector_sampler().sample( 30 | shots=shots, 31 | separate_observables=True, 32 | bit_packed=True, 33 | ) 34 | 35 | # Decode with Chromobius. 36 | decoder = chromobius.compile_decoder_for_dem(circuit.detector_error_model()) 37 | predicted_obs_flips = decoder.predict_obs_flips_from_dets_bit_packed(dets) 38 | 39 | # Count mistakes. 40 | return np.count_nonzero(np.any(predicted_obs_flips != actual_obs_flips, axis=1)) 41 | ``` 42 | 43 | From the command line: 44 | 45 | ```bash 46 | # Sample shots of detectors and observable flips. 47 | stim detect \ 48 | --shots 100000 \ 49 | --in "example_circuit.stim" \ 50 | --out "dets.b8" \ 51 | --out_format "b8" \ 52 | --obs_out "obs_actual.txt" \ 53 | --obs_out_format "01" 54 | 55 | # Extract a detector error model used to configure Chromobius. 56 | stim analyze_errors \ 57 | --in "example_circuit.stim" \ 58 | --fold_loops \ 59 | --out "dem.dem" 60 | 61 | # Decode the shots. 62 | chromobius predict \ 63 | --dem "dem.dem" \ 64 | --in "dets.b8" \ 65 | --in_format "b8" \ 66 | --out "obs_predicted.txt" \ 67 | --out_format "01" 68 | 69 | # Count the number of shots with a prediction mistake. 70 | paste obs_actual.txt obs_predicted.txt \ 71 | | grep -Pv "^([01]*)\\s*\\1$" \ 72 | | wc -l 73 | ``` 74 | 75 | From python using sinter: 76 | 77 | ```python 78 | import sinter 79 | import chromobius 80 | import os 81 | 82 | tasks: list[sinter.Task] = ... 83 | stats: list[sinter.TaskStats] = sinter.collect( 84 | decoders=["chromobius"], 85 | custom_decoders=chromobius.sinter_decoders(), 86 | tasks=tasks, 87 | num_workers=os.cpu_count(), 88 | max_shots=100_000, 89 | max_errors=100, 90 | ) 91 | ``` 92 | 93 | From the command line using sinter: 94 | 95 | ```bash 96 | sinter collect \ 97 | --circuits "example_circuit.stim" \ 98 | --decoders chromobius \ 99 | --custom_decoders_module_function "chromobius:sinter_decoders" \ 100 | --max_shots 100_000 \ 101 | --max_errors 100 102 | --processes auto \ 103 | --save_resume_filepath "stats.csv" \ 104 | ``` 105 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 2 | load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") 3 | 4 | git_repository( 5 | name = "stim", 6 | commit = "da4594c5ede00a063ec2b84bd830f846b5d097dd", 7 | remote = "https://github.com/quantumlib/stim.git", 8 | ) 9 | 10 | git_repository( 11 | name = "pymatching", 12 | commit = "40dcf8c01273ff7e23a7105d5cdc410ada067001", 13 | remote = "https://github.com/oscarhiggott/pymatching.git", 14 | ) 15 | 16 | http_archive( 17 | name = "gtest", 18 | sha256 = "353571c2440176ded91c2de6d6cd88ddd41401d14692ec1f99e35d013feda55a", 19 | strip_prefix = "googletest-release-1.11.0", 20 | urls = ["https://github.com/google/googletest/archive/refs/tags/release-1.11.0.zip"], 21 | ) 22 | 23 | http_archive( 24 | name = "pybind11", 25 | build_file = "@pybind11_bazel//:pybind11.BUILD", 26 | sha256 = "832e2f309c57da9c1e6d4542dedd34b24e4192ecb4d62f6f4866a737454c9970", 27 | strip_prefix = "pybind11-2.10.4", 28 | urls = ["https://github.com/pybind/pybind11/archive/v2.10.4.tar.gz"], 29 | ) 30 | 31 | http_archive( 32 | name = "pybind11_bazel", 33 | sha256 = "e8355ee56c2ff772334b4bfa22be17c709e5573f6d1d561c7176312156c27bd4", 34 | strip_prefix = "pybind11_bazel-2.11.1", 35 | urls = ["https://github.com/pybind/pybind11_bazel/releases/download/v2.11.1/pybind11_bazel-2.11.1.tar.gz"], 36 | ) 37 | 38 | http_archive( 39 | name = "rules_python", 40 | sha256 = "0a8003b044294d7840ac7d9d73eef05d6ceb682d7516781a4ec62eeb34702578", 41 | strip_prefix = "rules_python-0.24.0", 42 | url = "https://github.com/bazelbuild/rules_python/releases/download/0.24.0/rules_python-0.24.0.tar.gz", 43 | ) 44 | 45 | load("@rules_python//python:repositories.bzl", "py_repositories") 46 | load("@pybind11_bazel//:python_configure.bzl", "python_configure") 47 | 48 | py_repositories() 49 | 50 | python_configure(name = "local_config_python") 51 | -------------------------------------------------------------------------------- /assets/ablated_stabilizers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/ablated_stabilizers.png -------------------------------------------------------------------------------- /assets/cmp_transit_from_prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/cmp_transit_from_prev.png -------------------------------------------------------------------------------- /assets/color2surface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/color2surface.png -------------------------------------------------------------------------------- /assets/color_code_layouts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/color_code_layouts.png -------------------------------------------------------------------------------- /assets/example_boundary_match.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/example_boundary_match.png -------------------------------------------------------------------------------- /assets/generated/cmp_transit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/cmp_transit.png -------------------------------------------------------------------------------- /assets/generated/compare_X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/compare_X.png -------------------------------------------------------------------------------- /assets/generated/compare_XZ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/compare_XZ.png -------------------------------------------------------------------------------- /assets/generated/compare_Z.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/compare_Z.png -------------------------------------------------------------------------------- /assets/generated/midout_error_X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/midout_error_X.png -------------------------------------------------------------------------------- /assets/generated/midout_error_XZ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/midout_error_XZ.png -------------------------------------------------------------------------------- /assets/generated/midout_error_Z.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/midout_error_Z.png -------------------------------------------------------------------------------- /assets/generated/midout_footprint_X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/midout_footprint_X.png -------------------------------------------------------------------------------- /assets/generated/midout_footprint_XZ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/midout_footprint_XZ.png -------------------------------------------------------------------------------- /assets/generated/midout_footprint_Z.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/midout_footprint_Z.png -------------------------------------------------------------------------------- /assets/generated/phenom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/phenom.png -------------------------------------------------------------------------------- /assets/generated/prev_work_comparison_0.0001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/prev_work_comparison_0.0001.png -------------------------------------------------------------------------------- /assets/generated/prev_work_comparison_0.0002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/prev_work_comparison_0.0002.png -------------------------------------------------------------------------------- /assets/generated/prev_work_comparison_0.0003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/prev_work_comparison_0.0003.png -------------------------------------------------------------------------------- /assets/generated/prev_work_comparison_0.0005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/prev_work_comparison_0.0005.png -------------------------------------------------------------------------------- /assets/generated/prev_work_comparison_0.001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/prev_work_comparison_0.001.png -------------------------------------------------------------------------------- /assets/generated/prev_work_comparison_0.002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/prev_work_comparison_0.002.png -------------------------------------------------------------------------------- /assets/generated/superdense_error_X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/superdense_error_X.png -------------------------------------------------------------------------------- /assets/generated/superdense_error_XZ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/superdense_error_XZ.png -------------------------------------------------------------------------------- /assets/generated/superdense_error_Z.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/superdense_error_Z.png -------------------------------------------------------------------------------- /assets/generated/superdense_footprint_X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/superdense_footprint_X.png -------------------------------------------------------------------------------- /assets/generated/superdense_footprint_XZ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/superdense_footprint_XZ.png -------------------------------------------------------------------------------- /assets/generated/superdense_footprint_Z.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/superdense_footprint_Z.png -------------------------------------------------------------------------------- /assets/generated/timing_d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/timing_d.png -------------------------------------------------------------------------------- /assets/generated/timing_d_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/timing_d_full.png -------------------------------------------------------------------------------- /assets/generated/timing_r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/timing_r.png -------------------------------------------------------------------------------- /assets/generated/timing_r_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/timing_r_full.png -------------------------------------------------------------------------------- /assets/generated/timing_rq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/timing_rq.png -------------------------------------------------------------------------------- /assets/generated/timing_rq_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/timing_rq_full.png -------------------------------------------------------------------------------- /assets/generated/toric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/generated/toric.png -------------------------------------------------------------------------------- /assets/midout-detslice-cycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/midout-detslice-cycle.png -------------------------------------------------------------------------------- /assets/multiplexing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/multiplexing.png -------------------------------------------------------------------------------- /assets/perf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/perf.png -------------------------------------------------------------------------------- /assets/pyramid_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/pyramid_code.png -------------------------------------------------------------------------------- /assets/solved_euclidean_problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/solved_euclidean_problem.png -------------------------------------------------------------------------------- /assets/superdense-detslice-cycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/assets/superdense-detslice-cycle.png -------------------------------------------------------------------------------- /file_lists/perf_files: -------------------------------------------------------------------------------- 1 | src/chromobius/datatypes/atomic_error.perf.cc 2 | src/chromobius/decode/decoder.perf.cc 3 | src/chromobius/graph/euler_tours.perf.cc 4 | src/chromobius/util.perf.h 5 | src/main.perf.cc 6 | -------------------------------------------------------------------------------- /file_lists/pybind_files: -------------------------------------------------------------------------------- 1 | src/chromobius/pybind/chromobius.pybind.cc 2 | src/chromobius/pybind/sinter_compat.pybind.cc 3 | src/chromobius/pybind/sinter_compat.pybind.h 4 | -------------------------------------------------------------------------------- /file_lists/source_files_no_main: -------------------------------------------------------------------------------- 1 | src/chromobius.h 2 | src/chromobius/commands/main_all.cc 3 | src/chromobius/commands/main_all.h 4 | src/chromobius/commands/main_benchmark.cc 5 | src/chromobius/commands/main_benchmark.h 6 | src/chromobius/commands/main_describe_decoder.cc 7 | src/chromobius/commands/main_describe_decoder.h 8 | src/chromobius/commands/main_predict.cc 9 | src/chromobius/commands/main_predict.h 10 | src/chromobius/datatypes/atomic_error.cc 11 | src/chromobius/datatypes/atomic_error.h 12 | src/chromobius/datatypes/color_basis.cc 13 | src/chromobius/datatypes/color_basis.h 14 | src/chromobius/datatypes/conf.h 15 | src/chromobius/datatypes/rgb_edge.cc 16 | src/chromobius/datatypes/rgb_edge.h 17 | src/chromobius/datatypes/stim_integration.cc 18 | src/chromobius/datatypes/stim_integration.h 19 | src/chromobius/datatypes/xor_vec.h 20 | src/chromobius/decode/decoder.cc 21 | src/chromobius/decode/decoder.h 22 | src/chromobius/decode/matcher_interface.h 23 | src/chromobius/decode/pymatcher.cc 24 | src/chromobius/decode/pymatcher.h 25 | src/chromobius/graph/charge_graph.cc 26 | src/chromobius/graph/charge_graph.h 27 | src/chromobius/graph/choose_rgb_reps.cc 28 | src/chromobius/graph/choose_rgb_reps.h 29 | src/chromobius/graph/collect_atomic_errors.cc 30 | src/chromobius/graph/collect_atomic_errors.h 31 | src/chromobius/graph/collect_composite_errors.cc 32 | src/chromobius/graph/collect_composite_errors.h 33 | src/chromobius/graph/collect_nodes.cc 34 | src/chromobius/graph/collect_nodes.h 35 | src/chromobius/graph/drag_graph.cc 36 | src/chromobius/graph/drag_graph.h 37 | src/chromobius/graph/euler_tours.cc 38 | src/chromobius/graph/euler_tours.h 39 | -------------------------------------------------------------------------------- /file_lists/test_files: -------------------------------------------------------------------------------- 1 | src/chromobius/commands/main_all.test.cc 2 | src/chromobius/commands/main_all.test.h 3 | src/chromobius/commands/main_benchmark.test.cc 4 | src/chromobius/commands/main_describe_decoder.test.cc 5 | src/chromobius/commands/main_predict.test.cc 6 | src/chromobius/datatypes/atomic_error.test.cc 7 | src/chromobius/datatypes/color_basis.test.cc 8 | src/chromobius/datatypes/rgb_edge.test.cc 9 | src/chromobius/datatypes/stim_integration.test.cc 10 | src/chromobius/datatypes/xor_vec.test.cc 11 | src/chromobius/decode/decoder.test.cc 12 | src/chromobius/decode/decoder_integration.test.cc 13 | src/chromobius/graph/charge_graph.test.cc 14 | src/chromobius/graph/choose_rgb_reps.test.cc 15 | src/chromobius/graph/drag_graph.test.cc 16 | src/chromobius/graph/euler_tours.test.cc 17 | src/chromobius/test_util.test.cc 18 | src/chromobius/test_util.test.h 19 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # These are development requirements, not install requirements. 2 | matplotlib ~= 3.5 3 | numpy 4 | pymatching ~= 2.0 5 | pytest 6 | scipy 7 | sinter ~= 1.14 8 | stim ~= 1.14 9 | pygltflib 10 | -------------------------------------------------------------------------------- /src/chromobius.h: -------------------------------------------------------------------------------- 1 | #ifndef _CHROMOBIUS_H 2 | #define _CHROMOBIUS_H 3 | /// WARNING: THE chromobius C++ API MAKES NO COMPATIBILITY GUARANTEES. 4 | /// It may change arbitrarily and catastrophically from minor version to minor version. 5 | /// If you need a stable API, use chromobius's Python API. 6 | #include "chromobius/commands/main_all.h" 7 | #include "chromobius/commands/main_benchmark.h" 8 | #include "chromobius/commands/main_describe_decoder.h" 9 | #include "chromobius/commands/main_predict.h" 10 | #include "chromobius/datatypes/atomic_error.h" 11 | #include "chromobius/datatypes/color_basis.h" 12 | #include "chromobius/datatypes/conf.h" 13 | #include "chromobius/datatypes/rgb_edge.h" 14 | #include "chromobius/datatypes/stim_integration.h" 15 | #include "chromobius/datatypes/xor_vec.h" 16 | #include "chromobius/decode/decoder.h" 17 | #include "chromobius/decode/matcher_interface.h" 18 | #include "chromobius/decode/pymatcher.h" 19 | #include "chromobius/graph/charge_graph.h" 20 | #include "chromobius/graph/choose_rgb_reps.h" 21 | #include "chromobius/graph/collect_atomic_errors.h" 22 | #include "chromobius/graph/collect_composite_errors.h" 23 | #include "chromobius/graph/collect_nodes.h" 24 | #include "chromobius/graph/drag_graph.h" 25 | #include "chromobius/graph/euler_tours.h" 26 | #endif 27 | -------------------------------------------------------------------------------- /src/chromobius/commands/main_all.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_COMMANDS_MAIN_ALL_H 18 | #define _CHROMOBIUS_COMMANDS_MAIN_ALL_H 19 | 20 | namespace chromobius { 21 | 22 | int main(int argc, const char **argv); 23 | 24 | } 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/chromobius/commands/main_all.test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/commands/main_all.test.h" 16 | 17 | #include "gtest/gtest.h" 18 | 19 | #include "chromobius/test_util.test.h" 20 | 21 | using namespace chromobius; 22 | 23 | std::string chromobius::result_of_running_main(const std::vector args, const std::string &input) { 24 | std::vector argv; 25 | argv.push_back("TEST_PROCESS"); 26 | for (const auto &a : args) { 27 | argv.push_back(a.c_str()); 28 | } 29 | RaiiTempNamedFile inp; 30 | RaiiTempNamedFile out; 31 | argv.push_back("--in"); 32 | argv.push_back(inp.path.c_str()); 33 | argv.push_back("--out"); 34 | argv.push_back(out.path.c_str()); 35 | FILE *f = fopen(inp.path.c_str(), "w"); 36 | if (f == nullptr || fwrite(input.data(), 1, input.size(), f) != input.size()) { 37 | throw std::invalid_argument("Failed to write input."); 38 | } 39 | fclose(f); 40 | if (chromobius::main((int)argv.size(), argv.data()) != EXIT_SUCCESS) { 41 | throw std::invalid_argument("Returned not EXIT_SUCCESS"); 42 | } 43 | f = fopen(out.path.c_str(), "rb"); 44 | if (f == nullptr) { 45 | throw std::invalid_argument("Failed to read output."); 46 | } 47 | std::string s; 48 | while (true) { 49 | int i = getc(f); 50 | if (i == EOF) { 51 | break; 52 | } 53 | s.push_back((char)i); 54 | } 55 | return s; 56 | } 57 | 58 | TEST(main_all, unknown_command) { 59 | ASSERT_THROW({ result_of_running_main({"UNKNOWN_COMMAND"}, ""); }, std::invalid_argument); 60 | } 61 | -------------------------------------------------------------------------------- /src/chromobius/commands/main_all.test.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_COMMANDS_MAIN_ALL_TEST_H 18 | #define _CHROMOBIUS_COMMANDS_MAIN_ALL_TEST_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "chromobius/commands/main_all.h" 25 | 26 | namespace chromobius { 27 | 28 | std::string result_of_running_main(const std::vector args, const std::string &input); 29 | 30 | } // namespace chromobius 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/chromobius/commands/main_benchmark.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_COMMANDS_MAIN_COUNT_MISTAKES_H 18 | #define _CHROMOBIUS_COMMANDS_MAIN_COUNT_MISTAKES_H 19 | 20 | namespace chromobius { 21 | 22 | int main_benchmark(int argc, const char **argv); 23 | 24 | } 25 | 26 | #endif -------------------------------------------------------------------------------- /src/chromobius/commands/main_benchmark.test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gtest/gtest.h" 16 | 17 | #include "chromobius/commands/main_all.test.h" 18 | #include "chromobius/test_util.test.h" 19 | 20 | using namespace chromobius; 21 | 22 | TEST(main_benchmark, basic) { 23 | RaiiTempNamedFile dem; 24 | FILE *f = fopen(dem.path.c_str(), "w"); 25 | fprintf(f, "%s", R"DEM( 26 | error(0.1) D0 L0 27 | error(0.1) D0 D1 L1 28 | error(0.1) D1 L2 29 | detector(0, 0, 0, 0) D0 30 | detector(0, 0, 0, 1) D1 31 | )DEM"); 32 | fclose(f); 33 | auto stdout_text = result_of_running_main( 34 | { 35 | "benchmark", 36 | "--dem", 37 | dem.path, 38 | "--in_format", 39 | "dets", 40 | "--in_includes_appended_observables", 41 | }, 42 | R"stdin(shot L0 43 | shot D0 L0 44 | shot D1 L2 45 | shot D0 D1 L1)stdin"); 46 | 47 | std::string expected_prefix = R"OUT( 48 | num_shots = 4 49 | num_mistakes = 1 50 | mistakes_per_shot = 0.25 51 | 52 | num_detection_events = 4 53 | num_detectors_per_shot = 2 54 | detection_fraction = 0.5 55 | 56 | setup_seconds =)OUT"; 57 | ASSERT_EQ(stdout_text.substr(0, expected_prefix.size() - 1), expected_prefix.substr(1)); 58 | } 59 | -------------------------------------------------------------------------------- /src/chromobius/commands/main_describe_decoder.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/commands/main_describe_decoder.h" 16 | 17 | #include "chromobius/decode/decoder.h" 18 | #include "stim.h" 19 | 20 | using namespace chromobius; 21 | 22 | int chromobius::main_describe_decoder(int argc, const char **argv) { 23 | stim::check_for_unknown_arguments( 24 | { 25 | "--in", 26 | "--out", 27 | "--circuit", 28 | }, 29 | {}, 30 | "describe_decoder", 31 | argc, 32 | argv); 33 | 34 | auto out_file = stim::find_output_stream_argument("--out", true, argc, argv); 35 | auto &out = out_file.stream(); 36 | 37 | stim::DetectorErrorModel dem; 38 | 39 | if (stim::find_argument("--circuit", argc, argv) != nullptr) { 40 | FILE *circuit_in = stim::find_open_file_argument("--circuit", nullptr, "rb", argc, argv); 41 | auto circuit = stim::Circuit::from_file(circuit_in); 42 | fclose(circuit_in); 43 | dem = stim::ErrorAnalyzer::circuit_to_detector_error_model(circuit, false, true, false, true, false, false); 44 | } else { 45 | FILE *dem_in = stim::find_open_file_argument("--in", stdin, "rb", argc, argv); 46 | dem = stim::DetectorErrorModel::from_file(dem_in); 47 | fclose(dem_in); 48 | } 49 | 50 | auto decoder = Decoder::from_dem(dem, DecoderConfigOptions{.include_coords_in_mobius_dem=true}); 51 | out << decoder; 52 | out << "\n"; 53 | return EXIT_SUCCESS; 54 | } 55 | -------------------------------------------------------------------------------- /src/chromobius/commands/main_describe_decoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_COMMANDS_MAIN_DESCRIBE_DECODER_H 18 | #define _CHROMOBIUS_COMMANDS_MAIN_DESCRIBE_DECODER_H 19 | 20 | namespace chromobius { 21 | 22 | int main_describe_decoder(int argc, const char **argv); 23 | 24 | } 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/chromobius/commands/main_describe_decoder.test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gtest/gtest.h" 16 | 17 | #include "chromobius/commands/main_all.test.h" 18 | 19 | using namespace chromobius; 20 | 21 | TEST(main_describe_decoder, basic_usage) { 22 | auto result = result_of_running_main( 23 | {"describe_decoder"}, 24 | R"stdin( 25 | error(0.1) D0 L0 26 | error(0.1) D0 D1 L1 27 | error(0.1) D1 L2 28 | detector(0, 0, 0, 0) D0 29 | detector(0, 0, 0, 1) D1 30 | )stdin"); 31 | ASSERT_EQ("\n" + result, R"stdout( 32 | chromobius::Decoder{ 33 | 34 | .charge_graph=ChargeGraph{.nodes={ 35 | ChargeGraphNode{.neighbors={{0,0}, {1,2}, {BOUNDARY_NODE,1}}}, // node 0 36 | ChargeGraphNode{.neighbors={{0,2}, {1,0}, {BOUNDARY_NODE,4}}}, // node 1 37 | }} 38 | 39 | .rgb_reps={ 40 | RgbEdge{.red_node=0, .green_node=1, .blue_node=BOUNDARY_NODE, .obs_flip=2, .charge_flip=B} // rep 0 41 | RgbEdge{.red_node=0, .green_node=1, .blue_node=BOUNDARY_NODE, .obs_flip=2, .charge_flip=B} // rep 1 42 | } 43 | 44 | .drag_graph=DragGraph{.mmm={ 45 | NEUTRAL@0:NEUTRAL@0 = 0 46 | NEUTRAL@0:R@0 = 1 47 | R@0:NEUTRAL@0 = 1 48 | NEUTRAL@0:NEUTRAL@1 = 0 49 | R@0:R@1 = 0 50 | R@0:G@1 = 2 51 | G@0:G@1 = 0 52 | NEUTRAL@1:NEUTRAL@0 = 0 53 | R@1:R@0 = 0 54 | G@1:R@0 = 2 55 | G@1:G@0 = 0 56 | NEUTRAL@1:NEUTRAL@1 = 0 57 | NEUTRAL@1:G@1 = 4 58 | G@1:NEUTRAL@1 = 4 59 | }} 60 | 61 | .mobius_dem=stim::DetectorErrorModel{ 62 | detector(0, 0, 0, 0, 2) D0 63 | detector(0, 0, 0, 0, 3) D1 64 | detector(0, 0, 0, 1, 1) D2 65 | detector(0, 0, 0, 1, 3) D3 66 | error(0.01000000000000000194) D0 D1 67 | error(0.1000000000000000056) D0 D2 ^ D1 D3 68 | error(0.01000000000000000194) D2 D3 69 | } 70 | 71 | } 72 | )stdout"); 73 | } 74 | -------------------------------------------------------------------------------- /src/chromobius/commands/main_predict.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/commands/main_predict.h" 16 | 17 | #include "chromobius/decode/decoder.h" 18 | #include "stim.h" 19 | 20 | using namespace chromobius; 21 | 22 | int chromobius::main_predict(int argc, const char **argv) { 23 | stim::check_for_unknown_arguments( 24 | { 25 | "--in", 26 | "--in_format", 27 | "--in_includes_appended_observables", 28 | "--out", 29 | "--out_format", 30 | "--dem", 31 | }, 32 | {}, 33 | "predict", 34 | argc, 35 | argv); 36 | 37 | FILE *shots_in = stim::find_open_file_argument("--in", stdin, "rb", argc, argv); 38 | FILE *predictions_out = stim::find_open_file_argument("--out", stdout, "wb", argc, argv); 39 | FILE *dem_file = stim::find_open_file_argument("--dem", nullptr, "rb", argc, argv); 40 | stim::FileFormatData shots_in_format = 41 | stim::find_enum_argument("--in_format", "b8", stim::format_name_to_enum_map(), argc, argv); 42 | stim::FileFormatData predictions_out_format = 43 | stim::find_enum_argument("--out_format", "01", stim::format_name_to_enum_map(), argc, argv); 44 | bool append_obs = stim::find_bool_argument("--in_includes_appended_observables", argc, argv); 45 | 46 | stim::DetectorErrorModel dem = stim::DetectorErrorModel::from_file(dem_file); 47 | fclose(dem_file); 48 | auto decoder = Decoder::from_dem(dem, DecoderConfigOptions{}); 49 | 50 | size_t num_dets = dem.count_detectors(); 51 | size_t num_obs = dem.count_observables(); 52 | auto reader = stim::MeasureRecordReader::make( 53 | shots_in, shots_in_format.id, 0, dem.count_detectors(), append_obs * num_obs); 54 | auto writer = stim::MeasureRecordWriter::make(predictions_out, predictions_out_format.id); 55 | writer->begin_result_type('L'); 56 | 57 | stim::simd_bits buf_dets(reader->bits_per_record()); 58 | while (reader->start_and_read_entire_record(buf_dets)) { 59 | if (append_obs) { 60 | for (size_t k = 0; k < num_obs; k++) { 61 | buf_dets[num_dets + k] = 0; 62 | } 63 | } 64 | auto prediction = 65 | decoder.decode_detection_events({buf_dets.u8, buf_dets.u8 + buf_dets.num_u8_padded()}); 66 | for (size_t k = 0; k < num_obs; k++) { 67 | writer->write_bit((prediction >> k) & 1); 68 | } 69 | writer->write_end(); 70 | buf_dets.clear(); 71 | } 72 | 73 | if (predictions_out != stdout) { 74 | fclose(predictions_out); 75 | } 76 | if (shots_in != stdin) { 77 | fclose(shots_in); 78 | } 79 | 80 | return EXIT_SUCCESS; 81 | } 82 | -------------------------------------------------------------------------------- /src/chromobius/commands/main_predict.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_COMMANDS_MAIN_PREDICT_H 18 | #define _CHROMOBIUS_COMMANDS_MAIN_PREDICT_H 19 | 20 | namespace chromobius { 21 | 22 | int main_predict(int argc, const char **argv); 23 | 24 | } 25 | 26 | #endif -------------------------------------------------------------------------------- /src/chromobius/commands/main_predict.test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "gtest/gtest.h" 16 | 17 | #include "chromobius/commands/main_all.test.h" 18 | #include "chromobius/test_util.test.h" 19 | 20 | using namespace chromobius; 21 | 22 | TEST(main_predict, basic) { 23 | RaiiTempNamedFile dem; 24 | FILE *f = fopen(dem.path.c_str(), "w"); 25 | fprintf(f, "%s", R"DEM( 26 | error(0.1) D0 L0 27 | error(0.1) D0 D1 L1 28 | error(0.1) D1 L2 29 | detector(0, 0, 0, 0) D0 30 | detector(0, 0, 0, 1) D1 31 | )DEM"); 32 | fclose(f); 33 | auto stdout_content = result_of_running_main( 34 | {"predict", "--dem", dem.path, "--in_format", "dets", "--out_format", "dets"}, 35 | R"stdin(shot 36 | shot D0 37 | shot D1 38 | shot D0 D1)stdin"); 39 | ASSERT_EQ(stdout_content, R"stdout(shot 40 | shot L0 41 | shot L2 42 | shot L1 43 | )stdout"); 44 | } 45 | -------------------------------------------------------------------------------- /src/chromobius/datatypes/atomic_error.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | 17 | #include "chromobius/datatypes/atomic_error.h" 18 | 19 | using namespace chromobius; 20 | 21 | void AtomicErrorKey::check_invariants(std::span det_types) { 22 | Charge net_charge = Charge::NEUTRAL; 23 | for (auto d : dets) { 24 | if (d < det_types.size()) { 25 | net_charge ^= det_types[d].color; 26 | } else if (d != BOUNDARY_NODE) { 27 | std::stringstream ss; 28 | ss << *this << " has a too-large detector index. det_types.size() = " << det_types.size(); 29 | throw std::invalid_argument(ss.str()); 30 | } 31 | } 32 | 33 | if (dets[0] == BOUNDARY_NODE) { 34 | throw std::invalid_argument("Vacuous: " + str()); 35 | } 36 | if (dets[0] > dets[1] || dets[1] > dets[2]) { 37 | throw std::invalid_argument("Not sorted: " + str()); 38 | } 39 | if (net_charge != Charge::NEUTRAL && dets[2] != BOUNDARY_NODE) { 40 | std::stringstream ss; 41 | ss << "Triplet " << *this << " has non-neutral charge " << net_charge; 42 | throw std::invalid_argument(ss.str()); 43 | } 44 | } 45 | 46 | std::ostream &chromobius::operator<<(std::ostream &out, const AtomicErrorKey &val) { 47 | out << "AtomicErrorKey{.dets={"; 48 | for (size_t k = 0; k < 3; k++) { 49 | if (k > 0) { 50 | out << ", "; 51 | } 52 | if (val.dets[k] == BOUNDARY_NODE) { 53 | out << "BOUNDARY_NODE"; 54 | } else { 55 | out << val.dets[k]; 56 | } 57 | } 58 | out << "}}"; 59 | return out; 60 | } 61 | std::string AtomicErrorKey::str() const { 62 | std::stringstream ss; 63 | ss << *this; 64 | return ss.str(); 65 | } 66 | -------------------------------------------------------------------------------- /src/chromobius/datatypes/atomic_error.perf.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/datatypes/atomic_error.h" 16 | 17 | #include "chromobius/util.perf.h" 18 | #include "stim.h" 19 | 20 | using namespace chromobius; 21 | 22 | BENCHMARK(sort3) { 23 | std::vector data; 24 | std::mt19937_64 rng{0}; 25 | for (size_t k = 0; k < 999; k++) { 26 | data.push_back((node_offset_int)rng()); 27 | } 28 | 29 | size_t v0 = 0; 30 | size_t v1 = 0; 31 | size_t v2 = 0; 32 | benchmark_go([&]() { 33 | for (size_t k = 0; k < data.size(); k += 3) { 34 | auto abc = sort3(data[k], data[k + 1], data[k + 2]); 35 | v0 += abc[0]; 36 | v1 ^= abc[1]; 37 | v2 += abc[2]; 38 | } 39 | }) 40 | .goal_nanos(150) 41 | .show_rate("Constructions", data.size() / 3); 42 | if (v0 + v1 + v2 == 1) { 43 | std::cerr << "data dependence"; 44 | } 45 | } 46 | 47 | BENCHMARK(sort3_known_max) { 48 | std::vector data; 49 | std::mt19937_64 rng{0}; 50 | for (size_t k = 0; k < 999; k++) { 51 | data.push_back((node_offset_int)rng()); 52 | } 53 | 54 | size_t v0 = 0; 55 | size_t v1 = 0; 56 | size_t v2 = 0; 57 | benchmark_go([&]() { 58 | for (size_t k = 0; k < data.size(); k += 3) { 59 | auto e2 = sort3(data[k], UINT32_MAX, data[k + 1]); 60 | v0 += e2[0]; 61 | v1 ^= e2[1]; 62 | v2 += e2[2]; 63 | } 64 | }) 65 | .goal_nanos(100) 66 | .show_rate("Constructions", data.size() / 3); 67 | if (v0 + v1 + v2 == 1) { 68 | std::cerr << "data dependence"; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/chromobius/datatypes/color_basis.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_COLOR_BASIS_H 18 | #define _CHROMOBIUS_COLOR_BASIS_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "chromobius/datatypes/conf.h" 25 | 26 | namespace chromobius { 27 | 28 | enum Charge : uint8_t { 29 | NEUTRAL = 0, 30 | R = 1, 31 | G = 2, 32 | B = 3, 33 | }; 34 | inline Charge next_non_neutral_charge(Charge c) { 35 | return (Charge)(c % 3 + 1); 36 | } 37 | inline Charge operator^(Charge c1, Charge c2) { 38 | return (Charge)((uint8_t)c1 ^ (uint8_t)c2); 39 | } 40 | inline Charge &operator^=(Charge &c1, Charge c2) { 41 | c1 = (Charge)((uint8_t)c1 ^ (uint8_t)c2); 42 | return c1; 43 | } 44 | 45 | enum SubGraphCoord : uint8_t { 46 | UNKNOWN_SUBGRAPH_COORD = 0, 47 | NotRed = 1, 48 | NotGreen = 2, 49 | NotBlue = 3, 50 | }; 51 | 52 | constexpr uint8_t SUBGRAPH_OFFSET_Red_NotGreen = 0; 53 | constexpr uint8_t SUBGRAPH_OFFSET_Red_NotBlue = 1; 54 | constexpr uint8_t SUBGRAPH_OFFSET_Green_NotRed = 0; 55 | constexpr uint8_t SUBGRAPH_OFFSET_Green_NotBlue = 1; 56 | constexpr uint8_t SUBGRAPH_OFFSET_Blue_NotRed = 0; 57 | constexpr uint8_t SUBGRAPH_OFFSET_Blue_NotGreen = 1; 58 | 59 | enum Basis : uint8_t { 60 | UNKNOWN_BASIS = 0, 61 | X = 1, 62 | Z = 2, 63 | }; 64 | 65 | struct ColorBasis { 66 | Charge color; 67 | Basis basis; 68 | bool ignored = false; 69 | bool operator==(const ColorBasis &other) const; 70 | bool operator!=(const ColorBasis &other) const; 71 | std::string str() const; 72 | }; 73 | std::ostream &operator<<(std::ostream &out, const ColorBasis &val); 74 | std::ostream &operator<<(std::ostream &out, const Charge &val); 75 | std::ostream &operator<<(std::ostream &out, const SubGraphCoord &val); 76 | std::ostream &operator<<(std::ostream &out, const Basis &val); 77 | 78 | std::tuple mobius_node_to_detector( 79 | uint64_t mobius_node, std::span colors); 80 | uint64_t detector_to_mobius_node(node_offset_int node, SubGraphCoord subgraph, std::span colors); 81 | 82 | } // namespace chromobius 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /src/chromobius/datatypes/color_basis.test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/datatypes/color_basis.h" 16 | 17 | #include "gtest/gtest.h" 18 | 19 | using namespace chromobius; 20 | 21 | TEST(types, color_basis_basic) { 22 | ColorBasis e{.color = Charge::R, .basis = Basis::X}; 23 | 24 | ASSERT_TRUE(e == (ColorBasis{.color = Charge::R, .basis = Basis::X})); 25 | ASSERT_FALSE(e == (ColorBasis{.color = Charge::G, .basis = Basis::X})); 26 | ASSERT_FALSE(e == (ColorBasis{.color = Charge::R, .basis = Basis::Z})); 27 | 28 | ASSERT_TRUE(e != (ColorBasis{.color = Charge::G, .basis = Basis::X})); 29 | 30 | ASSERT_EQ(e.str(), "ColorBasis{.color=R, .basis=X}"); 31 | } 32 | 33 | TEST(atomic_error, mobius_node_to_detector_vs_detector_to_mobius_node) { 34 | std::vector colors; 35 | colors.resize(50); 36 | 37 | colors[29].color = Charge::R; 38 | ASSERT_EQ( 39 | mobius_node_to_detector(29 * 2 + SUBGRAPH_OFFSET_Red_NotGreen, colors), 40 | (std::tuple(29, Charge::R, SubGraphCoord::NotGreen))); 41 | ASSERT_EQ(detector_to_mobius_node(29, SubGraphCoord::NotGreen, colors), 29 * 2 + SUBGRAPH_OFFSET_Red_NotGreen); 42 | 43 | colors[31].color = Charge::R; 44 | ASSERT_EQ( 45 | mobius_node_to_detector(31 * 2 + SUBGRAPH_OFFSET_Red_NotBlue, colors), 46 | (std::tuple(31, Charge::R, SubGraphCoord::NotBlue))); 47 | ASSERT_EQ(detector_to_mobius_node(31, SubGraphCoord::NotBlue, colors), 31 * 2 + SUBGRAPH_OFFSET_Red_NotBlue); 48 | 49 | colors[36].color = Charge::G; 50 | ASSERT_EQ( 51 | mobius_node_to_detector(36 * 2 + SUBGRAPH_OFFSET_Green_NotRed, colors), 52 | (std::tuple(36, Charge::G, SubGraphCoord::NotRed))); 53 | ASSERT_EQ(detector_to_mobius_node(36, SubGraphCoord::NotRed, colors), 36 * 2 + SUBGRAPH_OFFSET_Green_NotRed); 54 | 55 | colors[41].color = Charge::G; 56 | ASSERT_EQ( 57 | mobius_node_to_detector(41 * 2 + SUBGRAPH_OFFSET_Green_NotBlue, colors), 58 | (std::tuple(41, Charge::G, SubGraphCoord::NotBlue))); 59 | ASSERT_EQ(detector_to_mobius_node(41, SubGraphCoord::NotBlue, colors), 41 * 2 + SUBGRAPH_OFFSET_Green_NotBlue); 60 | 61 | colors[43].color = Charge::B; 62 | ASSERT_EQ( 63 | mobius_node_to_detector(43 * 2 + SUBGRAPH_OFFSET_Blue_NotRed, colors), 64 | (std::tuple(43, Charge::B, SubGraphCoord::NotRed))); 65 | ASSERT_EQ(detector_to_mobius_node(43, SubGraphCoord::NotRed, colors), 43 * 2 + SUBGRAPH_OFFSET_Blue_NotRed); 66 | 67 | colors[47].color = Charge::B; 68 | ASSERT_EQ( 69 | mobius_node_to_detector(47 * 2 + SUBGRAPH_OFFSET_Blue_NotGreen, colors), 70 | (std::tuple(47, Charge::B, SubGraphCoord::NotGreen))); 71 | ASSERT_EQ(detector_to_mobius_node(47, SubGraphCoord::NotGreen, colors), 47 * 2 + SUBGRAPH_OFFSET_Blue_NotGreen); 72 | } 73 | -------------------------------------------------------------------------------- /src/chromobius/datatypes/conf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_TYPES_H 18 | #define _CHROMOBIUS_TYPES_H 19 | 20 | #include 21 | #include 22 | 23 | namespace chromobius { 24 | 25 | typedef uint64_t obsmask_int; 26 | typedef uint32_t node_offset_int; 27 | constexpr node_offset_int BOUNDARY_NODE = (node_offset_int)-1; 28 | 29 | } // namespace chromobius 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/chromobius/datatypes/rgb_edge.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/datatypes/rgb_edge.h" 16 | 17 | #include 18 | #include 19 | 20 | using namespace chromobius; 21 | 22 | bool RgbEdge::operator==(const RgbEdge &other) const { 23 | return red_node == other.red_node && blue_node == other.blue_node && green_node == other.green_node && 24 | obs_flip == other.obs_flip && charge_flip == other.charge_flip; 25 | } 26 | bool RgbEdge::operator!=(const RgbEdge &other) const { 27 | return !(*this == other); 28 | } 29 | std::string RgbEdge::str() const { 30 | std::stringstream ss; 31 | ss << *this; 32 | return ss.str(); 33 | } 34 | std::ostream &chromobius::operator<<(std::ostream &out, const RgbEdge &val) { 35 | out << "RgbEdge{.red_node="; 36 | if (val.red_node == BOUNDARY_NODE) { 37 | out << "BOUNDARY_NODE"; 38 | } else { 39 | out << val.red_node; 40 | } 41 | out << ", .green_node="; 42 | if (val.green_node == BOUNDARY_NODE) { 43 | out << "BOUNDARY_NODE"; 44 | } else { 45 | out << val.green_node; 46 | } 47 | out << ", .blue_node="; 48 | if (val.blue_node == BOUNDARY_NODE) { 49 | out << "BOUNDARY_NODE"; 50 | } else { 51 | out << val.blue_node; 52 | } 53 | out << ", .obs_flip=" << val.obs_flip; 54 | out << ", .charge_flip=" << val.charge_flip; 55 | out << "}"; 56 | return out; 57 | } 58 | size_t RgbEdge::weight() const { 59 | return (red_node != BOUNDARY_NODE) + (green_node != BOUNDARY_NODE) + (blue_node != BOUNDARY_NODE); 60 | } 61 | 62 | bool RgbEdge::operator<(const RgbEdge &other) const { 63 | if (red_node != other.red_node) { 64 | return red_node < other.red_node; 65 | } 66 | if (green_node != other.green_node) { 67 | return green_node < other.green_node; 68 | } 69 | if (blue_node != other.blue_node) { 70 | return blue_node < other.blue_node; 71 | } 72 | if (obs_flip != other.obs_flip) { 73 | return obs_flip < other.obs_flip; 74 | } 75 | return charge_flip < other.charge_flip; 76 | } 77 | -------------------------------------------------------------------------------- /src/chromobius/datatypes/rgb_edge.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_DEM_GRAPH_H 18 | #define _CHROMOBIUS_DEM_GRAPH_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "chromobius/datatypes/atomic_error.h" 25 | #include "chromobius/datatypes/color_basis.h" 26 | 27 | namespace chromobius { 28 | 29 | struct RgbEdge; 30 | 31 | /// Represents an error with at most one symptom of each color. 32 | struct RgbEdge { 33 | node_offset_int red_node; 34 | node_offset_int green_node; 35 | node_offset_int blue_node; 36 | obsmask_int obs_flip; 37 | Charge charge_flip; 38 | 39 | inline node_offset_int color_node(Charge c) const { 40 | if (c == 0) { 41 | return BOUNDARY_NODE; 42 | } 43 | return (&red_node)[c - 1]; 44 | } 45 | inline node_offset_int &color_node(Charge c) { 46 | assert(c != NEUTRAL); 47 | return (&red_node)[c - 1]; 48 | } 49 | 50 | size_t weight() const; 51 | bool operator<(const RgbEdge &other) const; 52 | bool operator==(const RgbEdge &other) const; 53 | bool operator!=(const RgbEdge &other) const; 54 | std::string str() const; 55 | }; 56 | std::ostream &operator<<(std::ostream &out, const RgbEdge &val); 57 | 58 | } // namespace chromobius 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /src/chromobius/datatypes/rgb_edge.test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/datatypes/rgb_edge.h" 16 | 17 | #include "gtest/gtest.h" 18 | 19 | using namespace chromobius; 20 | 21 | TEST(dem_rgb_edge, dem_rgb_edge_basics) { 22 | RgbEdge e{.red_node = 5, .green_node = 7, .blue_node = 9, .obs_flip = 1, .charge_flip = Charge::NEUTRAL}; 23 | 24 | ASSERT_TRUE(e == (RgbEdge{5, 7, 9, 1, Charge::NEUTRAL})); 25 | ASSERT_FALSE(e == (RgbEdge{4, 7, 9, 1, Charge::NEUTRAL})); 26 | ASSERT_FALSE(e == (RgbEdge{5, 6, 9, 1, Charge::NEUTRAL})); 27 | ASSERT_FALSE(e == (RgbEdge{5, 7, 8, 1, Charge::NEUTRAL})); 28 | ASSERT_FALSE(e == (RgbEdge{5, 7, 9, 2, Charge::NEUTRAL})); 29 | ASSERT_FALSE(e == (RgbEdge{5, 7, 9, 1, Charge::R})); 30 | 31 | ASSERT_TRUE(e != (RgbEdge{5, 7, 9, 2, Charge::NEUTRAL})); 32 | ASSERT_FALSE(e != (RgbEdge{5, 7, 9, 1, Charge::NEUTRAL})); 33 | 34 | ASSERT_EQ( 35 | e.str(), 36 | "RgbEdge{.red_node=5, .green_node=7, .blue_node=9, .obs_flip=1, " 37 | ".charge_flip=NEUTRAL}"); 38 | } 39 | 40 | TEST(dem_rgb_edge, dem_rgb_edge_weight) { 41 | ASSERT_EQ((RgbEdge{.red_node = 2, .green_node = 3, .blue_node = 7, .obs_flip = 5}.weight()), 3); 42 | ASSERT_EQ((RgbEdge{.red_node = 2, .green_node = 3, .blue_node = 7, .obs_flip = 0}.weight()), 3); 43 | ASSERT_EQ( 44 | (RgbEdge{.red_node = BOUNDARY_NODE, .green_node = BOUNDARY_NODE, .blue_node = 7, .obs_flip = 5}.weight()), 1); 45 | ASSERT_EQ( 46 | (RgbEdge{.red_node = BOUNDARY_NODE, .green_node = BOUNDARY_NODE, .blue_node = BOUNDARY_NODE, .obs_flip = 5} 47 | .weight()), 48 | 0); 49 | ASSERT_EQ((RgbEdge{.red_node = BOUNDARY_NODE, .green_node = 3, .blue_node = 7, .obs_flip = 5}.weight()), 2); 50 | ASSERT_EQ((RgbEdge{.red_node = 2, .green_node = BOUNDARY_NODE, .blue_node = 7, .obs_flip = 5}.weight()), 2); 51 | ASSERT_EQ((RgbEdge{.red_node = 2, .green_node = 5, .blue_node = BOUNDARY_NODE, .obs_flip = 5}.weight()), 2); 52 | } 53 | -------------------------------------------------------------------------------- /src/chromobius/datatypes/stim_integration.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/datatypes/stim_integration.h" 16 | 17 | #include 18 | 19 | using namespace chromobius; 20 | 21 | ColorBasis chromobius::detector_instruction_to_color_basis( 22 | const stim::DemInstruction &instruction, std::span coord_offsets) { 23 | assert(instruction.type == stim::DemInstructionType::DEM_DETECTOR); 24 | bool failed = false; 25 | double c = -2; 26 | if (instruction.arg_data.size() > 3) { 27 | c = instruction.arg_data[3]; 28 | if (coord_offsets.size() > 3) { 29 | c += coord_offsets[3]; 30 | } 31 | } else { 32 | failed = true; 33 | } 34 | 35 | int r = 0; 36 | if (c < -1 || c > 5) { 37 | failed = true; 38 | } else { 39 | r = (int)c; 40 | if (r != c) { 41 | failed = true; 42 | } 43 | } 44 | if (failed) { 45 | throw std::invalid_argument( 46 | "Expected all detectors to have at least 4 coordinates, with the 4th " 47 | "identifying the basis and color " 48 | "(RedX=0, GreenX=1, BlueX=2, RedZ=3, GreenZ=4, BlueZ=5), but got " + 49 | instruction.str()); 50 | } 51 | if (r == -1) { 52 | return ColorBasis{ 53 | .color=Charge::NEUTRAL, 54 | .basis=Basis::UNKNOWN_BASIS, 55 | .ignored=true, 56 | }; 57 | } 58 | constexpr std::array mapping{ 59 | ColorBasis{Charge::R, Basis::X}, 60 | ColorBasis{Charge::G, Basis::X}, 61 | ColorBasis{Charge::B, Basis::X}, 62 | ColorBasis{Charge::R, Basis::Z}, 63 | ColorBasis{Charge::G, Basis::Z}, 64 | ColorBasis{Charge::B, Basis::Z}, 65 | }; 66 | return mapping[r]; 67 | } 68 | -------------------------------------------------------------------------------- /src/chromobius/datatypes/stim_integration.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_STIM_INTEGRATION_H 18 | #define _CHROMOBIUS_STIM_INTEGRATION_H 19 | 20 | #include 21 | #include 22 | 23 | #include "chromobius/datatypes/conf.h" 24 | #include "chromobius/datatypes/color_basis.h" 25 | #include "stim.h" 26 | 27 | namespace chromobius { 28 | 29 | ColorBasis detector_instruction_to_color_basis( 30 | const stim::DemInstruction &instruction, std::span coord_offsets); 31 | 32 | } // namespace chromobius 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/chromobius/datatypes/stim_integration.test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/datatypes/stim_integration.h" 16 | 17 | #include "gtest/gtest.h" 18 | 19 | using namespace chromobius; 20 | 21 | TEST(atomic_error, detector_instruction_to_color_basis) { 22 | std::vector args{-1, -1, -1, 2}; 23 | std::vector offsets{-3, -3, -3, 3, -2}; 24 | stim::DemInstruction instruction{ 25 | .arg_data = args, 26 | .target_data = {}, 27 | .type = stim::DemInstructionType::DEM_DETECTOR, 28 | }; 29 | ASSERT_EQ(detector_instruction_to_color_basis(instruction, offsets), (ColorBasis{Charge::B, Basis::Z})); 30 | offsets[3] = 100; 31 | ASSERT_THROW({ detector_instruction_to_color_basis(instruction, offsets); }, std::invalid_argument); 32 | offsets[3] = 0.5; 33 | ASSERT_THROW({ detector_instruction_to_color_basis(instruction, offsets); }, std::invalid_argument); 34 | args[3] = 0.5; 35 | ASSERT_EQ(detector_instruction_to_color_basis(instruction, offsets), (ColorBasis{Charge::G, Basis::X})); 36 | args[3] = -1.5; 37 | ASSERT_EQ(detector_instruction_to_color_basis(instruction, offsets), (ColorBasis{Charge::NEUTRAL, Basis::UNKNOWN_BASIS, true})); 38 | } 39 | -------------------------------------------------------------------------------- /src/chromobius/datatypes/xor_vec.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_XOR_VEC_H 18 | #define _CHROMOBIUS_XOR_VEC_H 19 | 20 | #include 21 | #include 22 | 23 | namespace chromobius { 24 | 25 | template 26 | inline std::span inplace_xor_sort(std::span items) { 27 | std::sort(items.begin(), items.end()); 28 | size_t new_size = 0; 29 | for (size_t k = 0; k < items.size(); k++) { 30 | if (new_size > 0 && items[k] == items[new_size - 1]) { 31 | new_size--; 32 | } else { 33 | if (k != new_size) { 34 | std::swap(items[new_size], items[k]); 35 | } 36 | new_size++; 37 | } 38 | } 39 | return items.subspan(0, new_size); 40 | } 41 | 42 | } // namespace chromobius 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/chromobius/datatypes/xor_vec.test.cc: -------------------------------------------------------------------------------- 1 | #include "chromobius/datatypes/xor_vec.h" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | using namespace chromobius; 6 | 7 | TEST(xor_vec, inplace_xor_sort) { 8 | auto f = [](std::vector v) -> std::vector { 9 | std::span s = v; 10 | auto r = inplace_xor_sort(s); 11 | v.resize(r.size()); 12 | return v; 13 | }; 14 | ASSERT_EQ(f({}), (std::vector({}))); 15 | ASSERT_EQ(f({5}), (std::vector({5}))); 16 | ASSERT_EQ(f({5, 5}), (std::vector({}))); 17 | ASSERT_EQ(f({5, 5, 5}), (std::vector({5}))); 18 | ASSERT_EQ(f({5, 5, 5, 5}), (std::vector({}))); 19 | ASSERT_EQ(f({5, 4, 5, 5}), (std::vector({4, 5}))); 20 | ASSERT_EQ(f({4, 5, 5, 5}), (std::vector({4, 5}))); 21 | ASSERT_EQ(f({5, 5, 5, 4}), (std::vector({4, 5}))); 22 | ASSERT_EQ(f({4, 5, 5, 4}), (std::vector({}))); 23 | ASSERT_EQ(f({3, 5, 5, 4}), (std::vector({3, 4}))); 24 | } 25 | -------------------------------------------------------------------------------- /src/chromobius/decode/matcher_interface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_DECODE_MATCHER_INTERFACE_H 18 | #define _CHROMOBIUS_DECODE_MATCHER_INTERFACE_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "stim.h" 25 | 26 | namespace chromobius { 27 | 28 | /// This class is used to implement polymorphism 29 | struct MatcherInterface { 30 | virtual ~MatcherInterface(){}; 31 | 32 | /// Creates a new instance of the matcher, configured for the given detector error model. 33 | virtual std::unique_ptr configured_for_mobius_dem(const stim::DetectorErrorModel &dem) = 0; 34 | 35 | /// Performs matching on the given mobius dem detection events, producing edges. 36 | /// 37 | /// Args: 38 | /// mobius_detection_event_indices: The detection events to decode. 39 | /// out_edge_buffer: Where to write edges to. Edges should be rewritten in an 40 | /// interleaved fashion, so that (out_edge_buffer[2*k], out_edge_buffer[2*k+1]) 41 | /// is an edge. There should be no boundary edges in the result, since the mobius 42 | /// dem is guaranteed to not contain any boundary edges. 43 | virtual void match_edges( 44 | const std::vector &mobius_detection_event_indices, std::vector *out_edge_buffer, float *out_weight = nullptr) = 0; 45 | }; 46 | 47 | } // namespace chromobius 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/chromobius/decode/pymatcher.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/decode/pymatcher.h" 16 | 17 | using namespace chromobius; 18 | 19 | PymatchingMatcher::PymatchingMatcher() : pymatching_matcher(), weight_scaling_constant(1) { 20 | } 21 | 22 | PymatchingMatcher::PymatchingMatcher(const stim::DetectorErrorModel &dem) 23 | : pymatching_matcher(pm::detector_error_model_to_mwpm(dem, 1 << 24, true)), weight_scaling_constant(pymatching_matcher.flooder.graph.normalising_constant) { 24 | 25 | } 26 | 27 | void PymatchingMatcher::match_edges( 28 | const std::vector &mobius_detection_event_indices, 29 | std::vector *out_edge_buffer, 30 | float *out_weight) { 31 | pm::decode_detection_events_to_edges(pymatching_matcher, mobius_detection_event_indices, *out_edge_buffer); 32 | if (out_weight != nullptr) { 33 | pm::total_weight_int w = 0; 34 | auto &e = *out_edge_buffer; 35 | for (size_t k = 0; k < e.size(); k += 2) { 36 | auto &d1 = pymatching_matcher.search_flooder.graph.nodes[e[k]]; 37 | auto &d2 = pymatching_matcher.search_flooder.graph.nodes[e[k + 1]]; 38 | w += d2.neighbor_weights[d2.index_of_neighbor(&d1)]; 39 | } 40 | *out_weight = (float)(w / weight_scaling_constant); 41 | } 42 | } 43 | 44 | std::unique_ptr PymatchingMatcher::configured_for_mobius_dem(const stim::DetectorErrorModel &dem) { 45 | std::unique_ptr result; 46 | result.reset(new PymatchingMatcher(dem)); 47 | return result; 48 | } 49 | -------------------------------------------------------------------------------- /src/chromobius/decode/pymatcher.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_DECODE_PYMATCHER_H 18 | #define _CHROMOBIUS_DECODE_PYMATCHER_H 19 | 20 | #include "chromobius/decode/matcher_interface.h" 21 | #include "pymatching/sparse_blossom/driver/mwpm_decoding.h" 22 | #include "stim.h" 23 | 24 | namespace chromobius { 25 | 26 | struct PymatchingMatcher : MatcherInterface { 27 | pm::Mwpm pymatching_matcher; 28 | double weight_scaling_constant; 29 | 30 | PymatchingMatcher(); 31 | PymatchingMatcher(const stim::DetectorErrorModel &dem); 32 | virtual ~PymatchingMatcher() = default; 33 | 34 | virtual std::unique_ptr configured_for_mobius_dem(const stim::DetectorErrorModel &dem) override; 35 | 36 | virtual void match_edges( 37 | const std::vector &mobius_detection_event_indices, std::vector *out_edge_buffer, float *out_weight = nullptr) override; 38 | }; 39 | 40 | } // namespace chromobius 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/chromobius/graph/charge_graph.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_CHARGE_GRAPH_H 18 | #define _CHROMOBIUS_CHARGE_GRAPH_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "chromobius/datatypes/rgb_edge.h" 26 | 27 | namespace chromobius { 28 | 29 | struct ChargeGraphNode; 30 | 31 | /// Like the error graph, but hyperedges have been combined into normal edges. 32 | /// 33 | /// Every edge in the charge graph is graphlike (degree 2 or degree 1). The 34 | /// charge graph includes edges that were in the original detector error model, 35 | /// as well as synthetic edges that can be formed by combining pairs of RGB 36 | /// errors from the original detector error model. 37 | /// 38 | /// Stored as an adjacency list graph. 39 | struct ChargeGraph { 40 | std::vector nodes; 41 | 42 | static ChargeGraph from_atomic_errors(const std::map &atomic_errors, size_t num_nodes); 43 | 44 | void add_edge(node_offset_int n1, node_offset_int n2, obsmask_int obs_flip); 45 | bool operator==(const ChargeGraph &other) const; 46 | bool operator!=(const ChargeGraph &other) const; 47 | std::string str() const; 48 | }; 49 | std::ostream &operator<<(std::ostream &out, const ChargeGraph &val); 50 | 51 | struct ChargeGraphNode { 52 | std::unordered_map neighbors; 53 | 54 | bool operator==(const ChargeGraphNode &other) const; 55 | bool operator!=(const ChargeGraphNode &other) const; 56 | std::string str() const; 57 | }; 58 | std::ostream &operator<<(std::ostream &out, const ChargeGraphNode &val); 59 | 60 | } // namespace chromobius 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/chromobius/graph/choose_rgb_reps.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_CHOOSE_RGB_REPS_H 18 | #define _CHROMOBIUS_CHOOSE_RGB_REPS_H 19 | 20 | #include 21 | 22 | #include "chromobius/datatypes/atomic_error.h" 23 | #include "chromobius/datatypes/rgb_edge.h" 24 | 25 | namespace chromobius { 26 | 27 | std::vector choose_rgb_reps_from_atomic_errors( 28 | const std::map &atomic_errors, std::span node_colors); 29 | 30 | } // namespace chromobius 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/chromobius/graph/choose_rgb_reps.test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/graph/choose_rgb_reps.h" 16 | 17 | #include "gtest/gtest.h" 18 | 19 | using namespace chromobius; 20 | 21 | TEST(choose_rgb_reps, choose_rgb_reps_from_atomic_errors) { 22 | std::vector node_colors{ 23 | ColorBasis{.color = R, .basis = X}, 24 | ColorBasis{.color = G, .basis = X}, 25 | ColorBasis{.color = B, .basis = X}, 26 | ColorBasis{.color = R, .basis = X}, 27 | }; 28 | std::map atomic_errors{ 29 | {AtomicErrorKey{0, 1, 2}, 1}, 30 | {AtomicErrorKey{2, 3, BOUNDARY_NODE}, 02}, 31 | }; 32 | 33 | auto reps = choose_rgb_reps_from_atomic_errors(atomic_errors, node_colors); 34 | ASSERT_EQ( 35 | reps, 36 | (std::vector{ 37 | RgbEdge{.red_node = 0, .green_node = 1, .blue_node = 2, .obs_flip = 1, .charge_flip = Charge::NEUTRAL}, 38 | RgbEdge{.red_node = 0, .green_node = 1, .blue_node = 2, .obs_flip = 1, .charge_flip = Charge::NEUTRAL}, 39 | RgbEdge{.red_node = 0, .green_node = 1, .blue_node = 2, .obs_flip = 1, .charge_flip = Charge::NEUTRAL}, 40 | RgbEdge{ 41 | .red_node = 3, .green_node = BOUNDARY_NODE, .blue_node = 2, .obs_flip = 2, .charge_flip = Charge::G}, 42 | })); 43 | } 44 | -------------------------------------------------------------------------------- /src/chromobius/graph/collect_atomic_errors.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_COLLECT_ATOMIC_EDGES_H 18 | #define _CHROMOBIUS_COLLECT_ATOMIC_EDGES_H 19 | 20 | #include 21 | #include 22 | 23 | #include "chromobius/datatypes/atomic_error.h" 24 | #include "chromobius/datatypes/color_basis.h" 25 | #include "stim.h" 26 | 27 | namespace chromobius { 28 | 29 | /// Finds errors of known a list of color/basis data for all detectors in the dem. 30 | /// 31 | /// The color/basis data is read from the 4th coordinate of each detector's 32 | /// coordinate data using the convention 0=XR 1=XG 2=XB 3=ZR 4=ZG 5=ZB. 33 | /// 34 | /// Args: 35 | /// dem: The detector error model to read detector data from. 36 | /// out_mobius_dem: Optional. If not set to null, transformed coordinate 37 | /// data for the mobius dem's detectors is appended to this dem. 38 | /// 39 | /// Returns: 40 | /// A vector containing the color and basis data, indexed by detector id. 41 | std::map collect_atomic_errors( 42 | const stim::DetectorErrorModel &dem, std::span node_colors); 43 | 44 | /// Converts a stim::DemInstruction into a list of detection events and an obs mask. 45 | void extract_obs_and_dets_from_error_instruction( 46 | stim::DemInstruction instruction, 47 | stim::SparseXorVec *out_xor_detectors_buffer, 48 | obsmask_int *out_obs_flip, 49 | std::span node_colors); 50 | 51 | AtomicErrorKey extract_atomic_errors_from_dem_error_instruction_dets( 52 | std::span dets, 53 | obsmask_int obs_flip, 54 | std::span node_colors, 55 | std::map *out_atomic_errors); 56 | 57 | } // namespace chromobius 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /src/chromobius/graph/collect_composite_errors.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_COLLECT_COMPOSITE_ERRORS_H 18 | #define _CHROMOBIUS_COLLECT_COMPOSITE_ERRORS_H 19 | 20 | #include 21 | 22 | #include "chromobius/datatypes/atomic_error.h" 23 | #include "chromobius/datatypes/color_basis.h" 24 | #include "chromobius/graph/collect_atomic_errors.h" 25 | 26 | namespace chromobius { 27 | 28 | /// Builds the mobius dem by decomposing errors from a dem into known atomic errors. 29 | /// 30 | /// Args: 31 | /// dem: The detector error model to read original error instructions from. 32 | /// node_colors: Previously collected node color and basis data. 33 | /// atomic_errors: Previously collected basic errors to decompose into. 34 | /// ignore_decomposition_failures: If set to True, then failing to decompose 35 | /// an error into atomic errors causes the error to be discarded instead of 36 | /// throwing an exception. 37 | /// out_mobius_dem: Where to write the decomposed mobius error mechanisms. 38 | /// out_remnants: Some errors can't be perfectly decomposed into existing atomic 39 | /// errors, but can be decomposed into an atomic error and a leftover part that 40 | /// would be a valid atomic error. This is where the remnants that are used 41 | /// get written. 42 | void collect_composite_errors_and_remnants_into_mobius_dem( 43 | const stim::DetectorErrorModel &dem, 44 | std::span node_colors, 45 | const std::map &atomic_errors, 46 | bool drop_mobius_errors_involving_remnant_errors, 47 | bool ignore_decomposition_failures, 48 | stim::DetectorErrorModel *out_mobius_dem, 49 | std::map *out_remnants); 50 | 51 | } // namespace chromobius 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /src/chromobius/graph/collect_nodes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_COLLECT_NODES_H 18 | #define _CHROMOBIUS_COLLECT_NODES_H 19 | 20 | #include 21 | 22 | #include "chromobius/datatypes/atomic_error.h" 23 | #include "chromobius/datatypes/color_basis.h" 24 | #include "stim.h" 25 | 26 | namespace chromobius { 27 | 28 | /// Creates a list of color/basis data for all detectors in the dem. 29 | /// 30 | /// The color/basis data is read from the 4th coordinate of each detector's 31 | /// coordinate data using the convention 0=XR 1=XG 2=XB 3=ZR 4=ZG 5=ZB. 32 | /// 33 | /// Args: 34 | /// dem: The detector error model to read detector data from. 35 | /// out_mobius_dem: Optional. If not set to null, transformed coordinate 36 | /// data for the mobius dem's detectors is appended to this dem. 37 | /// 38 | /// Returns: 39 | /// A vector containing the color and basis data, indexed by detector id. 40 | std::vector collect_nodes_from_dem( 41 | const stim::DetectorErrorModel &dem, stim::DetectorErrorModel *out_mobius_dem); 42 | 43 | } // namespace chromobius 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/chromobius/graph/drag_graph.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_DRAG_GRAPH_H 18 | #define _CHROMOBIUS_DRAG_GRAPH_H 19 | 20 | #include "chromobius/datatypes/color_basis.h" 21 | #include "chromobius/graph/charge_graph.h" 22 | 23 | namespace chromobius { 24 | 25 | struct DragNode; 26 | struct DragChange; 27 | 28 | struct SortedPair { 29 | node_offset_int a; 30 | node_offset_int b; 31 | inline SortedPair() : a(0), b(0) { 32 | } 33 | inline SortedPair(node_offset_int init_a, node_offset_int init_b) : a(init_a), b(init_b) { 34 | inplace_sort2(a, b); 35 | } 36 | inline bool operator<(const SortedPair &other) const { 37 | return a != other.a ? a < other.a : b < other.b; 38 | } 39 | inline bool operator==(const SortedPair &other) const { 40 | return a == other.a && b == other.b; 41 | } 42 | }; 43 | 44 | struct ChargedEdge { 45 | node_offset_int n1; 46 | node_offset_int n2; 47 | Charge c1; 48 | Charge c2; 49 | inline bool operator<(const ChargedEdge &other) const { 50 | if (n1 != other.n1) { 51 | return n1 < other.n1; 52 | } 53 | if (n2 != other.n2) { 54 | return n2 < other.n2; 55 | } 56 | if (c1 != other.c1) { 57 | return c1 < other.c1; 58 | } 59 | return c2 < other.c2; 60 | } 61 | inline bool operator==(const ChargedEdge &other) const { 62 | return n1 == other.n1 && n2 == other.n2 && c1 == other.c1 && c2 == other.c2; 63 | } 64 | }; 65 | 66 | /// The drag graph stores information on how to drag charge from node to node. 67 | /// 68 | /// When dragging charge around, the charge is always kept near the current 69 | /// target node T. For charge of the same color as T, the charge is exactly a 70 | /// detection event at T. Charges for colors different from T, are kept on R 71 | /// where R is a node near T that matches the charge's color (R is called 72 | /// the `representative` of that charge color for T). In some cases, when there 73 | /// is no node of a color near T, charge of that color must be split into the 74 | /// two other color charges in order to be stored near T. In that case the 75 | /// representative for T of that color is actually two nodes (with one of them 76 | /// being T itself). 77 | struct DragGraph { 78 | std::map mmm; 79 | 80 | static DragGraph from_charge_graph_paths_for_sub_edges_of_atomic_errors( 81 | const ChargeGraph &charge_graph, 82 | const std::map &atomic_errors, 83 | std::span rgb_reps, 84 | std::span node_colors); 85 | 86 | bool operator==(const DragGraph &other) const; 87 | bool operator!=(const DragGraph &other) const; 88 | std::string str() const; 89 | }; 90 | std::ostream &operator<<(std::ostream &out, const DragGraph &val); 91 | 92 | } // namespace chromobius 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/chromobius/graph/drag_graph.test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/graph/drag_graph.h" 16 | 17 | #include "gtest/gtest.h" 18 | 19 | using namespace chromobius; 20 | -------------------------------------------------------------------------------- /src/chromobius/graph/euler_tours.perf.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/graph/euler_tours.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "chromobius/util.perf.h" 21 | 22 | using namespace chromobius; 23 | 24 | BENCHMARK(solve_euler_tours_n10000) { 25 | std::mt19937_64 rng{0}; 26 | std::vector edges; 27 | for (size_t k = 0; k < 2000; k++) { 28 | node_offset_int a; 29 | node_offset_int b; 30 | node_offset_int c; 31 | do { 32 | a = rng() % 9999 + 1; 33 | b = rng() % 9999 + 1; 34 | c = rng() % 9999 + 1; 35 | } while (a == b || b == c || a == c); 36 | edges.push_back(a); 37 | edges.push_back(b); 38 | edges.push_back(b); 39 | edges.push_back(c); 40 | edges.push_back(a); 41 | edges.push_back(c); 42 | } 43 | std::shuffle(edges.begin(), edges.end(), rng); 44 | EulerTourGraph g(10000); 45 | 46 | size_t n = 0; 47 | benchmark_go([&]() { 48 | g.iter_euler_tours_of_interleaved_edge_list(edges, {}, [&](std::span cycle) { 49 | n += cycle.size(); 50 | }); 51 | }) 52 | .goal_micros(230) 53 | .show_rate("edges", edges.size()); 54 | if (n == 1) { 55 | std::cerr << "data dependence"; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/chromobius/graph/euler_tours.test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/graph/euler_tours.h" 16 | 17 | #include "gtest/gtest.h" 18 | 19 | using namespace chromobius; 20 | 21 | TEST(euler_tours, euler_tours_of_edge_list) { 22 | EulerTourGraph g(10); 23 | 24 | auto do_case = [&](std::vector> edge_list) { 25 | std::vector> result; 26 | std::vector node_colors; 27 | node_colors.resize(g.nodes.size(), ColorBasis{Charge::R, Basis::X}); 28 | 29 | std::vector interleaved_edges; 30 | for (auto [a, b] : edge_list) { 31 | interleaved_edges.push_back(a); 32 | interleaved_edges.push_back(b); 33 | } 34 | 35 | g.iter_euler_tours_of_interleaved_edge_list( 36 | interleaved_edges, {}, [&](std::span cycle) { 37 | result.push_back({}); 38 | for (size_t k = 0; k < cycle.size(); k++) { 39 | result.back().push_back(cycle[k]); 40 | } 41 | }); 42 | 43 | return result; 44 | }; 45 | 46 | ASSERT_EQ((do_case({})), (std::vector>{})); 47 | 48 | ASSERT_THROW( 49 | { 50 | do_case({ 51 | {1, 2}, 52 | }); 53 | }, 54 | std::invalid_argument); 55 | 56 | ASSERT_EQ( 57 | (do_case({ 58 | {1, 2}, 59 | {2, 1}, 60 | })), 61 | (std::vector>{ 62 | {1, 2}, 63 | })); 64 | 65 | ASSERT_EQ( 66 | (do_case({ 67 | {1, 2}, 68 | {3, 1}, 69 | {2, 3}, 70 | })), 71 | (std::vector>{ 72 | {1, 2, 3}, 73 | })); 74 | 75 | ASSERT_EQ( 76 | (do_case({ 77 | {1, 2}, 78 | {4, 5}, 79 | {2, 1}, 80 | {5, 6}, 81 | {6, 4}, 82 | })), 83 | (std::vector>{ 84 | {1, 2}, 85 | {4, 5, 6}, 86 | })); 87 | 88 | ASSERT_EQ( 89 | (do_case({ 90 | {1, 2}, 91 | {2, 1}, 92 | {2, 3}, 93 | {3, 2}, 94 | {3, 4}, 95 | {4, 3}, 96 | {2, 5}, 97 | {5, 2}, 98 | })), 99 | (std::vector>{ 100 | {3, 2, 5, 2, 1, 2, 3, 4}, 101 | })); 102 | } 103 | -------------------------------------------------------------------------------- /src/chromobius/pybind/chromobius_pybind_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import numpy as np 16 | import pytest 17 | import stim 18 | 19 | import chromobius 20 | 21 | 22 | def test_version(): 23 | assert '.' in chromobius.__version__ 24 | assert chromobius.__version__ is not None 25 | 26 | 27 | def test_errors(): 28 | with pytest.raises(ValueError, match='must be a stim.DetectorErrorModel'): 29 | chromobius.compile_decoder_for_dem(object()) 30 | with pytest.raises(ValueError, match='4 coordinates'): 31 | chromobius.compile_decoder_for_dem(stim.DetectorErrorModel(""" 32 | error(0.1) D0 33 | """)) 34 | 35 | 36 | def test_decoding(): 37 | color_rep_code = stim.Circuit(""" 38 | X_ERROR(0.1) 0 1 2 3 4 5 6 7 8 39 | M 0 1 2 3 4 5 6 7 8 40 | DETECTOR(0, 0, 0, 0) rec[-9] rec[-8] rec[-7] 41 | DETECTOR(1, 0, 0, 1) rec[-8] rec[-7] rec[-6] 42 | DETECTOR(2, 0, 0, 2) rec[-7] rec[-6] rec[-5] 43 | DETECTOR(3, 0, 0, 0) rec[-6] rec[-5] rec[-4] 44 | DETECTOR(4, 0, 0, 1) rec[-5] rec[-4] rec[-3] 45 | DETECTOR(5, 0, 0, 2) rec[-4] rec[-3] rec[-2] 46 | DETECTOR(6, 0, 0, 0) rec[-3] rec[-2] rec[-1] 47 | DETECTOR(7, 0, 0, 1) rec[-2] rec[-1] 48 | OBSERVABLE_INCLUDE(0) rec[-1] 49 | OBSERVABLE_INCLUDE(1) rec[-2] 50 | OBSERVABLE_INCLUDE(2) rec[-3] 51 | OBSERVABLE_INCLUDE(3) rec[-4] 52 | OBSERVABLE_INCLUDE(4) rec[-5] 53 | OBSERVABLE_INCLUDE(5) rec[-6] 54 | OBSERVABLE_INCLUDE(6) rec[-7] 55 | OBSERVABLE_INCLUDE(7) rec[-8] 56 | OBSERVABLE_INCLUDE(8) rec[-9] 57 | """) 58 | 59 | err = color_rep_code.search_for_undetectable_logical_errors( 60 | dont_explore_detection_event_sets_with_size_above=7, 61 | dont_explore_edges_increasing_symptom_degree=False, 62 | dont_explore_edges_with_degree_above=10, 63 | canonicalize_circuit_errors=True, 64 | ) 65 | assert len(err) == 6 66 | 67 | decoder = chromobius.compile_decoder_for_dem(color_rep_code.detector_error_model()) 68 | decoder2 = chromobius.CompiledDecoder.from_dem(color_rep_code.detector_error_model()) 69 | shots = 1024 70 | dets, obs = color_rep_code.compile_detector_sampler().sample( 71 | shots=shots, separate_observables=True, bit_packed=True 72 | ) 73 | predictions = decoder.predict_obs_flips_from_dets_bit_packed(dets) 74 | predictions2 = decoder2.predict_obs_flips_from_dets_bit_packed(dets) 75 | assert np.array_equal(predictions, predictions2) 76 | mistakes = np.count_nonzero(np.any(predictions != obs, axis=1)) 77 | assert mistakes < 100 78 | 79 | 80 | def test_empty(): 81 | assert chromobius.compile_decoder_for_dem(stim.DetectorErrorModel()) is not None 82 | -------------------------------------------------------------------------------- /src/chromobius/pybind/sinter_compat.pybind.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_PYBIND_SINTER_COMPAT_H 18 | #define _CHROMOBIUS_PYBIND_SINTER_COMPAT_H 19 | 20 | #include 21 | 22 | namespace chromobius { 23 | 24 | void pybind_sinter_compat(pybind11::module &m); 25 | 26 | } 27 | 28 | #endif 29 | 30 | -------------------------------------------------------------------------------- /src/chromobius/pybind/sinter_compat_pybind_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import chromobius 16 | 17 | 18 | def test_sinter_obj_exists(): 19 | decoder = chromobius.sinter_decoders()['chromobius'] 20 | assert hasattr(decoder, 'compile_decoder_for_dem') 21 | assert hasattr(decoder, 'decode_via_files') 22 | -------------------------------------------------------------------------------- /src/chromobius/test_util.test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/test_util.test.h" 16 | 17 | #include "gtest/gtest.h" 18 | 19 | using namespace chromobius; 20 | 21 | FILE *chromobius::open_test_data_file(const char *name) { 22 | std::vector directories_to_check = { 23 | "test_data/", 24 | "../test_data/", 25 | "../../test_data/", 26 | }; 27 | for (const auto &d : directories_to_check) { 28 | std::string path = d + name; 29 | FILE *f = fopen((d + name).c_str(), "r"); 30 | if (f != nullptr) { 31 | return f; 32 | } 33 | } 34 | throw std::invalid_argument("Failed to find test data file " + std::string(name)); 35 | } 36 | 37 | static void init_path(RaiiTempNamedFile &self) { 38 | char tmp_stdin_filename[] = "/tmp/stim_test_named_file_XXXXXX"; 39 | self.descriptor = mkstemp(tmp_stdin_filename); 40 | if (self.descriptor == -1) { 41 | throw std::runtime_error("Failed to create temporary file."); 42 | } 43 | self.path = std::string(tmp_stdin_filename); 44 | } 45 | 46 | RaiiTempNamedFile::RaiiTempNamedFile() { 47 | init_path(*this); 48 | } 49 | 50 | RaiiTempNamedFile::RaiiTempNamedFile(const std::string &contents) { 51 | init_path(*this); 52 | write_contents(contents); 53 | } 54 | 55 | RaiiTempNamedFile::~RaiiTempNamedFile() { 56 | if (!path.empty()) { 57 | remove(path.data()); 58 | path = ""; 59 | } 60 | } 61 | 62 | std::string RaiiTempNamedFile::read_contents() { 63 | FILE *f = fopen(path.c_str(), "rb"); 64 | if (f == nullptr) { 65 | throw std::runtime_error("Failed to open temp named file " + path); 66 | } 67 | std::string result; 68 | while (true) { 69 | int c = getc(f); 70 | if (c == EOF) { 71 | break; 72 | } 73 | result.push_back(c); 74 | } 75 | fclose(f); 76 | return result; 77 | } 78 | 79 | void RaiiTempNamedFile::write_contents(const std::string &contents) { 80 | FILE *f = fopen(path.c_str(), "wb"); 81 | if (f == nullptr) { 82 | throw std::runtime_error("Failed to open temp named file " + path); 83 | } 84 | for (char c : contents) { 85 | putc(c, f); 86 | } 87 | fclose(f); 88 | } 89 | -------------------------------------------------------------------------------- /src/chromobius/test_util.test.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CHROMOBIUS_TEST_UTIL_H 18 | #define _CHROMOBIUS_TEST_UTIL_H 19 | 20 | #include 21 | #include 22 | 23 | namespace chromobius { 24 | 25 | FILE *open_test_data_file(const char *name); 26 | 27 | struct RaiiTempNamedFile { 28 | int descriptor; 29 | std::string path; 30 | RaiiTempNamedFile(); 31 | ~RaiiTempNamedFile(); 32 | RaiiTempNamedFile(const std::string &contents); 33 | std::string read_contents(); 34 | void write_contents(const std::string &contents); 35 | }; 36 | 37 | } // namespace chromobius 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/clorco/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /src/clorco/_circuits_vs_chromobius_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import numpy as np 16 | import pytest 17 | import sinter 18 | 19 | import gen 20 | from clorco._make_circuit import CONSTRUCTIONS 21 | from clorco._make_circuit import make_circuit 22 | import chromobius 23 | 24 | 25 | @pytest.mark.parametrize( 26 | "style", [style for style in CONSTRUCTIONS.keys() if "mxyz_" not in style] 27 | ) 28 | def test_constructions_are_decoded(style: str): 29 | r = 1 if style.startswith("transit") else 6 30 | d = 12 if "toric_" in style else 11 31 | circuit = make_circuit( 32 | style=style, 33 | noise_model=gen.NoiseModel.uniform_depolarizing(1e-3), 34 | noise_strength=1e-3, 35 | rounds=r, 36 | diameter=d, 37 | convert_to_cz=True, 38 | editable_extras={}, 39 | ) 40 | decoder = chromobius.compile_decoder_for_dem( 41 | circuit.detector_error_model() 42 | ) 43 | 44 | shots = 2048 45 | assert 0 < circuit.num_observables <= 8 46 | dets, obs = circuit.compile_detector_sampler().sample( 47 | shots=shots, separate_observables=True, bit_packed=True 48 | ) 49 | predictions = decoder.predict_obs_flips_from_dets_bit_packed(dets) 50 | mistakes = np.count_nonzero(np.any(predictions != obs, axis=1)) 51 | assert mistakes / shots < 0.42, (mistakes, shots) 52 | -------------------------------------------------------------------------------- /src/clorco/_make_circuit.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import pathlib 16 | from typing import Union, Callable, Any 17 | 18 | import stim 19 | 20 | import gen 21 | from clorco._make_circuit_params import Params 22 | from clorco.color2surface_code._keyed_constructions import ( 23 | make_named_color2surface_code_constructions, 24 | ) 25 | from clorco.color_code import make_named_color_code_constructions 26 | from clorco.pyramid_code._keyed_constructions import ( 27 | make_named_pyramid_code_constructions, 28 | ) 29 | from clorco.surface_code import make_named_surface_code_constructions 30 | from clorco.rep_code import make_named_rep_code_constructions 31 | 32 | 33 | def _make_constructions() -> dict[str, Callable[[Params], stim.Circuit]]: 34 | constructions: dict[str, Callable[[Params], stim.Circuit]] = { 35 | **make_named_color_code_constructions(), 36 | **make_named_surface_code_constructions(), 37 | **make_named_rep_code_constructions(), 38 | **make_named_color2surface_code_constructions(), 39 | **make_named_pyramid_code_constructions(), 40 | } 41 | return constructions 42 | 43 | 44 | CONSTRUCTIONS = _make_constructions() 45 | 46 | 47 | def make_circuit( 48 | *, 49 | style: str, 50 | noise_model: gen.NoiseModel | None, 51 | noise_strength: float, 52 | rounds: int, 53 | diameter: int = 0, 54 | debug_out_dir: Union[None, str, pathlib.Path] = None, 55 | convert_to_cz: bool = True, 56 | editable_extras: dict[str, Any], 57 | ) -> stim.Circuit: 58 | if debug_out_dir is not None: 59 | debug_out_dir = pathlib.Path(debug_out_dir) 60 | debug_out_dir.mkdir(exist_ok=True, parents=True) 61 | 62 | params = Params( 63 | style=style, 64 | rounds=rounds, 65 | diameter=diameter, 66 | debug_out_dir=debug_out_dir, 67 | convert_to_cz=convert_to_cz, 68 | noise_model=noise_model, 69 | noise_strength=noise_strength, 70 | editable_extras=editable_extras, 71 | ) 72 | construction = CONSTRUCTIONS.get(style) 73 | if construction is None: 74 | msg = f"Unrecognized circuit style: {style!r}.\n\nRecognized styles are:" 75 | for k in sorted(CONSTRUCTIONS.keys()): 76 | msg += "\n " + k 77 | raise NotImplementedError(msg) 78 | noisy_circuit = construction(params) 79 | 80 | if params.debug_out_dir is not None: 81 | gen.write_file(params.debug_out_dir / "noisy_circuit.stim", noisy_circuit) 82 | gen.write_file( 83 | params.debug_out_dir / "noisy_circuit_dets_ops.svg", 84 | noisy_circuit.without_noise().diagram("detslice-with-ops-svg"), 85 | ) 86 | gen.write_file( 87 | params.debug_out_dir / "noisy_circuit_dets.svg", 88 | noisy_circuit.without_noise().diagram("detslice-svg"), 89 | ) 90 | gen.write_file( 91 | params.debug_out_dir / "noisy_circuit_slice.svg", 92 | noisy_circuit.diagram("timeslice-svg"), 93 | ) 94 | 95 | return noisy_circuit 96 | -------------------------------------------------------------------------------- /src/clorco/_make_circuit_params.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import dataclasses 16 | import pathlib 17 | from typing import Any 18 | 19 | import gen 20 | 21 | 22 | @dataclasses.dataclass 23 | class Params: 24 | style: str 25 | rounds: int 26 | diameter: int 27 | noise_strength: float 28 | noise_model: gen.NoiseModel | None 29 | debug_out_dir: pathlib.Path | None 30 | convert_to_cz: bool 31 | editable_extras: dict[str, Any] 32 | -------------------------------------------------------------------------------- /src/clorco/_make_circuit_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import gen 16 | from clorco._make_circuit import make_circuit 17 | 18 | 19 | def test_make_circuit(): 20 | c = make_circuit( 21 | style="phenom_color_code", 22 | noise_model=gen.NoiseModel.uniform_depolarizing(1e-3), 23 | noise_strength=1e-3, 24 | diameter=5, 25 | editable_extras={}, 26 | rounds=7, 27 | ) 28 | err = c.search_for_undetectable_logical_errors( 29 | dont_explore_edges_increasing_symptom_degree=False, 30 | dont_explore_edges_with_degree_above=3, 31 | dont_explore_detection_event_sets_with_size_above=3, 32 | ) 33 | assert len(err) == 5 34 | -------------------------------------------------------------------------------- /src/clorco/color2surface_code/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumlib/chromobius/35e289570fdc1d71e73582e1fd4e0c8e29298ef5/src/clorco/color2surface_code/__init__.py -------------------------------------------------------------------------------- /src/clorco/color2surface_code/_color2surface_layouts_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import pytest 16 | 17 | import gen 18 | from clorco.color2surface_code._color2surface_layouts import make_color2surface_layout 19 | from clorco.color2surface_code._draw_mobius_graph import code_capacity_mobius_graph_svg 20 | 21 | 22 | @pytest.mark.parametrize("d", [3, 5, 7, 9]) 23 | def test_make_color2surface_layout(d: int): 24 | code = make_color2surface_layout(base_data_width=d) 25 | code.verify() 26 | for tile in code.patch.tiles: 27 | assert ('basis=Z' in tile.flags) == (tile.basis == "Z") 28 | err = code.make_code_capacity_circuit( 29 | noise=1e-3 30 | ).search_for_undetectable_logical_errors( 31 | dont_explore_edges_increasing_symptom_degree=False, 32 | dont_explore_edges_with_degree_above=3, 33 | dont_explore_detection_event_sets_with_size_above=6, 34 | ) 35 | assert len(err) == d 36 | 37 | 38 | def test_draw_mobius_graph(): 39 | code = make_color2surface_layout(base_data_width=13) 40 | assert code_capacity_mobius_graph_svg(code.patch) is not None 41 | -------------------------------------------------------------------------------- /src/clorco/color2surface_code/_keyed_constructions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from typing import Callable 16 | 17 | import stim 18 | 19 | import gen 20 | from clorco._make_circuit_params import Params 21 | from clorco.color2surface_code._color2surface_layouts import make_color2surface_layout 22 | from clorco.color2surface_code._color2surface_layouts import rgb2xyz 23 | 24 | 25 | def f2c(flow: gen.Flow) -> list[float]: 26 | c = 0 27 | if 'color=r' in flow.flags: 28 | c += 0 29 | elif 'color=g' in flow.flags: 30 | c += 1 31 | elif 'color=b' in flow.flags: 32 | c += 2 33 | else: 34 | raise NotImplementedError(f'{flow=}') 35 | if 'basis=X' in flow.flags: 36 | c += 0 37 | elif 'basis=Z' in flow.flags: 38 | c += 3 39 | else: 40 | raise NotImplementedError(f'{flow=}') 41 | return [c] 42 | 43 | 44 | def make_named_color2surface_code_constructions() -> ( 45 | dict[str, Callable[[Params], stim.Circuit]] 46 | ): 47 | constructions: dict[str, Callable[[Params], stim.Circuit]] = {} 48 | 49 | def _make_simple_circuit( 50 | params: Params, *, code: gen.StabilizerCode, phenom: bool 51 | ) -> stim.Circuit: 52 | if params.debug_out_dir is not None: 53 | code.patch.write_svg( 54 | params.debug_out_dir / "rgb_patch.svg", 55 | other=[rgb2xyz(code.patch, "X"), rgb2xyz(code.patch, "Z")], 56 | opacity=0.8, 57 | ) 58 | if phenom: 59 | return code.make_phenom_circuit( 60 | noise=params.noise_model.idle_depolarization, 61 | rounds=params.rounds, 62 | extra_coords_func=f2c, 63 | ) 64 | assert params.rounds == 1 65 | return code.make_code_capacity_circuit( 66 | noise=params.noise_model.idle_depolarization, 67 | extra_coords_func=f2c, 68 | ) 69 | 70 | constructions["transit_color2surface_code"] = lambda params: _make_simple_circuit( 71 | params=params, 72 | code=make_color2surface_layout( 73 | base_data_width=params.diameter, 74 | ), 75 | phenom=False, 76 | ) 77 | constructions["phenom_color2surface_code"] = lambda params: _make_simple_circuit( 78 | params=params, 79 | code=make_color2surface_layout( 80 | base_data_width=params.diameter, 81 | ), 82 | phenom=True, 83 | ) 84 | 85 | return constructions 86 | -------------------------------------------------------------------------------- /src/clorco/color_code/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from ._color_code_layouts import ( 16 | make_color_code_layout, 17 | make_color_code_layout_488, 18 | make_toric_color_code_layout, 19 | ) 20 | from ._keyed_constructions import ( 21 | make_named_color_code_constructions, 22 | ) 23 | -------------------------------------------------------------------------------- /src/clorco/pyramid_code/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /src/clorco/pyramid_code/_pyramid_code_layouts_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import pytest 16 | 17 | from clorco.pyramid_code._pyramid_code_layouts import make_planar_pyramid_code_layout 18 | from clorco.pyramid_code._pyramid_code_layouts import make_toric_pyramid_code_layout 19 | 20 | 21 | @pytest.mark.parametrize("h", [3, 6, 9]) 22 | @pytest.mark.parametrize("w", [4, 6, 8, 10]) 23 | def test_make_toric_pyramid_code_layout(w: int, h: int): 24 | code = make_toric_pyramid_code_layout( 25 | width=w, 26 | height=h, 27 | ) 28 | code.verify() 29 | assert ( 30 | len(code.patch.data_set) 31 | == len(code.patch.tiles) 32 | + max(len(code.observables_x), len(code.observables_z)) 33 | - 2 34 | ) 35 | c = code.make_code_capacity_circuit(noise=1e-3) 36 | err = c.search_for_undetectable_logical_errors( 37 | dont_explore_edges_with_degree_above=3, 38 | dont_explore_detection_event_sets_with_size_above=6, 39 | dont_explore_edges_increasing_symptom_degree=False, 40 | ) 41 | assert len(err) == min((w + 1) // 2, h * 2 // 3) 42 | 43 | 44 | @pytest.mark.parametrize("h", [3, 4, 5, 6, 7, 8, 9, 10]) 45 | @pytest.mark.parametrize("w", [3, 4, 5, 6, 7, 8, 9, 10]) 46 | def test_make_planar_pyramid_code_layout(w: int, h: int): 47 | code = make_planar_pyramid_code_layout( 48 | width=w, 49 | height=h, 50 | ) 51 | code.verify() 52 | c = code.make_code_capacity_circuit(noise=1e-3) 53 | assert len(code.patch.data_set) == len(code.patch.tiles) + max( 54 | len(code.observables_x), len(code.observables_z) 55 | ) 56 | err = c.search_for_undetectable_logical_errors( 57 | dont_explore_edges_with_degree_above=3, 58 | dont_explore_detection_event_sets_with_size_above=6, 59 | dont_explore_edges_increasing_symptom_degree=False, 60 | ) 61 | assert len(err) == min((w + 1) // 2, h * 2 // 3) 62 | -------------------------------------------------------------------------------- /src/clorco/rep_code/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from ._keyed_constructions import ( 16 | make_named_rep_code_constructions, 17 | ) 18 | -------------------------------------------------------------------------------- /src/clorco/rep_code/_rep_code_circuits.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from typing import Sequence 16 | 17 | import stim 18 | 19 | import gen 20 | from clorco.rep_code._rep_code_layouts import make_rep_code_layout 21 | 22 | 23 | def make_rep_code_circuit( 24 | *, 25 | distance: int, 26 | toric: bool = False, 27 | rounds: int, 28 | round_colorings: Sequence[str] = ("r",), 29 | ) -> stim.Circuit: 30 | assert rounds > 1 31 | code = make_rep_code_layout( 32 | distance=distance, 33 | toric=toric, 34 | ) 35 | color_indices = { 36 | "r": [3], 37 | "g": [4], 38 | "b": [5], 39 | "R": [3], 40 | "G": [4], 41 | "B": [5], 42 | } 43 | 44 | builder = gen.Builder.for_qubits(code.patch.used_set) 45 | 46 | for cur_round in range(rounds): 47 | if cur_round > 0: 48 | builder.tick() 49 | builder.append( 50 | "R", code.patch.used_set if cur_round == 0 else code.patch.measure_set 51 | ) 52 | builder.tick() 53 | builder.append("CX", [(m - 0.5, m) for m in code.patch.measure_set]) 54 | builder.tick() 55 | builder.append( 56 | "CX", [((m.real + 0.5) % distance, m) for m in code.patch.measure_set] 57 | ) 58 | builder.tick() 59 | builder.append( 60 | 'MZ', 61 | code.patch.used_set if cur_round == rounds - 1 else code.patch.measure_set, 62 | measure_key_func=lambda e: (e, cur_round), 63 | ) 64 | for m in gen.sorted_complex(code.patch.measure_set): 65 | m_key = (m, cur_round) 66 | other_key = [] if cur_round == 0 else [(m, cur_round - 1)] 67 | rc = round_colorings[cur_round % len(round_colorings)] 68 | c = color_indices[rc[int(m.real % len(rc) - 0.5)]] 69 | builder.detector([m_key] + other_key, pos=m, extra_coords=c) 70 | builder.shift_coords(dt=1) 71 | for m in gen.sorted_complex(code.patch.measure_set): 72 | rc = round_colorings[rounds % len(round_colorings)] 73 | c = color_indices[rc[int(m.real % len(rc) - 0.5)]] 74 | builder.detector( 75 | [ 76 | (m, rounds - 1), 77 | ((m.real + 0.5) % distance, rounds - 1), 78 | (m - 0.5, rounds - 1), 79 | ], 80 | pos=m, 81 | extra_coords=c, 82 | ) 83 | builder.obs_include([(0, rounds - 1)], obs_index=0) 84 | return builder.circuit 85 | -------------------------------------------------------------------------------- /src/clorco/rep_code/_rep_code_circuits_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import itertools 16 | 17 | import pytest 18 | 19 | import gen 20 | from clorco.rep_code._rep_code_circuits import make_rep_code_circuit 21 | 22 | 23 | @pytest.mark.parametrize( 24 | "d,toric,rounds", 25 | itertools.product( 26 | [3, 4, 5, 6], 27 | [False, True], 28 | [3, 4, 5], 29 | ), 30 | ) 31 | def test_make_rep_code_circuit(d: int, toric: bool, rounds: int): 32 | c = make_rep_code_circuit( 33 | distance=d, 34 | toric=toric, 35 | rounds=rounds, 36 | ) 37 | c = gen.NoiseModel.uniform_depolarizing(1e-3).noisy_circuit(c) 38 | assert c.detector_error_model(decompose_errors=True) is not None 39 | assert len(c.shortest_graphlike_error()) == d 40 | assert gen.count_measurement_layers(c) == rounds 41 | assert ( 42 | c.count_determined_measurements() == c.num_observables + c.num_detectors - toric 43 | ) 44 | -------------------------------------------------------------------------------- /src/clorco/rep_code/_rep_code_layouts.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import gen 16 | 17 | 18 | def make_rep_code_layout( 19 | *, 20 | distance: int, 21 | coloring: str = "r", 22 | toric: bool = False, 23 | replace_basis_with_coloring=False, 24 | ) -> gen.StabilizerCode: 25 | """Creates a rotated surface code. 26 | 27 | Data qubits are at integer coordinates. Measure qubits are at half integer coordinates. 28 | 29 | Args: 30 | distance: The number of data qubits. 31 | coloring: A coloring pattern to use. Must be a non-empty string made up 32 | of the characters 'r', 'g', and 'b'. These are the colors given to 33 | the color code decoder, which can affect its internal representation 34 | but ideally should not affect its final answer. 35 | 36 | If the coloring is shorter than the number of data qubits, it will 37 | be repeated until it is longer. 38 | 39 | If the coloring is longer than the number of data qubits, it will be 40 | truncated. 41 | toric: Defaults to False. When True, an additional stabilizer is added 42 | comparing the leftmost qubit to the rightmost qubit. 43 | replace_basis_with_coloring: For debugging purposes. This causes the 44 | stabilizer basis to be XYZ=RGB instead of always Z, so that a 45 | drawing of the code shows the coloring. 46 | 47 | Returns: 48 | A StabilizerCode object defining the stabilizers and X/Z observable pair of the code. 49 | """ 50 | assert distance > 1 51 | 52 | tiles = [] 53 | color_indices = { 54 | "r": 3, 55 | "g": 4, 56 | "b": 5, 57 | "R": 3, 58 | "G": 4, 59 | "B": 5, 60 | } 61 | for x in range(distance - (0 if toric else 1)): 62 | color = coloring[x % len(coloring)].lower() 63 | c = color_indices[color] 64 | tiles.append( 65 | gen.Tile( 66 | ordered_data_qubits=[x, (x + 1) % distance], 67 | bases="XYZ"[c % 3] if replace_basis_with_coloring else "Z", 68 | measurement_qubit=x + 0.5, 69 | flags={f'color={color}', f'basis=Z'}, 70 | ) 71 | ) 72 | patch = gen.Patch(tiles) 73 | 74 | return gen.StabilizerCode( 75 | patch=patch, 76 | observables_x=[], 77 | observables_z=[gen.PauliString({0: "Z"})], 78 | ) 79 | -------------------------------------------------------------------------------- /src/clorco/rep_code/_rep_code_layouts_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import gen 16 | from clorco.rep_code._rep_code_layouts import make_rep_code_layout 17 | 18 | 19 | def test_make_rep_code_layout(): 20 | v = make_rep_code_layout(distance=5, coloring="rgb") 21 | v.verify() 22 | assert v == gen.StabilizerCode( 23 | patch=gen.Patch( 24 | tiles=[ 25 | gen.Tile( 26 | ordered_data_qubits=(0, 1), 27 | measurement_qubit=0.5, 28 | bases="Z", 29 | flags={'color=r', 'basis=Z'}, 30 | ), 31 | gen.Tile( 32 | ordered_data_qubits=(1, 2), 33 | measurement_qubit=1.5, 34 | bases="Z", 35 | flags={'color=g', 'basis=Z'}, 36 | ), 37 | gen.Tile( 38 | ordered_data_qubits=(2, 3), 39 | measurement_qubit=2.5, 40 | bases="Z", 41 | flags={'color=b', 'basis=Z'}, 42 | ), 43 | gen.Tile( 44 | ordered_data_qubits=(3, 4), 45 | measurement_qubit=3.5, 46 | bases="Z", 47 | flags={'color=r', 'basis=Z'}, 48 | ), 49 | ] 50 | ), 51 | observables_x=[], 52 | observables_z=[ 53 | gen.PauliString({0: "Z"}), 54 | ], 55 | ) 56 | 57 | v = make_rep_code_layout(distance=3, coloring="rg", toric=True) 58 | v.verify() 59 | assert v == gen.StabilizerCode( 60 | patch=gen.Patch( 61 | tiles=[ 62 | gen.Tile( 63 | ordered_data_qubits=(0, 1), 64 | measurement_qubit=0.5, 65 | bases="Z", 66 | flags={'color=r', 'basis=Z'}, 67 | ), 68 | gen.Tile( 69 | ordered_data_qubits=(1, 2), 70 | measurement_qubit=1.5, 71 | bases="Z", 72 | flags={'color=g', 'basis=Z'}, 73 | ), 74 | gen.Tile( 75 | ordered_data_qubits=(2, 0), 76 | measurement_qubit=2.5, 77 | bases="Z", 78 | flags={'color=r', 'basis=Z'}, 79 | ), 80 | ] 81 | ), 82 | observables_x=[], 83 | observables_z=[ 84 | gen.PauliString({0: "Z"}), 85 | ], 86 | ) 87 | -------------------------------------------------------------------------------- /src/clorco/surface_code/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from ._keyed_constructions import ( 16 | make_named_surface_code_constructions, 17 | ) 18 | -------------------------------------------------------------------------------- /src/clorco/surface_code/_xz_surface_code_memory_circuits.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import gen 16 | from clorco.surface_code._surface_code_chunks import standard_surface_code_chunk 17 | from clorco.surface_code._surface_code_patches import make_xtop_qubit_patch 18 | 19 | 20 | def make_xz_memory_experiment_chunks( 21 | *, 22 | diameter: int, 23 | basis: str, 24 | rounds: int, 25 | ) -> list[gen.Chunk]: 26 | qubit_patch = make_xtop_qubit_patch(diameter=diameter) 27 | xs = {q for q in qubit_patch.data_set if q.real == 0} 28 | zs = {q for q in qubit_patch.data_set if q.imag == 0} 29 | assert len(xs & zs) % 2 == 1 30 | obs = gen.PauliString({q: basis for q in (xs if basis == "X" else zs)}) 31 | assert rounds > 0 32 | if rounds == 1: 33 | return [ 34 | standard_surface_code_chunk( 35 | qubit_patch, init_data_basis=basis, measure_data_basis=basis, obs=obs 36 | ) 37 | ] 38 | 39 | return [ 40 | standard_surface_code_chunk( 41 | qubit_patch, 42 | init_data_basis=basis, 43 | obs=obs, 44 | ), 45 | standard_surface_code_chunk( 46 | qubit_patch, 47 | obs=obs, 48 | ).with_repetitions(rounds - 2), 49 | standard_surface_code_chunk( 50 | qubit_patch, 51 | measure_data_basis=basis, 52 | obs=obs, 53 | ), 54 | ] 55 | -------------------------------------------------------------------------------- /src/gen/__init__.py: -------------------------------------------------------------------------------- 1 | from gen._circuit_util import ( 2 | gates_used_by_circuit, 3 | gate_counts_for_circuit, 4 | count_measurement_layers, 5 | ) 6 | from gen._core import ( 7 | AtLayer, 8 | Builder, 9 | complex_key, 10 | MeasurementTracker, 11 | min_max_complex, 12 | NoiseModel, 13 | NoiseRule, 14 | occurs_in_classical_control_system, 15 | Patch, 16 | PauliString, 17 | sorted_complex, 18 | StabilizerCode, 19 | Tile, 20 | KeyedPauliString, 21 | ) 22 | from gen._flows import ( 23 | Chunk, 24 | ChunkLoop, 25 | ChunkReflow, 26 | Flow, 27 | compile_chunks_into_circuit, 28 | magic_measure_for_flows, 29 | ChunkInterface, 30 | ) 31 | from gen._layers import ( 32 | transpile_to_z_basis_interaction_circuit, 33 | LayerCircuit, 34 | ResetLayer, 35 | InteractLayer, 36 | ) 37 | from gen._util import ( 38 | estimate_qubit_count_during_postselection, 39 | stim_circuit_with_transformed_coords, 40 | xor_sorted, 41 | write_file, 42 | ) 43 | from gen._viz_circuit_html import ( 44 | stim_circuit_html_viewer, 45 | ) 46 | from gen._viz_patch_svg import ( 47 | patch_svg_viewer, 48 | is_collinear, 49 | svg_path_directions_for_tile, 50 | ) 51 | from gen._viz_gltf_3d import ( 52 | ColoredLineData, 53 | ColoredTriangleData, 54 | gltf_model_from_colored_triangle_data, 55 | viz_3d_gltf_model_html, 56 | ) 57 | -------------------------------------------------------------------------------- /src/gen/_core/__init__.py: -------------------------------------------------------------------------------- 1 | from gen._core._builder import ( 2 | Builder, 3 | ) 4 | from gen._core._measurement_tracker import ( 5 | AtLayer, 6 | MeasurementTracker, 7 | ) 8 | from gen._core._noise import ( 9 | NoiseModel, 10 | NoiseRule, 11 | occurs_in_classical_control_system, 12 | ) 13 | from gen._core._patch import ( 14 | Patch, 15 | ) 16 | from gen._core._stabilizer_code import ( 17 | StabilizerCode, 18 | ) 19 | from gen._core._tile import ( 20 | Tile, 21 | ) 22 | from gen._core._util import ( 23 | sorted_complex, 24 | min_max_complex, 25 | complex_key, 26 | ) 27 | from gen._core._pauli_string import ( 28 | PauliString, 29 | ) 30 | from gen._core._keyed_pauli_string import ( 31 | KeyedPauliString, 32 | ) 33 | -------------------------------------------------------------------------------- /src/gen/_core/_builder_test.py: -------------------------------------------------------------------------------- 1 | import stim 2 | 3 | import gen 4 | 5 | 6 | def test_builder_init(): 7 | builder = gen.Builder.for_qubits([0, 1j, 3 + 2j]) 8 | assert builder.circuit == stim.Circuit( 9 | """ 10 | QUBIT_COORDS(0, 0) 0 11 | QUBIT_COORDS(0, 1) 1 12 | QUBIT_COORDS(3, 2) 2 13 | """ 14 | ) 15 | -------------------------------------------------------------------------------- /src/gen/_core/_keyed_pauli_string.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | from typing import Literal, Callable 3 | 4 | from gen._core._pauli_string import PauliString 5 | 6 | 7 | @dataclasses.dataclass(frozen=True) 8 | class KeyedPauliString: 9 | key: int 10 | pauli_string: PauliString 11 | 12 | @property 13 | def qubits(self) -> dict[complex, Literal['X', 'Y', 'Z']]: 14 | return self.pauli_string.qubits 15 | 16 | def __lt__(self, other) -> bool: 17 | if isinstance(other, PauliString): 18 | return True 19 | if isinstance(other, KeyedPauliString): 20 | return (self.key, self.pauli_string) < (other.key, other.pauli_string) 21 | return NotImplemented 22 | 23 | def __gt__(self, other) -> bool: 24 | if isinstance(other, PauliString): 25 | return False 26 | if isinstance(other, KeyedPauliString): 27 | return (self.key, self.pauli_string) > (other.key, other.pauli_string) 28 | return NotImplemented 29 | 30 | def with_transformed_coords( 31 | self, transform: Callable[[complex], complex] 32 | ) -> "KeyedPauliString": 33 | return KeyedPauliString(key=self.key, pauli_string=self.pauli_string.with_transformed_coords(transform)) 34 | 35 | def __str__(self): 36 | return f'(key={self.key}) {self.pauli_string}' -------------------------------------------------------------------------------- /src/gen/_core/_measurement_tracker.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | from typing import Iterable, Any 3 | 4 | import stim 5 | 6 | 7 | @dataclasses.dataclass(frozen=True) 8 | class AtLayer: 9 | """A special class that indicates the layer to read a measurement key from.""" 10 | 11 | key: Any 12 | layer: Any 13 | 14 | 15 | class MeasurementTracker: 16 | """Tracks measurements and groups of measurements, for producing stim record targets.""" 17 | 18 | def __init__(self): 19 | self.recorded: dict[Any, list[int] | None] = {} 20 | self.next_measurement_index = 0 21 | 22 | def copy(self) -> "MeasurementTracker": 23 | result = MeasurementTracker() 24 | result.recorded = {k: list(v) for k, v in self.recorded.items()} 25 | result.next_measurement_index = self.next_measurement_index 26 | return result 27 | 28 | def _rec(self, key: Any, value: list[int] | None) -> None: 29 | if key in self.recorded: 30 | raise ValueError(f"Measurement key collision: {key=}") 31 | self.recorded[key] = value 32 | 33 | def record_measurement(self, key: Any) -> None: 34 | self._rec(key, [self.next_measurement_index]) 35 | self.next_measurement_index += 1 36 | 37 | def make_measurement_group(self, sub_keys: Iterable[Any], *, key: Any) -> None: 38 | self._rec(key, self.lookup_recs(sub_keys)) 39 | 40 | def record_obstacle(self, key: Any) -> None: 41 | self._rec(key, None) 42 | 43 | def lookup_recs(self, keys: Iterable[Any]) -> list[int] | None: 44 | result = set() 45 | for key in keys: 46 | if key not in self.recorded: 47 | raise ValueError(f"No such measurement: {key=}") 48 | r = self.recorded[key] 49 | if r is None: 50 | return None 51 | for v in r: 52 | if v is None: 53 | raise ValueError(f"Obstacle at {key=}") 54 | if v in result: 55 | result.remove(v) 56 | else: 57 | result.add(v) 58 | return sorted(result) 59 | 60 | def current_measurement_record_targets_for( 61 | self, keys: Iterable[Any] 62 | ) -> list[stim.GateTarget] | None: 63 | t0 = self.next_measurement_index 64 | times = self.lookup_recs(keys) 65 | if times is None: 66 | return None 67 | return [stim.target_rec(t - t0) for t in sorted(times)] 68 | -------------------------------------------------------------------------------- /src/gen/_core/_patch_test.py: -------------------------------------------------------------------------------- 1 | import stim 2 | 3 | from gen._core._builder import Builder 4 | from gen._core._patch import Patch 5 | from gen._core._tile import Tile 6 | -------------------------------------------------------------------------------- /src/gen/_core/_pauli_string_test.py: -------------------------------------------------------------------------------- 1 | from gen._core._pauli_string import PauliString 2 | 3 | 4 | def test_mul(): 5 | a = "IIIIXXXXYYYYZZZZ" 6 | b = "IXYZ" * 4 7 | c = "IXYZXIZYYZIXZYXI" 8 | a = PauliString({q: p for q, p in enumerate(a) if p != "I"}) 9 | b = PauliString({q: p for q, p in enumerate(b) if p != "I"}) 10 | c = PauliString({q: p for q, p in enumerate(c) if p != "I"}) 11 | assert a * b == c 12 | -------------------------------------------------------------------------------- /src/gen/_core/_tile_test.py: -------------------------------------------------------------------------------- 1 | import functools 2 | from typing import Iterable, Optional, Callable 3 | 4 | from gen._core._tile import Tile 5 | 6 | 7 | def test_basis(): 8 | tile = Tile(bases="XYZX", measurement_qubit=0, ordered_data_qubits=(1, 2, None, 3)) 9 | assert tile.basis is None 10 | 11 | tile = Tile(bases="XXZX", measurement_qubit=0, ordered_data_qubits=(1, 2, None, 3)) 12 | assert tile.basis == "X" 13 | 14 | tile = Tile(bases="XXX", measurement_qubit=0, ordered_data_qubits=(1, 2, 3)) 15 | assert tile.basis == "X" 16 | 17 | tile = Tile(bases="ZZZ", measurement_qubit=0, ordered_data_qubits=(1, 2, 3)) 18 | assert tile.basis == "Z" 19 | 20 | tile = Tile(bases="ZXZ", measurement_qubit=0, ordered_data_qubits=(1, 2, 3)) 21 | assert tile.basis == None 22 | -------------------------------------------------------------------------------- /src/gen/_core/_util.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, Iterable, TypeVar, Any, Optional 2 | 3 | TItem = TypeVar("TItem") 4 | 5 | 6 | def complex_key(c: complex) -> Any: 7 | return c.real != int(c.real), c.real, c.imag 8 | 9 | 10 | def sorted_complex( 11 | values: Iterable[TItem], *, key: Callable[[TItem], Any] = lambda e: e 12 | ) -> list[TItem]: 13 | return sorted(values, key=lambda e: complex_key(key(e))) 14 | 15 | 16 | def min_max_complex( 17 | coords: Iterable[complex], *, default: Optional[complex] = None 18 | ) -> tuple[complex, complex]: 19 | """Computes the bounding box of a collection of complex numbers. 20 | 21 | Args: 22 | coords: The complex numbers to place a bounding box around. 23 | default: If no elements are included, the bounding box will cover this 24 | single value when the collection of complex numbers is empty. If 25 | this argument isn't set (or is set to None), an exception will be 26 | raised instead when given an empty collection. 27 | 28 | Returns: 29 | A pair of complex values (c_min, c_max) where c_min's real component 30 | where c_min is the minimum corner of the bounding box and c_max is the 31 | maximum corner of the bounding box. 32 | """ 33 | coords = list(coords) 34 | if not coords and default is not None: 35 | return default, default 36 | real = [c.real for c in coords] 37 | imag = [c.imag for c in coords] 38 | min_r = min(real) 39 | min_i = min(imag) 40 | max_r = max(real) 41 | max_i = max(imag) 42 | return min_r + min_i * 1j, max_r + max_i * 1j 43 | -------------------------------------------------------------------------------- /src/gen/_core/_util_test.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from gen._core._util import min_max_complex, sorted_complex 4 | 5 | 6 | def test_sorted_complex(): 7 | assert sorted_complex([1, 2j, 2, 1 + 2j]) == [2j, 1, 1 + 2j, 2] 8 | 9 | 10 | def test_min_max_complex(): 11 | with pytest.raises(ValueError): 12 | min_max_complex([]) 13 | assert min_max_complex([], default=0) == (0, 0) 14 | assert min_max_complex([], default=1 + 2j) == (1 + 2j, 1 + 2j) 15 | assert min_max_complex([1j], default=0) == (1j, 1j) 16 | assert min_max_complex([1j, 2]) == (0, 2 + 1j) 17 | assert min_max_complex([1j + 1, 2]) == (1, 2 + 1j) 18 | -------------------------------------------------------------------------------- /src/gen/_flows/__init__.py: -------------------------------------------------------------------------------- 1 | from gen._flows._chunk import ( 2 | Chunk, 3 | ) 4 | from gen._flows._chunk_loop import ( 5 | ChunkLoop, 6 | ) 7 | from gen._flows._chunk_reflow import ( 8 | ChunkReflow, 9 | ) 10 | from gen._flows._chunk_interface import ( 11 | ChunkInterface, 12 | ) 13 | from gen._flows._flow import ( 14 | Flow, 15 | ) 16 | from gen._flows._flow_util import ( 17 | compile_chunks_into_circuit, 18 | magic_measure_for_flows, 19 | ) 20 | -------------------------------------------------------------------------------- /src/gen/_flows/_chunk_loop.py: -------------------------------------------------------------------------------- 1 | from typing import Iterable, Union, TYPE_CHECKING 2 | 3 | from gen._core import Patch 4 | 5 | if TYPE_CHECKING: 6 | from gen._flows._chunk import Chunk 7 | 8 | 9 | class ChunkLoop: 10 | def __init__(self, chunks: Iterable[Union['Chunk', 'ChunkLoop']], repetitions: int): 11 | self.chunks = tuple(chunks) 12 | self.repetitions = repetitions 13 | 14 | @property 15 | def magic(self) -> bool: 16 | return any(c.magic for c in self.chunks) 17 | 18 | def verify(self): 19 | for c in self.chunks: 20 | c.verify() 21 | for k in range(len(self.chunks)): 22 | before: Chunk = self.chunks[k - 1] 23 | after: Chunk = self.chunks[k] 24 | after_in = {} 25 | before_out = {} 26 | for flow in before.flows: 27 | if flow.end: 28 | before_out[flow.end] = flow.obs_index 29 | for flow in after.flows: 30 | if flow.start: 31 | after_in[flow.start] = flow.obs_index 32 | for ps in before.discarded_outputs: 33 | after_in.pop(ps) 34 | for ps in after.discarded_inputs: 35 | before_out.pop(ps) 36 | if after_in != before_out: 37 | raise ValueError("Flows don't match between chunks.") 38 | 39 | def __mul__(self, other: int) -> "ChunkLoop": 40 | return self.with_repetitions(other * self.repetitions) 41 | 42 | def with_repetitions(self, new_repetitions: int) -> "ChunkLoop": 43 | return ChunkLoop(chunks=self.chunks, repetitions=new_repetitions) 44 | 45 | def mpp_init_chunk(self) -> "Chunk": 46 | return self.chunks[0].mpp_init_chunk() 47 | 48 | def mpp_end_chunk(self) -> "Chunk": 49 | return self.chunks[-1].mpp_end_chunk() 50 | 51 | def start_patch(self) -> Patch: 52 | return self.chunks[0].start_patch() 53 | 54 | def end_patch(self) -> Patch: 55 | return self.chunks[-1].end_patch() 56 | 57 | def tick_count(self) -> int: 58 | return sum(e.tick_count() for e in self.chunks) * self.repetitions 59 | 60 | def flattened(self) -> list["Chunk"]: 61 | return [e for c in self.chunks for e in c.flattened()] 62 | -------------------------------------------------------------------------------- /src/gen/_flows/_circuit_util.py: -------------------------------------------------------------------------------- 1 | import stim 2 | 3 | XZ_FLIPPED = { 4 | "I": "I", 5 | "X": "Z", 6 | "Y": "Y", 7 | "Z": "X", 8 | "C_XYZ": "C_ZYX", 9 | "C_ZYX": "C_XYZ", 10 | "H": "H", 11 | "H_XY": "H_YZ", 12 | "H_XZ": "H_XZ", 13 | "H_YZ": "H_XY", 14 | "S": "SQRT_X", 15 | "SQRT_X": "S", 16 | "SQRT_X_DAG": "S_DAG", 17 | "SQRT_Y": "SQRT_Y", 18 | "SQRT_Y_DAG": "SQRT_Y_DAG", 19 | "S_DAG": "SQRT_X_DAG", 20 | "CX": "XCZ", 21 | "CY": "XCY", 22 | "CZ": "XCX", 23 | "ISWAP": None, 24 | "ISWAP_DAG": None, 25 | "SQRT_XX": "SQRT_ZZ", 26 | "SQRT_XX_DAG": "SQRT_ZZ_DAG", 27 | "SQRT_YY": "SQRT_YY", 28 | "SQRT_YY_DAG": "SQRT_YY_DAG", 29 | "SQRT_ZZ": "SQRT_XX", 30 | "SQRT_ZZ_DAG": "SQRT_XX_DAG", 31 | "SWAP": "SWAP", 32 | "XCX": "CZ", 33 | "XCY": "CY", 34 | "XCZ": "CX", 35 | "YCX": "YCZ", 36 | "YCY": "YCY", 37 | "YCZ": "YCX", 38 | "DEPOLARIZE1": "DEPOLARIZE1", 39 | "DEPOLARIZE2": "DEPOLARIZE2", 40 | "E": None, 41 | "ELSE_CORRELATED_ERROR": None, 42 | "PAULI_CHANNEL_1": None, 43 | "PAULI_CHANNEL_2": None, 44 | "X_ERROR": "Z_ERROR", 45 | "Y_ERROR": "Y_ERROR", 46 | "Z_ERROR": "X_ERROR", 47 | "M": "MX", 48 | "MPP": None, 49 | "MR": "MRX", 50 | "MRX": "MRZ", 51 | "MRY": "MRY", 52 | "MX": "M", 53 | "MY": "MY", 54 | "R": "RX", 55 | "RX": "R", 56 | "RY": "RY", 57 | "DETECTOR": "DETECTOR", 58 | "OBSERVABLE_INCLUDE": "OBSERVABLE_INCLUDE", 59 | "QUBIT_COORDS": "QUBIT_COORDS", 60 | "SHIFT_COORDS": "SHIFT_COORDS", 61 | "TICK": "TICK", 62 | } 63 | 64 | 65 | def circuit_with_xz_flipped(circuit: stim.Circuit) -> stim.Circuit: 66 | result = stim.Circuit() 67 | for inst in circuit: 68 | if isinstance(inst, stim.CircuitRepeatBlock): 69 | result.append( 70 | stim.CircuitRepeatBlock( 71 | body=circuit_with_xz_flipped(inst.body_copy()), 72 | repeat_count=inst.repeat_count, 73 | ) 74 | ) 75 | else: 76 | other = XZ_FLIPPED.get(inst.name) 77 | if other is None: 78 | raise NotImplementedError(f"{inst=}") 79 | result.append( 80 | stim.CircuitInstruction( 81 | other, inst.targets_copy(), inst.gate_args_copy() 82 | ) 83 | ) 84 | return result 85 | 86 | 87 | def circuit_to_dem_target_measurement_records_map(circuit: stim.Circuit) -> dict[stim.DemTarget, list[int]]: 88 | result = {} 89 | for k in range(circuit.num_observables): 90 | result[stim.target_logical_observable_id(k)] = [] 91 | num_d = 0 92 | num_m = 0 93 | for inst in circuit.flattened(): 94 | if inst.name == 'DETECTOR': 95 | result[stim.target_relative_detector_id(num_d)] = [num_m + t.value for t in inst.targets_copy()] 96 | num_d += 1 97 | elif inst.name == 'OBSERVABLE_INCLUDE': 98 | result[stim.target_logical_observable_id(int(inst.gate_args_copy()[0]))].extend(num_m + t.value for t in inst.targets_copy()) 99 | else: 100 | c = stim.Circuit() 101 | c.append(inst) 102 | num_m += c.num_measurements 103 | return result 104 | -------------------------------------------------------------------------------- /src/gen/_flows/_flow_test.py: -------------------------------------------------------------------------------- 1 | from gen._flows._flow import Flow 2 | from gen._core._pauli_string import PauliString 3 | 4 | 5 | def test_with_xz_flipped(): 6 | assert Flow( 7 | start=PauliString({1: "X", 2: "Z"}), 8 | center=0, 9 | ).with_xz_flipped() == Flow( 10 | start=PauliString({1: "Z", 2: "X"}), 11 | center=0, 12 | ) 13 | -------------------------------------------------------------------------------- /src/gen/_flows/_test_util.py: -------------------------------------------------------------------------------- 1 | from typing import Iterable 2 | 3 | 4 | def assert_has_same_set_of_items_as( 5 | actual: Iterable, 6 | expected: Iterable, 7 | actual_name: str = 'actual', 8 | expected_name: str = 'expected') -> None: 9 | __tracebackhide__ = True 10 | 11 | actual = frozenset(actual) 12 | expected = frozenset(expected) 13 | if actual == expected: 14 | return 15 | 16 | lines = [f"set({actual_name}) != set({expected_name})", ""] 17 | if actual - expected: 18 | lines.append("Extra items in actual (left):") 19 | for d in sorted(actual - expected): 20 | lines.append(f' {d}') 21 | if expected - actual: 22 | lines.append("Missing items in actual (left):") 23 | for d in sorted(expected - actual): 24 | lines.append(f' {d}') 25 | raise AssertionError("\n".join(lines)) 26 | -------------------------------------------------------------------------------- /src/gen/_layers/__init__.py: -------------------------------------------------------------------------------- 1 | """Works with circuits in a layered representation that's easy to operate on. 2 | """ 3 | from gen._layers._transpile import ( 4 | transpile_to_z_basis_interaction_circuit, 5 | ) 6 | from gen._layers._layer_circuit import ( 7 | LayerCircuit, 8 | ) 9 | from gen._layers._interact_layer import ( 10 | InteractLayer, 11 | ) 12 | from gen._layers._reset_layer import ( 13 | ResetLayer, 14 | ) 15 | -------------------------------------------------------------------------------- /src/gen/_layers/_data.py: -------------------------------------------------------------------------------- 1 | from typing import Literal 2 | 3 | import numpy as np 4 | 5 | R_XYZ = 0 6 | R_XZY = 1 7 | R_YXZ = 2 8 | R_YZX = 3 9 | R_ZXY = 4 10 | R_ZYX = 5 11 | PERMUTATIONS: list[dict[Literal["X", "Y", "Z"], Literal["X", "Y", "Z"]]] = [ 12 | {"X": "X", "Y": "Y", "Z": "Z"}, 13 | {"X": "X", "Y": "Z", "Z": "Y"}, 14 | {"X": "Y", "Y": "X", "Z": "Z"}, 15 | {"X": "Y", "Y": "Z", "Z": "X"}, 16 | {"X": "Z", "Y": "X", "Z": "Y"}, 17 | {"X": "Z", "Y": "Y", "Z": "X"}, 18 | ] 19 | INVERSE_PERMUTATIONS: list[dict[Literal["X", "Y", "Z"], Literal["X", "Y", "Z"]]] = [ 20 | {"X": "X", "Y": "Y", "Z": "Z"}, 21 | {"X": "X", "Y": "Z", "Z": "Y"}, 22 | {"X": "Y", "Y": "X", "Z": "Z"}, 23 | {"X": "Z", "Y": "X", "Z": "Y"}, 24 | {"X": "Y", "Y": "Z", "Z": "X"}, 25 | {"X": "Z", "Y": "Y", "Z": "X"}, 26 | ] 27 | ORIENTATIONS = [ 28 | "I", 29 | "SQRT_X", 30 | "S", 31 | "C_XYZ", 32 | "C_ZYX", 33 | "H", 34 | ] 35 | ORIENTATION_MULTIPLICATION_TABLE = np.array( 36 | [ 37 | [0, 1, 2, 3, 4, 5], 38 | [1, 0, 4, 5, 2, 3], 39 | [2, 3, 0, 1, 5, 4], 40 | [3, 2, 5, 4, 0, 1], 41 | [4, 5, 1, 0, 3, 2], 42 | [5, 4, 3, 2, 1, 0], 43 | ], 44 | dtype=np.uint8, 45 | ) 46 | -------------------------------------------------------------------------------- /src/gen/_layers/_det_obs_annotation_layer.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | 3 | import stim 4 | 5 | from gen._layers._layer import Layer 6 | 7 | 8 | @dataclasses.dataclass 9 | class DetObsAnnotationLayer(Layer): 10 | circuit: stim.Circuit = dataclasses.field(default_factory=stim.Circuit) 11 | 12 | def with_rec_targets_shifted_by(self, shift: int) -> 'DetObsAnnotationLayer': 13 | result = DetObsAnnotationLayer() 14 | for inst in self.circuit: 15 | result.circuit.append(inst.name, [ 16 | stim.target_rec(t.value + shift) 17 | for t in inst.targets_copy() 18 | ], inst.gate_args_copy()) 19 | return result 20 | 21 | def copy(self) -> "DetObsAnnotationLayer": 22 | return DetObsAnnotationLayer(circuit=self.circuit.copy()) 23 | 24 | def touched(self) -> set[int]: 25 | return set() 26 | 27 | def requires_tick_before(self) -> bool: 28 | return False 29 | 30 | def implies_eventual_tick_after(self) -> bool: 31 | return False 32 | 33 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 34 | out += self.circuit 35 | -------------------------------------------------------------------------------- /src/gen/_layers/_empty_layer.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | 3 | import stim 4 | 5 | from gen._layers._layer import Layer 6 | 7 | 8 | @dataclasses.dataclass 9 | class EmptyLayer(Layer): 10 | def copy(self) -> "EmptyLayer": 11 | return EmptyLayer() 12 | 13 | def touched(self) -> set[int]: 14 | return set() 15 | 16 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 17 | pass 18 | 19 | def locally_optimized(self, next_layer: None | Layer) -> list[Layer | None]: 20 | return [next_layer] 21 | 22 | def is_vacuous(self) -> bool: 23 | return True 24 | -------------------------------------------------------------------------------- /src/gen/_layers/_feedback_layer.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | from typing import TYPE_CHECKING, Literal 3 | 4 | import stim 5 | 6 | from gen._layers._data import INVERSE_PERMUTATIONS 7 | from gen._layers._layer import Layer 8 | 9 | if TYPE_CHECKING: 10 | from gen._layers._rotation_layer import RotationLayer 11 | 12 | 13 | @dataclasses.dataclass 14 | class FeedbackLayer(Layer): 15 | controls: list[stim.GateTarget] = dataclasses.field(default_factory=list) 16 | targets: list[int] = dataclasses.field(default_factory=list) 17 | bases: list[Literal["X", "Y", "Z"]] = dataclasses.field(default_factory=list) 18 | 19 | def with_rec_targets_shifted_by(self, shift: int) -> 'FeedbackLayer': 20 | result = self.copy() 21 | result.controls = [stim.target_rec(t.value + shift) for t in result.controls] 22 | return result 23 | 24 | def copy(self) -> "FeedbackLayer": 25 | return FeedbackLayer( 26 | targets=list(self.targets), 27 | controls=list(self.controls), 28 | bases=list(self.bases), 29 | ) 30 | 31 | def touched(self) -> set[int]: 32 | return set(self.targets) 33 | 34 | def requires_tick_before(self) -> bool: 35 | return False 36 | 37 | def implies_eventual_tick_after(self) -> bool: 38 | return False 39 | 40 | def before(self, layer: "RotationLayer") -> "FeedbackLayer": 41 | return FeedbackLayer( 42 | controls=list(self.controls), 43 | targets=list(self.targets), 44 | bases=[ 45 | _basis_before_rotation(b, layer.rotations.get(t, 0)) 46 | for b, t in zip(self.bases, self.targets) 47 | ], 48 | ) 49 | 50 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 51 | for c, t, b in zip(self.controls, self.targets, self.bases): 52 | out.append("C" + b, [c, t]) 53 | 54 | 55 | def _basis_before_rotation( 56 | basis: Literal["X", "Y", "Z"], rotation: int 57 | ) -> Literal["X", "Y", "Z"]: 58 | return INVERSE_PERMUTATIONS[rotation][basis] 59 | -------------------------------------------------------------------------------- /src/gen/_layers/_feedback_layer_test.py: -------------------------------------------------------------------------------- 1 | import stim 2 | 3 | from gen._layers._data import R_ZXY 4 | from gen._layers._feedback_layer import _basis_before_rotation 5 | 6 | 7 | def test_basis_before_rotation(): 8 | assert _basis_before_rotation("X", R_ZXY) == "Y" 9 | assert _basis_before_rotation("Y", R_ZXY) == "Z" 10 | assert _basis_before_rotation("Z", R_ZXY) == "X" 11 | -------------------------------------------------------------------------------- /src/gen/_layers/_interact_layer.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import dataclasses 3 | 4 | import stim 5 | 6 | from gen._layers._data import R_XYZ, R_ZYX, R_XZY 7 | from gen._layers._layer import Layer 8 | 9 | 10 | @dataclasses.dataclass 11 | class InteractLayer(Layer): 12 | targets1: list[int] = dataclasses.field(default_factory=list) 13 | targets2: list[int] = dataclasses.field(default_factory=list) 14 | bases1: list[str] = dataclasses.field(default_factory=list) 15 | bases2: list[str] = dataclasses.field(default_factory=list) 16 | 17 | def touched(self) -> set[int]: 18 | return set(self.targets1 + self.targets2) 19 | 20 | def copy(self) -> "InteractLayer": 21 | return InteractLayer( 22 | targets1=list(self.targets1), 23 | targets2=list(self.targets2), 24 | bases1=list(self.bases1), 25 | bases2=list(self.bases2), 26 | ) 27 | 28 | def _rot_layer(self): 29 | from gen._layers._rotation_layer import RotationLayer 30 | 31 | result = RotationLayer() 32 | for targets, bases in [ 33 | (self.targets1, self.bases1), 34 | (self.targets2, self.bases2), 35 | ]: 36 | for q, b in zip(targets, bases): 37 | result.rotations[q] = ( 38 | R_XYZ if b == "Z" else R_ZYX if b == "X" else R_XZY 39 | ) 40 | return result 41 | 42 | def to_z_basis(self) -> list["Layer"]: 43 | rot = self._rot_layer() 44 | return [ 45 | rot, 46 | InteractLayer( 47 | targets1=list(self.targets1), 48 | targets2=list(self.targets2), 49 | bases1=["Z"] * len(self.targets1), 50 | bases2=["Z"] * len(self.targets2), 51 | ), 52 | rot.copy(), 53 | ] 54 | 55 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 56 | groups = collections.defaultdict(list) 57 | for k in range(len(self.targets1)): 58 | gate = self.bases1[k] + "C" + self.bases2[k] 59 | t1 = self.targets1[k] 60 | t2 = self.targets2[k] 61 | if gate in ["XCZ", "YCZ", "YCX"]: 62 | t1, t2 = t2, t1 63 | gate = gate[::-1] 64 | if gate in ["XCX", "YCY", "ZCZ"]: 65 | t1, t2 = sorted([t1, t2]) 66 | groups[gate].append((t1, t2)) 67 | for gate in sorted(groups.keys()): 68 | for pair in sorted(groups[gate]): 69 | out.append(gate, pair) 70 | 71 | def locally_optimized(self, next_layer: Layer | None) -> list[Layer | None]: 72 | from gen._layers._swap_layer import SwapLayer 73 | from gen._layers._interact_swap_layer import InteractSwapLayer 74 | 75 | if isinstance(next_layer, SwapLayer): 76 | return [ 77 | InteractSwapLayer(i_layer=self.copy(), swap_layer=next_layer.copy()) 78 | ] 79 | elif isinstance(next_layer, InteractLayer) and self.touched().isdisjoint(next_layer.touched()): 80 | return [ 81 | InteractLayer( 82 | targets1=self.targets1 + next_layer.targets1, 83 | targets2=self.targets2 + next_layer.targets2, 84 | bases1=self.bases1 + next_layer.bases1, 85 | bases2=self.bases2 + next_layer.bases2, 86 | ) 87 | ] 88 | return [self, next_layer] 89 | -------------------------------------------------------------------------------- /src/gen/_layers/_interact_swap_layer.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | 3 | import stim 4 | 5 | from gen._layers._data import R_YXZ 6 | from gen._layers._interact_layer import InteractLayer 7 | from gen._layers._iswap_layer import ISwapLayer 8 | from gen._layers._layer import Layer 9 | from gen._layers._rotation_layer import RotationLayer 10 | from gen._layers._swap_layer import SwapLayer 11 | 12 | 13 | @dataclasses.dataclass 14 | class InteractSwapLayer(Layer): 15 | i_layer: InteractLayer = dataclasses.field(default_factory=InteractLayer) 16 | swap_layer: SwapLayer = dataclasses.field(default_factory=SwapLayer) 17 | 18 | def copy(self) -> "InteractSwapLayer": 19 | return InteractSwapLayer( 20 | i_layer=self.i_layer.copy(), swap_layer=self.swap_layer.copy() 21 | ) 22 | 23 | def touched(self) -> set[int]: 24 | return self.i_layer.touched() | self.swap_layer.touched() 25 | 26 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 27 | self.i_layer.append_into_stim_circuit(out) 28 | out.append("TICK") 29 | self.swap_layer.append_into_stim_circuit(out) 30 | 31 | def to_z_basis(self) -> list["Layer"]: 32 | pairs_1 = { 33 | frozenset([a, b]) 34 | for a, b in zip(self.i_layer.targets1, self.i_layer.targets2) 35 | } 36 | pairs_2 = { 37 | frozenset([a, b]) 38 | for a, b in zip(self.swap_layer.targets1, self.swap_layer.targets2) 39 | } 40 | assert pairs_1 == pairs_2 41 | pre: RotationLayer 42 | post: RotationLayer 43 | pre, _, post = self.i_layer.to_z_basis() 44 | for x, y in zip(self.swap_layer.targets1, self.swap_layer.targets2): 45 | post.rotations[x], post.rotations[y] = post.rotations[y], post.rotations[x] 46 | post.prepend_rotation(R_YXZ, x) 47 | post.prepend_rotation(R_YXZ, y) 48 | mid = ISwapLayer( 49 | targets1=self.swap_layer.targets1, targets2=self.swap_layer.targets2 50 | ) 51 | return [pre, mid, post] 52 | 53 | def locally_optimized(self, next_layer: Layer | None) -> list[Layer | None]: 54 | return [self, next_layer] 55 | -------------------------------------------------------------------------------- /src/gen/_layers/_iswap_layer.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | 3 | import stim 4 | 5 | from gen._layers._layer import Layer 6 | 7 | 8 | @dataclasses.dataclass 9 | class ISwapLayer(Layer): 10 | targets1: list[int] = dataclasses.field(default_factory=list) 11 | targets2: list[int] = dataclasses.field(default_factory=list) 12 | 13 | def copy(self) -> "ISwapLayer": 14 | return ISwapLayer(targets1=list(self.targets1), targets2=list(self.targets2)) 15 | 16 | def touched(self) -> set[int]: 17 | return set(self.targets1 + self.targets2) 18 | 19 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 20 | pairs = [] 21 | for k in range(len(self.targets1)): 22 | t1 = self.targets1[k] 23 | t2 = self.targets2[k] 24 | t1, t2 = sorted([t1, t2]) 25 | pairs.append((t1, t2)) 26 | for pair in sorted(pairs): 27 | out.append("ISWAP", pair) 28 | 29 | def locally_optimized(self, next_layer: Layer | None) -> list[Layer | None]: 30 | return [self, next_layer] 31 | -------------------------------------------------------------------------------- /src/gen/_layers/_layer.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import stim 4 | 5 | 6 | class Layer: 7 | def copy(self) -> "Layer": 8 | raise NotImplementedError() 9 | 10 | def touched(self) -> set[int]: 11 | raise NotImplementedError() 12 | 13 | def to_z_basis(self) -> list["Layer"]: 14 | return [self] 15 | 16 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 17 | raise NotImplementedError() 18 | 19 | def locally_optimized( 20 | self, next_layer: Optional["Layer"] 21 | ) -> list[Optional["Layer"]]: 22 | return [self, next_layer] 23 | 24 | def is_vacuous(self) -> bool: 25 | return False 26 | 27 | def requires_tick_before(self) -> bool: 28 | return True 29 | 30 | def implies_eventual_tick_after(self) -> bool: 31 | return True 32 | -------------------------------------------------------------------------------- /src/gen/_layers/_loop_layer.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | from typing import TYPE_CHECKING 3 | 4 | import stim 5 | 6 | from gen._layers._layer import Layer 7 | 8 | if TYPE_CHECKING: 9 | from gen._layers._layer_circuit import LayerCircuit 10 | 11 | 12 | @dataclasses.dataclass 13 | class LoopLayer(Layer): 14 | body: "LayerCircuit" 15 | repetitions: int 16 | 17 | def copy(self) -> "LoopLayer": 18 | return LoopLayer(body=self.body.copy(), repetitions=self.repetitions) 19 | 20 | def touched(self) -> set[int]: 21 | return self.body.touched() 22 | 23 | def to_z_basis(self) -> list["Layer"]: 24 | return [ 25 | LoopLayer( 26 | body=self.body.to_z_basis(), 27 | repetitions=self.repetitions, 28 | ) 29 | ] 30 | 31 | def locally_optimized(self, next_layer: Layer | None) -> list[Layer | None]: 32 | optimized = LoopLayer( 33 | body=self.body.with_locally_optimized_layers(), 34 | repetitions=self.repetitions, 35 | ) 36 | return [optimized, next_layer] 37 | 38 | def implies_eventual_tick_after(self) -> bool: 39 | return False 40 | 41 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 42 | body = self.body.to_stim_circuit() 43 | body.append("TICK") 44 | out.append(stim.CircuitRepeatBlock(repeat_count=self.repetitions, body=body)) 45 | -------------------------------------------------------------------------------- /src/gen/_layers/_measure_layer.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | from typing import Optional, Set 3 | 4 | import stim 5 | 6 | from gen._layers._data import R_ZYX, R_XYZ, R_XZY 7 | from gen._layers._layer import Layer 8 | from gen._layers._rotation_layer import RotationLayer 9 | 10 | 11 | @dataclasses.dataclass 12 | class MeasureLayer(Layer): 13 | targets: list[int] = dataclasses.field(default_factory=list) 14 | bases: list[str] = dataclasses.field(default_factory=list) 15 | 16 | def copy(self) -> "MeasureLayer": 17 | return MeasureLayer(targets=list(self.targets), bases=list(self.bases)) 18 | 19 | def touched(self) -> set[int]: 20 | return set(self.targets) 21 | 22 | def to_z_basis(self) -> list["Layer"]: 23 | rot = RotationLayer( 24 | { 25 | q: R_XYZ if b == "Z" else R_ZYX if b == "X" else R_XZY 26 | for q, b in zip(self.targets, self.bases) 27 | } 28 | ) 29 | return [ 30 | rot, 31 | MeasureLayer(targets=list(self.targets), bases=["Z"] * len(self.targets)), 32 | rot.copy(), 33 | ] 34 | 35 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 36 | for t, b in zip(self.targets, self.bases): 37 | out.append("M" + b, [t]) 38 | 39 | def locally_optimized(self, next_layer: Layer | None) -> list[Layer | None]: 40 | if isinstance(next_layer, MeasureLayer) and set(self.targets).isdisjoint( 41 | next_layer.targets 42 | ): 43 | return [ 44 | MeasureLayer( 45 | targets=self.targets + next_layer.targets, 46 | bases=self.bases + next_layer.bases, 47 | ) 48 | ] 49 | if isinstance(next_layer, RotationLayer) and set(self.targets).isdisjoint( 50 | next_layer.rotations 51 | ): 52 | return [next_layer, self] 53 | return [self, next_layer] 54 | -------------------------------------------------------------------------------- /src/gen/_layers/_mpp_layer.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | 3 | import stim 4 | 5 | from gen._layers._data import R_ZYX, R_XZY 6 | from gen._layers._layer import Layer 7 | from gen._layers._rotation_layer import RotationLayer 8 | 9 | 10 | @dataclasses.dataclass 11 | class MppLayer(Layer): 12 | targets: list[list[stim.GateTarget]] = dataclasses.field(default_factory=list) 13 | 14 | def copy(self) -> "MppLayer": 15 | return MppLayer(targets=[list(e) for e in self.targets]) 16 | 17 | def touched(self) -> set[int]: 18 | return set(t.value for mpp in self.targets for t in mpp) 19 | 20 | def to_z_basis(self) -> list[Layer]: 21 | return [self] 22 | 23 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 24 | flat_targets = [] 25 | for group in self.targets: 26 | for t in group: 27 | flat_targets.append(t) 28 | flat_targets.append(stim.target_combiner()) 29 | flat_targets.pop() 30 | out.append("MPP", flat_targets) 31 | -------------------------------------------------------------------------------- /src/gen/_layers/_noise_layer.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | 3 | import stim 4 | 5 | from gen._layers._layer import Layer 6 | 7 | 8 | @dataclasses.dataclass 9 | class NoiseLayer(Layer): 10 | circuit: stim.Circuit = dataclasses.field(default_factory=stim.Circuit) 11 | 12 | def copy(self) -> "NoiseLayer": 13 | return NoiseLayer(circuit=self.circuit.copy()) 14 | 15 | def touched(self) -> set[int]: 16 | return { 17 | target.qubit_value 18 | for instruction in self.circuit 19 | for target in instruction.targets_copy() 20 | } 21 | 22 | def requires_tick_before(self) -> bool: 23 | return False 24 | 25 | def implies_eventual_tick_after(self) -> bool: 26 | return False 27 | 28 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 29 | out += self.circuit 30 | -------------------------------------------------------------------------------- /src/gen/_layers/_qubit_coord_annotation_layer.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | from typing import Iterable 3 | 4 | import stim 5 | 6 | from gen._layers._layer import Layer 7 | 8 | 9 | @dataclasses.dataclass 10 | class QubitCoordAnnotationLayer(Layer): 11 | coords: dict[int, list[float]] = dataclasses.field(default_factory=dict) 12 | 13 | def offset_by(self, args: Iterable[float]): 14 | for index, offset in enumerate(args): 15 | if offset: 16 | for q, qubit_coords in self.coords.items(): 17 | if index < len(qubit_coords): 18 | qubit_coords[index] += offset 19 | 20 | def copy(self) -> "Layer": 21 | return QubitCoordAnnotationLayer(coords=dict(self.coords)) 22 | 23 | def touched(self) -> set[int]: 24 | return set() 25 | 26 | def requires_tick_before(self) -> bool: 27 | return False 28 | 29 | def implies_eventual_tick_after(self) -> bool: 30 | return False 31 | 32 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 33 | for q in sorted(self.coords.keys()): 34 | out.append("QUBIT_COORDS", [q], self.coords[q]) 35 | -------------------------------------------------------------------------------- /src/gen/_layers/_reset_layer.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | from typing import Literal 3 | 4 | import stim 5 | 6 | from gen._layers._data import R_XYZ, R_ZYX, R_XZY 7 | from gen._layers._layer import Layer 8 | from gen._layers._rotation_layer import RotationLayer 9 | 10 | 11 | @dataclasses.dataclass 12 | class ResetLayer(Layer): 13 | targets: dict[int, Literal["X", "Y", "Z"]] = dataclasses.field(default_factory=dict) 14 | 15 | def copy(self) -> "ResetLayer": 16 | return ResetLayer(targets=dict(self.targets)) 17 | 18 | def touched(self) -> set[int]: 19 | return set(self.targets.keys()) 20 | 21 | def to_z_basis(self) -> list["Layer"]: 22 | return [ 23 | ResetLayer(targets={q: "Z" for q in self.targets.keys()}), 24 | RotationLayer( 25 | { 26 | q: R_XYZ if b == "Z" else R_ZYX if b == "X" else R_XZY 27 | for q, b in self.targets.items() 28 | } 29 | ), 30 | ] 31 | 32 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 33 | outs = {"X": [], "Y": [], "Z": []} 34 | for t, b in self.targets.items(): 35 | outs[b].append(t) 36 | for b, vs in outs.items(): 37 | if vs: 38 | out.append("R" + b, vs) 39 | 40 | def locally_optimized(self, next_layer: Layer | None) -> list[Layer | None]: 41 | if isinstance(next_layer, ResetLayer): 42 | return [ 43 | ResetLayer( 44 | targets={ 45 | t: b 46 | for layer in [self, next_layer] 47 | for t, b in layer.targets.items() 48 | } 49 | ) 50 | ] 51 | return [self, next_layer] 52 | -------------------------------------------------------------------------------- /src/gen/_layers/_rotation_layer.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | 3 | import sinter 4 | import stim 5 | 6 | from gen._layers._data import ( 7 | R_ZXY, 8 | R_YZX, 9 | ORIENTATIONS, 10 | R_XYZ, 11 | ORIENTATION_MULTIPLICATION_TABLE, 12 | ) 13 | from gen._layers._layer import Layer 14 | 15 | 16 | @dataclasses.dataclass 17 | class RotationLayer(Layer): 18 | rotations: dict[int, int] = dataclasses.field(default_factory=dict) 19 | 20 | def touched(self) -> set[int]: 21 | return {k for k, v in self.rotations.items() if v} 22 | 23 | def copy(self) -> "RotationLayer": 24 | return RotationLayer(dict(self.rotations)) 25 | 26 | def inverse(self) -> "RotationLayer": 27 | return RotationLayer( 28 | rotations={ 29 | q: R_YZX if r == R_ZXY else R_ZXY if r == R_YZX else r 30 | for q, r in self.rotations.items() 31 | } 32 | ) 33 | 34 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 35 | v = sinter.group_by(self.rotations.items(), key=lambda e: e[1]) 36 | for r, items in sorted(v.items(), key=lambda e: ORIENTATIONS[e[0]]): 37 | if r: 38 | out.append(ORIENTATIONS[r], sorted(q for q, _ in items)) 39 | 40 | def prepend_rotation(self, rotation_index: int, target: int): 41 | r1 = self.rotations.setdefault(target, R_XYZ) 42 | self.rotations[target] = ORIENTATION_MULTIPLICATION_TABLE[r1][rotation_index] 43 | 44 | def append_rotation(self, rotation_index: int, target: int): 45 | r1 = self.rotations.setdefault(target, R_XYZ) 46 | self.rotations[target] = ORIENTATION_MULTIPLICATION_TABLE[rotation_index][r1] 47 | 48 | def is_vacuous(self) -> bool: 49 | return not any(self.rotations.values()) 50 | 51 | def locally_optimized(self, next_layer: Layer | None) -> list[Layer | None]: 52 | from gen._layers._det_obs_annotation_layer import DetObsAnnotationLayer 53 | from gen._layers._feedback_layer import FeedbackLayer 54 | from gen._layers._reset_layer import ResetLayer 55 | from gen._layers._shift_coord_annotation_layer import ShiftCoordAnnotationLayer 56 | 57 | if isinstance(next_layer, (DetObsAnnotationLayer, ShiftCoordAnnotationLayer)): 58 | return [next_layer, self] 59 | if isinstance(next_layer, FeedbackLayer): 60 | return [next_layer.before(self), self] 61 | if isinstance(next_layer, ResetLayer): 62 | trimmed = self.copy() 63 | for t in next_layer.targets.keys(): 64 | if t in trimmed.rotations: 65 | del trimmed.rotations[t] 66 | if trimmed.rotations: 67 | return [trimmed, next_layer] 68 | else: 69 | return [next_layer] 70 | if isinstance(next_layer, RotationLayer): 71 | result = RotationLayer(rotations=dict(self.rotations)) 72 | for q, r in next_layer.rotations.items(): 73 | result.append_rotation(r, q) 74 | return [result] 75 | return [self, next_layer] 76 | -------------------------------------------------------------------------------- /src/gen/_layers/_shift_coord_annotation_layer.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | from typing import Iterable 3 | 4 | import stim 5 | 6 | from gen._layers._layer import Layer 7 | 8 | 9 | @dataclasses.dataclass 10 | class ShiftCoordAnnotationLayer(Layer): 11 | shift: list[float] = dataclasses.field(default_factory=list) 12 | 13 | def offset_by(self, args: Iterable[float]): 14 | for k, arg in enumerate(args): 15 | if k >= len(self.shift): 16 | self.shift.append(arg) 17 | else: 18 | self.shift[k] += arg 19 | 20 | def copy(self) -> "ShiftCoordAnnotationLayer": 21 | return ShiftCoordAnnotationLayer(shift=self.shift) 22 | 23 | def touched(self) -> set[int]: 24 | return set() 25 | 26 | def requires_tick_before(self) -> bool: 27 | return False 28 | 29 | def implies_eventual_tick_after(self) -> bool: 30 | return False 31 | 32 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 33 | out.append("SHIFT_COORDS", [], self.shift) 34 | 35 | def locally_optimized(self, next_layer: Layer | None) -> list[Layer | None]: 36 | if isinstance(next_layer, ShiftCoordAnnotationLayer): 37 | result = self.copy() 38 | result.offset_by(next_layer.shift) 39 | return [result] 40 | return [self, next_layer] 41 | -------------------------------------------------------------------------------- /src/gen/_layers/_sqrt_pp_layer.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import dataclasses 3 | 4 | import stim 5 | 6 | from gen._layers._data import R_XZY, R_ZYX, R_YXZ 7 | from gen._layers._interact_layer import InteractLayer 8 | from gen._layers._layer import Layer 9 | from gen._layers._rotation_layer import RotationLayer 10 | 11 | 12 | @dataclasses.dataclass 13 | class SqrtPPLayer(Layer): 14 | targets1: list[int] = dataclasses.field(default_factory=list) 15 | targets2: list[int] = dataclasses.field(default_factory=list) 16 | bases: list[str] = dataclasses.field(default_factory=list) 17 | 18 | def touched(self) -> set[int]: 19 | return set(self.targets1 + self.targets2) 20 | 21 | def copy(self) -> "SqrtPPLayer": 22 | return SqrtPPLayer( 23 | targets1=list(self.targets1), 24 | targets2=list(self.targets2), 25 | bases=list(self.bases), 26 | ) 27 | 28 | def to_z_basis(self) -> list["Layer"]: 29 | interact = InteractLayer() 30 | rot = RotationLayer() 31 | for q1, q2, b in zip(self.targets1, self.targets2, self.bases): 32 | interact.targets1.append(q1) 33 | interact.targets2.append(q2) 34 | interact.bases1.append(b) 35 | interact.bases1.append(b) 36 | if b == "X": 37 | r = R_XZY 38 | elif b == "Y": 39 | r = R_ZYX 40 | elif b == "Z": 41 | r = R_YXZ 42 | else: 43 | raise NotImplementedError(f"{b=}") 44 | rot.append_rotation(r, q1) 45 | rot.append_rotation(r, q2) 46 | 47 | return [ 48 | rot, 49 | *interact.to_z_basis(), 50 | ] 51 | 52 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 53 | groups = collections.defaultdict(list) 54 | for q1, q2, b in zip(self.targets1, self.targets2, self.bases): 55 | gate = f"SQRT_{b}{b}" 56 | if q2 < q1: 57 | q1, q2 = q2, q1 58 | groups[gate].append((q1, q2)) 59 | for gate in sorted(groups.keys()): 60 | for pair in sorted(groups[gate]): 61 | out.append(gate, pair) 62 | -------------------------------------------------------------------------------- /src/gen/_layers/_swap_layer.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | 3 | import stim 4 | 5 | from gen._layers._interact_layer import InteractLayer 6 | from gen._layers._det_obs_annotation_layer import DetObsAnnotationLayer 7 | from gen._layers._shift_coord_annotation_layer import ShiftCoordAnnotationLayer 8 | from gen._layers._layer import Layer 9 | 10 | 11 | @dataclasses.dataclass 12 | class SwapLayer(Layer): 13 | targets1: list[int] = dataclasses.field(default_factory=list) 14 | targets2: list[int] = dataclasses.field(default_factory=list) 15 | 16 | def touched(self) -> set[int]: 17 | return set(self.targets1 + self.targets2) 18 | 19 | def to_swap_dict(self) -> dict[int, int]: 20 | d = {} 21 | for a, b in zip(self.targets1, self.targets2): 22 | d[a] = b 23 | d[b] = a 24 | return d 25 | 26 | def copy(self) -> "SwapLayer": 27 | return SwapLayer(targets1=list(self.targets1), targets2=list(self.targets2)) 28 | 29 | def append_into_stim_circuit(self, out: stim.Circuit) -> None: 30 | pairs = [] 31 | for k in range(len(self.targets1)): 32 | t1 = self.targets1[k] 33 | t2 = self.targets2[k] 34 | t1, t2 = sorted([t1, t2]) 35 | pairs.append((t1, t2)) 36 | for pair in sorted(pairs): 37 | out.append("SWAP", pair) 38 | 39 | def locally_optimized(self, next_layer: Layer | None) -> list[Layer | None]: 40 | if isinstance(next_layer, InteractLayer): 41 | from gen._layers._interact_swap_layer import InteractSwapLayer 42 | 43 | i = next_layer.copy() 44 | i.targets1, i.targets2 = i.targets2, i.targets1 45 | return [InteractSwapLayer(i_layer=i, swap_layer=self.copy())] 46 | if isinstance(next_layer, (ShiftCoordAnnotationLayer, DetObsAnnotationLayer)): 47 | return [next_layer, self] 48 | if isinstance(next_layer, SwapLayer): 49 | total_swaps = self.to_swap_dict() 50 | leftover_swaps = SwapLayer() 51 | for a, b in zip(next_layer.targets1, next_layer.targets2): 52 | a2 = total_swaps.get(a) 53 | b2 = total_swaps.get(b) 54 | if a2 is None and b2 is None: 55 | total_swaps[a] = b 56 | total_swaps[b] = a 57 | elif a2 == b and b2 == a: 58 | del total_swaps[a] 59 | del total_swaps[b] 60 | else: 61 | leftover_swaps.targets1.append(a) 62 | leftover_swaps.targets2.append(b) 63 | result = [] 64 | if total_swaps: 65 | new_layer = SwapLayer() 66 | for k, v in total_swaps.items(): 67 | if k < v: 68 | new_layer.targets1.append(k) 69 | new_layer.targets2.append(v) 70 | result.append(new_layer) 71 | if leftover_swaps.targets1: 72 | result.append(leftover_swaps) 73 | return result 74 | return [self, next_layer] 75 | -------------------------------------------------------------------------------- /src/gen/_layers/_transpile.py: -------------------------------------------------------------------------------- 1 | import stim 2 | 3 | from gen._layers._layer_circuit import LayerCircuit 4 | 5 | 6 | def transpile_to_z_basis_interaction_circuit( 7 | circuit: stim.Circuit, *, is_entire_circuit: bool = True 8 | ) -> stim.Circuit: 9 | """Converts to a circuit using CZ, ISWAP, and MZZ as appropriate. 10 | 11 | This method mostly focuses on inserting single qubit rotations to convert 12 | interactions into their Z basis variant. It also does some optimizations 13 | that remove redundant rotations which would tend to be introduced by this 14 | process. 15 | """ 16 | c = LayerCircuit.from_stim_circuit(circuit) 17 | c = c.with_qubit_coords_at_start() 18 | c = c.with_locally_optimized_layers() 19 | c = c.to_z_basis() 20 | c = c.with_rotations_rolled_from_end_of_loop_to_start_of_loop() 21 | c = c.with_locally_optimized_layers() 22 | c = c.with_clearable_rotation_layers_cleared() 23 | c = c.with_rotations_merged_earlier() 24 | c = c.with_rotations_before_resets_removed() 25 | if is_entire_circuit: 26 | c = c.with_irrelevant_tail_layers_removed() 27 | return c.to_stim_circuit() 28 | -------------------------------------------------------------------------------- /src/gen/_util_test.py: -------------------------------------------------------------------------------- 1 | import stim 2 | 3 | from gen._util import estimate_qubit_count_during_postselection, xor_sorted 4 | 5 | 6 | def test_xor_sorted(): 7 | assert xor_sorted([]) == [] 8 | assert xor_sorted([2]) == [2] 9 | assert xor_sorted([2, 3]) == [2, 3] 10 | assert xor_sorted([3, 2]) == [2, 3] 11 | assert xor_sorted([2, 2]) == [] 12 | assert xor_sorted([2, 2, 2]) == [2] 13 | assert xor_sorted([2, 2, 2, 2]) == [] 14 | assert xor_sorted([2, 2, 3]) == [3] 15 | assert xor_sorted([3, 2, 2]) == [3] 16 | assert xor_sorted([2, 3, 2]) == [3] 17 | assert xor_sorted([2, 3, 3]) == [2] 18 | assert xor_sorted([2, 3, 5, 7, 11, 13, 5]) == [2, 3, 7, 11, 13] 19 | 20 | 21 | def test_estimate_qubit_count_during_postselection(): 22 | assert ( 23 | estimate_qubit_count_during_postselection( 24 | stim.Circuit( 25 | """ 26 | QUBIT_COORDS(0, 0) 100 27 | H 55 28 | M 55 29 | """ 30 | ) 31 | ) 32 | == 0 33 | ) 34 | 35 | assert ( 36 | estimate_qubit_count_during_postselection( 37 | stim.Circuit( 38 | """ 39 | QUBIT_COORDS(0, 0) 100 40 | H 55 41 | M 55 42 | DETECTOR(0, 0, 0, 999) rec[-1] 43 | """ 44 | ) 45 | ) 46 | == 1 47 | ) 48 | 49 | assert ( 50 | estimate_qubit_count_during_postselection( 51 | stim.Circuit( 52 | """ 53 | QUBIT_COORDS(0, 0) 100 54 | H 55 56 55 | M 55 56 | DETECTOR(0, 0, 0, 999) rec[-1] 57 | """ 58 | ) 59 | ) 60 | == 2 61 | ) 62 | 63 | assert ( 64 | estimate_qubit_count_during_postselection( 65 | stim.Circuit( 66 | """ 67 | QUBIT_COORDS(0, 0) 100 68 | H 55 56 69 | M 55 70 | DETECTOR(0, 0, 0, 999) rec[-1] 71 | H 57 72 | """ 73 | ) 74 | ) 75 | == 2 76 | ) 77 | 78 | assert ( 79 | estimate_qubit_count_during_postselection( 80 | stim.Circuit( 81 | """ 82 | QUBIT_COORDS(0, 0) 100 83 | H 55 56 84 | M 55 85 | REPEAT 10 { 86 | H 58 87 | } 88 | DETECTOR(0, 0, 0, 999) rec[-1] 89 | H 57 90 | """ 91 | ) 92 | ) 93 | == 3 94 | ) 95 | -------------------------------------------------------------------------------- /src/gen/_viz_patch_svg_test.py: -------------------------------------------------------------------------------- 1 | import gen 2 | 3 | 4 | def test_patch_svg_runs(): 5 | patch = gen.Patch( 6 | tiles=[ 7 | gen.Tile( 8 | ordered_data_qubits=(None, 1j, None, 2j), 9 | measurement_qubit=(-0.5 + 1.5j), 10 | bases="Z", 11 | ), 12 | gen.Tile( 13 | ordered_data_qubits=(None, 0j, None, (1 + 0j)), 14 | measurement_qubit=(0.5 - 0.5j), 15 | bases="X", 16 | ), 17 | gen.Tile( 18 | ordered_data_qubits=(0j, (1 + 0j), 1j, (1 + 1j)), 19 | measurement_qubit=(0.5 + 0.5j), 20 | bases="Z", 21 | ), 22 | gen.Tile( 23 | ordered_data_qubits=(1j, 2j, (1 + 1j), (1 + 2j)), 24 | measurement_qubit=(0.5 + 1.5j), 25 | bases="X", 26 | ), 27 | gen.Tile( 28 | ordered_data_qubits=((1 + 0j), (1 + 1j), (2 + 0j), (2 + 1j)), 29 | measurement_qubit=(1.5 + 0.5j), 30 | bases="X", 31 | ), 32 | gen.Tile( 33 | ordered_data_qubits=((1 + 1j), (2 + 1j), (1 + 2j), (2 + 2j)), 34 | measurement_qubit=(1.5 + 1.5j), 35 | bases="Z", 36 | ), 37 | gen.Tile( 38 | ordered_data_qubits=((1 + 2j), None, (2 + 2j), None), 39 | measurement_qubit=(1.5 + 2.5j), 40 | bases="X", 41 | ), 42 | gen.Tile( 43 | ordered_data_qubits=((2 + 0j), None, (2 + 1j), None), 44 | measurement_qubit=(2.5 + 0.5j), 45 | bases="Z", 46 | ), 47 | ] 48 | ) 49 | svg_content = gen.patch_svg_viewer([patch]) 50 | assert svg_content is not None 51 | -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "chromobius/commands/main_all.h" 16 | 17 | int main(int argc, const char **argv) { 18 | return chromobius::main(argc, argv); 19 | } 20 | -------------------------------------------------------------------------------- /test_data/rep_code_d9_transit_p10.stim: -------------------------------------------------------------------------------- 1 | QUBIT_COORDS(0, 0) 0 2 | QUBIT_COORDS(1, 0) 1 3 | QUBIT_COORDS(2, 0) 2 4 | QUBIT_COORDS(3, 0) 3 5 | QUBIT_COORDS(4, 0) 4 6 | QUBIT_COORDS(5, 0) 5 7 | QUBIT_COORDS(6, 0) 6 8 | QUBIT_COORDS(7, 0) 7 9 | QUBIT_COORDS(8, 0) 8 10 | MPP Z0 11 | OBSERVABLE_INCLUDE(0) rec[-1] 12 | MPP Z0*Z1 Z1*Z2 Z2*Z3 Z3*Z4 Z4*Z5 Z5*Z6 Z6*Z7 Z7*Z8 13 | TICK 14 | DEPOLARIZE1(0.1) 0 1 2 3 4 5 6 7 8 15 | TICK 16 | MPP Z0*Z1 Z1*Z2 Z2*Z3 Z3*Z4 Z4*Z5 Z5*Z6 Z6*Z7 Z7*Z8 17 | DETECTOR(0.5, 0, 0, 3) rec[-16] rec[-8] 18 | DETECTOR(1.5, 0, 0, 3) rec[-15] rec[-7] 19 | DETECTOR(2.5, 0, 0, 3) rec[-14] rec[-6] 20 | DETECTOR(3.5, 0, 0, 3) rec[-13] rec[-5] 21 | DETECTOR(4.5, 0, 0, 3) rec[-12] rec[-4] 22 | DETECTOR(5.5, 0, 0, 3) rec[-11] rec[-3] 23 | DETECTOR(6.5, 0, 0, 3) rec[-10] rec[-2] 24 | DETECTOR(7.5, 0, 0, 3) rec[-9] rec[-1] 25 | MPP Z0 26 | OBSERVABLE_INCLUDE(0) rec[-1] 27 | -------------------------------------------------------------------------------- /test_data/rep_code_rbrrr_d9_transit_p10.stim: -------------------------------------------------------------------------------- 1 | QUBIT_COORDS(0, 0) 0 2 | QUBIT_COORDS(1, 0) 1 3 | QUBIT_COORDS(2, 0) 2 4 | QUBIT_COORDS(3, 0) 3 5 | QUBIT_COORDS(4, 0) 4 6 | QUBIT_COORDS(5, 0) 5 7 | QUBIT_COORDS(6, 0) 6 8 | QUBIT_COORDS(7, 0) 7 9 | QUBIT_COORDS(8, 0) 8 10 | MPP Z0 11 | OBSERVABLE_INCLUDE(0) rec[-1] 12 | MPP Z0*Z1 Z1*Z2 Z2*Z3 Z3*Z4 Z4*Z5 Z5*Z6 Z6*Z7 Z7*Z8 13 | TICK 14 | DEPOLARIZE1(0.1) 0 1 2 3 4 5 6 7 8 15 | TICK 16 | MPP Z0*Z1 Z1*Z2 Z2*Z3 Z3*Z4 Z4*Z5 Z5*Z6 Z6*Z7 Z7*Z8 17 | DETECTOR(0.5, 0, 0, 3) rec[-16] rec[-8] 18 | DETECTOR(1.5, 0, 0, 5) rec[-15] rec[-7] 19 | DETECTOR(2.5, 0, 0, 3) rec[-14] rec[-6] 20 | DETECTOR(3.5, 0, 0, 3) rec[-13] rec[-5] 21 | DETECTOR(4.5, 0, 0, 3) rec[-12] rec[-4] 22 | DETECTOR(5.5, 0, 0, 3) rec[-11] rec[-3] 23 | DETECTOR(6.5, 0, 0, 5) rec[-10] rec[-2] 24 | DETECTOR(7.5, 0, 0, 3) rec[-9] rec[-1] 25 | MPP Z0 26 | OBSERVABLE_INCLUDE(0) rec[-1] 27 | -------------------------------------------------------------------------------- /test_data/rep_code_rg_d9_transit_p10.stim: -------------------------------------------------------------------------------- 1 | QUBIT_COORDS(0, 0) 0 2 | QUBIT_COORDS(1, 0) 1 3 | QUBIT_COORDS(2, 0) 2 4 | QUBIT_COORDS(3, 0) 3 5 | QUBIT_COORDS(4, 0) 4 6 | QUBIT_COORDS(5, 0) 5 7 | QUBIT_COORDS(6, 0) 6 8 | QUBIT_COORDS(7, 0) 7 9 | QUBIT_COORDS(8, 0) 8 10 | MPP Z0 11 | OBSERVABLE_INCLUDE(0) rec[-1] 12 | MPP Z0*Z1 Z1*Z2 Z2*Z3 Z3*Z4 Z4*Z5 Z5*Z6 Z6*Z7 Z7*Z8 13 | TICK 14 | DEPOLARIZE1(0.1) 0 1 2 3 4 5 6 7 8 15 | TICK 16 | MPP Z0*Z1 Z1*Z2 Z2*Z3 Z3*Z4 Z4*Z5 Z5*Z6 Z6*Z7 Z7*Z8 17 | DETECTOR(0.5, 0, 0, 3) rec[-16] rec[-8] 18 | DETECTOR(1.5, 0, 0, 4) rec[-15] rec[-7] 19 | DETECTOR(2.5, 0, 0, 3) rec[-14] rec[-6] 20 | DETECTOR(3.5, 0, 0, 4) rec[-13] rec[-5] 21 | DETECTOR(4.5, 0, 0, 3) rec[-12] rec[-4] 22 | DETECTOR(5.5, 0, 0, 4) rec[-11] rec[-3] 23 | DETECTOR(6.5, 0, 0, 3) rec[-10] rec[-2] 24 | DETECTOR(7.5, 0, 0, 4) rec[-9] rec[-1] 25 | MPP Z0 26 | OBSERVABLE_INCLUDE(0) rec[-1] 27 | -------------------------------------------------------------------------------- /tools/doctest_proper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """Runs doctests on a module, including any objects imported into the module.""" 4 | import argparse 5 | import doctest 6 | import inspect 7 | import sys 8 | from typing import Dict 9 | 10 | 11 | SKIPPED_FIELDS = { 12 | '__base__', 13 | '__name__', 14 | '__path__', 15 | '__spec__', 16 | '__version__', 17 | '__package__', 18 | '__subclasshook__', 19 | '__abstractmethods__', 20 | '__bases__', 21 | '__basicsize__', 22 | '__class__', 23 | '__builtins__', 24 | '__cached__', 25 | '__doc__', 26 | '__loader__', 27 | '__file__', 28 | } 29 | 30 | 31 | def no_really_i_have_a_doc_why_is_this_needed_argh(v: object, fullname: str) -> object: 32 | def so_much_doc(): 33 | pass 34 | so_much_doc.__doc__ = v.__doc__ 35 | so_much_doc.__qualname__ = fullname 36 | return so_much_doc 37 | 38 | 39 | def gen(*, obj: object, fullname: str, out: Dict[str, object]) -> None: 40 | if obj is None: 41 | return 42 | if inspect.isfunction(obj) or inspect.ismethod(obj) or inspect.isbuiltin(obj) or inspect.isroutine(obj): 43 | if hasattr(obj, '__doc__'): 44 | out[fullname] = obj 45 | return 46 | if not inspect.ismodule(obj) and not inspect.isclass(obj): 47 | if hasattr(obj, '__doc__'): 48 | out[fullname] = no_really_i_have_a_doc_why_is_this_needed_argh(obj, fullname) 49 | return 50 | if hasattr(obj, '__doc__'): 51 | out[fullname] = obj 52 | 53 | for sub_name in dir(obj): 54 | if sub_name in SKIPPED_FIELDS: 55 | continue 56 | if sub_name.startswith('__pybind11_module'): 57 | continue 58 | sub_obj = getattr(obj, sub_name, None) 59 | if inspect.ismodule(sub_obj): 60 | continue 61 | sub_full_name = fullname + "." + sub_name 62 | gen(obj=sub_obj, fullname=sub_full_name, out=out) 63 | 64 | 65 | def main(): 66 | parser = argparse.ArgumentParser() 67 | parser.add_argument( 68 | "--module", 69 | type=str, 70 | required=True, 71 | nargs='+', 72 | help="The module to test. " 73 | "This module will be imported, " 74 | "its imported values will be recursively explored, " 75 | "and doctests will be run on them.") 76 | parser.add_argument( 77 | '--import', 78 | default=(), 79 | nargs='*', 80 | type=str, 81 | help="Modules to import for each doctest.") 82 | args = parser.parse_args() 83 | 84 | globs = { 85 | k: __import__(k) for k in getattr(args, 'import') 86 | } 87 | any_failed = False 88 | for module_name in args.module: 89 | module = __import__(module_name) 90 | out = {} 91 | gen(obj=module, fullname=module_name, out=out) 92 | module.__test__ = {k: v for k, v in out.items()} 93 | if doctest.testmod(module, globs=globs).failed: 94 | any_failed = True 95 | if any_failed: 96 | sys.exit(1) 97 | 98 | 99 | if __name__ == '__main__': 100 | main() 101 | -------------------------------------------------------------------------------- /tools/fuse_xz_data: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import sys 5 | from typing import List 6 | 7 | import sinter 8 | 9 | 10 | def get_basis(stat: sinter.TaskStats) -> str | None: 11 | metadata = dict(sorted(stat.json_metadata.items())) 12 | if 'b' in metadata: 13 | return metadata['b'] 14 | if 'c' in metadata and metadata['c'][-2:] in ['_X', '_Z']: 15 | return metadata['c'][:-1] 16 | return None 17 | 18 | 19 | def sort_by_all_except_basis(stat: sinter.TaskStats) -> str: 20 | metadata = dict(sorted(stat.json_metadata.items())) 21 | if 'b' in metadata: 22 | del metadata['b'] 23 | if 'c' in metadata and metadata['c'][-2:] in ['_X', '_Z']: 24 | metadata['c'] = metadata['c'][:-2] 25 | return repr(metadata) + ":" + stat.decoder 26 | 27 | 28 | def main(): 29 | parser = argparse.ArgumentParser() 30 | parser.add_argument( 31 | "--stats", 32 | type=str, 33 | required=True, 34 | ) 35 | args = parser.parse_args() 36 | 37 | stats: List[sinter.TaskStats] = sinter.stats_from_csv_files(args.stats) 38 | 39 | print(sinter.CSV_HEADER) 40 | for _, pair in sinter.group_by(stats, key=sort_by_all_except_basis).items(): 41 | for e in pair: 42 | print(e) 43 | if len(pair) > 2: 44 | raise ValueError(f"More than two bases:\n " + '\n '.join(repr(e) for e in pair)) 45 | if len(pair) == 1: 46 | if get_basis(pair[0]) is None: 47 | continue 48 | print("WARNING: duplicating unpaired value with metadata ", pair[0].json_metadata, "and decoder", pair[0].decoder, file=sys.stderr) 49 | a, b = pair[0], pair[0] 50 | else: 51 | a, b = pair 52 | if a.shots > b.shots: 53 | a, b = b, a 54 | assert a.discards == b.discards == 0 55 | new_errors = round((1 - (1 - a.errors / a.shots) * (1 - b.errors / b.shots)) * a.shots) 56 | 57 | new_metadata = dict(a.json_metadata) 58 | if 'b' in a.json_metadata: 59 | new_metadata['b'] = 'XZ' 60 | elif 'c' in a.json_metadata and a.json_metadata['c'][-2:] in ['_X', '_Z']: 61 | new_metadata['c'] = a.json_metadata['c'][:-2] + '_XZ' 62 | else: 63 | raise NotImplementedError(f"Missed basis:\n " + '\n '.join(repr(e) for e in pair)) 64 | combo = sinter.TaskStats( 65 | strong_id=a.strong_id + '*' + b.strong_id, 66 | decoder=a.decoder, 67 | json_metadata=new_metadata, 68 | shots=a.shots, 69 | errors=new_errors, 70 | discards=0, 71 | seconds=a.seconds + b.seconds, 72 | ) 73 | print(combo) 74 | 75 | 76 | if __name__ == '__main__': 77 | main() 78 | -------------------------------------------------------------------------------- /tools/gen_chromobius_api_reference.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Iterates over modules and classes, listing their attributes and methods in markdown. 5 | """ 6 | 7 | import chromobius 8 | 9 | import sys 10 | 11 | from util_gen_stub_file import generate_documentation 12 | 13 | 14 | def main(): 15 | version = chromobius.__version__ 16 | if "dev" in version or version == "VERSION_INFO" or "-dev" in sys.argv: 17 | version = "(Development Version)" 18 | is_dev = True 19 | else: 20 | version = "v" + version 21 | is_dev = False 22 | objects = [ 23 | obj 24 | for obj in generate_documentation(obj=chromobius, full_name="chromobius", level=0) 25 | if all('[DEPRECATED]' not in line for line in obj.lines) 26 | ] 27 | global_methods = [obj for obj in objects if obj.full_name.islower()] 28 | not_global_methods = [obj for obj in objects if not obj.full_name.islower()] 29 | 30 | print(f"# Chromobius {version} API Reference") 31 | print() 32 | print("## Index") 33 | print("- ``") 34 | for obj in global_methods: 35 | level = obj.level + 1 36 | print((level - 1) * " " + f"- [`{obj.full_name}`](#{obj.full_name})") 37 | for obj in not_global_methods: 38 | level = obj.level 39 | print((level - 1) * " " + f"- [`{obj.full_name}`](#{obj.full_name})") 40 | 41 | print(f''' 42 | ```python 43 | # Types used by the method definitions. 44 | from typing import overload, TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple, Union 45 | import io 46 | import pathlib 47 | import numpy as np 48 | ``` 49 | '''.strip()) 50 | 51 | for obj in global_methods + not_global_methods: 52 | print() 53 | print(f'') 54 | print("```python") 55 | print(f'# {obj.full_name}') 56 | print() 57 | if len(obj.full_name.split('.')) > 2: 58 | print(f'# (in class {".".join(obj.full_name.split(".")[:-1])})') 59 | else: 60 | print(f'# (at top-level in the chromobius module)') 61 | print('\n'.join(obj.lines)) 62 | print("```") 63 | 64 | 65 | if __name__ == '__main__': 66 | main() 67 | -------------------------------------------------------------------------------- /tools/gen_chromobius_stub_file.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Produces a .pyi file for chromobius, describing the contained classes and functions. 5 | """ 6 | 7 | import chromobius 8 | import sys 9 | 10 | from util_gen_stub_file import generate_documentation 11 | 12 | 13 | def main(): 14 | version = chromobius.__version__ 15 | if "dev" in version or version == "VERSION_INFO" or "-dev" in sys.argv: 16 | version = "(Development Version)" 17 | else: 18 | version = "v" + version 19 | print(f''' 20 | """Chromobius {version}: an implementation of the mobius color code decoder.""" 21 | # (This a stubs file describing the classes and methods in stim.) 22 | from __future__ import annotations 23 | from typing import overload, TYPE_CHECKING, Any, Iterable 24 | if TYPE_CHECKING: 25 | import io 26 | import pathlib 27 | import numpy as np 28 | import stim 29 | import sinter 30 | import chromobius 31 | __version__: str 32 | '''.strip()) 33 | 34 | for obj in generate_documentation(obj=chromobius, full_name="chromobius", level=-1): 35 | text = '\n'.join((" " * obj.level + line).rstrip() 36 | for paragraph in obj.lines 37 | for line in paragraph.splitlines()) 38 | assert "stim::" not in text, "CONTAINS C++ STYLE TYPE SIGNATURE!!:\n" + text 39 | print(text) 40 | 41 | 42 | if __name__ == '__main__': 43 | main() 44 | -------------------------------------------------------------------------------- /tools/overwrite_dev_versions_with_date.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | ######################################################### 4 | # Sets version numbers to a date-based dev version. 5 | # 6 | # Does nothing if not on a dev version. 7 | ######################################################### 8 | # Example usage (from repo root): 9 | # 10 | # ./dev/overwrite_dev_versions_with_date.sh 11 | ######################################################### 12 | 13 | import os 14 | import pathlib 15 | import re 16 | import subprocess 17 | 18 | 19 | def main(): 20 | os.chdir(pathlib.Path(__file__).parent) 21 | os.chdir(subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode().strip()) 22 | 23 | # Generate dev version starting from major.minor version. 24 | # (Requires the existing version to have a 'dev' suffix.) 25 | # (Uses the timestamp of the HEAD commit, to ensure consistency when run multiple times.) 26 | with open('setup.py') as f: 27 | maj_min_version_line, = [line for line in f.read().splitlines() if re.match("^__version__ = '[^']+'", line)] 28 | maj_version, min_version, patch = maj_min_version_line.split()[-1].strip("'").split('.') 29 | if 'dev' not in patch: 30 | return # Do nothing for non-dev versions. 31 | timestamp = subprocess.check_output(['git', 'show', '-s', '--format=%ct', 'HEAD']).decode().strip() 32 | new_version = f"{maj_version}.{min_version}.dev{timestamp}" 33 | 34 | # Overwrite existing versions. 35 | package_setup_files = [ 36 | "setup.py", 37 | ] 38 | for path in package_setup_files: 39 | with open(path) as f: 40 | content = f.read() 41 | assert maj_min_version_line in content 42 | content = content.replace(maj_min_version_line, f"__version__ = '{new_version}'") 43 | with open(path, 'w') as f: 44 | print(content, file=f, end='') 45 | 46 | 47 | if __name__ == '__main__': 48 | main() 49 | -------------------------------------------------------------------------------- /tools/regen_docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | ######################################################################### 5 | # Regenerates doc files using the installed version of stim. 6 | ######################################################################### 7 | 8 | # Get to this script's git repo root. 9 | cd "$( dirname "${BASH_SOURCE[0]}" )" 10 | cd "$(git rev-parse --show-toplevel)" 11 | 12 | python tools/gen_chromobius_api_reference.py -dev > doc/chromobius_api_reference.md 13 | python tools/gen_chromobius_stub_file.py -dev > doc/chromobius.pyi 14 | -------------------------------------------------------------------------------- /tools/regen_file_lists.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2023 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -e 17 | 18 | ######################################################################### 19 | # Regenerate file_lists 20 | ######################################################################### 21 | 22 | if [ "$#" -ne 1 ]; then 23 | FOLDER=file_lists 24 | else 25 | FOLDER=$1 26 | fi 27 | 28 | # Get to this script's git repo root. 29 | cd "$( dirname "${BASH_SOURCE[0]}" )" 30 | cd "$(git rev-parse --show-toplevel)" 31 | 32 | # LC_ALL=C forces sorting to happen by byte value 33 | find src | grep "\\.\\(cc\\|h\\)$" | grep -v "\\.\(test\|perf\|pybind\)\\.\\(cc\\|h\\)$" | grep -v "main\\.cc$" | LC_ALL=C sort > "${FOLDER}/source_files_no_main" 34 | find src | grep "\\.test\\.\\(cc\\|h\\)$" | LC_ALL=C sort > "${FOLDER}/test_files" 35 | find src | grep "\\.perf\\.\\(cc\\|h\\)$" | LC_ALL=C sort > "${FOLDER}/perf_files" 36 | find src | grep "\\.pybind\\.\\(cc\\|h\\)$" | LC_ALL=C sort > "${FOLDER}/pybind_files" 37 | 38 | # Regenerate 'chromobius.h' to include all relevant headers. 39 | { 40 | echo "#ifndef _CHROMOBIUS_H"; 41 | echo "#define _CHROMOBIUS_H"; 42 | echo "/// WARNING: THE chromobius C++ API MAKES NO COMPATIBILITY GUARANTEES."; 43 | echo "/// It may change arbitrarily and catastrophically from minor version to minor version."; 44 | echo "/// If you need a stable API, use chromobius's Python API."; 45 | find src | grep "\\.h$" | grep -v "\\.\(test\|perf\|pybind\)\\.h$" | grep -v "src/chromobius\\.h" | LC_ALL=C sort | sed 's/src\/\(.*\)/#include "\1"/g'; 46 | echo "#endif"; 47 | } > src/chromobius.h 48 | -------------------------------------------------------------------------------- /tools/sinter_plot_print: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pathlib 4 | import subprocess 5 | import sys 6 | 7 | try: 8 | subprocess.check_output([ 9 | 'sinter', 10 | 'plot', 11 | *sys.argv[1:], 12 | ], stderr=sys.stderr) 13 | except: 14 | sys.exit(1) 15 | 16 | if '--out' in sys.argv: 17 | path = sys.argv[sys.argv.index('--out') + 1] 18 | print(f'wrote file://{pathlib.Path(path).absolute()}') 19 | -------------------------------------------------------------------------------- /tools/step1_generate_circuits.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2023 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | 17 | set -e 18 | set -o pipefail 19 | 20 | parallel --ungroup ./tools/gen_circuits \ 21 | --style {1} \ 22 | --out_dir out/circuits_matchable \ 23 | --noise_model uniform \ 24 | --rounds "d*4" \ 25 | --diameter {2} \ 26 | --noise_strength 0.001 \ 27 | ::: surface_code_X surface_code_Z \ 28 | ::: 3 5 7 9 11 13 29 | 30 | 31 | parallel --ungroup ./tools/gen_circuits \ 32 | --style {1} \ 33 | --out_dir out/circuits_unmatchable \ 34 | --noise_model uniform \ 35 | --rounds "d*4" \ 36 | --diameter {2} \ 37 | --noise_strength 0.001 \ 38 | ::: midout_color_code_488_X midout_color_code_488_Z \ 39 | ::: 3 5 7 9 11 13 15 17 19 40 | 41 | 42 | parallel --ungroup ./tools/gen_circuits \ 43 | --style {1} \ 44 | --out_dir out/circuits_unmatchable \ 45 | --noise_model uniform \ 46 | --rounds "d*4" \ 47 | --diameter {2} \ 48 | --noise_strength {3} \ 49 | ::: midout_color_code_X midout_color_code_Z superdense_color_code_X superdense_color_code_Z \ 50 | ::: 3 5 7 9 11 13 15 17 19 \ 51 | ::: 0.0001 0.0002 0.0003 0.0005 0.0007 0.001 0.002 0.003 0.005 0.006 0.007 0.008 0.01 52 | 53 | 54 | parallel --ungroup ./tools/gen_circuits \ 55 | --style {1} \ 56 | --out_dir out/circuits_unmatchable \ 57 | --noise_model uniform \ 58 | --rounds "d*4" \ 59 | --diameter {2} \ 60 | --noise_strength 0.01 \ 61 | ::: phenom_toric_color_code \ 62 | ::: 6 12 18 24 63 | parallel --ungroup ./tools/gen_circuits \ 64 | --style {1} \ 65 | --out_dir out/circuits_matchable \ 66 | --noise_model uniform \ 67 | --rounds "d*4" \ 68 | --diameter {2} \ 69 | --noise_strength 0.01 \ 70 | ::: phenom_ablated_toric_color_code \ 71 | ::: 6 12 18 24 72 | 73 | 74 | parallel --ungroup ./tools/gen_circuits \ 75 | --style {1} \ 76 | --out_dir out/circuits_unmatchable \ 77 | --noise_model uniform \ 78 | --rounds "d*4" \ 79 | --diameter {2} \ 80 | --noise_strength 0.01 \ 81 | ::: phenom_rep_code phenom_surface_code phenom_color_code phenom_pyramid_code phenom_color_code_488 phenom_color2surface_code \ 82 | ::: 3 5 7 9 11 13 83 | 84 | 85 | parallel --ungroup ./tools/gen_circuits \ 86 | --style transit_color_code \ 87 | --out_dir out/circuits_unmatchable \ 88 | --noise_model bitflip \ 89 | --rounds 1 \ 90 | --diameter {1} \ 91 | --noise_strength {2} \ 92 | ::: 11 15 19 23 27 31 \ 93 | ::: 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.1 94 | -------------------------------------------------------------------------------- /tools/step2_collect_stats.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2023 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | 17 | set -e 18 | set -o pipefail 19 | trap "exit 1" INT 20 | 21 | sinter collect \ 22 | --circuits out/circuits_unmatchable/*.stim out/circuits_matchable/*.stim \ 23 | --save_resume_filepath out/stats.csv \ 24 | --decoders chromobius \ 25 | --processes auto \ 26 | --max_shots 100_000_000 \ 27 | --max_errors 1000 \ 28 | --metadata_func auto \ 29 | --custom_decoders "chromobius:sinter_decoders" 30 | 31 | 32 | sinter collect \ 33 | --circuits out/circuits_matchable/*.stim \ 34 | --save_resume_filepath out/stats.csv \ 35 | --decoders chromobius pymatching sparse_blossom_correlated \ 36 | --processes auto \ 37 | --max_shots 100_000_000 \ 38 | --max_errors 1000 \ 39 | --metadata_func auto \ 40 | --custom_decoders "chromobius:sinter_decoders" "gqec:make_custom_sinter_decoders_dict" 41 | --------------------------------------------------------------------------------