Stabilizer simulation

The 'stabilizer' backend stores a state by its stabilizer generators instead of its amplitudes, so memory is O(n**2) bits rather than 2**n complex numbers:

from blueqat import Circuit

circuit = Circuit(200).h[0]
for q in range(199):
    circuit.cx[q, q + 1]
circuit.m[:].run(backend='stabilizer', shots=20, seed=1)
# => Counter({'000...0': 11, '111...1': 9})

The price is Gottesman-Knill: only Clifford gates (i, x, y, z, h, s, sdg, sx, sxdg, cx, cy, cz, swap), measurement and reset. A t or an rx raises – use the statevector or density-matrix backend for those.

This is what makes error-correction work possible: a distance-5 surface code needs 49 qubits before any noise is added, which is far past what a statevector (and much further past what a density matrix) can hold.

Inspecting the state

Without shots, the run returns the simulator itself:

sim = Circuit(3).h[0].cx[0, 1].cx[1, 2].run(backend='stabilizer')
sim.stabilizers()      # ['+XXX', '+ZZI', '+IZZ']
sim.measure(0)         # collapses, returns 0 or 1
sim.reset(0)
sim.copy()             # branch the state

Character q of a stabilizer string is the Pauli on qubit q.

shots takes the same seed and bit_order as the other backends. Each shot is an independent trajectory, since measurement outcomes are genuinely random and there is no final state to sample from afterwards.

Cost

A measurement touches every row, and each row is O(n) bits, so a full measurement pass is O(n**3 / 64) word operations per shot. Measured on a GHZ chain, 20 shots: 0.3 s at 100 qubits, 7 s at 500, 119 s at 2000. Circuits that measure only a few qubits are correspondingly cheaper.