Skip to main content

ILP-based Quantum Error Correction Decoder

Project description

ILPQEC

CI codecov

ILPQEC is a Python package for maximum-likelihood quantum error correction decoding using integer linear programming (ILP). It turns parity-check matrices or Stim DetectorErrorModels into an ILP and solves it with a built-in backend out of the box. It is aimed at correctness-focused baselines, solver comparisons, and small-to-medium code studies rather than high-throughput production decoding.

Documentation: https://nzy1997.github.io/ILPQEC/

Scope and Highlights

What it does well:

  • Out-of-the-box decoding with minimal setup.
  • Inputs from parity-check matrices or Stim DetectorErrorModel.
  • Maximum-likelihood decoding via weights or error probabilities.
  • PyMatching-like API for easy experimentation.

When it is not a fit:

  • Large code distances or high-shot workloads where ILP scaling dominates; use MWPM/BPOSD for throughput.

Installation

Python Environment Setup

ILPQEC supports Python 3.9+ (3.9–3.12 recommended). If you plan to use Gurobi, install Python < 3.13 because gurobipy wheels are not available on 3.13 at the moment.

Create and activate a virtual environment:

uv venv
source .venv/bin/activate
uv pip install --upgrade pip

On Windows:

uv venv
.venv\\Scripts\\activate
uv pip install --upgrade pip

Install the Package

PyPI package and import name: ilpqec.

# Basic installation
uv pip install ilpqec

# With Stim support
uv pip install ilpqec[stim]

# With sinter integration
uv pip install ilpqec[sinter]

# With SciPy sparse-matrix support
uv pip install ilpqec[scipy]

Development Setup

# Clone the repository
git clone https://github.com/nzy1997/ILPQEC
cd ILPQEC

# Create virtual environment (using uv)
uv venv
source .venv/bin/activate

# Install with dev dependencies
uv pip install -e ".[dev]"

Running Tests Locally

# Run all tests
uv run pytest tests/ -v

# Run specific test file
uv run pytest tests/test_decoder.py -v

# Run a quick functionality check
uv run python main.py

Running Examples

uv run python examples/basic_usage.py
uv run python examples/surface_code_example.py
uv run --python benchmark/.venv/bin/python benchmark/benchmark_decoders.py \
  --shots 10000 --distance 3 --rounds 3 --noise 0.01

Serve Docs Locally

# From the repo root
uv pip install -e ".[docs]"
uv run mkdocs serve

Quick Start

Parity-Check Matrix Decoding

import numpy as np
from ilpqec import Decoder

# Define a simple repetition code parity-check matrix
H = np.array([
    [1, 1, 0, 0, 0],
    [0, 1, 1, 0, 0],
    [0, 0, 1, 1, 0],
    [0, 0, 0, 1, 1],
])

# Create decoder
decoder = Decoder.from_parity_check_matrix(H)

# Decode a syndrome
syndrome = [1, 0, 0, 1]
correction = decoder.decode(syndrome)
print(f"Correction: {correction}")

Note: passing SciPy sparse matrices requires scipy to be installed (e.g., uv pip install ilpqec[scipy]).

Stim DetectorErrorModel Decoding

import stim
from ilpqec import Decoder

# Generate a surface code circuit
circuit = stim.Circuit.generated(
    "surface_code:rotated_memory_x",
    distance=3,
    rounds=3,
    after_clifford_depolarization=0.01
)

# Get detector error model
dem = circuit.detector_error_model(decompose_errors=True)

# Create decoder
decoder = Decoder.from_stim_dem(dem)

# Sample and decode
sampler = circuit.compile_detector_sampler()
detection_events, observables = sampler.sample(shots=100, separate_observables=True)

for i in range(10):
    _, predicted_obs = decoder.decode(detection_events[i])
    print(f"Shot {i}: predicted={predicted_obs}, actual={observables[i]}")

Stim DEM Support Notes

  • Only error(p) lines are parsed; tags in error[...] are ignored. detector and logical_observable metadata lines are ignored. shift_detectors offsets are applied. repeat blocks are flattened by default; this can expand large DEMs. detector_separator is unsupported and raises an error.
  • The ^ separator is treated as whitespace and does not change parsing.
  • If you want to fail fast instead of flattening, pass flatten_dem=False.

Sinter Integration (optional)

ILPQEC includes a sinter decoder wrapper for benchmarking and sampling. Install with:

uv pip install ilpqec[sinter]

Example usage:

import sinter
import stim
from ilpqec.sinter_decoder import SinterIlpDecoder

circuit = stim.Circuit.generated(
    "surface_code:rotated_memory_x",
    distance=3,
    rounds=3,
    after_clifford_depolarization=0.01,
)

tasks = [
    sinter.Task(
        circuit=circuit,
        decoder="ilpqec",
    )
]

stats = sinter.collect(
    tasks=tasks,
    custom_decoders={"ilpqec": SinterIlpDecoder()},
)

Notes:

  • The sinter adapter currently uses the direct HiGHS backend only.
  • ilpqec[sinter] includes stim and sinter dependencies.

Maximum-Likelihood Decoding with Weights

import numpy as np
from ilpqec import Decoder

H = np.array([[1, 1, 0], [0, 1, 1]])
error_probs = [0.1, 0.01, 0.1]

# Weights are computed automatically from probabilities
decoder = Decoder.from_parity_check_matrix(H, error_probabilities=error_probs)

syndrome = [1, 1]
correction, weight = decoder.decode(syndrome, return_weight=True)
print(f"ML correction: {correction}, weight: {weight}")

