⬡ Week 3

Classical Quantum Algorithms

The first algorithms to provably outperform classical computers — Deutsch–Jozsa, Bernstein–Vazirani, Simon's algorithm, and Grover's quadratic search speedup — decoded from first principles through phase kickback, oracle complexity, and geometric rotation.

1 History & Context: The Birth of Quantum Algorithms

Before the 1990s, quantum mechanics and computer science were largely separate intellectual territories. Physicists studied how quantum systems behaved; computer scientists proved theorems about Turing machines and Boolean circuits. The synthesis of the two — the idea that quantum mechanics could be exploited to solve computational problems faster than any classical machine — began not with a grand theory, but with a tiny toy problem.

David Deutsch's 1985 paper “Quantum Theory, the Church-Turing Principle and the Universal Quantum Computer” was the founding document. Deutsch noticed that a quantum computer could, in principle, evaluate a function on a superposition of all inputs simultaneously — what he called quantum parallelism. But parallelism alone doesn't give speedup; the hard part is extracting a useful global property from the interference of all those parallel computations. In 1992, Deutsch and Richard Jozsa found the first concrete demonstration that this could be done.

The period 1992–1996 produced a burst of landmark algorithms, each exploiting the same core toolkit — superposition, oracle queries, phase kickback, and interference — in increasingly sophisticated ways.

1.1 Algorithm Timeline

1985
Deutsch — Universal Quantum Computer
First quantum algorithm: determine if $f:\{0,1\}\to\{0,1\}$ is constant or balanced in 1 query. Exponential improvement for the 1-bit case.
1992
Deutsch–Jozsa
Generalised to $n$ bits: 1 quantum query vs $2^{n-1}+1$ deterministic classical queries. First exponential quantum speedup.
1992
Bernstein–Vazirani
Find hidden bit-string $s\in\{0,1\}^n$ with 1 quantum query vs $n$ classical queries. Same circuit as DJ, different problem.
1993
Simon's Algorithm
Exponential quantum speedup for finding a hidden XOR mask. Direct ancestor of Shor's algorithm.
1994
Shor's Factoring Algorithm
Exponential speedup for integer factorisation via Quantum Fourier Transform. Breaks RSA cryptography in polynomial time.
1996
Grover's Search Algorithm
Quadratic speedup for unstructured search: $O(\sqrt{N})$ queries vs $O(N)$ classical. Provably optimal.
2000s–today
NISQ & Variational Era
VQE, QAOA, qGANs — heuristic algorithms designed for noisy intermediate-scale hardware (Week 4).

1.2 Computational Complexity Classes

To understand what quantum algorithms achieve, we need the language of complexity classes — sets of problems grouped by the resources needed to solve them.

Complexity Class Hierarchy (relevant to quantum computing)

A complexity class is a set of decision problems (yes/no questions) that can be solved by some computational model within given resource bounds.

🤔 EQP vs BQP — Why "Perfect" Success Matters

The Deutsch–Jozsa algorithm is in EQP (succeeds with probability 1). If you allow a classical computer to guess and be wrong $1/3$ of the time (BPP), the "quantum speedup" actually disappears!

The Classical Shortcut: A randomized classical computer can just query the oracle twice. If $f$ is balanced, there is a 50% chance the two outputs are different. If they are different, you know it's balanced. If they are the same, you can still guess "constant" and be right with high probability.

The Real Win: DJ is only an exponential speedup if we demand zero error. For zero error, the classical computer must check $2^{n-1}+1$ inputs (more than half). Quantum computers, however, can provide mathematical certainty in just 1 query.

P
Polynomial Time
Problems solvable by a deterministic classical computer in polynomial time. Examples: sorting, shortest path, primality testing (AKS).
BPP
Bounded-Error Probabilistic Poly
Solvable with a randomised classical algorithm with error $\le 1/3$. Widely believed $\mathrm{P}=\mathrm{BPP}$.
BQP
Bounded-Error Quantum Poly
Solvable by a quantum computer in poly time with error $\le 1/3$. Contains factoring, discrete log. Believed $\mathrm{BPP}\subsetneq\mathrm{BQP}$.
EQP
Exact Quantum Poly
Solvable by a quantum computer in poly time with zero error. More restrictive than BQP. DJ is in EQP.
NP
Nondeterministic Poly
Problems whose solutions can be verified in poly time. SAT, graph colouring, TSP. Whether NP ⊆ BQP is a major open question.
Where do our algorithms sit?
  • Deutsch–Jozsa, BV: In EQP (exact quantum poly) — they succeed with probability 1. Classically they are outside BPP if we demand 0 error (they require $\Omega(2^n)$ deterministic queries).
  • Grover: Provides a quadratic speedup for unstructured search. It does not put NP inside BQP; it only improves the constant factor.
  • Shor: Factoring is in BQP but believed to be outside BPP — the best classical factoring algorithms are sub-exponential but super-polynomial.

It is widely believed that $\mathrm{P} \subsetneq \mathrm{BQP} \subsetneq \mathrm{PSPACE}$ but proving any of these separations rigorously remains an open problem.

2 The Query / Oracle Model

All of this week's algorithms are studied in the query complexity (or oracle complexity) model. Rather than measuring the total running time on some computational model, we count only the number of times the algorithm needs to evaluate a black-box function $f$. This isolates the quantum speedup cleanly from any implementation details.

⏱️ The "Waiting in Line" Analogy

In Query Complexity, we don't care how fast your computer calculates $1+1$. We only care how many times you have to "knock on the oracle's door" to ask for a result.

  • Wait classically: To know if a secret box is balanced, you might need to knock $2^{n-1}+1$ times, which could take a while.
  • Wait quantumly: We knock once and get the answer instantly.

The query model is like comparing a person who has to visit every house in a neighborhood to find someone, vs a person who can fly and shout to everyone at once.

2.1 The Standard Oracle — The Starting Point

In a classical setting, calling an oracle on input $x$ simply returns $f(x)$. In a quantum setting, we use an operation $U_f$ on two registers: the input register $\ket{x}$ and an ancilla (helper) register $\ket{y}$.

