A Thousand Nights in the Noisy Store

bit-flip code · logical error rate · Monte Carlo simulation · seeded randomness · Pauli errors

A memory chip stores one qubit in the three-qubit bit-flip code every night. During the night each of the three wires, independently, suffers an X (a bit flip) with probability px, a Z (a phase flip) with probability pz, or nothing. In the morning the chip runs the full round trip on the damaged register:

  • the code state is a|000\rangle + b|111\rangle (CNOT(0, 1) and CNOT(0, 2) applied to (a|0\rangle + b|1\rangle)|00\rangle);
  • the syndrome is (s_1, s_2) = (b_0 \oplus b_1,\ b_1 \oplus b_2) for the bits b_0 b_1 b_2 of any basis state with a nonzero amplitude;
  • the repair applies X to qubit 0, 1 or 2 for syndrome (1, 0), (1, 1) or (0, 1), and nothing for (0, 0);
  • the decoder applies CNOT(0, 1) and CNOT(0, 2) and reads qubit 0.

Every night ends with qubit 0 in one of four states, the same kind for every a and b, up to a global phase: |\psi\rangle itself ("none"), X|\psi\rangle ("flip"), Z|\psi\rangle ("phase") or XZ|\psi\rangle ("both").

Write logical_tally(px, pz, trials, seed) that simulates trials nights and returns a dict with exactly the keys "none", "flip", "phase", "both" and how many nights ended each way (zeros included). Use this procedure so that the counts are reproducible: rng = random.Random(seed); for each night, for qubits 0, 1, 2 in that order, draw r = rng.random(); if r < px the wire gets an X, else if r < px + pz it gets a Z, else nothing. Make no other draws.

With pz = 0 the share of "flip" nights should be close to 3p^2 - 2p^3 for p = px. Press Run with plot([logical_tally(p / 20, 0, 2000, 1)["flip"] / 2000 for p in range(11)]) to watch it grow.

Examples

Input:  px = 0.1, pz = 0, trials = 5000, seed = 2
Output: {"none": 4864, "flip": 136, "phase": 0, "both": 0}
Explanation: 3 * 0.1**2 - 2 * 0.1**3 = 0.028, and 0.028 * 5000 = 140.

Input:  px = 0, pz = 0.1, trials = 2000, seed = 4
Output: {"none": 1509, "flip": 0, "phase": 491, "both": 0}

Input:  px = 0.3, pz = 0.3, trials = 1, seed = 4
Output: {"none": 0, "flip": 0, "phase": 0, "both": 1}
Explanation: the draws are 0.236..., 0.103..., 0.396...: X on qubits 0 and 1, Z on qubit 2.

Constraints

  • A plain-Python simulation of the eight amplitudes for every night is fast enough.

Goals

  • Simulate many noisy runs of the bit-flip code with a prescribed random procedure
  • Classify each run's logical result and compare the counts with the formula $3p^2 - 2p^3$
Starting Python…