Note: error_probabilities must be in (0, 0.5]; pass explicit weights for p > 0.5.

Benchmark

Benchmarks use extra dependencies and optional solver backends. Use a dedicated virtual environment under benchmark/:

uv venv benchmark/.venv
uv pip install --python benchmark/.venv/bin/python --upgrade pip

# ILPQEC + optional solver backends
uv pip install --python benchmark/.venv/bin/python -e ".[pyomo,gurobi]"

# Benchmark dependencies
uv pip install --python benchmark/.venv/bin/python stim pymatching ldpc tesseract-decoder

Notes:

  • Gurobi requires a valid license and Python < 3.13.
  • If you do not need Gurobi, drop gurobi from the extras.
  • BPOSD runs with max_iter=50, osd_order=0, and bp_method=minimum_sum.
  • Tesseract runs with det_beam=50 by default (adjustable via --tesseract-beam).

Circuit-level rotated surface code memory

uv run --python benchmark/.venv/bin/python benchmark/benchmark_decoders.py \
  --compare-ilp-solvers --ilp-solvers highs,scip,gurobi,cbc,glpk \
  --shots 10000 --distance 3 --rounds 3 --noise 0.01

Results from a local macOS arm64 run (shots=10000, your numbers will vary):

Decoder Time (ms/shot) Logical Error Rate
ILP[highs] (direct) 2.7514 1.640%
ILP[gurobi] (direct) 0.6403 1.650%
ILP[scip] 28.2160 1.670%
ILP[cbc] 14.9315 1.670%
ILP[glpk] 8.6292 1.670%
MWPM (pymatching) 0.0035 2.150%
BPOSD (ldpc) 0.0308 7.680%
Tesseract 0.1602 1.640%

Code-capacity surface code (data errors only, perfect syndrome)

uv run --python benchmark/.venv/bin/python benchmark/benchmark_decoders.py \
  --noise-model code_capacity --compare-ilp-solvers --ilp-solvers highs,scip,gurobi,cbc,glpk \
  --shots 10000 --distance 3 --rounds 1 --noise 0.01

Results from a local macOS arm64 run (shots=10000, your numbers will vary):

Decoder Time (ms/shot) Logical Error Rate
ILP[highs] (direct) 3.2321 0.070%
ILP[gurobi] (direct) 0.0838 0.070%
ILP[scip] 23.4834 0.070%
ILP[cbc] 10.4697 0.070%
ILP[glpk] 5.0085 0.070%
MWPM (pymatching) 0.0036 0.070%
BPOSD (ldpc) 0.0028 0.070%
Tesseract 0.0093 0.070%

Color code (color_code:memory_xyz)

uv run --python benchmark/.venv/bin/python benchmark/benchmark_decoders.py \
  --code-task color_code:memory_xyz --compare-ilp-solvers --ilp-solvers highs,scip,gurobi,cbc,glpk \
  --shots 10000 --distance 3 --rounds 3 --noise 0.01

Results from a local macOS arm64 run (shots=10000, your numbers will vary):

Decoder Time (ms/shot) Logical Error Rate
ILP[highs] (direct) 2.0226 4.450%
ILP[gurobi] (direct) 0.3184 4.420%
ILP[scip] 24.9402 4.420%
ILP[cbc] 11.6961 4.450%
ILP[glpk] 6.0799 4.420%
MWPM (pymatching) 0.0034 13.420%
BPOSD (ldpc) 0.0114 9.830%
Tesseract 0.0600 4.450%

API Reference

Decoder

Main decoder class.

Class Methods:

  • from_parity_check_matrix(H, weights=None, error_probabilities=None, solver=None) - Create from parity-check matrix
  • from_stim_dem(dem, solver=None, merge_parallel_edges=True, flatten_dem=True) - Create from Stim DetectorErrorModel

Instance Methods:

  • decode(syndrome, return_weight=False) - Decode a single syndrome
  • decode_batch(syndromes) - Decode multiple syndromes
  • set_solver(name, **options) - Switch solver

Properties:

  • num_detectors - Number of parity checks/detectors
  • num_errors - Number of error mechanisms
  • num_observables - Number of logical observables (for DEM)
  • solver_name - Current solver name

get_available_solvers()

Returns a list of available solver names.

from ilpqec import get_available_solvers
print(get_available_solvers())  # e.g., ['scip', 'highs', 'cbc']

License

MIT License

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ilpqec-0.1.0.tar.gz (30.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ilpqec-0.1.0-py3-none-any.whl (20.5 kB view details)

Uploaded Python 3

File details

Details for the file ilpqec-0.1.0.tar.gz.

File metadata

  • Download URL: ilpqec-0.1.0.tar.gz
  • Upload date:
  • Size: 30.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for ilpqec-0.1.0.tar.gz
Algorithm Hash digest
SHA256 bf31ca059158d85b33289341ee3bbfc2e45a92a1694ded493235acbafd41850e
MD5 56e4ff280a197089056da5bd21d22274
BLAKE2b-256 cab20cbdd4c79f8260a141600189ec416faf0ce4e904271bb45c48655103eb97

See more details on using hashes here.

File details

Details for the file ilpqec-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ilpqec-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 20.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for ilpqec-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a3ceda51ad240107954c8279ab78a5c14155a8eeefab8017d000e37da7b81d9a
MD5 9321da5c79291df4b8134809cdc39aec
BLAKE2b-256 ea8d7b03d46902c6dd724577f23d6a6144cf5d73b051c3879e89c199ffb1262a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page