$$U_f \ket{x}\ket{y} = \ket{x}\ket{y \oplus f(x)}$$

This is called a standard oracle or bit oracle. It performs an XOR addition ($\oplus$) on the target register.

2.2 Setting up the Trick: The $|-\rangle$ State

To achieve phase kickback, we do not feed a basic $\ket{0}$ or $\ket{1}$ into the ancilla register. Instead, we prepare it in the minus state, which is a specific superposition: $$\ket{-} = \frac{\ket{0} - \ket{1}}{\sqrt{2}}$$

2.3 The Proof: Stepping Through the Phase Kickback

Now, let's look at the algebra of what happens when we feed our input $\ket{x}$ and our ancilla $\ket{-}$ into the oracle. First, we substitute our state into the equation: $$U_f \ket{x}\ket{-} = U_f \left( \ket{x} \otimes \frac{\ket{0} - \ket{1}}{\sqrt{2}} \right)$$

Next, we apply the oracle $U_f$. It acts on the $\ket{0}$ and $\ket{1}$ inside the superposition, XORing them with $f(x)$. Since $0 \oplus f(x)$ is simply $f(x)$: $$= \ket{x} \otimes \frac{\ket{f(x)} - \ket{1 \oplus f(x)}}{\sqrt{2}}$$

Now we evaluate the two possible outcomes for $f(x)$:

  • Case 1: If $f(x) = 0$
    Plug $0$ into the numerator: $\ket{0} - \ket{1 \oplus 0} = \ket{0} - \ket{1}$. $$\frac{\ket{0} - \ket{1}}{\sqrt{2}} = \ket{-}$$ Result: The state is entirely unchanged.
  • Case 2: If $f(x) = 1$
    Plug $1$ into the numerator: $\ket{1} - \ket{1 \oplus 1} = \ket{1} - \ket{0}$. $$\frac{\ket{1} - \ket{0}}{\sqrt{2}} = -\left( \frac{\ket{0} - \ket{1}}{\sqrt{2}} \right) = -\ket{-}$$ Result: A global phase of $-1$ is introduced.

Because multiplying by $(-1)^0$ gives $1$ (no change) and multiplying by $(-1)^1$ gives $-1$ (a phase flip), we can elegantly combine both cases into the core phase kickback equation:

$$\boxed{U_f \ket{x}\ket{-} = (-1)^{f(x)}\ket{x}\ket{-}}$$
💡 The Catalyst Analogy

In chemistry, a catalyst is something that speeds up a reaction without being consumed itself. In quantum computing, the ancilla qubit in state $\ket{-}$ acts as a phase catalyst.

When we run the oracle, the ancilla goes in as $\ket{-}$ and comes out as $\ket{-}$ unaffected. However, it "kicks" a phase shift back onto the data qubits.

2.4 Quantum Parallelism: The Engine at Work

The math above shows how a single input $\ket{x}$ gets a phase flip if $f(x)=1$. The real power is applied when our input is a massive superposition of all possible inputs: $$\ket{\psi} = \sum_x \alpha_x \ket{x}$$

When we apply the phase oracle, the math distributes perfectly across every single term:

$$U_f \left( \sum_x \alpha_x \ket{x} \right) \ket{-} = \left( \sum_x (-1)^{f(x)} \alpha_x \ket{x} \right) \ket{-}$$
🎯 The Takeaway

Look closely at the final equation. The ancilla $\ket{-}$ is sitting at the end, completely unchanged. However, inside the superposition, the oracle has evaluated $f(x)$ for every single $x$ simultaneously. It wrote the answers into the positive or negative phases without collapsing the state.

⚡ Phase Kickback Check
In phase kickback, what is the state of the ancilla qubit after the oracle operation?
It is flipped to $\ket{+}$
It collapses to $\ket{0}$ or $\ket{1}$
It remains in the $\ket{-}$ state
It becomes entangled with the data qubits
Correct! The ancilla acts as a catalyst; it provides the mechanism for the phase flip but returns to its original $\ket{-}$ state, ready for the next operation (or discard).

When the input is a superposition, the phase oracle acts as a "filter" that marks certain states with a negative sign: $$U_f \left(\sum_x \alpha_x \ket{x}\right)\ket{-} = \left(\sum_x (-1)^{f(x)}\alpha_x \ket{x}\right)\ket{-}$$ This is the fundamental engine behind all this week's algorithms.

3 The Deutsch–Jozsa Algorithm

3.1 The Problem

Deutsch–Jozsa Problem

You are given a function $f : \{0,1\}^n \to \{0,1\}$ (accessible only through an oracle) with the promise that $f$ is either:

  • Constant: $f(x)$ is the same for all $x$ (either always 0 or always 1), or
  • Balanced: $f(x) = 0$ for exactly half the inputs and $f(x) = 1$ for the other half.

Determine which case holds. You are guaranteed that these are the only two possibilities.

🖥 Classical Complexity

Worst case (deterministic): You must query at least $2^{n-1}+1$ times. If the first $2^{n-1}$ queries all return the same value, you can't yet decide — the function might be constant (with that value everywhere) or balanced (with all the opposite values on the other half). Only one more query settles it.

Randomised: With $k$ random queries all returning the same value, you can declare "constant" with error probability $\le 1/2^{k-1}$. But for zero-error you still need $2^{n-1}+1$.

⚛ Quantum Complexity

Exactly 1 oracle query, with zero error probability. The answer is obtained deterministically from a single application of $U_f$.

Quantum speedup: Exponential (in the zero-error setting).

3.2 Full Circuit Derivation

The circuit uses $n$ data qubits and 1 ancilla qubit.

Logic Visualization
Step 1 Step 2: Oracle S3 Step 4 |0⟩ |0⟩ |1⟩ ancilla H H H U_f (oracle) H H M M ⟵ measure ⟵ all 0s? (discard ancilla) Superposition Phase Kickback Interference Sampling

Let us track the state at each step rigorously.

Deutsch–Jozsa Algorithm — Step-by-Step State Derivation

Initial state:

$$\ket{\psi_0} = \ket{0}^{\otimes n}\ket{1}$$

