PyMatching: Minimum Weight Perfect Matching Decoder

repository·master·Indexed 43 days ago

https://github.com/oscarhiggott/pymatching

A high-performance Python/C++ library for decoding quantum error correcting (QEC) codes using the Minimum Weight Perfect Matching (MWPM) decoder. It features a sparse blossom algorithm for fast syndrome decoding of surface, subsystem, honeycomb, and 2D hyperbolic codes. Version 2.3+ includes correlated matching to improve accuracy with hyperedge errors. It integrates with Stim for circuit generation and error model analysis.

Tokens
13.2K
Snippets
41
Records
50
Agent score
55%

What's inside PyMatching

  1. Overview of PyMatching

    master

    PyMatching is a high-performance Python/C++ library designed for decoding quantum error correcting (QEC) codes using the Minimum Weight Perfect Matching (MWPM) decoder. It is optimized for speed using a 'sparse blossom' algorithm implementation that is significantly faster than previous versions and alternatives like NetworkX.

    Key features include:

    • MWPM Decoding: Finds the most probable set of errors assuming they are independent and graphlike.
    • Broad Code Support: Works with surface codes, subsystem codes, honeycomb codes, and 2D hyperbolic codes.
    • High Performance: Version 2 uses a sparse blossom algorithm that is 100-1000x faster than previous versions and scales roughly linearly with the number of nodes.
    • Correlated Matching: Supports correlated matching (as of v2.3) to improve accuracy in scenarios with hyperedge errors (e.g., $Y$ errors in surface codes).
  2. What is Correlated Matching in PyMatching?

    master

    Correlated matching is a feature (available since v2.3) that provides better accuracy than standard uncorrelated matching when hyperedge errors are present. In many QEC problems, hyperedge errors (like a $Y$ error) can be decomposed into graphlike edges that are correlated.

    PyMatching implements a "two-pass" correlated matching decoder:

    1. First Pass: Runs a standard (uncorrelated) sparse blossom to predict a set of edges.
    2. Reweighting: Assumes those edges occurred and reweights edges that are correlated with them.
    3. Second Pass: Runs the matching algorithm again on the reweighted graph to produce the final result.
  3. Decode Stim circuits with PyMatching

    master

    PyMatching can be used to decode quantum error correction circuits generated by Stim. The recommended workflow is:

    1. Generate a Stim circuit.
    2. Create a Detector Error Model (DEM): Use circuit.detector_error_model(decompose_errors=True). Setting decompose_errors=True is critical as it ensures the DEM is graphlike (each error mechanism affects one or two detection events), which is required by PyMatching.
    3. Load the model into PyMatching: Use pymatching.Matching.from_detector_error_model(model).
    4. Sample syndromes: Use a Stim DetectorSampler to generate syndromes and actual logical observables.
    5. Decode: Use matching.decode_batch(syndrome) to predict logical observables and compare them against the actual ones to calculate error rates.

    To enable correlated matching, set enable_correlations=True during both the Matching object construction and the decode_batch call.

    import numpy as np
    import stim
    import pymatching
    
    # 1. Generate circuit
    circuit = stim.Circuit.generated(
        "surface_code:rotated_memory_x", 
        distance=5, 
        rounds=5, 
        after_clifford_depolarization=0.005
    )
    
    # 2. Create graphlike DEM and load into PyMatching
    model = circuit.detector_error_model(decompose_errors=True)
    matching = pymatching.Matching.from_detector_error_model(model)
    
    # 3. Sample shots
    sampler = circuit.compile_detector_sampler()
    syndrome, actual_observables = sampler.sample(shots=1000, separate_observables=True)
    
    # 4. Decode
    predicted_observables = matching.decode_batch(syndrome)
    num_errors = np.sum(np.any(predicted_observables != actual_observables, axis=1))
    print(f"Errors: {num_errors}")
  4. Measure decoding performance (microseconds per shot) for surface codes

    master

    To accurately measure the decoding time per shot in microseconds using pymatching.Matching.decode_batch, follow these steps:

    1. Generate a stim.Circuit and its corresponding stim.DetectorErrorModel using circuit.detector_error_model(decompose_errors=True).
    2. Initialize the decoder using pymatching.Matching.from_detector_error_model(dem).
    3. Sample shots using a stim.DetectorSampler.
    4. Crucial: Run decode_batch on a single shot first. This ensures the internal C++ representation of the matching graph is fully cached before timing begins.
    5. Time the execution of decode_batch on the full batch of shots using time.time().

    Note: For surface code benchmarks, it is standard practice to include both X-type and Z-type detectors to accurately represent fault-tolerant workloads, even if only one basis is being measured.

    import time
    import stim
    import pymatching
    
    
    def time_surface_code_circuit(distance: int, p: float, num_shots: int = 10000) -> float:
        circuit = stim.Circuit.generated(
            "surface_code:rotated_memory_x",
            distance=distance,
            rounds=distance,
            after_clifford_depolarization=p,
            before_round_data_depolarization=p,
            before_measure_flip_probability=p,
            after_reset_flip_probability=p
        )
        dem = circuit.detector_error_model(decompose_errors=True)
        matching = pymatching.Matching.from_detector_error_model(dem)
        sampler = circuit.compile_detector_sampler()
        shots, actual_observables = sampler.sample(shots=num_shots, separate_observables=True)
        
        # Decode one shot first to ensure internal C++ representation of the matching graph is fully cached
        matching.decode_batch(shots[0:1, :])
        
        # Now time decoding the batch
        t0 = time.time()
        matching.decode_batch(shots)
        t1 = time.time()
        
        microseconds_per_shot = 1e6*(t1-t0)/num_shots
        return microseconds_per_shot
  5. Link to pymatching with Bazel

    master

    To use pymatching in a Bazel project, define the repository in your WORKSPACE file and add the dependency to your BUILD file.

    # In WORKSPACE
    load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
    git_repository(
        name = "pymatching",
        commit = "eea3501a1c1e2dc2b6d34221c6f1eb51edb06e1e",
        remote = "https://github.com/oscarhiggott/pymatching.git",
    )
    
    # In BUILD
    cc_binary(
        name = "your_project",
        ...
        deps = [
            "@pymatching//:libpymatching",
        ],
    )
  6. Decode a problem using the pymatching command line tool

    master

    You can use the pymatching predict command to solve decoding problems. This typically involves a workflow where you first generate a circuit and error model using stim, then pass the detection events to pymatching.

    Workflow steps:

    1. Generate Stim circuit: Use stim gen to create a .stim file.
    2. Analyze errors: Use stim analyze_errors to create a .dem (error model) file.
    3. Detect events: Use stim detect to generate detection events (e.g., in b8 format) and actual observation flips.
    4. Predict with PyMatching: Use pymatching predict to generate predicted observation flips.

    Example command:

    pmatching predict \
        --dem error_model.dem \
        --in detection_events.b8 \
        --in_format b8 \
        --out predicted_obs_flips.01 \
        --out_format 01
  7. Link to pymatching with CMake

    master

    To use pymatching as a dependency in a CMake project, use FetchContent to pull the repository and then link against libpymatching.

    FetchContent_Declare(pymatching
            GIT_REPOSITORY https://github.com/oscarhiggott/pymatching.git
            GIT_TAG eea3501a1c1e2dc2b6d34221c6f1eb51edb06e1e)
    FetchContent_GetProperties(pymatching)
    if(NOT pymatching_POPULATED)
      FetchContent_Populate(pymatching)
      add_subdirectory(${pymatching_SOURCE_DIR})
    endif()
    
    # ... later in your project ...
    target_link_libraries(YOURLIBRARY libpymatching)
  8. Create a decoding animation using the pymatching command line tool

    master

    The pymatching animate command produces SVG animation frames of the decoding process. These frames can then be rendered into a video using tools like ffmpeg.

    Steps:

    1. Generate data: Use stim to create a .stim circuit, a .dem error model, and b8 formatted detection events.
    2. Generate frames: Run pymatching animate specifying the input files and output directory.
    3. Render video: Use ffmpeg to convert the SVG frames into an .mp4 video.

    Example animation command:

    pmatching animate \
        --dem_in error_model.dem \
        --dets_in detection_events.b8 \
        --dets_in_format b8 \
        --held_frames_per_event 1 \
        --held_frames_at_start 10 \
        --held_frames_at_end 10 \
        --max_growth_between_frames 25 \
        --pixels_per_unit_length 20 \
        --out_dir out_frames
  9. Run C++ unit tests and performance benchmarks using cmake

    master

    To run the C++ test suites, use the following commands:

    Unit Tests:

    cmake .
    make pymatching_tests
    ./pymatching_tests

    Performance Benchmarks:

    cmake .
    make pymatching_perf
    ./pymatching_perf

    Benchmark Flags:

    • --target_seconds=#: Change the duration of each benchmark.
    • --only=name: Run a specific benchmark by name.
    • --only=prefix*: Run all benchmarks starting with a specific prefix.