DiracDirac

Part I · The Machinery of Quantum Information · Chapter 2

Gates and Circuits

A single qubit can be turned but not made to compute. Computation needs operations that bind qubits together — and, remarkably, a handful of them suffices to build every operation there is.

Sources: Nielsen & Chuang, Ch. 4 · Programming Quantum Computers, Chs. 2–5

Chapter 1 handed us a qubit and one verb: rotate it. That is not yet computing. To compute we need operations that act on several qubits at once and let them influence each other — and we need to know which operations are legal, how a machine strings them into a program, and how few distinct kinds we must build before we can build all the rest. This chapter answers all three. The legal operations are the unitary matrices; the program is a circuit of them; and a set as small as three — — is enough to approximate any operation whatsoever. By the end you will have grown the single-qubit kit into a full multi-qubit simulator, checked amplitude by amplitude against an independent matrix engine built out of explicit Kronecker products.

What this chapter covers

  • 2.1Gates are unitary. Why legal operations are exactly the unitary matrices; the Pauli gates X, Y, Z, then H, S, T, and the rotations Rx, Ry, Rz.
  • 2.2Many qubits. The tensor product: n qubits as a vector of 2ⁿ amplitudes, labelled by bitstrings, and how a one-qubit gate acts inside it.
  • 2.3Two-qubit gates and circuits. CNOT, CZ, SWAP, and controlled gates in general; the circuit model of wires and boxes; a Bell state you can build by toggling gates.
  • 2.4Universality and no-cloning. A tiny universal gate set, the Solovay–Kitaev theorem, and the no-cloning theorem — one line of linear algebra that forbids a copier.
  • 2.5The Lab. A from-scratch multi-qubit state-vector simulator, self-refereed — including a two-path cross-check against dense Kronecker-built unitaries on every basis input.

A qubit's state is a unit vector. Whatever we do to it between preparation and measurement must keep it a unit vector — total probability cannot leak away or be manufactured. The linear maps that preserve the length of every vector are precisely the unitary ones: a matrix is unitary when

(2.1)

where is the conjugate transpose. The one line of algebra that earns this: if , then , so the norm is unchanged for every input exactly when . A quantum gate is just such a matrix, and unitarity has a free bonus: every gate is invertible, with inverse . Quantum computation is reversible.

The workhorses on one qubit start with the three Pauli matrices, met informally in Chapter 1 and defined properly now:

(2.2)

is the quantum NOT (); flips the sign of and so writes phase; does both. Each squares to the identity and is its own inverse. Next the Hadamard, which makes superpositions, and the phase gates and , which rotate the relative phase by a quarter- and an eighth-turn:

(2.3)

Note and : the phase gates form a ladder. Finally, the continuous families — the rotations about the three Bloch axes, generated by the Paulis,

(2.4)

with the analogous exponentials of and . These are exactly the moves that steer the Bloch vector of Chapter 1: spins it about the pole, tip it. A physical control knob — a laser pulse, a microwave burst — is, in the mathematics, one of these rotations.

One qubit is a vector in . Two qubits are not two separate vectors — they live together in the tensor product , spanned by the four basis states

(2.5)

For qubits the pattern continues: the state space is , and a general state is a superposition over all bitstrings,

(2.6)

This is the exponential that defines the field: n qubits carry amplitudes. Thirty qubits already need a billion complex numbers; three hundred would need more numbers than there are atoms in the visible universe. That is the whole promise and the whole difficulty at once — and it is why §2.5 stores a state as a flat array of amplitudes and stops at on a laptop.

A single-qubit gate applied to qubit of an -qubit register is, formally, the tensor product of on that slot with the identity everywhere else — e.g. . You never build that matrix. Because touches only bit of the index, it mixes amplitudes strictly in pairs that differ in that one bit, leaving the other bits as spectators. That single observation is the engine of the whole simulator.

Convention · reading a basis label

We fix one convention for the whole book, and never break it. A basis label is written with leftmost. When we speak of “the state numbered ” — the index into the flat amplitude array — we read that label as a binary integer with the most-significant bit:

So is amplitude index , not . Individual Rust labs may, as an implementation detail, assign bits to array indices in the opposite order for speed — §2.5's simulator does exactly that, storing qubit in bit so that a gate sweep is a stride — but where a lab does, it says so in a comment and converts on the way out. Every printed label, and every bar the widgets draw, obeys the rule above in both its label and its position: the four two-qubit bars run , never first. Fixing this now spares us a subtle sign- and ordering-confusion in every chapter that follows.