After $H^{\otimes n}$ on data qubits and $H$ on ancilla:

$$\ket{\psi_1} = \left(\frac{1}{\sqrt{2^n}}\sum_{x=0}^{2^n-1}\ket{x}\right)\ket{-}$$

We have created a uniform superposition over all $n$-bit strings, and set the ancilla to $\ket{-}$.

After applying $U_f$ (using phase kickback):

$$\ket{\psi_2} = \frac{1}{\sqrt{2^n}}\sum_{x=0}^{2^n-1}(-1)^{f(x)}\ket{x}\ket{-}$$

Each basis state $\ket{x}$ picks up a phase $(-1)^{f(x)}$.

After $H^{\otimes n}$ on data qubits:

We use the key identity: $H^{\otimes n}\ket{x} = \frac{1}{\sqrt{2^n}}\sum_{z=0}^{2^n-1}(-1)^{x\cdot z}\ket{z}$ where $x\cdot z = \bigoplus_{i=1}^n x_i z_i$ is the binary inner product.

Proof of the $H^{\otimes n}$ identity

Step 1 — verify for $n=1$: The single-qubit Hadamard gives $H\ket{x} = \frac{1}{\sqrt{2}}\sum_{z\in\{0,1\}}(-1)^{xz}\ket{z}$. Let's check both cases:

  • $H\ket{0} = \frac{1}{\sqrt{2}}((-1)^{0\cdot0}\ket{0}+(-1)^{0\cdot1}\ket{1}) = \frac{1}{\sqrt{2}}(\ket{0}+\ket{1})\;\checkmark$
  • $H\ket{1} = \frac{1}{\sqrt{2}}((-1)^{1\cdot0}\ket{0}+(-1)^{1\cdot1}\ket{1}) = \frac{1}{\sqrt{2}}(\ket{0}-\ket{1})\;\checkmark$

Step 2 — general $n$: Since $\ket{x} = \ket{x_1}\otimes\ket{x_2}\otimes\cdots\otimes\ket{x_n}$ and $H^{\otimes n} = H\otimes H\otimes\cdots\otimes H$, the tensor product factors: $$H^{\otimes n}\ket{x} = \bigotimes_{i=1}^n H\ket{x_i} = \bigotimes_{i=1}^n \frac{1}{\sqrt{2}}\sum_{z_i}(-1)^{x_i z_i}\ket{z_i}$$ Distributing the products: $$= \frac{1}{\sqrt{2^n}}\sum_{z_1,z_2,\ldots,z_n}(-1)^{x_1 z_1 + x_2 z_2 + \cdots + x_n z_n}\ket{z_1 z_2\cdots z_n} = \frac{1}{\sqrt{2^n}}\sum_{z}(-1)^{x\cdot z}\ket{z}\;\square$$

$$\ket{\psi_3} = \frac{1}{\sqrt{2^n}}\sum_{x}(-1)^{f(x)} \cdot \frac{1}{\sqrt{2^n}}\sum_{z}(-1)^{x\cdot z}\ket{z}$$ $$= \sum_{z}\left[\frac{1}{2^n}\sum_{x}(-1)^{f(x)+x\cdot z}\right]\ket{z}$$

The amplitude of $\ket{z=0^n}$ is especially revealing:

$$\text{Amplitude of }\ket{0^n} = \frac{1}{2^n}\sum_{x=0}^{2^n-1}(-1)^{f(x)}$$

If $f$ is constant: All $(-1)^{f(x)}$ have the same sign, so the sum is $\pm 1$, and the amplitude of $\ket{0^n}$ is $\pm 1$. Measuring gives $0^n$ with certainty (probability $= 1$).

If $f$ is balanced: Exactly half the $(-1)^{f(x)}$ are $+1$ and half are $-1$, so they cancel perfectly. The amplitude of $\ket{0^n}$ is $0$. Measuring gives $0^n$ with probability $0$ — we will never measure the all-zeros string.

Decision Rule

Measure the $n$ data qubits. If the outcome is $\mathbf{0} = 00\cdots0$, declare constant. If any qubit is $1$, declare balanced.

This succeeds with probability exactly $1$ in one query.

⚡ DJ Logic Check
If the Deutsch-Jozsa algorithm measures the state $\ket{001}$, what do we conclude about the function $f$?
It is Constant
It is Balanced
The experiment failed
We need more queries to decide
Spot on! If the outcome is anything other than all zeros, the constant case is completely ruled out. In the balanced case, the amplitude of the all-zeros state is exactly zero, so any measurement you get must be a non-zero string.

3.3 Why It Works: The Key Insight

The crucial insight is that $H^{\otimes n}$ is its own inverse ($(H^{\otimes n})^2 = I$). If $f$ is constant, $(-1)^{f(x)}$ is a global phase on all states — applying $H^{\otimes n}$ a second time undoes the first $H^{\otimes n}$ and sends every state back to $\ket{0^n}$. If $f$ is balanced, the phases are not all the same; there's a non-trivial pattern across $x$, and the final Hadamard performs a kind of Fourier transform that sends the amplitude of $\ket{0^n}$ to zero — exactly the pattern that a balanced function creates.

Let's verify with a concrete $n=2$ example to build intuition:

Constant-0
x f(x) phase
00 0 +1
01 0 +1
10 0 +1
11 0 +1

Sum=+4, amplitude of |00⟩=1

Constant-1
x f(x) phase
00 1 −1
01 1 −1
10 1 −1
11 1 −1

Sum=−4, amplitude of |00⟩=−1

Balanced (type A)
x f(x) phase
00 0 +1
01 1 −1
10 0 +1
11 1 −1

Sum=0, amplitude of |00⟩=0

Balanced (type B)
x f(x) phase
00 1 −1
01 0 +1
10 1 −1
11 0 +1

Sum=0, amplitude of |00⟩=0

3.4 Interactive Oracle Demo

Choose an oracle type, then watch the algorithm run step by step. The final measurement of the data register reveals constant vs balanced.

n = 2 qubits
Waiting — select an oracle above to begin.

3.5 Qiskit Implementation

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.primitives import StatevectorSampler
import numpy as np

