Skip to main content
About 15 minutes · Tested on 1.0.1

Getting Started

From pip install to decoding a surface-code circuit and a qLDPC code. Each step is a complete script you can copy and run.

Step 1

Install and check

Install from PyPI. Wheels exist for CPython 3.9 to 3.14 on Linux x86_64, Windows x64 and macOS on Apple silicon, so there is nothing to compile. The tier stays "Community" until you activate a licence, and every decoder works on the CPU in that tier.

terminalbash
python -m pip install --upgrade "qector-decoder-v3>=1.0.1,<2" stim sinter
step1_check.pypython
import qector_decoder_v3 as q
print(q.__version__)
print(q.get_license_info()["tier"])   # "Community" until you activate a licence

Step 2

Your first decode, checked

Build a distance-5 repetition code, inject one error, and decode its syndrome with Union-Find and exact matching. The assert is the contract every QECTOR decoder keeps: the correction reproduces the syndrome, H·c = s (mod 2).

step2_first_decode.pypython
import numpy as np
from qector_decoder_v3 import BlossomDecoder, UnionFindDecoder, generate_parity_check_matrix

# Distance-5 repetition code: 5 data qubits, 4 parity checks.
check_to_qubits = [[0, 1], [1, 2], [2, 3], [3, 4]]
H = generate_parity_check_matrix(check_to_qubits, 5)

error = np.array([0, 0, 1, 0, 0], dtype=np.uint8)
syndrome = (H @ error) % 2                      # which checks fire

for decoder in (UnionFindDecoder(check_to_qubits, 5), BlossomDecoder(check_to_qubits, 5)):
    correction = np.asarray(decoder.decode(syndrome), dtype=np.uint8)
    assert np.array_equal((H @ correction) % 2, syndrome)   # H·c = s (mod 2)
    print(type(decoder).__name__, correction)

Step 3

Decode a Stim circuit

Generate a rotated surface-code memory experiment with Stim, turn its detector error model into a weighted matching graph, and count logical errors over sampled shots. The decoder uses the error model’s weights, so rare and common faults are treated differently.

step3_stim.pypython
import stim
from qector_decoder_v3 import dem

circuit = stim.Circuit.generated(
    "surface_code:rotated_memory_z", distance=5, rounds=5,
    after_clifford_depolarization=0.005,
)
graph = dem.from_stim(circuit.detector_error_model(decompose_errors=True)).collapse_to_graph()
decoder = graph.make_decoder("blossom")          # uses the DEM's edge weights

shots = 2_000
dets, obs = circuit.compile_detector_sampler(seed=7).sample(shots, separate_observables=True)
fails = 0
for d, o in zip(dets, obs):
    correction = decoder.decode(d.astype("uint8"))
    predicted = graph.predicted_observables(correction)
    fails += int(predicted[0] != o[0])
print(f"logical errors: {fails} / {shots}")

Step 4

qLDPC with BP-OSD

Matching needs graph-like errors; qLDPC codes do not have them. Build the [[144,12,12]] bivariate bicycle code and decode a random error with belief propagation plus ordered statistics, again checking the syndrome.

step4_qldpc.pypython
import numpy as np
from qector_decoder_v3 import codes
from qector_decoder_v3.bposd import BpOsdDecoder

# [[144,12,12]] bivariate bicycle code: A = x^3 + y + y^2, B = y^3 + x + x^2
cx, cz = codes.bivariate_bicycle_code(
    12, 6, [("x", 3), ("y", 1), ("y", 2)], [("y", 3), ("x", 1), ("x", 2)]
)
H = cx.parity_check_matrix()
decoder = BpOsdDecoder(H, error_rate=0.01, osd_order=0)

rng = np.random.default_rng(1)
error = (rng.random(H.shape[1]) < 0.01).astype(np.uint8)
syndrome = (H @ error) % 2
correction = np.asarray(decoder.decode(syndrome), dtype=np.uint8)
assert np.array_equal((H @ correction) % 2, syndrome)
print(H.shape, "syndrome reproduced")

Step 5

Scale up with Sinter

Sinter runs many circuits across worker processes and collects statistics. QECTOR registers its decoders as Sinter custom decoders. Keep the main guard: Sinter starts worker processes.

step5_sinter.pypython
import sinter
import stim
from qector_decoder_v3.sinter_compat import qector_sinter_decoders


def main():
    tasks = [
        sinter.Task(
            circuit=stim.Circuit.generated(
                "surface_code:rotated_memory_z", distance=d, rounds=d,
                after_clifford_depolarization=p,
            ),
            json_metadata={"d": d, "p": p},
        )
        for d in (3, 5)
        for p in (0.002, 0.004)
    ]

    stats = sinter.collect(
        num_workers=2, tasks=tasks,
        decoders=["qector_blossom"],
        custom_decoders=qector_sinter_decoders(),
        max_shots=2_000, max_errors=50,
    )
    for s in sorted(stats, key=lambda s: (s.json_metadata["d"], s.json_metadata["p"])):
        print(s.json_metadata, s.errors, "/", s.shots)


if __name__ == "__main__":   # Sinter uses worker processes
    main()

Where to go next

  • Every class and function, with signatures: the API index.
  • Choosing a decoder and the full feature set: Decoder and the user manual.
  • Using QECTOR from Claude: the Claude plugin.
  • GPU batch decoding and commercial use: plans, from $79/month.

Verified: each script above was run against the published qector-decoder-v3 1.0.1 wheel on Linux (CPython 3.13) on 6 November 2026. Logical-error counts depend on the random seed and noise settings; measure on your own workloads.