2.3Two-qubit gates and the circuit model

C · ConceptsF · Formalism

Single-qubit gates never entangle: applied to a product state they return a product state. To bind qubits we need gates that act on two at once. The fundamental one is the controlled-NOT (CNOT): pick a control qubit and a target, and flip the target if and only if the control is . In the basis (control first),

(2.7)

CNOT is one instance of a general recipe: a controlled- applies the single-qubit gate to the target only on the branch where the control is set, and does nothing on the other branch — . Taking gives CZ; three CNOTs in a row (alternating control and target) give SWAP, which exchanges two qubits. Every one of these is unitary, so every one is reversible.

A quantum circuit draws a computation as a diagram: each qubit is a horizontal wire, each gate a box (or a control dot joined to a target ) sitting on the wires it touches, and time runs left to right. At the right edge, a measurement meter turns each qubit into a classical bit by the Born rule of Chapter 1. The most famous small circuit is two gates long:

the Bell circuittime flows left → rightq0: |0⟩q1: |0⟩HCNOTbitbitoutput: (|00⟩ + |11⟩)/√2 — the two meters always agree
Figure 2.1. The Bell circuit. A Hadamard puts q0 into an equal superposition; the CNOT then copies that indecision onto q1 by flipping it exactly on the |1⟩ branch. The output (|00⟩+|11⟩)/√2 is entangled: neither qubit has a state of its own, yet the two measurement meters are guaranteed to read the same bit.

Build it yourself. Toggle the gates below and watch the four output probabilities, ordered by the convention of §2.2: alone spreads q0 over two outcomes; adding the CNOT ties the two wires together so only and survive — the Bell state. The two toggles let you start from a different corner of the basis; the readout tells you, from the determinant of the amplitude matrix, whether what you have built factors.

Toggle the gates

Gates act in the order listed, top to bottom in time. The state starts in |00⟩, and the bars are ordered |00⟩, |01⟩, |10⟩, |11⟩ — q0 first, as in §2.2.

The amplitude determinant is 0.500, not zero, so this output cannot be written as a product of a q0 state and a q1 state: the two qubits are entangled — measuring one fixes the other. With H and CNOT both on you have built the Bell circuit. Entanglement is the subject of Chapter 3.
0.50|000.00|010.00|100.50|11

The output of then CNOT cannot be factored as for any amplitudes — expand that product and you always get a or term unless one qubit is trivial. That un-factorability is entanglement, and it is the whole subject of Chapter 3; here we only note that two ordinary gates produce it.

How many kinds of gate must a machine physically build? Astonishingly few. A set of gates is universal if any unitary on any number of qubits can be built (or approximated as closely as you like) from gates in the set. The discrete set

(2.8)

is universal. CNOT supplies entanglement between qubits; and together generate a dense set of single-qubit rotations (their product is an irrational rotation of the Bloch sphere, so repeated application lands arbitrarily near any target). “Dense” is the key word: you cannot hit most unitaries exactly with a finite gate set, only approach them. The Solovay–Kitaev theorem makes that approach cheap — any single-qubit unitary can be approximated to accuracy using only gates from the set, with a small constant . We state it without proof; the practical upshot is that the finiteness of a real machine's gate menu costs only polylogarithmic overhead, not an exponential one.

Linearity giveth reversibility; it also taketh away a power you might expect. The no-cloning theorem: there is no unitary that copies an arbitrary unknown qubit. Suppose one existed, with for every . Apply it to and to : it must give and . Now feed it . Linearity forces

(2.9)

but a genuine copy would have to be . These are different states, so no such exists. You cannot photocopy an unknown quantum state. The proof is a single use of linearity, yet the consequence is enormous: it forbids the obvious classical error-correction trick (Chapter 10 must be cleverer), and it secures quantum key distribution against a copying eavesdropper. What (2.9) also shows, in passing, is that our copier does not fail quietly — it entangles the two qubits instead. That output is exactly the Bell state of §2.3, and the reason it is not a copy is the reason Chapter 3 exists.

Now we cash in §2.2. A state is a flat vector of complex amplitudes; a one-qubit gate on qubit sweeps the amplitude pairs that differ in bit , leaving the rest untouched:

