Quantum Gates — Hadamard, Pauli, CNOT and Toffoli Explained
In this tutorial, you'll learn about Quantum Gates. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
Quantum gates are unitary operations that manipulate qubits, forming the building blocks of quantum circuits just as logic gates form the building blocks of classical circuits.
What You'll Learn
By the end of this tutorial, you will understand the matrix representations of single-qubit gates (Pauli X, Y, Z, Hadamard, phase), two-qubit gates (CNOT, SWAP), and three-qubit gates (Toffoli), and you will implement them in Python.
Why It Matters
Every quantum algorithm from Shor to Grover to VQE is built from quantum gates. Understanding gates is essential for programming quantum computers at any level — whether in Qiskit, Cirq, or Q#.
Real-World Use
When Google demonstrated quantum supremacy with Sycamore in 2019, the experiment consisted of applying a specific sequence of gates (a random Quantum Circuit) and sampling the output distribution. The choice and ordering of gates determines what computation is performed.
Learning Path
flowchart LR
A[Qubits and Superposition] --> B[Quantum Gates]
B --> C[Quantum Circuits]
C --> D[Entanglement]
D --> E[Algorithms]
B --> F{You Are Here}
style F fill:#f90,color:#fff
Prerequisites: Understand qubits and superposition and Dirac notation. Familiarity with Python and basic linear algebra helps.
Properties of Quantum Gates
All quantum gates share three essential properties:
- Unitary: U†U = UU† = I, meaning gates are reversible
- Linear: U(α|ψ⟩ + β|φ⟩) = αU|ψ⟩ + βU|φ⟩
- Norm-preserving: The output state always satisfies normalization
Why Unitarity Matters
Since measurement is probabilistic, the total probability must always sum to 1. Unitary gates preserve the norm of the state vector, ensuring valid probabilities. Classical AND and OR gates are not reversible, but quantum gates must be.
Single-Qubit Gates
Pauli-X Gate (Quantum NOT)
The X gate flips |0⟩ to |1⟩ and |1⟩ to |0⟩, analogous to the classical NOT gate.
X = [0 1]
[1 0]
X|0⟩ = |1⟩
X|1⟩ = |0⟩
X|+⟩ = |+⟩ (|+⟩ is an eigenstate)
# pauli_x_gate.py
import numpy as np
# Pauli matrices
X = np.array([[0, 1], [1, 0]], dtype=complex)
Y = np.array([[0, -1j], [1j, 0]], dtype=complex)
Z = np.array([[1, 0], [0, -1]], dtype=complex)
def apply_gate(gate, state):
return gate @ state
# Test X gate
ket0 = np.array([1, 0], dtype=complex)
ket1 = np.array([0, 1], dtype=complex)
print("=== Pauli-X Gate ===")
print(f"X|0⟩ = {apply_gate(X, ket0)}")
print(f"X|1⟩ = {apply_gate(X, ket1)}")
# X is self-inverse
print(f"\nX²|0⟩ = {apply_gate(X, apply_gate(X, ket0))}")
Expected output:
=== Pauli-X Gate ===
X|0⟩ = [0 1]
X|1⟩ = [1 0]
X²|0⟩ = [1 0]
Pauli-Y Gate
The Y gate combines bit flip and phase flip.
Y = [0 -i]
[i 0]
Y|0⟩ = i|1⟩
Y|1⟩ = -i|0⟩
Pauli-Z Gate (Phase Flip)
The Z gate flips the phase of the |1⟩ component while leaving |0⟩ unchanged.
Z = [1 0]
[0 -1]
Z|0⟩ = |0⟩
Z|1⟩ = -|1⟩
Z|+⟩ = |−⟩
# pauli_yz_gates.py
import numpy as np
Y = np.array([[0, -1j], [1j, 0]], dtype=complex)
Z = np.array([[1, 0], [0, -1]], dtype=complex)
ket0 = np.array([1, 0], dtype=complex)
ket1 = np.array([0, 1], dtype=complex)
plus = (ket0 + ket1) / np.sqrt(2)
print("=== Pauli-Y Gate ===")
print(f"Y|0⟩ = {Y @ ket0}")
print(f"Y|1⟩ = {Y @ ket1}")
print("\n=== Pauli-Z Gate ===")
print(f"Z|0⟩ = {Z @ ket0}")
print(f"Z|1⟩ = {Z @ ket1}")
print(f"Z|+⟩ = {Z @ plus}")
Expected output:
=== Pauli-Y Gate ===
Y|0⟩ = [0.+0.j 0.+1.j]
Y|1⟩ = [0.-1.j 0.+0.j]
=== Pauli-Z Gate ===
Z|0⟩ = [1 0]
Z|1⟩ = [0 -1]
Z|+⟩ = [ 0.70710678 -0.70710678]
Hadamard Gate (Superposition)
The Hadamard gate creates Superposition. It maps the computational basis to the X basis.
H = 1/√2 [1 1]
[1 -1]
Phase Gate (S and T)
The S gate (√Z) and T gate (⁴√Z) add specific phases:
S = [1 0] T = [1 0 ]
[0 i] [0 e^(iπ/4)]
# hadamard_phase.py
import numpy as np
H = (1/np.sqrt(2)) * np.array([[1, 1], [1, -1]], dtype=complex)
S = np.array([[1, 0], [0, 1j]], dtype=complex)
T = np.array([[1, 0], [0, np.exp(1j * np.pi / 4)]], dtype=complex)
ket0 = np.array([1, 0], dtype=complex)
ket1 = np.array([0, 1], dtype=complex)
print("=== Hadamard Gate ===")
print(f"H|0⟩ = {H @ ket0}")
print(f"H|1⟩ = {H @ ket1}")
print("\n=== S Gate ===")
print(f"S|0⟩ = {S @ ket0}")
print(f"S|1⟩ = {S @ ket1}")
print("\n=== T Gate ===")
print(f"T|0⟩ = {T @ ket0}")
print(f"T|1⟩ = {T @ ket1}")
Expected output:
=== Hadamard Gate ===
H|0⟩ = [0.70710678 0.70710678]
H|1⟩ = [ 0.70710678 -0.70710678]
=== S Gate ===
S|0⟩ = [1 0]
S|1⟩ = [0+0.j 0+1.j]
=== T Gate ===
T|0⟩ = [1 0]
T|1⟩ = [0.+0.j 0.+0.70710678j]
Two-Qubit Gates
CNOT Gate (Controlled-NOT)
The CNOT gate flips the target qubit if the control qubit is |1⟩. It is the canonical two-qubit entangling gate.
CNOT = [1 0 0 0]
[0 1 0 0]
[0 0 0 1]
[0 0 1 0]
# cnot_gate.py
import numpy as np
CNOT = np.array([
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
[0, 0, 1, 0]
], dtype=complex)
# Basis states
ket00 = np.array([1, 0, 0, 0], dtype=complex)
ket01 = np.array([0, 1, 0, 0], dtype=complex)
ket10 = np.array([0, 0, 1, 0], dtype=complex)
ket11 = np.array([0, 0, 0, 1], dtype=complex)
# Test CNOT on all basis states
print("=== CNOT Gate ===")
print(f"CNOT|00⟩ = {CNOT @ ket00} → |00⟩")
print(f"CNOT|01⟩ = {CNOT @ ket01} → |01⟩")
print(f"CNOT|10⟩ = {CNOT @ ket10} → |11⟩ (target flipped)")
print(f"CNOT|11⟩ = {CNOT @ ket11} → |10⟩ (target flipped)")
# Create Bell state with H + CNOT
H_on_first = np.kron(H, np.eye(2))
bell_state = CNOT @ H_on_first @ ket00
print(f"\nBell state |Φ+⟩: {bell_state}")
print(f"= (|00⟩ + |11⟩)/√2")
Expected output:
=== CNOT Gate ===
CNOT|00⟩ = [1 0 0 0] → |00⟩
CNOT|01⟩ = [0 1 0 0] → |01⟩
CNOT|10⟩ = [0 0 0 1] → |11⟩ (target flipped)
CNOT|11⟩ = [0 0 1 0] → |10⟩ (target flipped)
Bell state |Φ+⟩: [0.70710678 0. 0. 0.70710678]
= (|00⟩ + |11⟩)/√2
The combination of Hadamard + CNOT creates the Bell state, a maximally entangled two-qubit state.
SWAP Gate
The SWAP gate exchanges the states of two qubits.
SWAP = [1 0 0 0]
[0 0 1 0]
[0 1 0 0]
[0 0 0 1]
Three-Qubit Gates
Toffoli Gate (CCNOT)
The Toffoli gate is a controlled-controlled-NOT. It flips the target qubit only if both control qubits are |1⟩. It is universal for classical reversible computation.
Toffoli = [I 0 0 0]
[0 I 0 0]
[0 0 I 0]
[0 0 0 X]
Implementing a Toffoli Gate
# toffoli_gate.py
import numpy as np
def toffoli_matrix():
"""Construct 8x8 Toffoli gate matrix."""
mat = np.eye(8, dtype=complex)
# Flip the last two basis states: |110⟩ ↔ |111⟩
mat[6, 6] = 0
mat[6, 7] = 1
mat[7, 6] = 1
mat[7, 7] = 0
return mat
TOFFOLI = toffoli_matrix()
# Test on all basis states where target might flip
for i in range(8):
state = np.zeros(8, dtype=complex)
state[i] = 1
result = TOFFOLI @ state
control_bits = (i & 4) >> 2, (i & 2) >> 1
target = i & 1
flipped = (i & 4) and (i & 2)
new_target = target ^ (1 if flipped else 0)
new_state = i
if flipped:
new_state = (i & 0b110) | new_target
print(f"|{i:03b}⟩ → |{new_state:03b}⟩ (controls={control_bits}, flip={flipped})")
Expected output:
|000⟩ → |000⟩ (controls=(0, 0), flip=False)
|001⟩ → |001⟩ (controls=(0, 0), flip=False)
|010⟩ → |010⟩ (controls=(0, 1), flip=False)
|011⟩ → |011⟩ (controls=(0, 1), flip=False)
|100⟩ → |100⟩ (controls=(1, 0), flip=False)
|101⟩ → |101⟩ (controls=(1, 0), flip=False)
|110⟩ → |111⟩ (controls=(1, 1), flip=True)
|111⟩ → |110⟩ (controls=(1, 1), flip=True)
Gate Universality
A set of gates is universal if any unitary operation can be approximated using gates from that set. Common universal sets:
- {H, T, CNOT} — Clifford + T set
- {Toffoli, H} — classical + Superposition
- {CNOT, single-qubit gates} — any controlled unitary
Solovay-Kitaev Theorem
Any single-qubit gate can be approximated to precision ε using O(log²(1/ε)) gates from a finite universal set. This means we do not need infinite gate types.
Common Mistakes
1. Assuming Quantum Gates are like Classical Logic Gates
Classical gates (AND, OR) are irreversible and consume inputs. Quantum gates are unitary, reversible, and preserve information. There is no quantum AND gate.
2. Forgetting the Global Phase
Multiplying a quantum state by e^(iθ) does not change any measurement outcome. States that differ only by a global phase are physically equivalent.
3. Misordering Gates
Quantum gates are applied right-to-left in circuit diagrams but left-to-right in matrix multiplication. Always verify the order: U₂U₁|ψ⟩ means apply U₁ first, then U₂.
4. Using Non-Unitary Matrices
If a proposed gate matrix is not unitary (U†U ≠ I), it is not a valid Quantum Gate. Non-unitary operations would not preserve probability.
5. Thinking CNOT Copies Qubits
CNOT does not copy a qubit. It entangles the control and target. Copying a quantum state is impossible due to the no-cloning theorem.
Practice Questions
1. What makes a valid Quantum Gate?
A valid Quantum Gate must be unitary: U†U = UU† = I. It must also be linear and norm-preserving.
2. Why is the Hadamard gate its own inverse?
H² = I because the Hadamard matrix is both unitary and Hermitian. Applying it twice cancels the Superposition.
3. What is the difference between CNOT and classical XOR?
CNOT is the quantum version of XOR but with the control qubit preserved. Classical XOR overwrites one input. CNOT is reversible: applying it twice returns the original state.
4. How do you create a Bell state using H and CNOT?
Apply H to the first qubit, then CNOT with first qubit as control and second as target: CNOT(H ⊗ I)|00⟩ = (|00⟩ + |11⟩)/√2.
5. Why is the Toffoli gate universal for classical computation?
Toffoli can implement NAND (universal for classical logic) by fixing two inputs. Since NAND can build any classical circuit, Toffoli is classically universal.
Challenge: Build a Quantum Half-Adder
Implement a quantum half-adder using CNOT and Toffoli gates. The circuit should take two input qubits (a and b) and produce sum (a ⊕ b) and carry (a AND b):
def quantum_half_adder():
"""Build a quantum half-adder circuit.
Input: |a⟩|b⟩|0⟩|0⟩ (4 qubits: a, b, sum, carry)
Output: |a⟩|b⟩|a⊕b⟩|a∧b⟩
"""
# Use CNOT for sum, Toffoli for carry
pass
Hints: The sum bit is a XOR b (use CNOT). The carry bit is a AND b (use Toffoli on the carry qubit with controls a and b).
Real-World Task: Noise Simulation
Add a simple noise model to your gate simulations. After each gate application, add a small probability of bit-flip (X error) or phase-flip (Z error). Measure how the output distribution degrades with increasing error rate.
This simulates the real challenge faced by quantum hardware teams. For example, IBM's quantum processors have gate error rates around 0.1% to 1%, requiring error correction for long computations.
FAQ
Try It Yourself
Run the Python gate examples. Modify the code to:
- Create a custom gate by defining a 2×2 unitary matrix
- Apply sequences of gates and verify they match expected outputs
- Build a circuit that creates the Bell state and verify entanglement
What's Next
You now have a solid understanding of quantum gates. Next, you will combine gates into quantum circuits to build complete quantum programs.
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Last updated: 2026-06-21.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro