Quantum Computation & Cryptography · Lecture 23

Lecture 23
Surface Codes, Thresholds, Decoders and Fault Tolerance

Week 12  |  the code that runs on today's hardware, why bigger becomes better below a threshold, and what it takes to compute on encoded qubits  |  Lab 6 launches today  |  reading: Watrous Ch. 15.2–16; Google Quantum AI (2024), abstract and Figs. 1–3

Cartoon: connecting the dotsA grid of small circles. A few circles are lit in pairs. A stick figure draws short lines between the nearby lit circles. A speech bubble says: errors come in pairs of alarms. My job is to guess which alarms belong together. errors come in pairs ofalarms. My job: guesswhich alarms go together
in the surface code an error chain lights the checks at its two ends; decoding is pairing up the lights
In this lecture
  1. Codes on a lattice: the toric code
  2. The rotated surface code
  3. Decoding as matching
  4. Thresholds and the suppression factor Λ
  5. Below threshold on real hardware
  6. Fault tolerance: noisy checks and logical gates
  7. Beyond the surface code, and how to read "logical qubit" claims
  8. Try it in code
  9. Summary, exercises and Lab 6

Lecture 22's codes need checks on qubits that may be far apart. Superconducting chips, like most hardware, connect only nearby qubits. The surface code needs only nearest-neighbour checks on a square grid, has one of the highest thresholds of any practical code, and is what Google and others run today. By the end of this lecture you should be able to describe its layout and logical operators, explain how a matching decoder works, state the threshold theorem and use the suppression factor \(\Lambda\) to estimate how large a code must be, interpret the 2024 below-threshold experiment, and explain why fault-tolerant computation needs more than a good memory.

1 — Codes on a lattice: the toric code

definition 23.1 — the toric code (Kitaev, 1997)

Place qubits on the edges of an \(L\times L\) square grid wrapped into a torus (\(2L^2\) qubits). For each vertex \(v\), the star operator \(A_v\) is \(X\) on the four edges meeting at \(v\); for each face \(f\), the plaquette operator \(B_f\) is \(Z\) on the four edges around \(f\). The toric code is the stabilizer code of all \(A_v\) and \(B_f\).

A star and a plaquette share either zero or two edges, so all checks commute (Proposition 22.1). The product of all stars is \(I\), and so is the product of all plaquettes, so only \(2L^2-2\) checks are independent: \(k=2\) logical qubits. Each check acts on 4 nearby qubits, however large \(L\) is.

A toric code is built on a \(6\times6\) torus. How many physical qubits does it use, and how many of its checks are independent?

There are two edges per vertex, so \(2L^2\) qubits; there are \(L^2\) stars and \(L^2\) plaquettes, but the product of all stars is \(I\), and so is the product of all plaquettes.
\(2\cdot36=72\) qubits and \(72-2=70\) independent checks, so \(k=72-70=2\) for every \(L\). Only the distance grows with the torus.

The geometry explains everything else. A string of \(Z\) errors along a path of edges anticommutes only with the stars at its two endpoints: the checks light up in pairs, at the ends of error chains. A closed loop of \(Z\)s is invisible. If the loop can be shrunk to a point it is a product of plaquettes, a stabilizer, and harmless. If it wraps around the torus it cannot be shrunk, and it is a logical operator. So the distance is \(L\), the length of the shortest non-contractible loop, and the code has parameters \([[2L^2,2,L]]\).

A closed loop of \(Z\) errors wraps once around the torus. What happens?

Ask two questions: does any star meet the loop in an odd number of edges, and can the loop be shrunk to a point?
Every star meets the loop in 0 or 2 edges, so the syndrome is trivial; but the loop cannot be shrunk, so it is a logical operator. A trivial syndrome does not mean "no harm".

2 — The rotated surface code

Real chips are flat, not toroidal. The surface code cuts the torus open; logical operators then run from one boundary to the opposite one, instead of around a loop. The version used in practice, the rotated surface code, puts data qubits on a \(d\times d\) grid and one measure qubit in each plaquette.

definition 23.2 — the rotated surface code of distance d