def deutsch_jozsa_circuit(oracle_type: str, n: int = 3) -> QuantumCircuit:
    """
    Build n-qubit Deutsch-Jozsa circuit.
    oracle_type: 'constant_0', 'constant_1', or 'balanced'
    """
    qr = QuantumRegister(n + 1, 'q')   # n data + 1 ancilla
    cr = ClassicalRegister(n, 'c')
    qc = QuantumCircuit(qr, cr)

    # Step 1: Initialise ancilla to |1⟩
    qc.x(qr[n])                   # ancilla = qubit n

    # Step 2: Apply H to all qubits
    qc.h(qr)                       # creates |+⟩^n |−⟩ on data, ancilla

    qc.barrier()

    # Step 3: Apply oracle
    if oracle_type == 'constant_0':
        pass                       # f(x)=0 for all x: do nothing

    elif oracle_type == 'constant_1':
        qc.x(qr[n])                # f(x)=1: flip ancilla unconditionally
                                   # (global phase of -1 on all states)

    elif oracle_type == 'balanced':
        # Example balanced oracle: f(x) = x_0  (XOR with first qubit)
        # Implement as CNOT from data qubit 0 to ancilla
        for i in range(n):
            qc.cx(qr[i], qr[n])   # phase kickback from each data qubit

    qc.barrier()

    # Step 4: Apply H again to data qubits
    qc.h(qr[:n])

    # Step 5: Measure data qubits
    qc.measure(qr[:n], cr)

    return qc

# Run with exact statevector sampler (no noise)
qc = deutsch_jozsa_circuit('balanced', n=3)
sampler = StatevectorSampler()
job = sampler.run([qc], shots=1024)
result = job.result()[0]
counts = result.data.c.get_counts()

# Interpret result
total = sum(counts.values())
zero_count = counts.get('000', 0)
if zero_count / total > 0.95:
    print("Function is CONSTANT (measured all-zeros)")
else:
    print("Function is BALANCED (measured non-zero string)")
    print(f"Measured: {counts}")
Classical lower bound: why you need 2^(n−1)+1 queries

Suppose a deterministic algorithm makes $k$ queries, receiving $k$ output values. If all $k$ values are equal (say all 0), could the function be constant? Yes — it might be constant-0. Could it be balanced? Also yes — if $k \le 2^{n-1}$, there exist balanced functions that map all $k$ queried inputs to 0 (since a balanced function is 0 on half the inputs, and your $k \le 2^{n-1}$ queries might have all landed on that half). So you cannot decide. This argument holds for any $k \le 2^{n-1}$, showing you need at least $2^{n-1}+1$ queries in the worst case.

4 The Bernstein–Vazirani Algorithm

4.1 The Problem

Bernstein–Vazirani Problem

There is a hidden secret string $s \in \{0,1\}^n$. You are given oracle access to the linear function $$f(x) = s \cdot x \pmod{2} = \bigoplus_{i=1}^{n} s_i x_i$$ (the bitwise inner product of $s$ and $x$, reduced mod 2). Find $s$.

🖥 Classical Complexity

To find bit $s_i$, query $f$ on the input $e_i = 00\cdots010\cdots0$ (the $i$-th standard basis vector). Then $f(e_i) = s \cdot e_i = s_i$. You need $n$ queries — one per bit. This is tight: any classical algorithm requires $n$ queries in the worst case.

⚛ Quantum Complexity

Exactly 1 oracle query. The quantum algorithm recovers all $n$ bits of $s$ simultaneously. Speedup: $n$-fold (linear).

4.2 Full Derivation

Remarkably, the circuit is identical to the Deutsch–Jozsa circuit. What changes is the oracle and the interpretation of the measurement.

Bernstein–Vazirani Step-by-Step Logic

Step 1: Create the superposition

We start with $\ket{0^n}$ and apply Hadamards to get: $$\frac{1}{\sqrt{2^n}}\sum_x \ket{x}$$

Step 2: Apply the Oracle (Phase Kickback)

The oracle adds a phase of $(-1)^{f(x)}$ to each state. Since $f(x) = s \cdot x$: $$\frac{1}{\sqrt{2^n}}\sum_{x \in \{0,1\}^n}(-1)^{s \cdot x}\ket{x}$$

Step 3: The Second Hadamard Layer

We apply $H^{\otimes n}$ again. This is where the magic (interference) happens: $$\sum_z \left(\frac{1}{2^n}\sum_x(-1)^{x\cdot(s\oplus z)}\right)\ket{z}$$

Look closely at that inner sum: $\frac{1}{2^n}\sum_x(-1)^{x\cdot(s\oplus z)}$. It acts as a searchlight that only lights up when $z$ matches our secret string $s$.

$$\text{Final State} = \ket{s}$$
Walkthrough — Seeing it with n=2, s=11

Let's trace it through. Our goal is to find $s=11$ in 1 query.

  1. Start: $\frac{1}{2}(\ket{00} + \ket{01} + \ket{10} + \ket{11})$
  2. Apply Oracle ($s \cdot x$):
    • $11 \cdot 00 = 0 \to +\ket{00}$
    • $11 \cdot 01 = 1 \to -\ket{01}$
    • $11 \cdot 10 = 1 \to -\ket{10}$
    • $11 \cdot 11 = 0 \pmod 2 \to +\ket{11}$
    State is now $\frac{1}{2}(\ket{00} - \ket{01} - \ket{10} + \ket{11})$.
  3. Final Hadamard: This specific phase pattern converts exactly back to $\ket{11}$ through constructive interference. Try it: $H^{\otimes 2}\ket{11}$ gives exactly this state. Since $H$ is its own inverse, this state gives $\ket{11}$ back!
The miracle of BV

After the second Hadamard layer, the state has collapsed deterministically to $\ket{s}$ — the exact bitstring we're looking for. Measuring gives $s$ with probability 1. In a single query, we have performed $n$ independent classical computations simultaneously and read off all their results through interference. This is sometimes called quantum learning.

The Deep Reason BV Works: Fourier Analysis over GF(2)