ch02-simulator/src/main.rs — a 1-qubit gate on qubit q
1/// An n-qubit pure state: 2^n complex amplitudes, indexed so that qubit q is
2/// bit q of the index (qubit 0 is the least-significant bit).
3#[derive(Clone)]
4struct State {
5 n: usize,
6 amps: Vec<C>,
7}
8
9impl State {
10 // … basis(), dim(), norm_sqr() …
11
12 /// Apply a 1-qubit gate to qubit q: sweep the 2^(n-1) amplitude pairs
13 /// (i, i|2^q) that differ only in bit q, mixing each by the 2x2 matrix.
14 fn apply_1q(&mut self, u: &Gate1, q: usize) {
15 let bit = 1usize << q;
16 for i in 0..self.dim() {
17 if i & bit == 0 {
18 let j = i | bit;
19 let a = self.amps[i];
20 let b = self.amps[j];
21 self.amps[i] = u[0][0] * a + u[0][1] * b;
22 self.amps[j] = u[1][0] * a + u[1][1] * b;
23 }
24 }
25 }
26
27 // … apply_ctrl(), swap(), prob0(), measure(), expect_z() …
28}

A controlled gate is the same sweep, gated by the control bit — do the mix only on the pairs whose control is set. CNOT and CZ fall straight out:

ch02-simulator/src/main.rs — controlled gates
1/// Apply a controlled 1-qubit gate: run `u` on qubit `tgt` only for the
2 /// amplitude pairs whose control bit `ctrl` is set.
3 fn apply_ctrl(&mut self, u: &Gate1, ctrl: usize, tgt: usize) {
4 assert!(ctrl != tgt, "control and target must differ");
5 let cbit = 1usize << ctrl;
6 let tbit = 1usize << tgt;
7 for i in 0..self.dim() {
8 if i & tbit == 0 && i & cbit != 0 {
9 let j = i | tbit;
10 let a = self.amps[i];
11 let b = self.amps[j];
12 self.amps[i] = u[0][0] * a + u[0][1] * b;
13 self.amps[j] = u[1][0] * a + u[1][1] * b;
14 }
15 }
16 }
17
18 fn cnot(&mut self, ctrl: usize, tgt: usize) {
19 self.apply_ctrl(&g_x(), ctrl, tgt);
20 }
21 fn cz(&mut self, ctrl: usize, tgt: usize) {
22 self.apply_ctrl(&g_z(), ctrl, tgt);
23 }

The strongest referee runs one fixed three-qubit circuit two independent ways: the fast per-qubit simulator above, and a plodding reference that assembles the full unitary of every gate by explicit Kronecker products and multiplies them in time order. Sharing no application code — only the nine 2×2 gate-matrix constants themselves — the two must still agree. Note that they are compared on all eight basis inputs, not just : one input only ever probes one column of the matrix, and it is easy to write a circuit whose error hides in the other seven — the on q1, for instance, does nothing at all to , so a wrong phase there would pass a single-input check untouched.

ch02-simulator/src/main.rs — the two-path cross-check
1/// The circuit the cross-check referee runs: nine gates on three qubits,
2/// chosen to touch every primitive (1-qubit gates, a controlled-X, a
3/// controlled-Z, a SWAP, and an irrational-angle rotation so no amplitude is
4/// exactly representable). Path (a) — the fast simulator — is this function;
5/// path (b) rebuilds the same nine gates as dense 8x8 Kronecker products.
6fn fixed_circuit(s: &mut State) {
7 s.apply_1q(&g_h(), 0);
8 s.apply_1q(&g_t(), 1);
9 s.apply_1q(&g_ry(0.9), 2);
10 s.cnot(0, 1);
11 s.cz(1, 2);
12 s.apply_1q(&g_rz(0.7), 0);
13 s.swap(0, 2);
14 s.cnot(2, 1);
15 s.apply_1q(&g_x(), 0);
16}
17
18// … in main(): …
19
20 // --- Referee 6: the cross-check (strongest) ---------------------------
21 // Path (a): the fast per-qubit simulator runs a fixed 9-gate circuit.
22 // Path (b): dense 8x8 operators, Kronecker-built, multiplied in TIME order
23 // (each new gate multiplies on the LEFT: U_k · … · U_1). The two paths
24 // share no application code — only the nine 2x2 gate-matrix constants, so
25 // a mis-defined gate ENTRY is out of this referee's reach (Bell/GHZ/truth
26 // tables cover H and X; T/S entries rest on the definitions) — and we
27 // compare them on ALL EIGHT basis inputs — one input would only exercise a
28 // single column of the 8x8 unitary.
29 let ops: Vec<Dense> = vec![
30 full_1q(3, 0, &g_h()),
31 full_1q(3, 1, &g_t()),
32 full_1q(3, 2, &g_ry(0.9)),
33 full_ctrl(3, 0, 1, &g_x()),
34 full_ctrl(3, 1, 2, &g_z()),
35 full_1q(3, 0, &g_rz(0.7)),
36 full_swap(3, 0, 2),
37 full_ctrl(3, 2, 1, &g_x()),
38 full_1q(3, 0, &g_x()),
39 ];
40 let mut full = Dense::ident(8);
41 for op in &ops {
42 full = op.matmul(&full);
43 }
44 let mut worst_cross: f64 = 0.0;
45 for b in 0..8 {
46 let mut fast = State::basis(3, b);
47 fixed_circuit(&mut fast);
48 let dense_vec = full.apply(&State::basis(3, b).amps);
49 for i in 0..8 {
50 worst_cross = worst_cross.max((fast.amps[i] - dense_vec[i]).norm());
51 }
52 }
53 assert!(worst_cross.is_finite()); // FINITE before small
54 // … the referee's tolerance and the JSON it writes …

Each referee's achieved value and the tolerance it was gated at are written to JSON, which the panel below reads live — no number in this section is hardcoded. Between them the referees cover unitarity (the norm survives a long random circuit), the CNOT truth table written out by hand, the Bell and GHZ states amplitude by amplitude, collapse and correlation under actual measurement, the Kronecker cross-check, and GHZ built at every register size the sweep reaches. A few are gated at exactly zero rather than at a tolerance, because their arithmetic is exact; the panel says which.

Loading /data/ch02/simulator.json… (run cargo run --release in Rust-QML/ch02-simulator)

Run it yourself with cargo run --release in Rust-QML/ch02-simulator. This simulator is not a one-chapter prop: it is the laboratory bench for the rest of the book. Every algorithm in Part II — the oracles, the Fourier transform, Grover's search — will be built and refereed on exactly this engine.

2.6Exercises

1. (F) Verify directly that is unitary by computing . Then show and by matrix multiplication — the Hadamard swaps the and axes of the Bloch sphere.

2. (F) Write out as a 4-component vector in the basis, apply the CNOT matrix (2.7), and confirm you land on the Bell state .

3. (C) Using the circuit explorer, find two different toggle settings whose output is the single basis state with probability 1. Then find a setting whose output is an un-entangled superposition — q0 spread over both outcomes while q1 stays definite — and check that the readout reports a zero determinant. Which single toggle turns that one into an entangled state? Finally, argue from the gate list why no setting can put both qubits into a superposition at once, and name the one gate you would have to add.

4. (P) Add a toffoli (CCNOT) gate to the simulator: flip the target only when both controls are set. Verify its truth table on all eight three-qubit basis states, and confirm it is its own inverse.

5. (P, hard) Extend the Kronecker cross-check to a random circuit: generate a random sequence of gates on 4 qubits, run it through both the fast simulator and the dense reference, and assert agreement to . How does the dense path's runtime scale with the number of qubits, and why does it cap the check at a handful?

The bridgeChapter 3: Entanglement as a Resource

Where you stand. You have the legal operations — the unitary gates — a universal set as small as {H, T, CNOT}, the circuit model, a fixed convention for reading and ordering basis labels, and a from-scratch multi-qubit simulator whose every amplitude is reproduced by an independently built matrix engine on every basis input. You have also seen, twice, that two ordinary gates produce a state that cannot be factored.

The open question. That un-factorable Bell state kept appearing — as the output of H then CNOT, and as the reason a quantum copier fails. What exactly is this entanglement, how do we measure how much of it a state has, and what can it actually do for us?

What comes next. We make entanglement precise: the Schmidt decomposition and entanglement entropy that quantify it, Bell inequalities that prove it is not classical correlation in disguise, and its first payoffs — superdense coding and teleportation — computed and refereed on the simulator you just built.

Continue to Chapter 3