Skip to content

Error Correction

qliff.qec turns a noisy Circuit into decoder inputs: detection events, a detector error model, and the parity-check / matching exports built from it. No decoder is bundled. The exports drop straight into MWPM (pymatching), belief propagation, or an ML decoder. Ready-made code circuits let you jump straight to a logical-error-rate curve.

Code circuits

Each generator returns a Circuit under circuit-level Pauli noise with its detectors and logical observable already declared.

GeneratorArgumentsDescription
repetition_code(distance, rounds, p)code distance, syndrome rounds, per-round X error pbit-flip repetition-code Z memory
rotated_surface_code(distance, rounds, p)distance, rounds, depolarizing protated planar surface-code Z memory
python
from qliff.qec import repetition_code, rotated_surface_code

rep = repetition_code(distance=3, rounds=3, p=0.05)
sur = rotated_surface_code(distance=3, rounds=3, p=0.01)

You can also declare your own on any circuit with c.detector(*recs) and c.observable(index, *recs), using stim's rec[-1] negative indexing into the measurement record (see Circuit).

DetectorSampler

circuit.detector_sampler() returns a sampler whose sample(shots, seed=None) yields two numpy uint8 arrays: the syndrome and the logical labels. A detection event is a detector's measured parity XORed with its noiseless value, so a clean run produces all-zero syndromes.

OutputShapeMeaning
dets(shots, n_detectors)detection events
obs(shots, n_observables)logical-observable flips (the labels)
python
dets, obs = rep.detector_sampler().sample(10000, seed=0)

These feed a decoder directly, or serve as a training set for an ML decoder.

DetectorErrorModel

circuit.dem() builds the error model by propagating each Pauli fault branch sign-free to the end of the circuit, recording which detectors and observables it flips. Mechanisms with identical signatures merge as independent errors. It is exact for Pauli noise.

Property/MethodReturnsDescription
mechanismslist(probability, detectors, observables) per mechanism
check_matrix()(H, priors, obs_matrix)F2 parity-check H (detectors×mechanisms), priors, observable matrix — for BP
weights()ndarrayper-mechanism MWPM weights log1pp
graphlike_edges()listmechanisms flipping 2 detectors, as (dets, obs, weight) — a matching graph

Decoding and logical error rate

Wire the exports into pymatching, decode the sampled syndromes, and compare to the labels. logical_fidelity(predictions, observed) returns 1LER.

python
from pymatching import Matching
from qliff.qec import repetition_code, logical_fidelity

rep = repetition_code(distance=3, rounds=3, p=0.05)

dem = rep.dem()
H, priors, obs_matrix = dem.check_matrix()

matching = Matching.from_check_matrix(
    H, weights=dem.weights(), faults_matrix=obs_matrix
)

dets, obs = rep.detector_sampler().sample(20000, seed=0)
predicted = matching.decode_batch(dets)
fidelity = logical_fidelity(predicted, obs)   # 1 - logical error rate

Below threshold, the logical error rate falls as distance grows — the signature of working error correction. See base/surface_code.py for full LER / fidelity tables across distances.

NOTE

The detector error model and detection sampler use Pauli-noise trajectories. For logical error rates under coherent noise, estimate the logical observable directly with the importance sampler.