The function $f_s(x) = s \cdot x \bmod 2$ is linear over the field $\mathbb{F}_2 = \{0,1\}$. The Hadamard transform $H^{\otimes n}$ is exactly the quantum Fourier transform over $(\mathbb{Z}_2)^n$ (i.e., over $n$-bit binary strings with componentwise XOR). The BV algorithm performs Fourier analysis of $f_s$ in a single shot — it "probes all $n$ Fourier frequencies simultaneously" using superposition, and the inverse transform picks out the unique frequency $s$ that characterises $f_s$.

This insight generalises directly: Shor's algorithm uses the Quantum Fourier Transform over $\mathbb{Z}_N$ (the cyclic group) to find the period of the modular exponentiation function $f(x) = a^x \bmod N$. BV is the $\mathbb{F}_2^n$ version; Shor is the $\mathbb{Z}_N$ version.

Complexity Separation: Oracle BPP ≠ Oracle BQP

The BV problem gives a provable oracle separation between BPP (randomised classical) and BQP (quantum polynomial time): $$Q(BV_n) = 1 \qquad \text{vs} \qquad R(BV_n) = n$$ No randomised classical algorithm can solve BV in fewer than $n$ queries. Each query gives at most 1 bit of information about the $n$-bit string $s$, and $s$ has $n$ bits. The quantum algorithm extracts all $n$ bits in a single parallel query via superposition. This is an unconditional separation in the oracle model — not dependent on unproven complexity assumptions like $P \ne NP$.

4.3 Interactive Demo — Recover the Secret String

Set the secret bit-string $s$ by toggling the bits below. The BV algorithm finds $s$ in a single oracle query.

Secret string s (click bits to toggle):
Oracle: f(x) = s·x mod 2

Qiskit code for BV:

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister

def bernstein_vazirani(s: str) -> QuantumCircuit:
    """
    s: secret bit-string (e.g. '10110')
    Returns circuit that recovers s in one query.
    """
    n = len(s)
    qr = QuantumRegister(n + 1, 'q')
    cr = ClassicalRegister(n, 'c')
    qc = QuantumCircuit(qr, cr)

    # Prepare ancilla |−⟩
    qc.x(qr[n])
    qc.h(qr)          # H on all: data→|+⟩^n, ancilla→|−⟩
    qc.barrier()

    # Oracle: implement f(x) = s·x mod 2
    # = CNOT from each data qubit i to ancilla where s[i]='1'
    for i, bit in enumerate(reversed(s)):   # LSB first
        if bit == '1':
            qc.cx(qr[i], qr[n])

    qc.barrier()
    qc.h(qr[:n])       # Second Hadamard layer
    qc.measure(qr[:n], cr)
    return qc

# Test
s = '10110'
qc = bernstein_vazirani(s)
print(f"Hidden string: {s}")
print(f"Circuit depth: {qc.depth()}")

5 Simon's Algorithm

5.1 The Problem

🏙️ The Twin Cities Analogy

Imagine a weird world where every city has a "twin" city. If you visit any city, the food, weather, and people look exactly the same as in its twin.

  • The cities are the inputs ($x, y$).
  • The "look" of the city is the function output ($f(x)$).

There's a secret offset ($s$) that connects every city to its twin: $\text{City } A \oplus s = \text{City } B$. Your goal is to find that secret offset $s$ as quickly as possible.

If $s = 0^n$, then $f$ is one-to-one (injective). Otherwise, every output value is hit exactly twice, by $x$ and $x \oplus s$. This is like a "hidden XOR symmetry" — and the technique used to solve it is the prototype for Shor's algorithm.

🖥 Classical Complexity

To find the secret $s$, a classical computer has to keep checking cities until it happens to find two that look identical. By the "Birthday Paradox," this takes about $\sqrt{2^n}$ queries—which is exponentially slow.

⚛ Quantum Complexity

A quantum computer finds the secret $s$ in just $O(n)$ queries. It doesn't look for collisions one by one; it detects the global symmetry of the entire function at once.

5.2 Step-by-Step Evolution

Simon's Register Visualization
Step 1 Step 2: Oracle Step 3 & 4 q0|0⟩ q1|0⟩ a0|0⟩ a1|0⟩ data register ancilla register H H H U_f f(x) = f(x ⊕ s) H H H M M ⟵ z s.t. s·z = 0
🛤️ Tracking the Data's Journey

Step 1 — Let's start everywhere at once: Apply $H^{\otimes n}$ to the data register: $$\ket{\psi_1} = \frac{1}{\sqrt{2^n}}\sum_{x}\ket{x}\ket{0}$$

Step 2 — "Photographing" each city: When we apply $U_f$, each city in the superposition gets tagged with its appearance ($f(x)$): $$\ket{\psi_2} = \frac{1}{\sqrt{2^n}}\sum_x \ket{x}\ket{f(x)}$$ The input and output are now entangled.

Step 3 — The Collapsing Trick: We measure the output register. Suppose the "appearance" was $y_0$. Suddenly, the data register collapses to just the two cities that look like that: $$\ket{\text{Data after measurement}} = \frac{1}{\sqrt{2}}(\ket{x_0} + \ket{x_0 \oplus s})$$

Why does measuring the ancilla collapse the data register?

Before measurement, the state $\ket{\psi_2} = \frac{1}{\sqrt{2^n}}\sum_x\ket{x}\ket{f(x)}$ contains all $2^n$ input-output pairs in superposition. Because $f$ is 2-to-1 with period $s$, every output value $y$ has exactly two pre-images: $x_0$ and $x_0 \oplus s$. So we can regroup the sum by unique output values:

$$\ket{\psi_2} = \frac{1}{\sqrt{2^n}}\sum_{\text{coset representatives } x_0}\big(\ket{x_0}+\ket{x_0\oplus s}\big)\ket{f(x_0)}$$

Now, when we measure the ancilla and get a specific outcome $y_0 = f(x_0)$, quantum mechanics tells us the state projects onto the subspace where the ancilla equals $y_0$. This keeps only the terms with $\ket{f(x_0)}$ in the ancilla, killing everything else:

$$\text{Data register after measurement:} \quad \frac{1}{\sqrt{2}}\big(\ket{x_0}+\ket{x_0\oplus s}\big)$$