Data qubits sit on a \(d\times d\) grid. The squares between them are coloured like a checkerboard, alternately carrying \(X\)-type and \(Z\)-type stabilizers on their four corner qubits. Along two opposite edges, half-squares (two qubits) carry \(X\)-type checks, and along the other two edges \(Z\)-type checks. For \(d=5\) this gives 25 data qubits and 24 checks, half of each type, each measured with its own measure qubit: 49 physical qubits for one logical qubit of distance 5. (HW12 asks you to count in general.) Logical operators are strings of one Pauli type joining the two boundaries of the other type; the figure marks one of each for \(d=5\).

The rotated surface code of distance 5A 5 by 5 grid of data qubits (circles). Between them, a checkerboard of square plaquettes alternates between X-type (rose) and Z-type (sky) stabilizers, each acting on its four corner qubits; along the top and bottom edges there are half-plaquettes (two qubits) of X type, and along the left and right edges half-plaquettes of Z type. In total 25 data qubits and 24 measure qubits (12 X-type, 12 Z-type), 49 in all. A vertical line of X on the left column, from top to bottom edge, is a logical X operator of weight 5; a horizontal line of Z along the top row is a logical Z of weight 5.X_LZ_LX-type stabilizerZ-type stabilizerdata qubitd = 5: 25 data qubits24 measure qubits(12 X-type, 12 Z-type)X_L: X down a columnZ_L: Z along a rowmeasure qubits sit at thecentre of each plaquette
The rotated surface code with \(d=5\): 25 data qubits and 24 measure qubits (one per coloured plaquette), 49 in all (geometry in layout.py; verify_L23.py checks that all 24 checks commute and that \(\bar X\) and \(\bar Z\) are logical operators). Every check involves at most four nearest neighbours.

The code encodes one logical qubit (HW12 Problem 1 checks the count). The logical operators have weight \(d\), and no shorter Pauli connects opposite edges without being caught, so this is a \([[d^2,1,d]]\) code, correcting \(\lfloor(d-1)/2\rfloor\) errors. Distance 5 corrects any 2 errors; distance 7, any 3.

3 — Decoding as matching

The syndrome of the surface code does not point at an error the way the Hamming syndrome does: it shows only the endpoints of error chains, called defects (checks returning \(-1\)). The decoder's task is to guess chains that explain the defects. A chain may also end on a boundary of the matching type, which lights only one check. Since errors are rare, the most likely explanation is the one with the fewest errors: pair up the defects (or pair a defect with a boundary) so that the total length of the connecting paths is minimal. This is the minimum-weight perfect matching problem on a graph, which Edmonds' blossom algorithm solves in polynomial time.

Four defects sit on a line at positions 0, 1, 5 and 6 (the distance between two defects is the difference of their positions; ignore the boundaries). What is the total length of the minimum-weight matching?

There are only three ways to pair up four points. Add the two path lengths for each.
Pairing \(0\)–\(1\) and \(5\)–\(6\) costs \(1+1=2\); the other two pairings cost 10. The matching explains the defects by two short chains, the fewest errors.

The decoder succeeds when the guessed chains and the real ones together form only closed loops (stabilizers), and fails when together they connect opposite boundaries, completing a logical operator. With \(\lfloor(d-1)/2\rfloor\) or fewer errors the matching always succeeds; with more, it often still does, which is why the code performs better than its distance alone suggests.

The decoder flips a set of qubits that is completely different from the set that actually had errors. Then the encoded qubit is

What matters is the product of the error and the correction: is it a stabilizer or a logical operator?
Error times correction has trivial syndrome. If it forms closed loops it is a stabilizer and does nothing; if it connects opposite boundaries it is a logical operator. The decoder only has to be right "up to stabilizers".

Surface-code decoder

Data qubits are circles; the shaded plaquettes are \(Z\)-checks (sky) and \(X\)-checks (rose). Click data qubits to place \(X\) errors: the \(Z\)-checks at the ends of each chain light up. "decode" runs an exact minimum-weight matching (the dashed lines) and flips the ringed qubits. Find the smallest set of errors that fools the decoder, for \(d=5\) and for \(d=7\).

