CSS Codes
Building a quantum error-correcting code from scratch feels daunting: a single qubit can suffer a
bit-flip (X), a phase-flip
(Z), or both at once, and the errors form a continuum. So here is a beautiful
shortcut. CSS codes — named for Calderbank, Shor and Steane — take two ordinary
classical error-correcting codes off the shelf and weld them into a quantum code, letting decades of
classical coding theory do the hard work. The trick handles bit-flips with one classical code and
phase-flips with another, separately, so the frightening quantum problem splits into two
problems we already know how to solve. The flagship result: the Steane code, seven
physical qubits protecting one logical qubit, built entirely out of the humble
[7,4] Hamming code.
A one-minute recap of classical linear codes
A classical binary linear code C of length
n is a set of codewords (bit strings) closed under addition mod 2. It is pinned
down by a parity-check matrix H: a bit string
x is a valid codeword exactly when
H x = 0 \pmod 2.
If a codeword picks up an error e (a 1 in each flipped position), the received
word is x + e, and multiplying by H gives the
syndrome
H(x + e) = Hx + He = He \pmod 2.
The codeword vanishes; only the error survives. The syndrome depends on the error alone, and for a
good code it names which error occurred. That single idea — a matrix whose product with the error
reveals the error — is the whole engine we are about to bolt onto qubits.
The CSS construction
Take two classical linear codes C_1 and C_2 of the
same length n, satisfying the nesting condition
C_2 \subset C_1 \qquad\big(\text{equivalently } C_2^{\perp} \subset C_1\big).
Now build the stabilizer
generators in two families, from two parity-check matrices:
- Z-type stabilizers — take each row of H(C_1) and turn its
1s into Z operators. Because
Z anticommutes with X, these checks light up for
bit-flip (X) errors, and their measured syndrome is exactly
the classical C_1 syndrome.
- X-type stabilizers — take each row of H(C_2^{\perp}) and
turn its 1s into X operators. Since
X anticommutes with Z, these catch
phase-flip (Z) errors.
Why the nesting? A stabilizer group must be commuting. An X-type
generator from a vector u and a Z-type generator from a
vector v commute precisely when they overlap in an even number of
places, i.e. u \cdot v = 0 \pmod 2. Requiring every such pair to commute is
exactly the statement C_2^{\perp} \subset C_1 — the nesting condition
is the commutation condition. The code encodes
k = \dim C_1 - \dim C_2 logical qubits in n physical
ones.
Naming the two check families (mind the convention)
A tiny but perennial source of confusion. A stabilizer built from Z operators
catches X errors — so some texts call it an
"X-check" (named for the error it catches) while others call it a
"Z-type stabilizer" (named for the Paulis it is made of). Both describe the same
operator. The physics is unambiguous and worth memorising once:
Z\text{-operators} \;\longrightarrow\; \text{catch } X \text{ (bit-flip) errors}, \qquad X\text{-operators} \;\longrightarrow\; \text{catch } Z \text{ (phase-flip) errors}.
Whenever you read "the X checks," pause and ask whether the author means
made of X or catches X. On this page
we always name a stabilizer by the Paulis it is built from.
Worked example 1: the Steane code [[7,1,3]]
The Steane code is CSS at its most elegant. Start with the classical
[7,4,3] Hamming code, whose parity-check matrix has as its
columns the binary numbers 1 through 7:
H = \begin{pmatrix} 1 & 0 & 1 & 0 & 1 & 0 & 1 \\ 0 & 1 & 1 & 0 & 0 & 1 & 1 \\ 0 & 0 & 0 & 1 & 1 & 1 & 1 \end{pmatrix}.
This code is special: it contains its own dual, C^{\perp} \subset C.
So we may take C_1 = C and C_2 = C^{\perp}; the nesting
condition holds automatically. Even better, the same matrix H serves for
both families, so the six stabilizer generators are the three rows of H read once
as X strings and once as Z strings:
\begin{aligned} S_1^{X} &= X_1 X_3 X_5 X_7, & S_2^{X} &= X_2 X_3 X_6 X_7, & S_3^{X} &= X_4 X_5 X_6 X_7, \\ S_1^{Z} &= Z_1 Z_3 Z_5 Z_7, & S_2^{Z} &= Z_2 Z_3 Z_6 Z_7, & S_3^{Z} &= Z_4 Z_5 Z_6 Z_7. \end{aligned}
Count the parameters. There are n = 7 physical qubits and
6 independent generators, leaving
k = 7 - 6 = 1 logical qubit; the code distance is
d = 3, enough to correct any single-qubit error. In the standard
bracket notation that is [[7,1,3]] — the inner brackets flagging that it is a
quantum code. (As a bonus the Steane code has transversal Clifford gates: a
logical H, S or CNOT is just the physical gate applied
qubit-by-qubit — a gift of the code's symmetry.)
The stabilizer tableau
Here are all six generators laid out over the seven qubits. Notice the two families share the identical
pattern of columns — that is the Hamming matrix appearing twice, once in each Pauli flavour. Step through to
reveal the X-type block, then the Z-type block.
Any two rows overlap in an even number of qubits (check S_1 and
S_2: they share qubits 3 and
7), so every X-row commutes with every
Z-row — the nesting condition, visible in the picture.
Worked example 2: the syndrome points straight at the error
Suppose a single bit-flip X_5 strikes qubit 5. Measure
the three Z-type stabilizers. Each returns -1
(syndrome bit 1) exactly when it anticommutes with the error — that is,
when its qubit set contains qubit 5:
- S_1^{Z} = Z_1 Z_3 Z_5 Z_7 touches qubit 5 →
1.
- S_2^{Z} = Z_2 Z_3 Z_6 Z_7 does not →
0.
- S_3^{Z} = Z_4 Z_5 Z_6 Z_7 touches qubit 5 →
1.
The syndrome is (s_1, s_2, s_3) = (1, 0, 1). Read as a binary number with
s_1 the ones place, s_2 the twos and
s_3 the fours:
1\cdot 1 + 0\cdot 2 + 1\cdot 4 = 5.
The syndrome is the binary address of the flipped qubit — because it equals
H e_5, the fifth column of H, which by construction is
the binary numeral for 5. This is precisely the classical Hamming
decoder, imported wholesale: apply X_5 again to undo the error and the
qubit is repaired. Phase-flips are handled by the mirror-image computation on the
X-type checks, and a Y = iXZ error simply lights up
both families at once. Bit-flips and phase-flips, corrected on two independent channels.
There is something almost cheeky about the CSS construction. Classical coding theory had a half-century head
start — Hamming codes, Reed–Muller codes, BCH codes, all with polished decoders and sharp bounds. Quantum
error correction looked like it would have to start over from nothing, against a nastier enemy: a continuum
of errors, the no-cloning theorem forbidding backup copies, and measurement that destroys the very state
you are trying to protect. CSS codes sidestep almost all of it. Feed in two good classical codes with the
right nesting, and out comes a quantum code whose decoding is literally two runs of the classical
decoder — one for bit-flips, one for phase-flips. The Steane code is a whole quantum memory conjured out of
the [7,4] Hamming code, an object schoolchildren meet in an intro course. Much of
the modern theory of fault tolerance, right up to the
surface
code, is CSS at heart.
The magic has a price, and it is the nesting condition
C_2 \subset C_1. Two arbitrary classical codes, however excellent on their own,
will not combine into a valid quantum code: unless
C_2^{\perp} \subset C_1, some X-type check fails to
commute with some Z-type check, the "stabilizer group" is not abelian, and there
is no shared +1 codespace to protect. The Steane code works only because the
Hamming code happens to contain its own dual. So do not read CSS as "any classical code gives a quantum
code" — it gives one only with this extra structure, and the structure is exactly what buys you the clean
split into independent X and Z correction. A general
stabilizer code need not separate this way; CSS is the well-behaved special case, not the rule.
The idea in one box
- a CSS code is a quantum code built from two classical binary linear codes
C_2 \subset C_1 (equivalently C_2^{\perp} \subset C_1);
- Z-type stabilizers come from H(C_1) and catch
bit-flip (X) errors; X-type stabilizers
come from H(C_2^{\perp}) and catch phase-flip
(Z) errors — the two are corrected independently;
- the nesting condition C_2^{\perp} \subset C_1 is exactly what makes the two
families commute; it encodes
k = \dim C_1 - \dim C_2 logical qubits in n;
- the flagship is the Steane code [[7,1,3]], made from the
classical [7,4] Hamming code — one logical qubit in seven, correcting any
single-qubit error, with transversal Clifford gates.