This collapse happens because of entanglement — the data and ancilla were correlated. Measuring one forces the other into a definite state. The data register now contains a superposition of exactly the two inputs that map to the measured output.

Simon's Algorithm — Continued

Step 4 — Apply $H^{\otimes n}$ to the data register: Now we have $\frac{1}{\sqrt{2}}(\ket{x_0}+\ket{x_0\oplus s})$. Let's apply the Hadamard transform to each term individually (using the identity $H^{\otimes n}\ket{x} = \frac{1}{\sqrt{2^n}}\sum_z(-1)^{x\cdot z}\ket{z}$ that we proved in §3.2):

First term: $$H^{\otimes n}\ket{x_0} = \frac{1}{\sqrt{2^n}}\sum_z(-1)^{x_0\cdot z}\ket{z}$$

Second term: Since $(x_0\oplus s)\cdot z = x_0\cdot z \oplus s\cdot z$ (the binary inner product distributes over XOR): $$H^{\otimes n}\ket{x_0\oplus s} = \frac{1}{\sqrt{2^n}}\sum_z(-1)^{(x_0\oplus s)\cdot z}\ket{z} = \frac{1}{\sqrt{2^n}}\sum_z(-1)^{x_0\cdot z}\cdot(-1)^{s\cdot z}\ket{z}$$

Adding both terms (with the $\frac{1}{\sqrt{2}}$ normalization): $$\ket{\psi_4} = \frac{1}{\sqrt{2^{n+1}}}\sum_z(-1)^{x_0\cdot z}\underbrace{\big(1+(-1)^{s\cdot z}\big)}_{\text{interference filter}}\ket{z}$$

The factor $(1+(-1)^{s\cdot z})$ is an interference filter:

  • When $s\cdot z = 0$: $(1+(-1)^0) = 1+1 = 2$ — constructive interference. This $z$ can be measured.
  • When $s\cdot z = 1$: $(1+(-1)^1) = 1-1 = 0$ — destructive interference. This $z$ has zero amplitude and can never be measured.

So measurement only yields strings $z$ satisfying $s\cdot z = 0 \pmod{2}$. Each measurement gives us one linear equation about $s$ — for free!

Classical Post-Processing

Each run of the quantum circuit yields a uniformly random $z$ in the hyperplane $\{z : s \cdot z = 0\}$. After $n-1$ linearly independent measurements $z_1, z_2, \ldots, z_{n-1}$, we have a linear system of $n-1$ equations in $n$ unknowns (the bits of $s$) over $\mathbb{F}_2$: $$z_1 \cdot s = 0, \quad z_2 \cdot s = 0, \quad \ldots, \quad z_{n-1} \cdot s = 0$$ The null space of this system is 1-dimensional (over $\mathbb{F}_2$), giving either $s = 0^n$ or the correct $s$. Gaussian elimination over $\mathbb{F}_2$ ($O(n^3)$ classical operations) yields $s$.

The probability that $n-1$ random samples are linearly independent is at least $\prod_{k=0}^{n-2}(1-2^{k-n}) \ge 1-O(2^{-(n-1)})$ — exponentially high. So $O(n)$ quantum queries suffice with high probability.

Worked Example: n=3, s=110 — Full Gaussian Elimination

Setup: We have $n=3$ qubits, and the hidden string is $s = 110$. The oracle satisfies $f(x) = f(x \oplus 110)$ for all $x$. The valid measurement outcomes are all $z$ with $s\cdot z = 0$, i.e., $z_1 \oplus z_2 = 0$ (so $z_1 = z_2$):

$z \in \{000, 001, 110, 111\}$ — these are the only possible outcomes.

Run 1: Suppose we measure $z_1 = 001$. This gives us the equation: $$001 \cdot s = 0 \quad\Rightarrow\quad s_3 = 0$$ So the last bit of $s$ is 0. Good — one equation from one query.

Run 2: Suppose we measure $z_2 = 110$. This gives: $$110 \cdot s = 0 \quad\Rightarrow\quad s_1 \oplus s_2 = 0 \quad\Rightarrow\quad s_1 = s_2$$

Gaussian elimination over GF(2): We now have the system:

$$\begin{pmatrix}0&0&1\\1&1&0\end{pmatrix}\begin{pmatrix}s_1\\s_2\\s_3\end{pmatrix} = \begin{pmatrix}0\\0\end{pmatrix}$$

Row 1 tells us $s_3 = 0$. Row 2 tells us $s_1 = s_2$. The null space is $\{(0,0,0),\;(1,1,0)\}$. Since we want $s \neq 0^n$, the answer is $\boxed{s = 110}$ ✓

Two quantum queries + simple linear algebra → the secret string that would take $\Omega(2^{3/2}) \approx 3$ classical queries to find in the best case (and exponentially many for larger $n$).

🚀 Next Stop: Shor's Algorithm

Simon's algorithm is the "grandfather" of the most famous algorithm in quantum history: Shor's Factoring Algorithm.

While Simon's looks for a secret XOR pattern, Shor's looks for a secret repeating cycle (periodicity) in numbers. They both use the same trick: using quantum interference to reveal a global pattern that is invisible to classical computers.

Master Simon's, and you've already understood the core "aha!" moment of Shor's.

6 Grover's Search Algorithm

6.1 The Search Problem

Given $N = 2^n$ items (indexed $0, 1, \ldots, N-1$), of which $M$ are "marked" (satisfy some criterion), find one. In the oracle model, we can ask "is item $x$ marked?" — a single yes/no query.

🖥 Classical Complexity

With a single marked item ($M=1$), a classical search must examine $O(N)$ items on average. No randomised classical algorithm can do better than $\Omega(N)$ queries in the worst case.

⚛ Quantum Complexity

Grover's algorithm finds a marked item with probability $\ge 1/2$ in $O(\sqrt{N/M})$ queries. For $M=1$: $O(\sqrt{N})$ queries. This is a provably optimal quantum algorithm — no quantum algorithm can do better than $\Omega(\sqrt{N})$ for unstructured search (Bennett–Bernstein–Brassard–Vazirani 1997).

