mdopt is a python package built on top of numpy for discrete optimisation (with the main application to classical and quantum decoding) in the tensor-network (specifically, Matrix Product States / Operators) language. The intended audience includes physicists, quantum information / error correction researchers, and those interested in exploring tensor-network methods beyond traditional applications.
To install the current release, use the package manager pip.
pip install mdoptOtherwise, you can clone the repository and use uv.
uv syncuv sync --group dev --group test also installs the development and test tools.
On Apple silicon running macOS 14 or later, pip installs the NumPy and
SciPy wheels tagged macosx_14_0_arm64, which link Apple's Accelerate
framework. On mdopt's matrices (rank-deficient, with singular values
spanning many orders of magnitude) Accelerate's LAPACK corrupted memory:
numpy.linalg.qr died with SIGBUS on a 636x304 matrix, and
numpy.linalg.svd tripped malloc's heap check. How much this showed in the
decoding depended on the bond dimension. On the [[72,12,6]]
bivariate-bicycle code in the natural qubit order, at chi_max=400, where
the SVDs act on matrices of about 800x1600, 21 of 22 shots with a
non-trivial error decoded wrongly while mdopt still reduced its SVDs by QR
first, against 3 of 24 on OpenBLAS, and every unit test still passed. The
same test shot decoded correctly at chi_max 64, 128 and 256. Not seeing
the fault at those smaller bond dimensions is no guarantee, because it
depends on the state of the heap. mdopt's SVD helpers call both libraries,
and mdopt warns at import when it finds Accelerate behind either one. The
same versions are also published as OpenBLAS builds, the macosx_11_0_arm64
NumPy wheel and the macosx_12_0_arm64 SciPy wheel, which install on the
same machines:
pip download "numpy==$(python -c 'import numpy; print(numpy.__version__)')" \
--platform macosx_11_0_arm64 --only-binary=:all: --no-deps -d /tmp/openblas-wheels
pip download "scipy==$(python -c 'import scipy; print(scipy.__version__)')" \
--platform macosx_12_0_arm64 --only-binary=:all: --no-deps -d /tmp/openblas-wheels
pip install --force-reinstall --no-deps /tmp/openblas-wheels/*.whlIn a uv checkout, a reinstall like this lasts only until the next uv run
or uv sync, which put the locked Accelerate wheels back. Sync against a
macOS 13 target instead (adding your usual --group flags). It selects the
same OpenBLAS wheels, and later uv run and uv sync calls keep them. Run
it again whenever the lock moves NumPy or SciPy to a new version:
uv sync --python-platform aarch64-apple-darwinSet MDOPT_ALLOW_ACCELERATE=1 to silence the warning if you must keep
Accelerate. Use OMP_NUM_THREADS=1 (or OPENBLAS_NUM_THREADS=1) for the
per-process BLAS of worker pools.
import logging
import numpy as np
import qecstruct as qec
from mdopt.decoding import decode_css
# The library does not configure logging; opt in to see progress with silent=False.
logging.basicConfig(level=logging.INFO)
# Define a small instance of the surface code
LATTICE_SIZE = 3
surface_code = qec.hypergraph_product(
qec.repetition_code(LATTICE_SIZE),
qec.repetition_code(LATTICE_SIZE),
)
# Input an error and choose decoder controls
logicals, success = decode_css(
code=surface_code,
error="IIXIIIIIIIIII",
bias_prob=0.01,
bias_type="Bitflip",
chi_max=64,
renormalise=True,
contraction_strategy="Optimised",
tolerance=1e-12,
silent=False,
)Circuit-level noise enters through a stim detector error model (DEM). Every error mechanism becomes one MPS site, each detector a parity-check (XOR) constraint and each logical observable a readout, so the same MPS-MPO machinery returns the maximum-likelihood observable flip for a sampled syndrome.
import stim
from mdopt.decoding import decode_dem, dem_to_problem
circuit = stim.Circuit.generated(
"surface_code:rotated_memory_x",
distance=3,
rounds=3,
after_clifford_depolarization=0.008,
before_measure_flip_probability=0.008,
after_reset_flip_probability=0.008,
)
# Maximum likelihood wants the undecomposed hyperedges, so keep decompose_errors off.
problem = dem_to_problem(
circuit.detector_error_model(decompose_errors=False, flatten_loops=True)
)
sampler = circuit.compile_detector_sampler(seed=1)
detections, observables = sampler.sample(1, separate_observables=True)
class_masses, predicted_flips = decode_dem(problem, detections[0].astype(int), chi_max=32)
print(predicted_flips, observables[0].astype(int))decode_dem returns one non-normalized weight per observable-flip class together with the most likely class; divide class_masses by class_masses.sum() to obtain probabilities. A materially negative, non-finite, or collapsed class-mass vector raises an ArithmeticError, but a successful contraction does not by itself certify convergence. For reliable results, decode at increasing chi_max values and require the normalized class weights and prediction to stabilize. The harnesses behind the decoder's validation (code-capacity thresholds of the surface code against minimum-weight perfect matching, a d=5 circuit-level cell decoded on a per-shot bond-dimension ladder with a calibration audit, and a reproduction of Fig. 1d of Piveteau, Chubb and Renes, PRX Quantum 5, 040303) live in examples/decoding/dem_campaign together with a README on how every number is regenerated.
mdopt.mps— matrix product states in the explicit (Vidal) and canonical forms.mdopt.contractor— MPS-MPO zip-up contraction with truncation.mdopt.optimiser— DMRG, dephasing DMRG, and the parity-check MPO tensors applied byapply_constraints.mdopt.decoding— the decoders:decode_css,decode_customanddecode_messagefor stabiliser and classical codes,decode_demfor detector error models.mdopt.utils— truncated SVD/QR and tensor helpers.mdopt.examples— the runnable campaign scripts behind the example notebooks.
The decoder modules used to live at mdopt.examples.decoding.decoding and mdopt.examples.decoding.dem. Those import paths still work, but emit a DeprecationWarning and will be removed in a future release; import from mdopt.decoding instead.
The examples folder contains full workflows that demonstrate typical use cases, such as quantum / classical LDPC code decoding, ground state search for the quantum Ising model and random quantum circuit simulation. Each example is fully documented and serves as a starting point for building your own experiments. The shell scripts in examples/decoding run the Monte Carlo campaigns behind the notebooks, locally (*.sh) or as Slurm jobs (*_cc.sh), and the detector-error-model harnesses live in examples/decoding/dem_campaign.
The package has been tested on macOS and Linux (Compute Canada clusters) and does not currently support Windows.
If you happen to find mdopt useful in your work, please consider supporting development by citing it.
@article{berezutskii2025mdopt,
title={mdopt: A code-agnostic tensor-network decoder for quantum error-correcting codes},
author={Berezutskii, Aleksandr},
journal={Journal of Open Source Software},
volume={10},
number={115},
pages={9125},
year={2025}
}
If you want to contribute to mdopt, be sure to follow GitHub's contribution guidelines.
This project adheres to our code of conduct.
By participating, you are expected to uphold this code.
We use GitHub issues for tracking requests and bugs, please direct specific questions to the maintainers.
The mdopt project strives to abide by generally accepted best practices in
open-source software development, such as:
- apply the desired changes and resolve any code conflicts,
- run the tests and ensure they pass,
- build the package from source.
Developers may find the following guidelines useful:
-
Running tests. Tests are executed using pytest:
pytest tests
-
Building documentation. Documentation is built with Sphinx. A convenience script is provided:
./generate_docs.sh
-
Coding style. The code is formatted with Ruff, whose style matches Black's. Please run the formatter before submitting a pull request:
uv run --group dev ruff format . -
Pre-commit hooks. Pre-commit hooks are configured to enforce consistent style automatically. To enable them:
pre-commit install
This project is licensed under the MIT License.
Full documentation is available at mdopt.readthedocs.io.