Real decoders face two extra problems. Measurements themselves fail, so a defect may be a measurement error rather than a data error (Section 6). And decoding must keep up with the hardware: one round every microsecond or so. PyMatching (the decoder of Lab 6) implements a fast "sparse blossom" matching; belief-propagation and neural-network decoders trade speed for accuracy in other ways.

4 — Thresholds and the suppression factor Λ

theorem 23.1 — the threshold theorem (statement)

For a family of codes like the surface code, a decoder, and a noise model in which each physical operation fails independently with probability \(p\), there is a threshold \(p_{\text{th}}\gt0\) such that for \(p\lt p_{\text{th}}\) the logical error rate falls exponentially in the distance:

$$\varepsilon_d\approx A\left(\frac p{p_{\text{th}}}\right)^{(d+1)/2}.$$

Above threshold, larger codes are worse. For the surface code with matching decoders and realistic circuit-level noise, \(p_{\text{th}}\) is around 1%.

A device runs with \(p\) above \(p_{\text{th}}\). Replacing its distance-5 code by a distance-7 code makes the logical error rate .

Look at \((p/p_{\text{th}})^{(d+1)/2}\) when the base is greater than 1.
With \(p/p_{\text{th}}\gt1\), a bigger exponent makes the rate bigger: the larger code just gives the noise more places to strike. Growing the code only helps below threshold.

The same shape as the repetition code of Lecture 21 (threshold \(\tfrac12\) there): \(\lfloor(d-1)/2\rfloor+1=(d+1)/2\) errors are needed to cause a logical error, each with probability about \(p\). The ratio for each step of 2 in distance is the suppression factor

$$\Lambda=\frac{\varepsilon_d}{\varepsilon_{d+2}}\approx\frac{p_{\text{th}}}{p}.$$

\(\Lambda\gt1\) means "below threshold": growing the code helps. Because \(\Lambda\approx p_{\text{th}}/p\), every increase of \(d\) by 2 then divides the logical error rate by \(\Lambda\).

A device has \(p_{\text{th}}=1\%\) and physical error rate \(p=0.2\%\). Estimate \(\Lambda\), and the ratio \(\varepsilon_9/\varepsilon_3\) of logical error rates.

\(\Lambda\approx p_{\text{th}}/p\). Going from \(d=3\) to \(d=9\) is three steps of 2.
\(\Lambda\approx0.01/0.002=5\), and three steps divide the rate by \(5^3=125\): \(\varepsilon_9/\varepsilon_3\approx0.008\).
A threshold in simulated surface-code dataLog-log plot of the logical error rate per run (d rounds) against the physical error rate p, from 1% to 4.5%, for rotated surface codes of distance 3, 5 and 7, simulated with Stim (data-qubit depolarization and measurement errors, both with probability p, every round) and decoded with PyMatching, 100,000 runs per point. Below about 4% the larger codes have lower logical error rates; above it, higher. At p = 1% the logical error rate per run falls by factors of about 3.6 and 4.1 from d = 3 to 5 to 7.1e-41e-31e-21e-110.010.020.030.040.05physical error rate p (log scale)logical error per runthreshold ~ 4%d = 3d = 5d = 7Stim + PyMatching1e5 runs per pointbars: +/- 2 s.e.noise: data depol.+ measurement flips,both p, each round
A threshold appears in simulated data (threshold_data.py: Stim circuits for the rotated surface code with \(d\) rounds of checks, data-qubit depolarization and measurement errors at rate \(p\), decoded by PyMatching). Plotted per run of \(d\) rounds, the usual way to draw a threshold plot, the curves cross near \(p\approx4\%\) for this simplified noise model; Lab 6 repeats the experiment with noise on every gate. Converted to a rate per round, as in the definition of \(\Lambda\), the data at \(p=1\%\) give \(\Lambda\approx6.1\) from \(d=3\) to 5 and \(\approx5.8\) from 5 to 7 (only about 60 failures at \(d=7\), so the second value is uncertain by roughly 15%).
example 23.1 — how big must the code be?