For $n=30$ qubits ($N = 10^9$ items), Grover uses about $\pi/4 \cdot \sqrt{10^9} \approx 24{,}868$ queries; a classical search averages $500{,}000{,}000$. The speedup is quadratic — modest compared to Shor's exponential, but the setting is completely generic (no structure assumed on $f$).

6.2 The Phase Oracle

Grover's oracle marks the target state $\ket{t}$ with a phase flip: $$\mathcal{O}\ket{x} = \begin{cases} -\ket{x} & \text{if } x = t \\ +\ket{x} & \text{otherwise} \end{cases} \equiv (-1)^{f(x)}\ket{x}$$ where $f(x) = 1$ iff $x$ is a marked item. In matrix form, $\mathcal{O} = I - 2\ket{t}\bra{t}$. This is a reflection about the hyperplane orthogonal to $\ket{t}$ — the oracle simply flips the phase of the marked state.

6.3 The Diffusion Operator (The Mirror about the Mean)

The second component is the diffusion operator. While the oracle "marks" our target with a minus sign, the diffuser is what actually amplifies its probability.

🪞 The Mirror Analogy

Imagine a row of bars of different heights. The Mean is the average height of all the bars.

The diffusion operator acts like a mirror placed at the height of the mean. Every bar is "reflected" through this mirror. If a bar is below the mean, it gets flipped to be equally far above the mean. If it's above the mean, it gets flipped below it.

Mathematically, the diffuser $D$ performs inversion about the mean:

$$\text{New Amplitude} = 2 \cdot (\text{Average}) - (\text{Old Amplitude})$$

Because the oracle flipped the target's amplitude to be negative, the target is now way below the average. When we reflect it through the mean, it jumps high above everyone else!

The formal definition is $D = 2\ket{s}\bra{s} - I$, where $\ket{s}$ is the uniform superposition. In a circuit, this is implemented by wrapping a 0-state phase flip between two Hadamard layers ($H^{\otimes n}$).

⚡ Grover Intuition
Why do we need the Diffuser? Why isn't the Oracle enough to find the item?
The oracle only works for one qubit
The oracle only changes the phase, not the probability of measuring the item
The diffuser is what actually marks the item
The oracle is too slow on its own
Exactly! Probability depends on the square of the amplitude. Since $|1|^2 = |-1|^2$, flipping the sign doesn't change how likely we are to measure the item. We need the diffuser to convert that phase difference into an amplitude (and thus probability) difference.

6.4 Grover Iterator Circuit

Grover Iteration Component
q0|0⟩ q1|0⟩ H H Oracle O_f Mark Target Diffuser D Invert about Mean Repeat k times... M M Prep Reflection (O) Amplification (D)

6.5 Geometric Picture — Rotation in 2D

Grover's algorithm has an elegant geometric interpretation. Consider the 2D plane spanned by:

  • $\ket{\alpha}$ = uniform superposition over unmarked states (normalised)
  • $\ket{\beta}$ = uniform superposition over marked states (normalised)

Any state in this plane can be written $\cos\theta\,\ket{\alpha} + \sin\theta\,\ket{\beta}$. The initial uniform superposition $\ket{s} = \sqrt{1-M/N}\,\ket{\alpha} + \sqrt{M/N}\,\ket{\beta}$, so the initial angle is $\sin\theta_0 = \sqrt{M/N}$.

Geometric Theorem

Each application of the Grover operator $G = D \cdot \mathcal{O}$ rotates the state by angle $2\theta_0$ towards $\ket{\beta}$ in this 2D plane. After $k$ iterations, the state is: $$\ket{\psi_k} = \cos((2k+1)\theta_0)\,\ket{\alpha} + \sin((2k+1)\theta_0)\,\ket{\beta}$$ The probability of measuring a marked item is $\sin^2((2k+1)\theta_0)$.

We want $(2k+1)\theta_0 \approx \pi/2$, i.e., $k \approx \frac{\pi}{4\theta_0} - \frac{1}{2}$. For $M=1$: $\theta_0 = \arcsin(1/\sqrt{N}) \approx 1/\sqrt{N}$ (for large $N$), giving: $$k_{\text{opt}} \approx \frac{\pi}{4}\sqrt{N}$$

6.5 Iteration Count and Success Probability

Optimal Number of Grover Iterations
$$k_{\text{opt}} = \left\lfloor \frac{\pi}{4}\sqrt{\frac{N}{M}} \right\rfloor$$

The success probability after $k_{\text{opt}}$ iterations is at least $1 - M/N$ (close to 1 for $M \ll N$). More precisely:

$$P(\text{success after } k_{\text{opt}} \text{ iterations}) = \sin^2\!\left(\,(2k_{\text{opt}}+1)\arcsin\!\sqrt{M/N}\,\right) \ge 1 - \frac{M}{N}$$
N (database) M (marked) k_opt Classical (avg) Speedup
4 1 1 2.5 2.5×
16 1 3 8 2.7×
256 1 12 128 10.7×
1,024 1 25 512 20.5×
1,048,576 (2²⁰) 1 804 524,288 652×
2³² 1 51,472 2.15×10⁹ 41,774×
Don't over-iterate!

If you apply too many Grover iterations (past $k_{\text{opt}}$), the angle overshoots $\pi/2$ and the success probability decreases. Running $2k_{\text{opt}}$ iterations brings you back to the initial state with near-zero success probability. The rotation is reversible; Grover's algorithm must be stopped at the right moment.

6.6 Interactive Grover Visualizer

The visualization below shows the geometric rotation in the $(\ket{\alpha}, \ket{\beta})$ plane. The blue arrow shows the current state; the gold arrow shows the target direction. Adjust $N$ and watch how many iterations are needed.

k=0 | P=1/N

6.7 Amplitude Evolution Visualizer — The Full Picture

The geometric view shows why Grover works; the amplitude bar chart shows how it feels from inside. Watch every basis state's amplitude bar evolve with each iteration — the marked element's bar shoots up while all others are suppressed. The gold dashed line is the mean amplitude (the pivot for the diffusion operator).

