Quantum circuits are the standard model for quantum computation. Just as classical computers use logic gates (AND, OR, NOT), quantum computers use quantum gates that manipulate qubits.
Qubits and State Vectors¶
A qubit exists in a superposition of $|0\rangle$ and $|1\rangle$:
$$|\psi\rangle = \alpha|0\rangle + \beta|1\rangle$$
where $\alpha, \beta \in \mathbb{C}$ and $|\alpha|^2 + |\beta|^2 = 1$. We represent this as a column vector:
$$|\psi\rangle = \begin{pmatrix} \alpha \\ \beta \end{pmatrix}$$
Essential Single-Qubit Gates¶
Pauli-X (NOT gate): Flips $|0\rangle \leftrightarrow |1\rangle$
$$X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix}$$
Hadamard: Creates superposition
$$H = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}$$
$$H|0\rangle = \frac{|0\rangle + |1\rangle}{\sqrt{2}} = |+\rangle$$
Phase Gate (S): Adds a phase of $i$ to $|1\rangle$
$$S = \begin{pmatrix} 1 & 0 \\ 0 & i \end{pmatrix}$$
T Gate: Adds a phase of $e^{i\pi/4}$ — essential for universal computation.
Multi-Qubit Gates¶
CNOT (Controlled-NOT): The fundamental two-qubit gate:
$$\text{CNOT} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \end{pmatrix}$$
It flips the target qubit if and only if the control qubit is $|1\rangle$.
Building the Deutsch-Jozsa Algorithm¶
The Deutsch-Jozsa algorithm determines whether a function $f: \{0,1\}^n \to \{0,1\}$ is constant or balanced with just one query — exponentially faster than any classical algorithm.
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
def deutsch_jozsa(oracle_type='balanced'):
"""
Deutsch-Jozsa algorithm for 2-qubit oracle.
oracle_type: 'constant' or 'balanced'
"""
n = 2 # number of input qubits
qc = QuantumCircuit(n + 1, n)
# Initialize output qubit to |1⟩
qc.x(n)
# Apply Hadamard to all qubits
qc.h(range(n + 1))
qc.barrier()
# Oracle
if oracle_type == 'balanced':
# Balanced oracle: f(x) = x₀ ⊕ x₁
qc.cx(0, n)
qc.cx(1, n)
else:
# Constant oracle: f(x) = 0 (identity)
pass
qc.barrier()
# Apply Hadamard to input qubits
qc.h(range(n))
# Measure input qubits
qc.measure(range(n), range(n))
return qc
# Test with balanced oracle
circuit = deutsch_jozsa('balanced')
print(circuit.draw())
print("If all measurements are 0 → constant")
print("If any measurement is 1 → balanced")
Universal Gate Sets¶
A key theorem in quantum computing: the set $\{H, T, \text{CNOT}\}$ is universal — any quantum computation can be approximated to arbitrary precision using only these three gates. This is the quantum analog of classical universality (NAND gates).
Measurement¶
Measurement in the computational basis collapses the state:
$$|\psi\rangle = \alpha|0\rangle + \beta|1\rangle \xrightarrow{\text{measure}} \begin{cases} |0\rangle & \text{with probability } |\alpha|^2 \\ |1\rangle & \text{with probability } |\beta|^2 \end{cases}$$
This irreversibility is what makes quantum algorithms so interesting — the challenge is designing circuits that amplify the probability of the correct answer before measurement.