A quantum gate is a unitary matrix. A quantum circuit is a sequence of gates applied to qubits — which, mathematically, is a product of unitary matrices (with tensor products for parallel operations). If this sounds like linear algebra, that's...
In This Chapter
- Learning Objectives
- 6.1 Introduction: Gates Are Matrices, Circuits Are Matrix Multiplication
- 6.2 Single-Qubit Gates
- 6.3 Two-Qubit Gates
- 6.4 Three-Qubit Gates
- 6.5 Universal Gate Sets
- 6.6 Gate Decomposition: Building Big Gates from Small Ones
- 6.7 Gate Identities and Commutation Relations
- 6.8 Complete Gate Reference
- 6.9 The Gottesman-Knill Theorem and Why Clifford Is Not Enough
- 6.10 Gate Compilation and Transpilation
- 6.11 Verifying Gate Operations
Chapter 6: Quantum Gates: Pauli (X, Y, Z), Hadamard, CNOT, Phase, Toffoli — The Building Blocks of Quantum Circuits
Learning Objectives
By the end of this chapter, you will be able to:
- Represent every fundamental quantum gate as a unitary matrix and understand its action on the Bloch sphere
- Construct quantum circuits using single-qubit, two-qubit, and three-qubit gates in Qiskit
- Decompose arbitrary single-qubit unitaries into elementary rotation gates
- Understand the concept of universal gate sets and the significance of the Solovay-Kitaev theorem
- Verify gate operations through measurement and state tomography
- Read and write quantum circuit diagrams
- Derive gate identities from matrix multiplication and interpret them geometrically
- Understand the role of entangling gates in creating quantum correlations
- Explain why the Clifford group is classically simulable and why non-Clifford gates are needed for quantum advantage
- Implement gate decompositions and verify their correctness
6.1 Introduction: Gates Are Matrices, Circuits Are Matrix Multiplication
A quantum gate is a unitary matrix. A quantum circuit is a sequence of gates applied to qubits — which, mathematically, is a product of unitary matrices (with tensor products for parallel operations). If this sounds like linear algebra, that's because it is.
Recurring Theme — Quantum Is Linear Algebra, Not Magic: Every quantum gate is a matrix. Every quantum circuit is a matrix product. Every quantum algorithm is a sequence of matrix multiplications. There is no mystery in the individual operations — the power of quantum computing comes from the interference patterns that emerge when these matrices are composed and measured, not from any individual gate.
Let us be precise about what "unitary" means. A matrix $U$ is unitary if $U^\dagger U = UU^\dagger = I$, where $U^\dagger$ is the conjugate transpose. Unitarity has three important consequences:
- Probability conservation: Unitary evolution preserves the norm of quantum states, $\langle\psi'|\psi'\rangle = \langle\psi|U^\dagger U|\psi\rangle = \langle\psi|\psi\rangle = 1$.
- Reversibility: Every unitary operation has an inverse ($U^{-1} = U^\dagger$). In principle, any quantum computation can be run backwards.
- Linearity: Unitary evolution is linear: $U(\alpha|\psi\rangle + \beta|\phi\rangle) = \alpha U|\psi\rangle + \beta U|\phi\rangle$. This is the source of both the power (quantum parallelism) and the constraints (no-cloning) of quantum mechanics.
In this chapter, we survey the essential gates of quantum computing. For each gate, we provide: - The matrix representation - The Bloch sphere interpretation - The Qiskit implementation - Measurement-based verification - Geometric intuition and worked examples
6.2 Single-Qubit Gates
Single-qubit gates are $2 \times 2$ unitary matrices. They correspond to rotations on the Bloch sphere. Any single-qubit unitary can be written (up to a global phase) as:
$$U(\theta, \phi, \lambda) = \begin{pmatrix} \cos\frac{\theta}{2} & -e^{i\lambda}\sin\frac{\theta}{2} \\ e^{i\phi}\sin\frac{\theta}{2} & e^{i(\phi+\lambda)}\cos\frac{\theta}{2} \end{pmatrix}$$
This is the IBM Qiskit $U_3$ gate (now called $U$ gate). Every single-qubit gate in this section is a special case.
6.2.1 Pauli-X Gate (NOT Gate)
The quantum analog of the classical NOT gate:
$$X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix}$$
Action: $X|0\rangle = |1\rangle$, $X|1\rangle = |0\rangle$. On the Bloch sphere, this is a $\pi$ rotation about the X-axis.
Properties: - $X^2 = I$ (self-inverse) - $X = X^\dagger$ (Hermitian) - $\det(X) = -1$ - Eigenvalues: $+1$ with eigenvector $|+\rangle$, $-1$ with eigenvector $|-\rangle$
Action on superposition: $X(\alpha|0\rangle + \beta|1\rangle) = \beta|0\rangle + \alpha|1\rangle$. The X gate swaps the amplitudes of $|0\rangle$ and $|1\rangle$.
|0⟩ |1⟩
| |
| X |
| =====> |
| |
|1⟩ |0⟩
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.quantum_info import Statevector, Operator
from qiskit_aer import AerSimulator
import numpy as np
# X gate: flips |0⟩ to |1⟩
qc = QuantumCircuit(1, 1)
qc.x(0)
qc.measure(0, 0)
simulator = AerSimulator()
result = simulator.run(qc, shots=1024).result()
counts = result.get_counts()
print("X|0⟩ measurement:", counts) # Should be all '1'
Worked Example 6.1: $X|+\rangle = X \cdot \frac{1}{\sqrt{2}}(|0\rangle + |1\rangle) = \frac{1}{\sqrt{2}}(|1\rangle + |0\rangle) = |+\rangle$. The X gate leaves $|+\rangle$ unchanged (it's an eigenstate with eigenvalue $+1$).
Worked Example 6.2: $X|i+\rangle = X \cdot \frac{1}{\sqrt{2}}(|0\rangle + i|1\rangle) = \frac{1}{\sqrt{2}}(|1\rangle + i|0\rangle) = \frac{i}{\sqrt{2}}(|0\rangle + \frac{1}{i}|1\rangle)$. Since $\frac{1}{i} = -i$, this gives $\frac{i}{\sqrt{2}}(|0\rangle - i|1\rangle) = i \cdot |-i\rangle$. Up to a global phase, X maps $|i+\rangle$ to $|-i\rangle$.
Try It Yourself: Compute $X|\psi\rangle$ for $|\psi\rangle = \cos(\pi/8)|0\rangle + \sin(\pi/8)|1\rangle$. Verify that the probabilities are swapped: $P(0) = \sin^2(\pi/8)$ and $P(1) = \cos^2(\pi/8)$.
6.2.2 Pauli-Y Gate
$$Y = \begin{pmatrix} 0 & -i \\ i & 0 \end{pmatrix}$$
Action: $Y|0\rangle = i|1\rangle$, $Y|1\rangle = -i|0\rangle$. A $\pi$ rotation about the Y-axis, with a phase kick. The $i$ factor is a relative phase — it matters in interference but not in measurement probabilities of a Z-basis measurement.
Properties: - $Y^2 = -I$ (not $I$! — but $Y^4 = I$) - $Y = -iXZ$ (relationship to other Paulis) - $Y^\dagger = Y$ (Hermitian, despite the complex entries) - Eigenvalues: $+1$ with eigenvector $|i+\rangle = \frac{1}{\sqrt{2}}(|0\rangle + i|1\rangle)$, $-1$ with eigenvector $|-i\rangle = \frac{1}{\sqrt{2}}(|0\rangle - i|1\rangle)$
The phase matters in superposition. While $Y$ and $X$ have the same effect on measurement probabilities in the Z-basis ($Y|0\rangle = i|1\rangle$ gives $P(1) = 1$, same as $X|0\rangle = |1\rangle$), they have different effects in other bases and in interference experiments.
Worked Example 6.3: Compute $HYH|0\rangle$:
$$H|0\rangle = |+\rangle = \frac{1}{\sqrt{2}}(|0\rangle + |1\rangle)$$
$$Y|+\rangle = \frac{1}{\sqrt{2}}(i|1\rangle - i|0\rangle) = \frac{-i}{\sqrt{2}}(|0\rangle - |1\rangle) = -i|-\rangle$$
$$H(-i|-\rangle) = -iH|-\rangle = -i|1\rangle$$
So $HYH|0\rangle = -i|1\rangle$. Compare with $HXH|0\rangle = Z|0\rangle = |0\rangle$. The conjugation $HYH$ gives a different result than $HXH$, even though both involve the "same type" of rotation.
qc = QuantumCircuit(1, 1)
qc.y(0)
qc.measure(0, 0)
result = simulator.run(qc, shots=1024).result()
print("Y|0⟩ measurement:", result.get_counts()) # All '1' (phase invisible to Z-measurement)
# But the phase matters in interference
qc2 = QuantumCircuit(1, 1)
qc2.h(0) # |+>
qc2.y(0) # Y|+> = -i|->
qc2.h(0) # H|-i|-> = -i|1>
qc2.measure(0, 0)
result2 = simulator.run(qc2, shots=1024).result()
print("HYH|0> measurement:", result2.get_counts()) # All '1'
# Compare with X conjugation
qc3 = QuantumCircuit(1, 1)
qc3.h(0)
qc3.x(0) # X|+> = |+>
qc3.h(0) # H|+> = |0>
qc3.measure(0, 0)
result3 = simulator.run(qc3, shots=1024).result()
print("HXH|0> measurement:", result3.get_counts()) # All '0'
6.2.3 Pauli-Z Gate (Phase Flip)
$$Z = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix}$$
Action: $Z|0\rangle = |0\rangle$, $Z|1\rangle = -|1\rangle$. A $\pi$ rotation about the Z-axis. It flips the phase of $|1\rangle$ while leaving $|0\rangle$ unchanged. In the $\{|+\rangle, |-\rangle\}$ basis, $Z$ acts as a bit-flip.
Properties: - $Z^2 = I$ (self-inverse) - $Z = Z^\dagger$ (Hermitian) - $Z = R_z(\pi)$ (rotation by $\pi$ around Z-axis, up to global phase) - Eigenvalues: $+1$ with eigenvector $|0\rangle$, $-1$ with eigenvector $|1\rangle$
Worked Example 6.4: Compute $Z|+\rangle$:
$$Z|+\rangle = Z \cdot \frac{1}{\sqrt{2}}(|0\rangle + |1\rangle) = \frac{1}{\sqrt{2}}(|0\rangle - |1\rangle) = |-\rangle$$
So $Z$ flips $|+\rangle$ to $|-\rangle$ — it acts as a "bit flip" in the X-basis.
Worked Example 6.5: Compute $HZH$:
$$HZH = \frac{1}{2}\begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}\begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix}\begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} = \frac{1}{2}\begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}\begin{pmatrix} 1 & 1 \\ -1 & 1 \end{pmatrix} = \frac{1}{2}\begin{pmatrix} 0 & 2 \\ 2 & 0 \end{pmatrix} = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} = X$$
This is a fundamental identity: $HZH = X$. Conjugating $Z$ by $H$ converts it to $X$. Similarly, $HXH = Z$. The Hadamard interchanges the X and Z Pauli operators.
# Z gate on |+⟩ state flips it to |-⟩
qc = QuantumCircuit(1, 1)
qc.h(0) # |0⟩ → |+⟩
qc.z(0) # |+⟩ → |-⟩
qc.h(0) # |-⟩ → |1⟩ (Hadamard is self-inverse)
qc.measure(0, 0)
result = simulator.run(qc, shots=1024).result()
print("HZH|0⟩ measurement:", result.get_counts()) # All '1'
6.2.4 The Pauli Group and Pauli Commutation Relations
The three Pauli matrices $\{I, X, Y, Z\}$ form a group under multiplication (up to phases):
$$X^2 = Y^2 = Z^2 = I$$ $$XY = iZ, \quad YX = -iZ$$ $$YZ = iX, \quad ZY = -iX$$ $$ZX = iY, \quad XZ = -iY$$
The commutation and anti-commutation relations are:
$$[X, Y] = 2iZ, \quad [Y, Z] = 2iX, \quad [Z, X] = 2iY$$ $$\{X, Y\} = 0, \quad \{Y, Z\} = 0, \quad \{Z, X\} = 0$$
where $[A, B] = AB - BA$ is the commutator and $\{A, B\} = AB + BA$ is the anti-commutator.
The Pauli group $\mathcal{P}_1$ on one qubit is $\{\pm I, \pm iI, \pm X, \pm iX, \pm Y, \pm iY, \pm Z, \pm iZ\}$. It is closed under multiplication and contains all possible products of Pauli matrices (with phases).
The $n$-qubit Pauli group $\mathcal{P}_n$ consists of all tensor products of Pauli matrices: $\{P_1 \otimes P_2 \otimes \cdots \otimes P_n : P_i \in \{I, X, Y, Z\}\}$ (with phases $\pm 1, \pm i$). It has $4^n$ elements (ignoring phases) and plays a central role in stabilizer codes and the Gottesman-Knill theorem.
Common Misconception: "Pauli matrices are just arbitrary basis choices." The Pauli matrices are deeply connected to the geometry of the Bloch sphere. They generate rotations: $R_x(\theta) = e^{-i\theta X/2}$, $R_y(\theta) = e^{-i\theta Y/2}$, $R_z(\theta) = e^{-i\theta Z/2}$. Any single-qubit unitary can be expressed as a product of Pauli rotations. Moreover, the anti-commutation relations $\{X, Y\} = 0$ are the algebraic origin of the uncertainty principle: $X$ and $Y$ cannot be simultaneously measured.
6.2.5 Hadamard Gate
$$H = \frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}$$
The Hadamard is the superposition creator. It maps the computational basis to the superposition basis:
$$H|0\rangle = \frac{|0\rangle + |1\rangle}{\sqrt{2}} = |+\rangle, \qquad H|1\rangle = \frac{|0\rangle - |1\rangle}{\sqrt{2}} = |-\rangle$$
On the Bloch sphere, $H$ is a $\pi$ rotation about the axis $(X+Z)/\sqrt{2}$ (equivalently, a $\pi$ rotation about the axis at 45 degrees between X and Z in the XZ-plane).
|0⟩ |+⟩ = (|0⟩+|1⟩)/√2
| |
| H |
| =====> |
| |
|1⟩ |-⟩ = (|0⟩-|1⟩)/√2
Key property: $H^2 = I$ (the Hadamard is self-inverse). Applying it twice returns the original state.
Other key properties: - $H = \frac{1}{\sqrt{2}}(X + Z)$ (up to normalization) - $HXH = Z$ and $HZH = X$ (H interchanges X and Z) - $H = R_y(\pi/4) \cdot R_z(\pi) \cdot R_y(\pi/4)$... wait, let me be more precise: - $H = e^{i\pi/2} R_y(\pi/2) \cdot R_z(\pi)$... actually, the decomposition is simpler: $H = R_z(\pi) \cdot R_y(\pi/2)$ up to a global phase. - $H$ is Hermitian ($H = H^\dagger$) and unitary ($H^2 = I$)
Worked Example 6.6: What is $H|+\rangle$?
$$H|+\rangle = H \cdot \frac{1}{\sqrt{2}}(|0\rangle + |1\rangle) = \frac{1}{\sqrt{2}}(H|0\rangle + H|1\rangle) = \frac{1}{\sqrt{2}}(|+\rangle + |-\rangle) = \frac{1}{\sqrt{2}} \cdot \frac{2}{\sqrt{2}}|0\rangle = |0\rangle$$
So $H|+\rangle = |0\rangle$ and $H|-\rangle = |1\rangle$, confirming $H^2 = I$.
# Create equal superposition and verify
qc = QuantumCircuit(1, 1)
qc.h(0)
qc.measure(0, 0)
result = simulator.run(qc, shots=1024).result()
counts = result.get_counts()
print("H|0⟩ measurement:", counts) # ~50% '0', ~50% '1'
# Verify H² = I
qc2 = QuantumCircuit(1, 1)
qc2.h(0)
qc2.h(0)
qc2.measure(0, 0)
result2 = simulator.run(qc2, shots=1024).result()
print("HH|0⟩ measurement:", result2.get_counts()) # All '0'
6.2.6 Phase Gate (S) and π/8 Gate (T)
$$S = \begin{pmatrix} 1 & 0 \\ 0 & i \end{pmatrix} = \sqrt{Z}, \qquad T = \begin{pmatrix} 1 & 0 \\ 0 & e^{i\pi/4} \end{pmatrix} = \sqrt{S} = Z^{1/4}$$
The $S$ gate adds a $\pi/2$ phase to $|1\rangle$. The $T$ gate adds a $\pi/4$ phase. These are essential for constructing arbitrary gates and for magic state distillation in fault-tolerant quantum computing.
$$S = Z^{1/2}, \quad T = Z^{1/4}, \quad Z = S^2 = T^4$$
Properties of S: - $S^\dagger = S^3$ (inverse is $S^\dagger = S^{-1} = S^3$) - $S^2 = Z$ - $S^\dagger H S = H$ only if... let me compute: $S|0\rangle = |0\rangle$, $S|1\rangle = i|1\rangle$
Properties of T: - $T^\dagger = T^7$ (in the group, $T^8 = I$ up to global phase) - $T^2 = S$ - $T^4 = Z$
Worked Example 6.7: Compute $S|+\rangle$:
$$S|+\rangle = S \cdot \frac{1}{\sqrt{2}}(|0\rangle + |1\rangle) = \frac{1}{\sqrt{2}}(|0\rangle + i|1\rangle) = |i+\rangle$$
The $S$ gate maps the equator of the Bloch sphere from the X-axis toward the Y-axis. Specifically, it rotates the state $|+\rangle$ to $|i+\rangle$.
Worked Example 6.8: Compute $T|+\rangle$:
$$T|+\rangle = \frac{1}{\sqrt{2}}(|0\rangle + e^{i\pi/4}|1\rangle)$$
This state is on the equator of the Bloch sphere, rotated by $\pi/4$ from $|+\rangle$ toward $|i+\rangle$.
# Demonstrate S gate
qc = QuantumCircuit(1, 1)
qc.h(0)
qc.s(0)
qc.h(0)
qc.measure(0, 0)
result = simulator.run(qc, shots=1024).result()
print("HSH|0⟩:", result.get_counts()) # S maps |+⟩ to |+i⟩ = (|0⟩+i|1⟩)/√2
# T gate: T^4 = Z
qc_t = QuantumCircuit(1, 1)
qc_t.h(0)
for _ in range(4):
qc_t.t(0)
qc_t.h(0)
qc_t.measure(0, 0)
result_t = simulator.run(qc_t, shots=1024).result()
print("HT^4H|0⟩:", result_t.get_counts()) # Should be all '1' (same as HZH)
Common Misconception: "The T gate is just a phase — it can't create superpositions or entanglement." While $T$ only adds a phase, it is the key ingredient that makes quantum computing universal. Clifford circuits ($H$, $S$, $CNOT$) can be efficiently simulated classically. Adding $T$ gates breaks this simulability. The "magic" of quantum computing is concentrated in the $T$ gates — this is why the number of $T$ gates ($T$-count) is a key metric for fault-tolerant quantum computing.
6.2.7 Rotation Gates: Rx, Ry, Rz
The most general single-qubit gates are rotations:
$$R_x(\theta) = e^{-i\theta X/2} = \begin{pmatrix} \cos\frac{\theta}{2} & -i\sin\frac{\theta}{2} \\ -i\sin\frac{\theta}{2} & \cos\frac{\theta}{2} \end{pmatrix}$$
$$R_y(\theta) = e^{-i\theta Y/2} = \begin{pmatrix} \cos\frac{\theta}{2} & -\sin\frac{\theta}{2} \\ \sin\frac{\theta}{2} & \cos\frac{\theta}{2} \end{pmatrix}$$
$$R_z(\theta) = e^{-i\theta Z/2} = \begin{pmatrix} e^{-i\theta/2} & 0 \\ 0 & e^{i\theta/2} \end{pmatrix}$$
Derivation of $R_z(\theta)$:
$$R_z(\theta) = e^{-i\theta Z/2} = \sum_{n=0}^{\infty} \frac{(-i\theta/2)^n}{n!} Z^n$$
Since $Z^2 = I$, we can separate even and odd terms:
$$= \sum_{k=0}^{\infty} \frac{(-i\theta/2)^{2k}}{(2k)!} I + \sum_{k=0}^{\infty} \frac{(-i\theta/2)^{2k+1}}{(2k+1)!} Z = \cos(\theta/2) I - i\sin(\theta/2) Z$$
$$= \begin{pmatrix} \cos(\theta/2) - i\sin(\theta/2) & 0 \\ 0 & \cos(\theta/2) + i\sin(\theta/2) \end{pmatrix} = \begin{pmatrix} e^{-i\theta/2} & 0 \\ 0 & e^{i\theta/2} \end{pmatrix}$$
Any single-qubit unitary can be decomposed as:
$$U = e^{i\alpha} R_z(\phi) R_y(\theta) R_z(\lambda)$$
This is the Z-Y-Z decomposition, the foundation of Qiskit's $U$ gate. The three angles $(\theta, \phi, \lambda)$ plus the global phase $\alpha$ parameterize any $2 \times 2$ unitary.
Worked Example 6.9: Decompose the Hadamard gate using Z-Y-Z.
We need $H = e^{i\alpha} R_z(\phi) R_y(\theta) R_z(\lambda)$.
By inspection (or by solving the system of equations), we get $\theta = \pi/2$, $\phi = 0$, $\lambda = \pi$, $\alpha = \pi/2$:
$$e^{i\pi/2} R_z(0) R_y(\pi/2) R_z(\pi) = i \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix} \begin{pmatrix} \cos(\pi/4) & -\sin(\pi/4) \\ \sin(\pi/4) & \cos(\pi/4) \end{pmatrix} \begin{pmatrix} e^{-i\pi/2} & 0 \\ 0 & e^{i\pi/2} \end{pmatrix}$$
$$= i \begin{pmatrix} 1/\sqrt{2} & -1/\sqrt{2} \\ 1/\sqrt{2} & 1/\sqrt{2} \end{pmatrix} \begin{pmatrix} -i & 0 \\ 0 & i \end{pmatrix} = i \begin{pmatrix} -i/\sqrt{2} & -i/\sqrt{2} \\ -i/\sqrt{2} & i/\sqrt{2} \end{pmatrix} = \frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} = H$$
Worked Example 6.10: Compute $R_y(\pi/2)|0\rangle$:
$$R_y(\pi/2)|0\rangle = \begin{pmatrix} \cos(\pi/4) & -\sin(\pi/4) \\ \sin(\pi/4) & \cos(\pi/4) \end{pmatrix} \begin{pmatrix} 1 \\ 0 \end{pmatrix} = \begin{pmatrix} 1/\sqrt{2} \\ 1/\sqrt{2} \end{pmatrix} = |+\rangle$$
So $R_y(\pi/2)$ applied to $|0\rangle$ creates an equal superposition (like Hadamard, but without the phase flip on $|1\rangle$).
# Demonstrate Rx(π/2) creates equal superposition (like H but with different phases)
qc = QuantumCircuit(1, 1)
qc.rx(np.pi/2, 0)
qc.measure(0, 0)
result = simulator.run(qc, shots=1024).result()
print("Rx(π/2)|0⟩:", result.get_counts()) # ~50-50
# Z-Y-Z decomposition: reconstruct H gate
def u3_gate(theta, phi, lam):
"""Qiskit's U gate as a numpy matrix."""
return np.array([
[np.cos(theta/2), -np.exp(1j*lam)*np.sin(theta/2)],
[np.exp(1j*phi)*np.sin(theta/2), np.exp(1j*(phi+lam))*np.cos(theta/2)]
], dtype=complex)
H_reconstructed = u3_gate(np.pi/2, 0, np.pi)
print("Reconstructed H:\n", H_reconstructed)
print("Matches H?", np.allclose(H_reconstructed, np.array([[1,1],[1,-1]])/np.sqrt(2)))
6.2.8 The Global Phase Convention
Two unitaries that differ by a global phase $e^{i\alpha}$ are physically indistinguishable: $U$ and $e^{i\alpha}U$ produce the same measurement statistics for all possible measurements. This is because the probability of outcome $k$ is $|\langle k|U|\psi\rangle|^2$, and a global phase cancels out.
However, global phases do matter when gates are used in controlled operations. For example, $R_z(\theta)$ has a global phase of $e^{-i\theta/2}$ compared to the "phase gate" convention. When we write $CR_z(\theta)$ (controlled-$R_z$), the global phase becomes a relative phase on the target qubit and cannot be ignored.
In Qiskit, the convention is that the $U$ gate includes the global phase, while native hardware gates ($R_z$, $SX$, $X$) may drop it. The transpiler handles this distinction automatically.
6.2.9 The Bloch Sphere Picture
All single-qubit gates (up to a global phase) correspond to rotations on the Bloch sphere. This geometric picture is the most intuitive way to understand single-qubit dynamics.
The Bloch sphere is a unit sphere in $\mathbb{R}^3$ where: - The north pole ($+z$) represents $|0\rangle$ - The south pole ($-z$) represents $|1\rangle$ - The $+x$ direction represents $|+\rangle$ - The $-x$ direction represents $|-\rangle$ - The $+y$ direction represents $|i+\rangle = \frac{1}{\sqrt{2}}(|0\rangle + i|1\rangle)$ - The $-y$ direction represents $|-i\rangle = \frac{1}{\sqrt{2}}(|0\rangle - i|1\rangle)$
A general pure state $|\psi\rangle = \cos(\theta/2)|0\rangle + e^{i\phi}\sin(\theta/2)|1\rangle$ corresponds to the point $(\sin\theta\cos\phi, \sin\theta\sin\phi, \cos\theta)$ on the Bloch sphere.
Gate-rotation correspondence:
| Gate | Rotation axis | Rotation angle |
|---|---|---|
| $X$ | $x$-axis | $\pi$ |
| $Y$ | $y$-axis | $\pi$ |
| $Z$ | $z$-axis | $\pi$ |
| $H$ | $(x+z)/\sqrt{2}$ | $\pi$ |
| $S$ | $z$-axis | $\pi/2$ |
| $T$ | $z$-axis | $\pi/4$ |
| $R_x(\theta)$ | $x$-axis | $\theta$ |
| $R_y(\theta)$ | $y$-axis | $\theta$ |
| $R_z(\theta)$ | $z$-axis | $\theta$ |
ASCII Art: The Bloch Sphere with Key States and Rotations
|0⟩ (north pole)
●
/|\
/ | \
|+i⟩ ● | ● |-i⟩ (equator, ±y)
/ | \
/ | \
|+⟩ ●------●------● |-⟩ (equator, ±x)
\ | / (equator, ±x)
\ | /
\ | /
|1⟩ ● |/ (south pole)
Rotations:
X-gate: π rotation about x-axis (flips north↔south, keeps ±x)
Y-gate: π rotation about y-axis (flips north↔south, with phase)
Z-gate: π rotation about z-axis (keeps north and south, flips ±x and ±y)
H-gate: π rotation about (x+z)/√2 axis (swaps north↔+x, south↔-x)
Euler's rotation theorem and single-qubit unitaries: By Euler's rotation theorem, any rotation of the sphere can be decomposed into three rotations about two axes. The Z-Y-Z decomposition $U = e^{i\alpha}R_z(\phi)R_y(\theta)R_z(\lambda)$ reflects this: we rotate by $\lambda$ about $z$, then by $\theta$ about $y$, then by $\phi$ about $z$, and finally apply a global phase $e^{i\alpha}$.
Worked Example 6.14: Trace the state $|0\rangle$ through the circuit $H \cdot R_y(\pi/4) \cdot H$ on the Bloch sphere.
- Start at $|0\rangle$ (north pole)
- $H$ rotates to $|+\rangle$ (equator, $+x$)
- $R_y(\pi/4)$ rotates by $\pi/4$ about the $y$-axis: from $+x$ toward $-z$
- $H$ rotates back
The final state is at angle $\theta = \pi/4$ from $|0\rangle$ toward $|1\rangle$ on the $xz$-plane.
# Visualize gate sequences on the Bloch sphere using state vectors
from qiskit.quantum_info import Statevector
import numpy as np
# Trace states through the circuit H · Ry(π/4) · H
states = []
qc = QuantumCircuit(1)
states.append(Statevector.from_instruction(qc)) # |0⟩
qc.h(0)
states.append(Statevector.from_instruction(qc)) # |+⟩
qc.ry(np.pi/4, 0)
states.append(Statevector.from_instruction(qc)) # Ry(π/4)|+⟩
print("State evolution:")
for i, s in enumerate(states):
print(f" Step {i}: {np.round(s.data, 4)}")
# Verify HZH = X by tracing on Bloch sphere
# |0⟩ → H|0⟩ = |+⟩ → Z|+⟩ = |-⟩ → H|-⟩ = |1⟩
# This traces: north → equator+x → equator-x → south
6.3 Two-Qubit Gates
Two-qubit gates create entanglement — the defining resource of quantum computation. A two-qubit gate is a $4 \times 4$ unitary matrix acting on $\mathbb{C}^2 \otimes \mathbb{C}^2$.
6.3.1 CNOT (Controlled-NOT, CX)
The CNOT gate flips the target qubit if and only if the control qubit is $|1\rangle$:
● (control)
|
⊕ (target)
$$CNOT = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \end{pmatrix} = |0\rangle\langle 0| \otimes I + |1\rangle\langle 1| \otimes X$$
Action on computational basis states:
$$CNOT|00\rangle = |00\rangle, \quad CNOT|01\rangle = |01\rangle, \quad CNOT|10\rangle = |11\rangle, \quad CNOT|11\rangle = |10\rangle$$
The CNOT, together with single-qubit gates, is universal for quantum computation.
Action on superposition — creating entanglement:
$$CNOT \cdot (H \otimes I)|00\rangle = CNOT \cdot \frac{1}{\sqrt{2}}(|00\rangle + |10\rangle) = \frac{1}{\sqrt{2}}(|00\rangle + |11\rangle) = |\Phi^+\rangle$$
This is the fundamental entangling operation: starting from a product state, CNOT creates a Bell state.
Detailed action on general two-qubit states:
For $|\psi\rangle = \alpha|00\rangle + \beta|01\rangle + \gamma|10\rangle + \delta|11\rangle$:
$$CNOT|\psi\rangle = \alpha|00\rangle + \beta|01\rangle + \gamma|11\rangle + \delta|10\rangle$$
The CNOT swaps the amplitudes $\gamma$ and $\delta$ (corresponding to the control being $|1\rangle$).
The CNOT is self-inverse: $CNOT^2 = I$. Applying CNOT twice with the same control and target returns the original state.
# CNOT: create a Bell state
qc = QuantumCircuit(2, 2)
qc.h(0) # Control in superposition
qc.cx(0, 1) # CNOT entangles
qc.measure([0, 1], [0, 1])
result = simulator.run(qc, shots=1024).result()
counts = result.get_counts()
print("Bell state (H+CNOT):", counts) # Only '00' and '11', ~50% each
# Verify the state vector
qc_no_meas = QuantumCircuit(2)
qc_no_meas.h(0)
qc_no_meas.cx(0, 1)
state = Statevector.from_instruction(qc_no_meas)
print("State vector:\n", state.data)
Worked Example 6.11: Compute $CNOT \cdot (X \otimes I) \cdot CNOT|00\rangle$:
$$CNOT|00\rangle = |00\rangle$$ $$(X \otimes I)|00\rangle = |10\rangle$$ $$CNOT|10\rangle = |11\rangle$$
So $CNOT \cdot (X \otimes I) \cdot CNOT|00\rangle = |11\rangle$.
Alternatively, $CNOT \cdot (X \otimes I) \cdot CNOT = (I \otimes X) \cdot CNOT$. This is an important identity: conjugating $X$ on the control by CNOT moves the $X$ to the target.
Worked Example 6.12: Compute $(CNOT)^{\otimes n}|0\rangle^{\otimes n}|1\rangle$ for the $n$-qubit case where CNOT has the first qubit as control and all others as targets. This creates the state $|0\rangle^{\otimes n} + |1\rangle^{\otimes n}$ (a GHZ-like state).
6.3.2 Controlled-Z (CZ)
$$CZ = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & -1 \end{pmatrix} = |0\rangle\langle 0| \otimes I + |1\rangle\langle 1| \otimes Z$$
The CZ gate applies a $Z$ (phase flip) to the target when the control is $|1\rangle$. Unlike CNOT, CZ is symmetric — it doesn't matter which qubit is "control" and which is "target."
● ●
| ≡ |
● ●
CZ is equivalent to CNOT conjugated by Hadamards on the target:
$$CZ = (I \otimes H) \cdot CNOT \cdot (I \otimes H)$$
Proof:
$$(I \otimes H) \cdot CNOT \cdot (I \otimes H) = (I \otimes H)(|0\rangle\langle 0| \otimes I + |1\rangle\langle 1| \otimes X)(I \otimes H)$$ $$= |0\rangle\langle 0| \otimes HXH + |1\rangle\langle 1| \otimes HXH$$
Wait, that's not right. Let me redo this carefully:
$$CNOT = |0\rangle\langle 0| \otimes I + |1\rangle\langle 1| \otimes X$$
$$(I \otimes H) \cdot CNOT \cdot (I \otimes H) = |0\rangle\langle 0| \otimes HIH + |1\rangle\langle 1| \otimes HXH$$ $$= |0\rangle\langle 0| \otimes H^2 + |1\rangle\langle 1| \otimes HXH$$ $$= |0\rangle\langle 0| \otimes I + |1\rangle\langle 1| \otimes Z = CZ$$
using $H^2 = I$ and $HXH = Z$. ✓
# CZ gate: create a Bell state using CZ
qc = QuantumCircuit(2, 2)
qc.h(0) # |+0⟩
qc.h(1) # |++⟩
qc.cz(0, 1) # Entangle
qc.h(1) # Back to computational basis
qc.measure([0, 1], [0, 1])
result = simulator.run(qc, shots=1024).result()
print("CZ-based entanglement:", result.get_counts())
6.3.3 SWAP Gate
$$SWAP = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}$$
The SWAP gate exchanges the states of two qubits: $SWAP|ab\rangle = |ba\rangle$.
×
|
×
Properties: - $SWAP^2 = I$ (self-inverse) - $SWAP = SWAP^\dagger$ (Hermitian) - $\det(SWAP) = -1$
SWAP can be decomposed into three CNOTs:
$$SWAP = CNOT_{12} \cdot CNOT_{21} \cdot CNOT_{12}$$
× ● ⊕ ●
| = | | |
× ⊕ ● ⊕
Proof of the decomposition: Let's verify on all four computational basis states.
$SWAP|00\rangle = |00\rangle$. Let's trace through: $CNOT_{12}|00\rangle = |00\rangle$, then $CNOT_{21}|00\rangle = |00\rangle$, then $CNOT_{12}|00\rangle = |00\rangle$. ✓
$SWAP|01\rangle = |10\rangle$. $CNOT_{12}|01\rangle = |01\rangle$, $CNOT_{21}|01\rangle = |11\rangle$, $CNOT_{12}|11\rangle = |10\rangle$. ✓
$SWAP|10\rangle = |01\rangle$. $CNOT_{12}|10\rangle = |11\rangle$, $CNOT_{21}|11\rangle = |01\rangle$, $CNOT_{12}|01\rangle = |01\rangle$. ✓
$SWAP|11\rangle = |11\rangle$. $CNOT_{12}|11\rangle = |10\rangle$, $CNOT_{21}|10\rangle = |10\rangle$... hmm, let me be more careful about which qubit is control and which is target.
$CNOT_{12}$ means qubit 1 controls qubit 2. $CNOT_{21}$ means qubit 2 controls qubit 1.
$SWAP|11\rangle = |11\rangle$: - $CNOT_{12}|11\rangle = |10\rangle$ (control q1=1, flip q2) - $CNOT_{21}|10\rangle = |10\rangle$... Wait, qubit 2 is 0, so control is 0, no flip: $|10\rangle$ - $CNOT_{12}|10\rangle = |11\rangle$... Wait, that's wrong. Let me redo.
Actually, let me use indices more carefully. $CNOT_{ij}$ means qubit $i$ is control, qubit $j$ is target.
For $SWAP = CNOT_{1,2} \cdot CNOT_{2,1} \cdot CNOT_{1,2}$:
$|10\rangle$ (qubit 1 = 1, qubit 2 = 0): - $CNOT_{1,2}|10\rangle = |11\rangle$ (control=1, flip qubit 2) - $CNOT_{2,1}|11\rangle = |01\rangle$ (control qubit 2=1, flip qubit 1) - $CNOT_{1,2}|01\rangle = |01\rangle$ (control qubit 1=0, no flip) - Result: $|01\rangle = SWAP|10\rangle$ ✓
# SWAP gate
qc = QuantumCircuit(2, 2)
qc.x(0) # Set qubit 0 to |1⟩
qc.swap(0, 1) # Swap qubits
qc.measure([0, 1], [0, 1])
result = simulator.run(qc, shots=1024).result()
print("SWAP(|10⟩):", result.get_counts()) # Should be '01'
# Decompose SWAP into 3 CNOTs
qc_decomp = QuantumCircuit(2, 2)
qc_decomp.x(0)
qc_decomp.cx(0, 1)
qc_decomp.cx(1, 0)
qc_decomp.cx(0, 1)
qc_decomp.measure([0, 1], [0, 1])
result_d = simulator.run(qc_decomp, shots=1024).result()
print("3-CNOT SWAP:", result_d.get_counts()) # Also '01'
6.3.4 Other Two-Qubit Gates
Controlled-U gates. The general controlled-$U$ gate applies $U$ to the target if the control is $|1\rangle$:
$$CU = |0\rangle\langle 0| \otimes I + |1\rangle\langle 1| \otimes U = \begin{pmatrix} I & 0 \\ 0 & U \end{pmatrix}$$
Any controlled-$U$ can be decomposed into CNOTs and single-qubit gates using the ABC decomposition (see Section 6.6).
iSWAP gate. Another native gate on some quantum hardware:
$$iSWAP = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & i & 0 \\ 0 & i & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}$$
The iSWAP swaps the states $|01\rangle$ and $|10\rangle$ while adding a phase of $i$. It is native to superconducting qubit architectures.
Common Misconception: "CNOT is the only two-qubit gate we need." While CNOT plus single-qubit gates form a universal set, other two-qubit gates may be more natural for specific hardware. IBM's superconducting qubits use CNOT, but Google's uses CZ (plus Hadamard for conversion), and some architectures use iSWAP or the Mølmer-Sørensen gate. The choice of native gate set is a hardware engineering decision, and the transpiler converts between them.
6.3.5 Entangling Power of Two-Qubit Gates
Not all two-qubit gates create entanglement. The entangling power of a gate measures how much entanglement it can create when applied to product states.
Definition: The entangling power $e_P(U)$ of a two-qubit unitary $U$ is the maximum entanglement entropy it can create when acting on product states:
$$e_P(U) = \max_{|\psi\rangle, |\phi\rangle} S(\text{Tr}_B(U|\psi\rangle \otimes |\phi\rangle\langle\psi| \otimes \langle\phi|U^\dagger))$$
Key results: - $e_P(I \otimes I) = 0$ (identity creates no entanglement) - $e_P(\text{SWAP}) = 0$ (SWAP permutes qubits but doesn't create entanglement from product states) - $e_P(\text{CNOT}) = 1$ (CNOT can create maximal entanglement: $H \otimes I$ followed by CNOT creates a Bell state) - $e_P(\text{CZ}) = 1$ (CZ is locally equivalent to CNOT)
The entangling power of any two-qubit gate is between 0 and 1 ebit. Gates with $e_P(U) > 0$ are called entangling gates.
Common Misconception: "SWAP creates entanglement." The SWAP gate does NOT create entanglement. It merely exchanges the states of two qubits. $SWAP(|\psi\rangle \otimes |\phi\rangle) = |\phi\rangle \otimes |\psi\rangle$, which is still a product state. However, SWAP can reveal entanglement that was already present in a different form, and it is useful in circuits that create entanglement through other means.
Worked Example 6.17: Show that CNOT creates maximal entanglement from a product state.
$CNOT \cdot (H \otimes I)|00\rangle = CNOT \cdot \frac{1}{\sqrt{2}}(|00\rangle + |10\rangle) = \frac{1}{\sqrt{2}}(|00\rangle + |11\rangle) = |\Phi^+\rangle$
The entanglement entropy of $|\Phi^+\rangle$ is 1 ebit — maximal for a two-qubit state. So $e_P(\text{CNOT}) = 1$.
Worked Example 6.18: Show that $SWAP$ does not create entanglement.
$SWAP(|\psi\rangle \otimes |\phi\rangle) = |\phi\rangle \otimes |\psi\rangle$, which is still a product state. The partial trace gives $\rho_A' = |\phi\rangle\langle\phi|$, which is pure, so $S(\rho_A') = 0$.
# Qiskit: Comparing entangling power of different gates
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector, partial_trace, DensityMatrix, entropy
import numpy as np
def entangling_power(gate_name):
"""Compute the entanglement created by a gate on a product state."""
# Try multiple input states and find the maximum entanglement
max_entanglement = 0
inputs = [
('|0>|0>', [1, 0, 0, 0]),
('|0>|+>', np.kron([1, 0], [1/np.sqrt(2), 1/np.sqrt(2)])),
('|+>|0>', np.kron([1/np.sqrt(2), 1/np.sqrt(2)], [1, 0])),
('|+>|+>', np.kron([1/np.sqrt(2), 1/np.sqrt(2)], [1/np.sqrt(2), 1/np.sqrt(2)])),
]
for name, state in inputs:
qc = QuantumCircuit(2)
if gate_name == 'CNOT':
pass # CNOT is default
elif gate_name == 'SWAP':
qc.swap(0, 1)
elif gate_name == 'CZ':
qc.cz(0, 1)
# Apply gate to state
from qiskit.quantum_info import Operator
if gate_name == 'CNOT':
gate_op = Operator(np.array([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]))
elif gate_name == 'SWAP':
gate_op = Operator(np.array([[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]]))
elif gate_name == 'CZ':
gate_op = Operator(np.diag([1,1,1,-1]))
state_vec = Statevector(state)
state_after = state_vec.evolve(gate_op)
rho = DensityMatrix(state_after)
rho_A = partial_trace(rho, [1])
S = entropy(rho_A, base=2)
max_entanglement = max(max_entanglement, S)
if S > 0.01:
print(f" Input {name}: entanglement = {S:.4f} ebits")
return max_entanglement
for gate in ['CNOT', 'SWAP', 'CZ']:
print(f"\n{gate} entangling power:")
ep = entangling_power(gate)
print(f" Maximum entanglement created: {ep:.4f} ebits")
6.3.6 Controlled Gates and Their Matrix Structure
A general controlled-$U$ gate has the form:
$$CU = |0\rangle\langle 0| \otimes I + |1\rangle\langle 1| \otimes U = \begin{pmatrix} I & 0 \\ 0 & U \end{pmatrix}$$
The top-left block applies the identity when the control is $|0\rangle$, and the bottom-right block applies $U$ when the control is $|1\rangle$.
Worked Example 6.19: Matrix of the controlled-Hadamard (CH):
$$CH = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \frac{1}{\sqrt{2}} & \frac{1}{\sqrt{2}} \\ 0 & 0 & \frac{1}{\sqrt{2}} & -\frac{1}{\sqrt{2}} \end{pmatrix}$$
This applies $H$ to the target only when the control is $|1\rangle$.
Worked Example 6.20: Verify that CNOT can be written as $|0\rangle\langle 0| \otimes I + |1\rangle\langle 1| \otimes X$:
$$|0\rangle\langle 0| \otimes I = \begin{pmatrix} 1 & 0 \\ 0 & 0 \end{pmatrix} \otimes \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \end{pmatrix}$$
$$|1\rangle\langle 1| \otimes X = \begin{pmatrix} 0 & 0 \\ 0 & 1 \end{pmatrix} \otimes \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} = \begin{pmatrix} 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \end{pmatrix}$$
Sum: $\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \end{pmatrix}$ = CNOT ✓
The projector decomposition of controlled gates reveals a deep connection between quantum control flow and classical conditioning: the control qubit "steers" the target qubit through two alternative unitary evolutions.
6.4 Three-Qubit Gates
6.4.1 Toffoli Gate (CCNOT)
The Toffoli gate flips the target if both controls are $|1\rangle$:
● (control 1)
|
● (control 2)
|
⊕ (target)
$$CCNOT = I^{\otimes 3} - |11\rangle\langle 11| \otimes I + |11\rangle\langle 11| \otimes X$$
Explicit matrix (8×8):
The Toffoli gate acts on three qubits. In the computational basis $\{|000\rangle, |001\rangle, |010\rangle, |011\rangle, |100\rangle, |101\rangle, |110\rangle, |111\rangle\}$, it is the identity on all states except $|110\rangle \leftrightarrow |111\rangle$.
The Toffoli gate is universal for classical reversible computation — any classical Boolean function can be implemented with Toffoli gates. Together with the Hadamard, it is universal for quantum computation.
Classical universality of Toffoli:
- AND gate: Set target to 0. Toffoli with controls $a, b$ and target $c=0$ gives $|a, b, c \oplus ab\rangle = |a, b, ab\rangle$.
- NOT gate: Toffoli with both controls set to $|1\rangle$ gives $|1, 1, c \oplus 1\rangle = |1, 1, \bar{c}\rangle$.
- NAND gate: Toffoli followed by NOT on the target gives $|a, b, \overline{ab}\rangle$.
- Since NAND is universal for classical logic, Toffoli is universal for classical reversible logic.
FANOUT from Toffoli: Set one control to $|1\rangle$ and target to $|0\rangle$. Toffoli with control $a$, control 1, and target 0 gives $|a, 1, a\rangle$ — we've copied bit $a$.
Wait — this copies a classical bit, not a quantum state. The no-cloning theorem is not violated because Toffoli copies $|0\rangle$ and $|1\rangle$ (which are orthogonal), not arbitrary superpositions.
# Toffoli gate: quantum AND
qc = QuantumCircuit(3, 3)
qc.x(0) # control 1 = |1⟩
qc.x(1) # control 2 = |1⟩
qc.ccx(0, 1, 2) # Toffoli: target flips if both controls are 1
qc.measure([0, 1, 2], [0, 1, 2])
result = simulator.run(qc, shots=1024).result()
print("Toffoli(|110⟩):", result.get_counts()) # Should be '111'
# Toffoli with one control off
qc2 = QuantumCircuit(3, 3)
qc2.x(0) # control 1 = |1⟩
# control 2 = |0⟩ (default)
qc2.ccx(0, 1, 2)
qc2.measure([0, 1, 2], [0, 1, 2])
result2 = simulator.run(qc2, shots=1024).result()
print("Toffoli(|100⟩):", result2.get_counts()) # Should be '100' (no flip)
6.4.2 Fredkin Gate (CSWAP)
The Fredkin (controlled-SWAP) gate swaps two target qubits if the control is $|1\rangle$:
● (control)
|
× (target 1)
|
× (target 2)
The Fredkin gate is universal for classical reversible computation and has the elegant property that it preserves the Hamming weight (number of $|1\rangle$s) of the input.
Hamming weight preservation. The Fredkin gate preserves the number of 1s in the input. For example: $|101\rangle \to |110\rangle$ (both have Hamming weight 2), $|100\rangle \to |100\rangle$ (Hamming weight 1), $|111\rangle \to |111\rangle$ (Hamming weight 3).
This conservation property makes the Fredkin gate useful in conservative logic, where no information is created or destroyed.
# Fredkin gate
qc = QuantumCircuit(3, 3)
qc.x(0) # control = |1⟩
qc.x(1) # target1 = |1⟩, target2 = |0⟩
qc.cswap(0, 1, 2) # Swap targets
qc.measure([0, 1, 2], [0, 1, 2])
result = simulator.run(qc, shots=1024).result()
print("Fredkin(|110⟩):", result.get_counts()) # Should be '101'
6.5 Universal Gate Sets
A set of gates is universal if any unitary operation on $n$ qubits can be approximated to arbitrary precision using only gates from that set.
6.5.1 Theoretical Universality Results
Theorem (Barenco et al., 1995). The set $\{CNOT\} \cup \{\text{all single-qubit gates}\}$ is universal for quantum computation.
Theorem (Universal finite sets). The following sets are each universal: 1. $\{H, T, CNOT\}$ — The standard fault-tolerant set. $H$ and $T$ generate any single-qubit gate (approximately), and CNOT provides entanglement. 2. $\{H, S, CNOT, T\}$ — The Clifford+T set, the workhorse of fault-tolerant quantum computing. 3. $\{R_x(\theta), R_y(\theta), R_z(\theta), CNOT\}$ — Continuous parameterization; not directly fault-tolerant but useful for variational algorithms. 4. $\{\text{Toffoli}, H\}$ — Toffoli plus Hadamard is universal.
Why is universality non-trivial? Note that $\{H, S, CNOT\}$ alone is not universal — it generates only the Clifford group, which can be efficiently simulated classically. We need at least one non-Clifford gate (like $T$) for universality.
6.5.2 The Clifford Group
The Clifford group $\mathcal{C}_n$ consists of gates that map Pauli operators to Pauli operators under conjugation:
$$C \in \mathcal{C}_n \iff C P C^\dagger \in \mathcal{P}_n \quad \forall P \in \mathcal{P}_n$$
The Clifford group is generated by $\{H, S, CNOT\}$. Its key properties:
- Size: $|\mathcal{C}_n|$ grows as $O(2^{n^2 + 2n + 3})$ — it is a small fraction of all possible unitaries.
- Stabilizer states: Clifford gates map stabilizer states (simultaneous eigenstates of commuting Pauli groups) to stabilizer states.
- Efficient simulation: Clifford circuits can be efficiently simulated classically (Gottesman-Knill theorem). The stabilizer tableau of $n$ qubits requires only $O(n^2)$ classical bits.
Examples of Clifford gates: $H$, $S$, $X$, $Y$, $Z$, $CNOT$, $SWAP$, and any gate that maps Pauli operators to Pauli operators.
Non-Clifford gates: $T$, $R_x(\pi/3)$, $R_y(\pi/5)$, and essentially "most" rotations. The $T$ gate maps $X \to Y \cdot S^\dagger$... wait, let me be precise: $TXT^\dagger = \frac{X + Y}{\sqrt{2}}$, which is not a Pauli operator. Hence $T$ is non-Clifford.
from qiskit.quantum_info import Clifford, StabilizerState
# Create a Clifford circuit: H, S, CNOT only
qc_clifford = QuantumCircuit(3)
qc_clifford.h(0)
qc_clifford.s(1)
qc_clifford.cx(0, 1)
qc_clifford.cx(1, 2)
qc_clifford.h(2)
# Convert to Clifford operator and simulate efficiently
cliff = Clifford(qc_clifford)
print("Clifford tableau (binary symplectic form):")
print(cliff.to_matrix())
# Adding a single T gate makes it non-Clifford
qc_nonclifford = QuantumCircuit(1)
qc_nonclifford.h(0)
qc_nonclifford.t(0)
qc_nonclifford.h(0)
# This circuit cannot be represented as a Clifford operator
6.5.3 The Solovay-Kitaev Theorem (Conceptual)
Theorem (Solovay-Kitaev): Given a dense set of gates $\mathcal{G}$ in $SU(d)$ closed under inverses, any unitary $U$ can be approximated to within error $\epsilon$ using $O(\log^c(1/\epsilon))$ gates from $\mathcal{G}$, where $c \approx 3.97$.
In practical terms: you can approximate any gate efficiently using a finite gate set. The overhead is polylogarithmic in the desired precision — this is what makes fault-tolerant quantum computing feasible.
What does "approximate" mean? A circuit $\tilde{U}$ approximates $U$ to within $\epsilon$ if $\|\tilde{U} - U\| \leq \epsilon$, where $\|\cdot\|$ is the operator norm (spectral norm). This means that for any input state $|\psi\rangle$:
$$\|\tilde{U}|\psi\rangle - U|\psi\rangle\| \leq \epsilon$$
Why is this important? Without Solovay-Kitaev, approximating an arbitrary rotation might require exponentially many elementary gates. With it, the overhead is manageable — a rotation to precision $10^{-10}$ requires only about $\log^4(10^{10}) \approx 160$ elementary gates.
Recurring Theme — We're at the Beginning: The Solovay-Kitaev theorem guarantees that any gate can be efficiently approximated, but the constant factors matter enormously in practice. Current compilations often use much more than the theoretical minimum number of $T$ gates. Improved compilation techniques are an active area of research.
# Decompose an arbitrary unitary into {H, T, CNOT} using Qiskit's transpiler
from qiskit import transpile
from qiskit.circuit.library import UGate
# Create a circuit with an arbitrary U gate
qc_arb = QuantumCircuit(1)
qc_arb.u(np.pi/3, np.pi/4, np.pi/5, 0) # Arbitrary rotation
# Transpile to {H, T, CNOT} basis (approximately)
qc_decomposed = transpile(qc_arb, basis_gates=['h', 't', 'tdg', 'cx'])
print("Decomposed circuit:")
print(qc_decomposed.draw('text'))
6.5.4 Why T-Gates Are Expensive: Magic State Distillation
The $T$ gate is the bottleneck of fault-tolerant quantum computing. In most error-correcting codes (particularly the surface code), Clifford gates ($H$, $S$, $CNOT$) can be performed transversally — that is, by applying the gate to each physical qubit independently. This makes them inherently fault-tolerant.
The $T$ gate, however, cannot be performed transversally in the surface code (this follows from the Eastin-Knill theorem, which states that no quantum error-correcting code can implement a universal set of gates transversally). Instead, we must use magic state distillation:
- Prepare many noisy physical $T$-states (approximately $|T\rangle = T|+\rangle$)
- Apply a distillation protocol that uses only Clifford operations to extract fewer, higher-fidelity $T$-states
- Use the distilled $T$-states as resources to implement $T$ gates via gate teleportation
The most common protocol is the 15-to-1 distillation: 15 noisy $T$-states of fidelity $p > 0.856$ are consumed to produce 1 high-fidelity $T$-state. For lower input fidelities, more rounds of distillation are needed, each consuming $\sim 15\times$ more $T$-states.
Resource cost: Each logical $T$ gate may require thousands to millions of physical qubits, depending on the target fidelity and the noise rate. This is why the $T$-count (total number of $T$ gates in a circuit) is a key metric — reducing it by even a factor of 2 can dramatically reduce the physical resource requirements.
Recurring Theme — Noise Is the Enemy: The $T$ gate exemplifies the noise problem in quantum computing. Each $T$ gate requires distilling magic states, which consumes many physical qubits. Reducing the $T$-count through better compilation (e.g., $T$-par, $T$-opt) is an active area of research that directly impacts the feasibility of quantum algorithms.
ASCII Art: Magic State Distillation Pipeline
Noisy physical Clifford High-fidelity
T-states (15) ──→ distillation circuit ──→ T-state (1)
(fidelity ~95%) (uses only Clifford (fidelity >99.99%)
gates + measurements)
Cost: ~15 physical qubits per distillation round
Multiple rounds needed for high target fidelity
This is why T-count matters: each T gate ≈ thousands of physical qubits
6.6 Gate Decomposition: Building Big Gates from Small Ones
6.6.1 Controlled-U Decomposition
Any controlled-$U$ gate can be decomposed using the ABC decomposition. If $U = e^{i\alpha}AXBXC$ with $ABC = I$, then:
$$CU = |0\rangle\langle 0| \otimes I + |1\rangle\langle 1| \otimes U$$
can be implemented as:
● ● ● ●
| = | + | + |
U C B A
Where $A = R_z(\beta)R_y(\gamma/2)$, $B = R_y(-\gamma/2)R_z(-(\delta+\beta)/2)$, $C = R_z((\delta-\beta)/2)$, and the angles $\alpha, \beta, \gamma, \delta$ are derived from the Z-Y-Z decomposition of $U$.
Worked Example 6.13: Decompose the controlled-$Y$ gate.
$Y = iXZ = i \begin{pmatrix} 0 & -i \\ i & 0 \end{pmatrix}$... Actually, let's use the Z-Y-Z decomposition directly:
$$Y = \begin{pmatrix} 0 & -i \\ i & 0 \end{pmatrix} = e^{i\pi/2} R_y(\pi) = e^{i\pi/2} \begin{pmatrix} \cos(\pi/2) & -\sin(\pi/2) \\ \sin(\pi/2) & \cos(\pi/2) \end{pmatrix} = e^{i\pi/2} \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix}$$
Hmm, $Y$ has $\theta = \pi, \phi = 0, \lambda = 0$ in the Z-Y-Z decomposition (up to phase). But actually the exact decomposition requires careful handling.
Let me use a simpler approach: $CY = (I \otimes H_S) \cdot CZ \cdot (I \otimes H_S)$ where $H_S = R_y(-\pi/4)$ (not the Hadamard, but a specific rotation). This shows that any controlled gate can be built from CNOT and single-qubit rotations.
6.6.2 Toffoli Decomposition
The Toffoli gate can be decomposed into 6 CNOTs and several single-qubit gates. The minimal CNOT count for a Toffoli is 6 (in the absence of ancillas) or 4 (with one ancilla qubit).
Standard Toffoli decomposition (6 CNOTs):
● ● ● ●
| = | + ● | + |
● | | | + ●
| = | + | | |
⊕ ⊕ ⊕ ⊕ ⊕
The exact decomposition involves 6 CNOT gates, 9 single-qubit gates (including 7 T gates), and has a T-count of 7.
# Toffoli decomposition in Qiskit
from qiskit.circuit.library import CCXGate
toffoli_decomposed = CCXGate().decompose()
print("Toffoli decomposition:")
print(toffoli_decomposed.draw('text'))
# Verify: Toffoli has 6 CNOT gates
from qiskit import QuantumCircuit
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
qc_decomposed = transpile(qc, basis_gates=['u', 'cx'])
print(f"CNOT count: {qc_decomposed.count_ops().get('cx', 0)}")
6.6.3 SWAP Decomposition
As shown earlier, SWAP = CNOT₁₂ · CNOT₂₁ · CNOT₁₂. This uses 3 CNOT gates and no single-qubit gates.
A SWAP can also be decomposed using 3 CZ gates plus Hadamards (6 Hadamard + 3 CZ), but the CNOT decomposition is more efficient.
6.6.4 Arbitrary Two-Qubit Gate Decomposition
Theorem (Vatan-Williams, 2004): Any two-qubit unitary can be decomposed using at most 3 CNOTs and 15 single-qubit gates.
The canonical form is:
$$U = (A_1 \otimes A_2) \cdot \text{CNOT} \cdot (B_1 \otimes B_2) \cdot \text{CNOT} \cdot (C_1 \otimes C_2)$$
where $A_i, B_i, C_i$ are single-qubit unitaries, and some of the single-qubit gates can be merged. The exact structure depends on the specific unitary being decomposed.
This is an important result: no two-qubit gate requires more than 3 CNOTs to implement. Since CNOTs are typically the most error-prone gate on current hardware, minimizing CNOT count is a key optimization target.
6.7 Gate Identities and Commutation Relations
Understanding how gates compose and commute is essential for circuit optimization. Two gates $A$ and $B$ commute if $AB = BA$, and anti-commute if $AB = -BA$.
6.7.1 Single-Qubit Commutation and Anti-Commutation
The Pauli matrices satisfy:
$$\sigma_i \sigma_j = i\epsilon_{ijk}\sigma_k + \delta_{ij}I$$
where $\epsilon_{ijk}$ is the Levi-Civita symbol and $\delta_{ij}$ is the Kronecker delta. This gives us:
- $XZ = -ZX$ (anti-commute)
- $XY = -YX$ (anti-commute)
- $YZ = -ZY$ (anti-commute)
- $X^2 = Y^2 = Z^2 = I$ (self-inverse)
- $XY = iZ$, $YZ = iX$, $ZX = iY$
The anti-commutation of Pauli matrices is the algebraic origin of the uncertainty principle: if two observables anti-commute, they cannot be simultaneously measured with arbitrary precision.
Worked Example 6.15: Verify $HXH = Z$.
$$HXH = \frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}\begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix}\frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}$$
$$= \frac{1}{2}\begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}\begin{pmatrix} 1 & -1 \\ 1 & 1 \end{pmatrix} = \frac{1}{2}\begin{pmatrix} 2 & 0 \\ 0 & -2 \end{pmatrix} = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix} = Z$$
This identity is fundamental: conjugating $X$ by $H$ gives $Z$, and conjugating $Z$ by $H$ gives $X$. The Hadamard "swaps" the X and Z axes.
6.7.2 Two-Qubit Gate Identities
CNOT conjugation rules are perhaps the most important two-qubit identities. They describe how CNOT "spreads" Pauli operators:
$$CNOT_{12} \cdot (X \otimes I) \cdot CNOT_{12} = X \otimes X$$ $$CNOT_{12} \cdot (I \otimes X) \cdot CNOT_{12} = I \otimes X$$ $$CNOT_{12} \cdot (Z \otimes I) \cdot CNOT_{12} = Z \otimes I$$ $$CNOT_{12} \cdot (I \otimes Z) \cdot CNOT_{12} = Z \otimes Z$$
These rules have a beautiful interpretation: - $X$ on the control spreads to $X$ on both qubits - $X$ on the target stays on the target (commutes) - $Z$ on the control stays on the control (commutes) - $Z$ on the target spreads to $Z$ on both qubits
ASCII Art: CNOT Conjugation Rules (Data Flow)
Control X "spreads": Control Z "stays":
X ──●── X ──●── Z ──●── Z ──●──
│ = │ │ = │
⊕ ⊕ ⊕ ⊕
Target X "stays": Target Z "spreads":
──●── ──●── ──●── Z ──●──
│ = │ │ = │ Z
⊕ X ⊕ ⊕ ⊕
Worked Example 6.16: Verify $CNOT_{12} \cdot (X \otimes I) \cdot CNOT_{12} = X \otimes X$.
$$CNOT \cdot (X \otimes I) = \begin{pmatrix} 1&0&0&0 \\ 0&1&0&0 \\ 0&0&0&1 \\ 0&0&1&0 \end{pmatrix}\begin{pmatrix} 0&0&1&0 \\ 0&0&0&1 \\ 1&0&0&0 \\ 0&1&0&0 \end{pmatrix} = \begin{pmatrix} 0&0&1&0 \\ 0&0&0&1 \\ 0&1&0&0 \\ 1&0&0&0 \end{pmatrix}$$
$$\text{CNOT} \cdot \begin{pmatrix} 0&0&1&0 \\ 0&0&0&1 \\ 0&1&0&0 \\ 1&0&0&0 \end{pmatrix} = \begin{pmatrix} 0&0&0&1 \\ 0&0&1&0 \\ 0&1&0&0 \\ 1&0&0&0 \end{pmatrix} = X \otimes X$$
✓
6.7.3 Circuit Optimization Identities
These identities are used by the transpiler to reduce circuit depth:
- H-H = I: Two consecutive Hadamards cancel.
- S-S = Z: $S^2 = Z$, so $S \cdot S$ can be replaced by $Z$.
- T-T-T-T = Z: Four consecutive $T$ gates equal $Z$.
- CNOT-CNOT = I: Two consecutive CNOTs with the same control and target cancel.
- $R_z(\alpha) \cdot R_z(\beta) = R_z(\alpha + \beta)$: Adjacent rotations about the same axis merge.
- $R_x(\alpha) \cdot R_x(\beta) = R_x(\alpha + \beta)$: Same for X-axis.
- CNOT-H-CNOT identity: $(I \otimes H) \cdot CNOT \cdot (I \otimes H) = CZ$
These identities are applied automatically by Qiskit's transpiler at optimization levels 1-3. Understanding them manually is valuable for hand-optimizing circuits.
# Demonstrate circuit identities in Qiskit
from qiskit import QuantumCircuit
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import Optimize1qGates
from qiskit.quantum_info import Operator
import numpy as np
# Identity 1: H-H = I
qc1 = QuantumCircuit(1)
qc1.h(0)
qc1.h(0)
print("H·H = I?", np.allclose(Operator(qc1).data, np.eye(2)))
# Identity 2: S-S = Z
qc2 = QuantumCircuit(1)
qc2.s(0)
qc2.s(0)
print("S·S = Z?", np.allclose(Operator(qc2).data, Operator(QuantumCircuit(1).z(0)).data))
# Identity 3: CNOT-CNOT = I
qc3 = QuantumCircuit(2)
qc3.cx(0, 1)
qc3.cx(0, 1)
print("CNOT·CNOT = I?", np.allclose(Operator(qc3).data, np.eye(4)))
# Identity 4: (I⊗H)·CNOT·(I⊗H) = CZ
qc4 = QuantumCircuit(2)
qc4.h(1)
qc4.cx(0, 1)
qc4.h(1)
qc5 = QuantumCircuit(2)
qc5.cz(0, 1)
print("(I⊗H)·CNOT·(I⊗H) = CZ?", np.allclose(Operator(qc4).data, Operator(qc5).data))
6.8 Complete Gate Reference
GATE MATRIX BLOCH SPHERE ACTION
──── ────── ──────────────────
X [[0,1],[1,0]] π rotation about X-axis
Y [[0,-i],[i,0]] π rotation about Y-axis
Z [[1,0],[0,-1]] π rotation about Z-axis
H 1/√2 [[1,1],[1,-1]] π rotation about (X+Z)/√2
S [[1,0],[0,i]] π/2 rotation about Z-axis
T [[1,0],[0,e^{iπ/4}]] π/4 rotation about Z-axis
Rx(θ) [[cos(θ/2), -i sin(θ/2)], Rotation by θ about X-axis
[-i sin(θ/2), cos(θ/2)]]
Ry(θ) [[cos(θ/2), -sin(θ/2)], Rotation by θ about Y-axis
[sin(θ/2), cos(θ/2)]]
Rz(θ) [[e^{-iθ/2}, 0], Rotation by θ about Z-axis
[0, e^{iθ/2}]]
CNOT diag(1,1) + anti-diag(1,1) Controlled X
CZ diag(1,1,1,-1) Controlled Z
SWAP Permutation matrix Swaps two qubits
Toffoli I_8 - |11⟩⟨11|⊗I + |11⟩⟨11|⊗X Doubly-controlled X
Gate identities cheat sheet:
HXH = Z HZH = X HYH = -Y
XSX = -Y XTX = S† S² = Z
T² = S T⁴ = Z H² = I
CNOT² = I SWAP² = I Toffoli² = I
(H⊗H)·CNOT·(H⊗H) = CZ (with reversed roles)
SWAP = CNOT₁₂·CNOT₂₁·CNOT₁₂
6.9 The Gottesman-Knill Theorem and Why Clifford Is Not Enough
A profound result in quantum computing is the Gottesman-Knill theorem: any quantum circuit composed entirely of Clifford gates (H, S, CNOT, and Pauli gates), applied to computational basis states and measured in the computational basis, can be simulated efficiently on a classical computer.
This means Clifford-only circuits, despite creating entanglement and superposition, do not offer quantum advantage. The resource that makes quantum computers powerful is non-Clifford operations — specifically, the $T$ gate (or any gate outside the Clifford group).
Why does the Gottesman-Knill theorem work? The mathematical reason is the stabilizer formalism. An $n$-qubit stabilizer state can be described by $n$ Pauli operators that "stabilize" it, requiring only $O(n^2)$ classical bits rather than $O(2^n)$ amplitudes. Clifford gates map stabilizer states to stabilizer states, so the entire circuit can be tracked using the stabilizer tableau — a polynomial-sized data structure.
Example of a Clifford circuit:
# A Clifford circuit: H, CNOT only
qc = QuantumCircuit(2)
qc.h(0) # Creates |+⟩|0⟩
qc.cx(0, 1) # Creates |Φ+⟩
qc.h(0) # Applies H to first qubit of Bell state
# This can be efficiently simulated using stabilizer formalism
# A non-Clifford circuit: H, T, CNOT
qc2 = QuantumCircuit(2)
qc2.h(0)
qc2.t(0) # Non-Clifford!
qc2.cx(0, 1)
qc2.h(1) # Still non-Clifford
# This CANNOT be efficiently simulated using stabilizer formalism
The ratio of $T$ gates to Clifford gates in a circuit is called the T-count, and it is a key metric for fault-tolerant quantum computing. $T$ gates are expensive because they require magic state distillation — a process that consumes many noisy physical qubits to produce one high-fidelity $T$ gate.
Common Misconception: "Clifford circuits can't create entanglement." This is false! The CNOT gate is Clifford, and H+CNOT creates a Bell state, which is maximally entangled. The point is that Clifford entanglement has a specific structure (stabilizer entanglement) that is classically trackable. Non-Clifford gates create entanglement patterns that are not efficiently describable classically.
6.10 Gate Compilation and Transpilation
In real quantum hardware, the native gate set is often limited. IBM's superconducting qubits natively support only $\{R_z(\theta), \sqrt{X}, CNOT\}$ — all other gates must be compiled (transpiled) into this native set.
The transpilation process involves:
- Gate decomposition: Breaking arbitrary unitaries into the native gate set
- Qubit mapping: Assigning logical qubits to physical qubits respecting connectivity constraints
- SWAP insertion: Adding SWAP gates when two-qubit gates require non-adjacent qubits
- Optimization: Canceling redundant gates, merging rotations, reducing circuit depth
Gate decomposition into native gates:
- $H = R_z(\pi/2) \cdot \sqrt{X} \cdot R_z(\pi/2)$ (up to global phase)
- $X = \sqrt{X} \cdot \sqrt{X}$ (two $\sqrt{X}$ gates)
- $T = R_z(\pi/4)$ (native, since $R_z$ is native)
- $S = R_z(\pi/2)$ (native)
The Hadamard requires 3 native single-qubit gates, while $X$ requires 2. CNOT is native.
from qiskit import transpile
from qiskit.circuit.library import QuantumVolume
from qiskit.providers.fake_provider import FakeSherbrooke
# Create a random circuit
qv = QuantumVolume(4, depth=3, seed=42)
# Transpile for a realistic backend
backend = FakeSherbrooke()
qc_transpiled = transpile(qv, backend=backend,
optimization_level=3,
basis_gates=['rz', 'sx', 'x', 'cx'])
print(f"Original depth: {qv.depth()}")
print(f"Transpiled depth: {qc_transpiled.depth()}")
print(f"Original CNOT count: {qv.count_ops().get('cx', 0)}")
print(f"Transpiled CNOT count: {qc_transpiled.count_ops().get('cx', 0)}")
print(f"SWAP gates inserted: {qc_transpiled.count_ops().get('swap', 0)}")
The transpiler's optimization level (0-3) trades off compilation time against circuit quality. Level 3 uses advanced techniques like template matching and gate cancellation to minimize depth and gate count — critical for running algorithms on noisy hardware where every gate degrades fidelity.
Recurring Theme — Noise Is the Enemy: Every gate on current quantum hardware introduces error. Gate fidelities of 99.5-99.9% per gate mean that a circuit with 100 gates has a cumulative error rate of 10-50%. Transpilation optimization and error correction are essential for making quantum computation practical.
6.11 Verifying Gate Operations
How do we verify that a gate does what we think it does? The gold standard is quantum process tomography — the complete characterization of a quantum operation. But for quick verification, we can use simpler methods:
6.11.1 State Tomography After Gate Application
Apply a gate to a known input state and verify the output using state tomography.
# Verify that X gate flips |0> to |1>
from qiskit.quantum_info import Statevector
# Method 1: Statevector simulation
qc_x = QuantumCircuit(1)
qc_x.x(0)
state_after_x = Statevector.from_instruction(qc_x)
print("X|0⟩ =", state_after_x) # Should be |1⟩
# Method 2: Statistical measurement
qc_x_meas = QuantumCircuit(1, 1)
qc_x_meas.x(0)
qc_x_meas.measure(0, 0)
result = simulator.run(qc_x_meas, shots=10000).result()
print("X|0⟩ measurement:", result.get_counts()) # Should be all '1'
6.11.2 Gate Tomography
Quantum process tomography characterizes an unknown operation $\mathcal{E}$ by applying it to a complete set of input states and measuring in a complete set of bases. For a single-qubit gate, this requires:
- Input states: $\{|0\rangle, |1\rangle, |+\rangle, |+\rangle_y = |i+\rangle\}$
- Measurement bases: X, Y, Z
Total: $4 \times 3 = 12$ measurement settings, each with many shots.
The output is the chi matrix ($\chi$), a $4 \times 4$ matrix in the Pauli basis that completely characterizes the operation. For an ideal gate, $\chi$ should match the expected matrix.
6.11.3 Randomized Benchmarking
A more practical approach is randomized benchmarking (RB): apply a sequence of random gates, followed by their inverse, and measure the probability of returning to the initial state. The decay of fidelity with sequence length gives the average gate error per gate.
# Simple randomized benchmarking for single-qubit gates
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
import numpy as np
def rb_experiment(length, num_sequences=20):
"""Run a randomized benchmarking experiment."""
simulator = AerSimulator()
fidelities = []
for _ in range(num_sequences):
qc = QuantumCircuit(1, 1)
# Apply random Clifford gates
cliffords = ['h', 's', 'x', 'y', 'z']
for _ in range(length):
gate = np.random.choice(cliffords)
getattr(qc, gate)(0)
# Invert the sequence (compute inverse)
# For simplicity, we'll measure the final state
# In a proper RB experiment, we'd compute and apply the inverse
qc.measure(0, 0)
result = simulator.run(qc, shots=1000).result()
counts = result.get_counts()
fidelities.append(counts.get('0', 0) / 1000)
return np.mean(fidelities)
# Note: This is a simplified demonstration
# Proper RB requires computing and applying the inverse of the random sequence
for length in [1, 5, 10, 20]:
print(f"RB length {length}: survival probability ≈ {rb_experiment(length):.4f}")