A device achieves a logical error rate of \(10^{-3}\) per round at \(d=3\). An algorithm needs \(10^{-10}\) per round per logical qubit. Find the distance and the number of physical qubits per logical qubit if \(\Lambda=2\), and if \(\Lambda=3\).

We need \(10^{-3}/\Lambda^m\le10^{-10}\), with \(d=3+2m\). For \(\Lambda=2\): \(2^m\ge10^7\), so \(m\ge\log_210^7\approx23.3\), \(m=24\), \(d=51\), and \(2d^2\approx5{,}200\) physical qubits per logical qubit. For \(\Lambda=3\): \(3^m\ge10^7\), \(m\ge14.7\), \(m=15\), \(d=33\), about \(2{,}200\) physical qubits.

5 — Below threshold on real hardware

In August 2024 Google Quantum AI reported surface-code memories on its 105-qubit Willow processor (published online in Nature in December 2024), and this is the result to read critically.

With Willow's \(\Lambda=2.14\), by what factor is the logical error rate per cycle at \(d=3\) larger than at \(d=7\)?

From \(d=3\) to \(d=7\) is two steps of 2 in distance, and each step divides by \(\Lambda\).
\(\Lambda^2=2.14^2\approx4.58\). Going from \(d=3\) to \(d=7\) multiplies the qubit count by more than 5 and buys less than a factor of 5: \(\Lambda\) near 2 is below threshold, but not far below.
example 23.2 — extrapolating Willow

If \(\Lambda=2.14\) held at larger distances, what distance would bring the per-cycle logical error rate from \(0.143\%\) at \(d=7\) down to \(10^{-6}\)? How many physical qubits is that?

\(1.43\times10^{-3}/2.14^m\le10^{-6}\) needs \(m\ge\ln1430/\ln2.14\approx9.6\), so \(m=10\) and \(d=27\), about \(2d^2\approx1{,}460\) physical qubits for one logical qubit. That is one logical qubit at an error rate still far above what Shor's algorithm needs (Lecture 17), and it assumes that no new error source (cosmic rays, leakage, correlated errors) appears at scale. Google observed rare correlated error bursts that set a floor near \(10^{-10}\) in repetition-code tests.

6 — Fault tolerance: noisy checks and logical gates

Noisy syndrome extraction. Each check is measured by a small circuit (four CNOTs or CZs into a measure qubit), and any of its gates can fail. A single measurement error looks exactly like a data error, so one round of checks cannot be trusted. Instead the checks are measured repeatedly, about \(d\) rounds, and the decoder works with detectors: changes of a check's value between consecutive rounds. A data error lights the same detector pair in space; a measurement error lights a pair of detectors in time (the check flips and flips back). Matching then runs on a three-dimensional space–time graph. A circuit is fault tolerant when no single faulty component can cause an uncorrectable error, which requires care with the order of the CNOTs (a single fault on a measure qubit can spread to two data qubits, a "hook" error).

One check's detectors fire in two consecutive rounds, and no other detector nearby fires. The most likely cause is a single .

A detector fires when a check's value changes between rounds. Which fault makes one check's value change and then change back?
A wrong measurement makes the check look flipped for one round only, so the detectors before and after it fire: a pair in time. A data error stays, so its detector pair is in space.

Logical gates. A memory is not a computer. Some gates are easy on CSS codes: a CNOT between two code blocks, applied qubit by qubit (transversally), is a logical CNOT, and transversal gates cannot spread one error into two errors within a block. On the surface code, logical CNOTs are done by lattice surgery, merging and splitting patches by measuring joint checks along their boundary. But no code has a universal set of transversal gates (the Eastin–Knill theorem), and the Clifford gates alone are classically simulable (Lecture 22). The missing ingredient is a non-Clifford gate such as \(T\).

Magic states. The standard solution prepares special states such as \(T|+\rangle\) ("magic states") at low quality, purifies them (magic-state distillation: for example, 15 noisy copies give 1 better one), and consumes one per \(T\) gate by a teleportation-like circuit. Distillation factories once dominated resource estimates; magic-state cultivation (Gidney, Shutty and Jones, 2024) grows a high-quality magic state inside a single surface-code patch at a fraction of the cost, one of the three ideas behind Gidney's 2025 RSA estimate (HW9).

