Lab 3 — Grover Search
Overview
Suppose you have an unsorted list of items and exactly one of them is "marked." Classically you must check items one by one, taking on average queries and in the worst case. Grover's algorithm finds the marked item in about queries — a quadratic speedup that is provably optimal for unstructured search.
The trick is amplitude amplification: we start in a uniform superposition over all candidates, then repeatedly rotate the state vector so that amplitude piles up on the marked item, making it overwhelmingly likely to appear when measured. In this lab you mark a target 3-bit state and watch it dominate the output.
Theory
Work with qubits, so . Start from the uniform superposition
Each Grover iteration applies two operators.
The oracle flips the sign of the marked state and leaves all others alone:
The diffusion operator (Grover diffuser) reflects every amplitude about the mean amplitude:
Geometrically, the combined step is a rotation of the state vector by a fixed angle toward , where . The optimal number of iterations is
For () the optimum is iterations. Running more than the optimal number actually over-rotates and lowers the success probability.
Implementation
We mark the 3-qubit target state 101 (decimal 5). The oracle is built from a multi-controlled-Z surrounded by X gates that select exactly the target bit pattern.
import numpy as np
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
def phase_oracle(n: int, marked: str) -> QuantumCircuit:
"""Flip the phase of the basis state `marked` (a bitstring like '101')."""
qc = QuantumCircuit(n, name="oracle")
# X on qubits where the target bit is 0, so the all-ones state selects `marked`.
zero_positions = [i for i, bit in enumerate(reversed(marked)) if bit == "0"]
if zero_positions:
qc.x(zero_positions)
# Multi-controlled Z = H on target, multi-controlled X, H on target.
qc.h(n - 1)
qc.mcx(list(range(n - 1)), n - 1)
qc.h(n - 1)
if zero_positions:
qc.x(zero_positions)
return qc
def diffuser(n: int) -> QuantumCircuit:
"""Reflection about the uniform superposition |s>."""
qc = QuantumCircuit(n, name="diffuser")
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))
return qc
def grover(n: int, marked: str) -> QuantumCircuit:
qc = QuantumCircuit(n, n)
qc.h(range(n)) # uniform superposition
iterations = int(round(np.pi / 4 * np.sqrt(2 ** n)))
oracle = phase_oracle(n, marked)
diff = diffuser(n)
for _ in range(iterations):
qc.compose(oracle, inplace=True)
qc.compose(diff, inplace=True)
qc.measure(range(n), range(n))
return qc, iterations
if __name__ == "__main__":
n, target = 3, "101"
qc, iters = grover(n, target)
print(f"n={n}, target={target}, Grover iterations={iters}")
sim = AerSimulator()
counts = sim.run(transpile(qc, sim), shots=2048).result().get_counts()
for state in sorted(counts):
bar = "#" * (counts[state] * 40 // 2048)
print(f" {state}: {counts[state]:4d} {bar}")
best = max(counts, key=counts.get)
print(f"Most frequent outcome: {best} (target was {target})")
How it works:
phase_oraclewraps a multi-controlled-Z (H–mcx–H) inXgates so that the all-ones controlled condition lands exactly on the chosen bitstring. This flips only the target's phase.diffuserimplements via the standardH–X–(controlled-Z)–X–Hsandwich.groverrepeats oracle + diffuser the optimal number of times computed from .
Run it
After 2 iterations the marked state 101 should dominate, appearing with probability above 90%:
n=3, target=101, Grover iterations=2
000: 16 #
001: 14 #
010: 18 #
011: 17 #
100: 15 #
101: 1925 #####################################
110: 13 #
111: 12 #
Most frequent outcome: 101 (target was 101)
Exact counts vary run to run, but 101 should win decisively. Compare this to a classical search, which would need up to 8 checks; Grover used 2 oracle queries.
Exercises
- (Beginner) Change
targetto"011"and confirm Grover now amplifies that state instead. - (Beginner) Run the circuit with
iterationsforced to1, then3. Explain why 3 iterations gives a worse success probability than 2. - (Intermediate) Extend the code to (). What is the optimal iteration count, and what success probability do you measure?
- (Intermediate) Modify
phase_oracleto mark two target states. How does the optimal iteration count change, and does the algorithm split probability between them? - (Advanced) Plot the success probability as a function of the number of iterations from 0 to 6 (for ) and overlay the theoretical curve with .
Further reading
- Grover, A fast quantum mechanical algorithm for database search, STOC 1996.
- Nielsen & Chuang, Section 6.1 (the quantum search algorithm).
- The intermediate roadmap for related algorithms.
- Previous: Quantum Teleportation. Next: Deutsch–Jozsa.