k=0 — uniform superposition
Worked Example: N=4 (n=2), Target |11⟩ — 1 Iteration Gives 100% Success

Initial state: $|\psi_0\rangle = \tfrac{1}{2}(|00\rangle + |01\rangle + |10\rangle + |11\rangle)$. All amplitudes $= +\tfrac{1}{2}$. Mean $\bar{\alpha} = \tfrac{1}{2}$.

After oracle $\mathcal{O}$ (flip sign of $|11\rangle$): amplitudes $= [+\tfrac{1}{2}, +\tfrac{1}{2}, +\tfrac{1}{2}, -\tfrac{1}{2}]$. New mean $\bar{\alpha} = \tfrac{1}{4}(3\cdot\tfrac{1}{2} + (-\tfrac{1}{2})) = \tfrac{1}{4}$.

After diffuser $D$ (reflect each amplitude about mean $\tfrac{1}{4}$, i.e., $\alpha_x \to 2\cdot\tfrac{1}{4} - \alpha_x$):

  • $|00\rangle, |01\rangle, |10\rangle$: $2\cdot\tfrac{1}{4} - \tfrac{1}{2} = \tfrac{1}{2} - \tfrac{1}{2} = \mathbf{0}$
  • $|11\rangle$: $2\cdot\tfrac{1}{4} - (-\tfrac{1}{2}) = \tfrac{1}{2} + \tfrac{1}{2} = \mathbf{1}$

After a single Grover iteration, $|11\rangle$ has amplitude $1$ — success probability $\mathbf{100\%}$! ($k_\text{opt} = \lfloor \tfrac{\pi}{4}\sqrt{4}\rfloor = \lfloor\tfrac{\pi}{2}\rfloor = 1$. ✓)

Grover's Algorithm — Pseudocode
Algorithm GROVER(oracle O_f, n qubits, N = 2^n):
  T_opt ← ⌊(π/4)·√N⌋         // optimal number of iterations
  |ψ⟩ ← H^⊗n |0⟩^n           // uniform superposition

  for t = 1 to T_opt:
    |ψ⟩ ← O_f |ψ⟩              // Phase oracle: flip sign of |x*⟩
    |ψ⟩ ← D |ψ⟩                // Diffusion: invert about mean
    // D = H^⊗n · (2|0^n⟩⟨0^n| − I) · H^⊗n

  result ← MEASURE(|ψ⟩)        // Success with prob ≥ 1 − 1/N
  return result
Optimality: The BBBV Lower Bound — Grover Cannot Be Improved

Bennett, Bernstein, Brassard, and Vazirani (1997) proved using the polynomial method that no quantum algorithm can solve unstructured search in fewer than $\Omega(\sqrt{N})$ queries. Grover's algorithm is therefore optimal — no cleverer quantum circuit can beat the $\sqrt{N}$ barrier for a black-box oracle.

Critical implication for NP: Grover does not put NP inside BQP. For an NP problem with $N = 2^n$ potential witnesses to verify, Grover gives $O(2^{n/2})$ quantum queries — still exponential! The speedup is quadratic, not polynomial. NP-complete problems remain intractable quantumly (as far as we know).

Cryptographic implication: Any symmetric cipher with an $n$-bit key that is secure against $O(2^n)$ classical brute force offers only $O(2^{n/2})$ security against a quantum adversary with Grover. This is why post-quantum standards (NIST PQC 2022–24) doubled symmetric key sizes: AES-128 → AES-256.

6.8 Generalisations and Applications

Amplitude Amplification (Brassard et al. 2002)

Grover's algorithm is a special case of the more general technique of amplitude amplification. Given any quantum algorithm $\mathcal{A}$ that finds a solution with probability $p$ (starting from any initial state), amplitude amplification can boost this to near-certainty in $O(1/\sqrt{p})$ calls to $\mathcal{A}$, quadratically improving any randomised algorithm.

Grover's algorithm (or amplitude amplification) is used as a subroutine in:

  • Collision finding: Find two inputs with the same output in $O(N^{1/3})$ time (quantum birthday problem)
  • Element distinctness: Determine if a list has duplicates in $O(N^{2/3})$ queries
  • Triangle finding: Quantum speedup for graph problems
  • Constraint satisfaction / SAT: Quadratic speedup over classical exhaustive search
  • Quantum minimum finding: Find the minimum of $N$ values in $O(\sqrt{N})$ queries
  • Cryptographic applications: Grover halves the effective key length of symmetric ciphers — AES-128 becomes equivalent to AES-64 against a quantum adversary, motivating post-quantum cryptography
from qiskit import QuantumCircuit
from qiskit.circuit.library import GroverOperator
from qiskit.primitives import StatevectorSampler
import math

def grover_circuit(n: int, target: int) -> QuantumCircuit:
    """
    Build Grover's circuit to find 'target' in {0,...,2^n - 1}.
    """
    N = 2 ** n
    # Optimal number of iterations
    k_opt = round((math.pi / 4) * math.sqrt(N))

    qc = QuantumCircuit(n)
    qc.h(range(n))          # Initial uniform superposition

    for _ in range(k_opt):
        # Phase oracle: flip phase of |target⟩
        # Implement as X on 0-bits, multi-controlled-Z, X back
        target_bits = format(target, f'0{n}b')
        for i, bit in enumerate(reversed(target_bits)):
            if bit == '0':
                qc.x(i)
        qc.h(n-1)
        qc.mcx(list(range(n-1)), n-1)   # multi-controlled Toffoli
        qc.h(n-1)
        for i, bit in enumerate(reversed(target_bits)):
            if bit == '0':
                qc.x(i)

        # Diffusion operator: 2|s⟩⟨s| - I
        qc.h(range(n))
        qc.x(range(n))
        qc.h(n-1)
        qc.mcx(list(range(n-1)), n-1)
        qc.h(n-1)
        qc.x(range(n))
        qc.h(range(n))

    qc.measure_all()
    return qc

# Example: find |101⟩ = item 5 in N=8
qc = grover_circuit(n=3, target=5)
sampler = StatevectorSampler()
result = sampler.run([qc], shots=1000).result()[0]
print(result.data.meas.get_counts())