Tick every true statement about computing with surface-code logical qubits.

Sort the gates into Clifford (cheap on the code) and non-Clifford (needs an extra resource).
Clifford gates come from lattice surgery and code deformation; \(T\) gates come from consuming magic states. Without some non-Clifford gate the computer would be classically simulable.

7 — Beyond the surface code, and how to read "logical qubit" claims

The surface code encodes one logical qubit per patch, which is why each logical qubit costs \(\sim2d^2\) physical ones. Quantum LDPC codes keep checks of bounded weight but allow long-range connections, and encode many logical qubits per block. IBM's "gross" bivariate-bicycle code \([[144,12,12]]\) (Bravyi et al., Nature 2024) stores 12 logical qubits at distance 12 in 288 physical qubits (data plus check), roughly a tenth of what the surface code would need, at the price of couplers between distant qubits. Neutral-atom and trapped-ion devices, whose qubits can move, have demonstrated tens of logical qubits running small encoded circuits: for example, up to 48 logical qubits in error-detecting codes on a neutral-atom array (Bluvstein et al., Nature 2024).

caution — five questions for any "N logical qubits" headline
  1. What distance? Ask what the reported distance can and cannot do with a single error (HW12 Problem 1).
  2. Memory or computation? Storing a state is easier than computing with it, especially with non-Clifford gates.
  3. Corrected or postselected? Discarding runs where an error was detected can make logical error rates look tiny, but the discard rate grows exponentially with size.
  4. Better than physical? Is the logical error rate lower than the physical one, and does it fall as the code grows (\(\Lambda\gt1\))?
  5. Real-time decoding? Was decoding done during the experiment, fast enough to keep up, or afterwards?

8 — Try it in code

tool — a surface-code memory in Stim, decoded by PyMatching (stim 1.16, pymatching 2.4)
import numpy as np, stim, pymatching

d, p = 5, 0.001
circ = stim.Circuit.generated("surface_code:rotated_memory_z", distance=d, rounds=d,
        after_clifford_depolarization=p, before_round_data_depolarization=p,
        before_measure_flip_probability=p, after_reset_flip_probability=p)       # circuit-level noise, all at p
coords = circ.get_final_qubit_coordinates()
print(f"d = {d}: {len(coords)} qubits, {circ.num_detectors} detectors, {circ.num_observables} logical observable")
dem = circ.detector_error_model(decompose_errors=True)                          # which errors flip which detectors
matcher = pymatching.Matching.from_detector_error_model(dem)
det, obs = circ.compile_detector_sampler(seed=23).sample(200_000, separate_observables=True)
pred = matcher.decode_batch(det)
fails = np.sum(pred[:, 0] != obs[:, 0])
print(f"{fails} logical errors in 200,000 runs of {d} rounds: {fails/200_000:.2e} per run, about {fails/200_000/d:.1e} per round")
print("mean number of detection events per run:", round(det.sum(axis=1).mean(), 2))
d = 5: 49 qubits, 120 detectors, 1 logical observable
27 logical errors in 200,000 runs of 5 rounds: 1.35e-04 per run, about 2.7e-05 per round
mean number of detection events per run: 1.76

stim.Circuit.generated writes the whole experiment: reset, \(d\) rounds of syndrome extraction with the chosen noise, and a final measurement of the data qubits, with detectors and the logical observable annotated. detector_error_model lists every fault and the detectors it flips, which is exactly the matching graph. At \(p=10^{-3}\), well below threshold, \(d=5\) fails about once in 7,000 runs of 5 rounds. Lab 6 sweeps \(p\) and \(d\) to find the threshold and \(\Lambda\).

tool — the Λ arithmetic
import math

def distance_needed(eps_ref, d_ref, Lam, target):
    """Smallest odd d >= d_ref with eps_ref / Lam**((d - d_ref)/2) <= target."""
    m = max(0, math.ceil(math.log(eps_ref / target) / math.log(Lam)))
    return d_ref + 2 * m

for name, eps, dref, Lam, target in [("Example 23.1, Lambda = 2", 1e-3, 3, 2, 1e-10),
                                      ("Example 23.1, Lambda = 3", 1e-3, 3, 3, 1e-10),
                                      ("Willow extrapolated", 1.43e-3, 7, 2.14, 1e-6)]:
    d = distance_needed(eps, dref, Lam, target)
    print(f"{name:26s}: d = {d:3d}, about 2d^2 = {2 * d * d:6d} physical qubits per logical qubit")
Example 23.1, Lambda = 2  : d =  51, about 2d^2 =   5202 physical qubits per logical qubit
Example 23.1, Lambda = 3  : d =  33, about 2d^2 =   2178 physical qubits per logical qubit
Willow extrapolated       : d =  27, about 2d^2 =   1458 physical qubits per logical qubit

9 — Summary, exercises and Lab 6

summary

Next time: near-term algorithms and honest advantage, and the course wrap-up. HW11 is due at the start of Week 13; HW12 is posted after Lecture 24.

exercises 23
  1. How many data qubits, measure qubits and total qubits does a rotated surface code of distance 9 need? How many errors does it correct?
  2. A code has \(\varepsilon_5=5\times10^{-4}\) per round and \(\Lambda=2.5\). Estimate \(\varepsilon_{11}\).
  3. In the toric code, show that a single \(Z\) error lights exactly two star checks, and that a closed loop of \(Z\) errors lights none.
  4. A logical memory must hold for one hour with 1 μs cycles and total failure probability at most 1%. What per-cycle logical error rate is needed? Using the Willow numbers of Example 23.2, what distance?
  5. Explain why a single measurement error, in a single round of syndrome extraction, could make the decoder apply a wrong correction, and how repeating the rounds fixes this.
  6. Why do magic states not make the code's Clifford gates unnecessary? Which gates are cheap and which are expensive in a surface-code computer?
  1. 81 data, 80 measure, 161 total; it corrects \(\lfloor8/2\rfloor=4\) errors.
  2. Three steps of 2 in distance: \(5\times10^{-4}/2.5^3=3.2\times10^{-5}\).
  3. A star \(A_v\) anticommutes with \(Z\) on edge \(e\) iff \(e\) meets \(v\); an edge has two endpoints. For a loop, every vertex on it meets exactly two of its edges (an even number), so every star commutes with the loop.
  4. \(3.6\times10^9\) cycles, so about \(0.01/3.6\times10^9\approx2.8\times10^{-12}\) per cycle. From \(1.43\times10^{-3}\) at \(d=7\) with \(\Lambda=2.14\): \(m\ge\ln(5.1\times10^8)/\ln2.14\approx26.4\), so \(m=27\), \(d=61\), about 7,400 physical qubits.
  5. A flipped measurement makes one check look violated, as if a data error had occurred next to it; the decoder would "correct" a qubit that was fine, creating a real error. Repeating the measurement, the faulty outcome appears in one round only (the check returns to normal next round), while a real data error persists; detectors that compare rounds distinguish the two.
  6. Magic states supply only the non-Clifford gates; everything else (CNOTs by lattice surgery, Paulis, which are tracked in software, \(H\) and \(S\) by code deformation) is Clifford. Paulis are essentially free, Clifford gates moderately expensive (they take \(\sim d\) cycles of lattice surgery), and \(T\) gates the most expensive, since each consumes a magic state; resource estimates therefore count Toffoli or \(T\) gates (Lecture 17, HW8).
lab 6 — quantum error correction with Stim and PyMatching (launch)

Lab 6 simulates repetition-code and surface-code memories with Stim, decodes them with PyMatching, finds the thresholds, fits \(\Lambda\) below threshold, compares it with Willow's, and estimates the overhead of one logical qubit at \(10^{-12}\). No hardware: Stim runs millions of shots per second.

Reading and sources

all numbers and figures in these notes are computed in python (verify_L23.py, threshold_data.py) and follow the page theme