Week 5 | the first exponential gap over every classical algorithm, and where quantum computers sit among complexity classes | reading: Watrous §5.4; de Wolf Ch. 3
Lecture 9 ended with a caveat: Deutsch–Jozsa beats deterministic algorithms exponentially but randomised algorithms hardly at all. Simon's algorithm (1994) closes that gap. It solves a problem with about \(n\) quantum queries that needs about \(2^{n/2}\) queries from any classical algorithm, randomised or not, and it was the direct inspiration for Shor's factoring algorithm. By the end you should be able to state Simon's problem, explain the birthday-style classical lower bound, analyse the quantum circuit, recover the secret from its outputs by solving linear equations mod 2, and place BQP correctly among P, BPP, NP and PSPACE.
Given oracle access to \(f:\{0,1\}^n\to\{0,1\}^m\), promised that there is a secret string \(s\in\{0,1\}^n\) such that, for all \(x,y\),
$$f(x)=f(y)\iff y=x\ \text{ or }\ y=x\oplus s ,$$find \(s\). If \(s\neq0^n\), \(f\) is exactly two-to-one: each value is taken on a pair \(\{x,x\oplus s\}\). If \(s=0^n\), \(f\) is one-to-one.
Think of \(s\) as a hidden period: \(f(x\oplus s)=f(x)\), where "adding" means bitwise XOR. A convenient concrete example, used throughout this lecture, takes \(n=4\) and \(s=1011\) and defines
$$f(x)=\begin{cases}x, & x_0=0,\\ x\oplus s, & x_0=1,\end{cases}$$which replaces each \(x\) by whichever of \(x,\ x\oplus s\) has last bit 0. (Since \(s_0=1\), exactly one of the two does.) The oracle needs only CNOTs: copy \(x\) into the output register, then, controlled on \(x_0\), XOR \(s\) into it.
A Simon function with \(n=3\) satisfies \(f(010)=f(111)\). What is \(s\)?
Classically, a single collision \(f(x)=f(y)\) with \(x\neq y\) reveals \(s=x\oplus y\). Without one, all you have seen is distinct values, which look the same whatever \(s\) is. So the classical question is how many queries it takes to find a collision, which is the birthday problem from probability.
Query \(q\) distinct inputs chosen uniformly at random. The probability that two of them form a pair \(\{x,x\oplus s\}\) is approximately
$$1-\exp\!\Big(-\frac{q(q-1)}{2(2^n-1)}\Big),$$which reaches \(\tfrac12\) at \(q\approx\sqrt{2\ln2}\,2^{n/2}\approx1.18\cdot2^{n/2}\). Conversely, for \(s\) chosen at random, every classical algorithm, randomised or not, needs \(\Omega(2^{n/2})\) queries to find \(s\) with probability \(\tfrac12\) (Simon, 1994).
Each of the \(\binom q2\) pairs of queried inputs is a colliding pair with probability \(1/(2^n-1)\), because \(x\oplus y\) is equally likely to be any nonzero string. If these events were independent, the probability of no collision would be \(\big(1-\tfrac{1}{2^n-1}\big)^{\binom q2}\approx\exp\!\big(-\binom q2/(2^n-1)\big)\). They are not quite independent, but the exact product (computed in verify_L10.py) agrees to within 0.01 for \(n=20\). For the lower bound, a union bound shows the collision probability is at most about \(\binom q2/(2^n-1)\) for any choice of queries, adaptive or not (each failed pair rules out one candidate for \(s\), which changes the denominator only slightly while \(q\ll2^{n/2}\)), because until a collision appears every answer is a fresh value that carries no information about \(s\). Making this probability \(\tfrac12\) needs \(q\gtrsim2^{n/2}\).
For \(n=20\), the exact computation gives 1,206 random queries for a 50% chance of a collision, matching \(1.18\cdot2^{10}\). For \(n=128\) it is about \(2^{64}\) queries. The quantum algorithm needs about \(n\) queries: 128 instead of \(2^{64}\).
For large \(n\), a classical attacker makes \(q=2^{n/2}\) random queries. Using Proposition 10.1, about how likely is it that they contain a collision? (Two decimal places are enough.)
One run of the circuit outputs a string \(y\) that is uniformly distributed over the \(2^{n-1}\) strings with \(y\cdot s=0\) (when \(s\neq0^n\)).
After the first Hadamards and the query, the state is \(2^{-n/2}\sum_x|x\rangle|f(x)\rangle\). By Proposition 9.1, the final \(H^{\otimes n}\) turns this into
$$\frac{1}{2^n}\sum_y\sum_x(-1)^{x\cdot y}\,|y\rangle|f(x)\rangle .$$Fix a value \(z\) of \(f\); it is taken at exactly two inputs, \(x_z\) and \(x_z\oplus s\). The amplitude of \(|y\rangle|z\rangle\) collects their two terms:
$$\frac{1}{2^n}\Big((-1)^{x_z\cdot y}+(-1)^{(x_z\oplus s)\cdot y}\Big)=\frac{(-1)^{x_z\cdot y}}{2^n}\Big(1+(-1)^{s\cdot y}\Big),$$using \((x\oplus s)\cdot y=x\cdot y\oplus s\cdot y\). This is 0 when \(s\cdot y=1\) and has absolute value \(2^{1-n}\) when \(s\cdot y=0\). Summing the squares over the \(2^{n-1}\) values \(z\), the probability of measuring \(y\) is \(2^{n-1}\cdot2^{2-2n}=2^{1-n}\) for every \(y\) with \(s\cdot y=0\), and 0 otherwise. There are \(2^{n-1}\) such \(y\), so this is the uniform distribution on them.
Simon's circuit is run with \(n=4\) and \(s=0110\). Tick every output \(y\) that can occur.
The proof has the same shape as Lecture 7's garbage example, used in reverse. The two inputs \(x_z\) and \(x_z\oplus s\) share the same "record" \(|z\rangle\) in the output register, so they can interfere, and they cancel exactly on the strings \(y\) with \(s\cdot y=1\). Inputs with different records cannot interfere at all. If you do measure the output register and see \(z\), the input register collapses to \(\tfrac{1}{\sqrt2}(|x_z\rangle+|x_z\oplus s\rangle)\), a superposition of one pair, and the Hadamard transform of that pair is what produces \(y\).
Each run gives one linear equation \(y\cdot s=0\) over the field \(\mathbb F_2=\{0,1\}\), where addition is XOR. The solutions \(s\) of a set of such equations form a subspace of \(\mathbb F_2^n\). Once we have \(n-1\) linearly independent equations, that subspace has dimension 1, so it is \(\{0^n,s\}\), and the unique nonzero solution is the secret.
With \(n=5\) and \(s\neq0^5\), the runs so far supply 3 linearly independent equations. The number of nonzero candidates for \(s\) still consistent with them is .
Three runs of Simon's algorithm with \(n=4\) return \(y=0100\), \(1001\) and \(0011\). Find \(s\). Bits are written \(s_3s_2s_1s_0\).
Each \(y\) gives one equation \(y\cdot s=0\):
$$0100:\ s_2=0,\qquad 1001:\ s_3\oplus s_0=0,\qquad 0011:\ s_1\oplus s_0=0 .$$The last two say \(s_3=s_0\) and \(s_1=s_0\). The solutions are \(s=0000\) (with \(s_0=0\)) and \(s=1011\) (with \(s_0=1\)). Since \(s\neq0^n\) by assumption (or check one more query \(f(0)=f(1011)\)), the secret is \(s=1011\).
Not every set of runs is enough. Suppose instead the three runs returned \(1001\), \(0011\) and \(1010\). Since \(1010=1001\oplus0011\), the third equation is the sum of the first two and adds nothing. The remaining equations \(s_3=s_0\) and \(s_1=s_0\) leave \(s_2\) free, so three nonzero candidates survive, \(0100\), \(1011\) and \(1111\), and one more run is needed.
For larger \(n\) the hand method becomes Gaussian elimination over \(\mathbb F_2\): row reduction where adding rows means XOR and there is no division to do. HW5 asks you to do this by hand for \(n=3\) and to write the solver in Python. How many runs does it take?
The expected number of runs needed to collect \(n-1\) independent equations is
$$\sum_{j=1}^{n-1}\frac{1}{1-2^{-j}}\;<\;n-1+1.607 ,$$and \(n-1+t\) runs succeed with probability at least \(1-2^{-t}\).
The outputs are uniform on the \((n-1)\)-dimensional space \(s^\perp=\{y:y\cdot s=0\}\). Suppose the equations so far span a subspace of dimension \(k\lt n-1\), which contains \(2^k\) of the \(2^{n-1}\) strings in \(s^\perp\). The next run lands outside it with probability \(1-2^{k-(n-1)}\), so the number of runs until the dimension grows is geometric with mean \(1/(1-2^{k-n+1})\). Adding these means for \(k=0,\dots,n-2\) and writing \(j=n-1-k\) gives the sum. Each term is \(1+\tfrac{1}{2^j-1}\), and \(\sum_{j\ge1}\tfrac{1}{2^j-1}\approx1.6067\). For the tail bound: a fixed nonzero vector of the \((n-1)\)-dimensional dual space is orthogonal to all of \(n-1+t\) independent uniform samples with probability \(2^{-(n-1+t)}\); there are fewer than \(2^{n-1}\) such vectors, so by the union bound the samples fail to span with probability below \(2^{-t}\).
Use Proposition 10.3 to compute the expected number of runs of Simon's circuit when \(n=5\) (two decimal places).
For \(n=4\) the expected number of runs is \(\tfrac{8}{7}+\tfrac43+2\approx4.48\); a simulation of 200,000 trials gives 4.48. Each run uses one query, so Simon's algorithm finds \(s\) with about \(n\) queries plus polynomial-time classical post-processing. Against Proposition 10.1's \(2^{n/2}\), that is an exponential separation from every classical algorithm, randomised ones included. This is the advance over Deutsch–Jozsa.
Simon's problem is an instance of the hidden subgroup problem: a function on a group is constant on the cosets of an unknown subgroup and distinct on different cosets, and we want the subgroup. Here the group is \(\mathbb F_2^n\) under XOR and the hidden subgroup is \(\{0^n,s\}\). Shor's insight (Lectures 15–16) was to run the same plan on the integers: the function \(f(x)=a^x\bmod N\) is periodic under ordinary addition, the hidden subgroup is the multiples of its period \(r\), and the Hadamard transform is replaced by the quantum Fourier transform (Lecture 13). Knowing \(r\) factors \(N\).
The Even–Mansour cipher builds a block cipher from a public random permutation \(P\) and two secret keys: \(E(x)=P(x\oplus k_1)\oplus k_2\). Classically it is secure up to about \(2^{n/2}\) queries. Now define \(f(x)=E(x)\oplus P(x)\). Then
$$f(x\oplus k_1)=P(x)\oplus k_2\oplus P(x\oplus k_1)=f(x),$$so \(f\) has the hidden XOR-period \(k_1\) (plus, typically, no other collisions). An attacker who can query \(E\) in superposition runs Simon's algorithm on \(f\) and recovers \(k_1\) with about \(n\) queries, and then \(k_2=E(0)\oplus P(k_1)\). This is the Kuwakado–Morii attack (2012), and Kaplan et al. (2016) extended the idea to CBC-MAC and several authenticated-encryption modes. As noted in Lecture 9, the attack needs superposition access to the keyed cipher, which a server that encrypts classical messages does not provide.
Compared with a classical attacker, the extra power the Kuwakado–Morii attack needs to find \(k_1\) with about \(n\) queries is .
Query complexity counts oracle calls. Complexity classes count all the work, as a function of the input length \(n\), for decision problems (yes/no questions about an input string).
If the success probability \(\tfrac23\) in the definition of BPP is replaced by \(0.51\), the class .
Tick every statement that is a proven theorem (not merely a widely held belief).
The widespread belief is \(\mathrm{NP}\not\subseteq\mathrm{BQP}\). The evidence is that the obvious quantum approach, searching all \(2^n\) candidate certificates, gains only a square root: Grover's algorithm (next lecture) needs about \(2^{n/2}\) queries, and Bennett, Bernstein, Brassard and Vazirani proved that no quantum algorithm does better for unstructured search (Lecture 12). Exponential quantum speedups need structure, such as the hidden periods of Simon and Shor. When you read that a quantum computer "tries all answers at once", recall Lecture 1: measurement returns one answer, and interference must do the work.
from qiskit import QuantumCircuit
from qiskit.primitives import StatevectorSampler
def simon_circuit(s):
"""Simon's circuit for f(x) = x xor (x_k * s), k = lowest index with s_k = 1"""
n = len(s)
k = s[::-1].index("1")
qc = QuantumCircuit(2 * n, n) # input q0..q(n-1), output q(n)..q(2n-1)
qc.h(range(n))
for i in range(n):
qc.cx(i, n + i) # copy x into the output register
for j in range(n):
if s[n - 1 - j] == "1":
qc.cx(k, n + j) # if x_k = 1, XOR s into the output
qc.h(range(n))
qc.measure(range(n), range(n)) # only the input register is read
return qc
counts = StatevectorSampler(seed=4).run([simon_circuit("1011")], shots=4000).result()[0].data.c.get_counts()
print(dict(sorted(counts.items())))
{'0000': 526, '0011': 461, '0100': 485, '0111': 509, '1001': 481, '1010': 506, '1101': 495, '1110': 537}
Exactly the eight strings with \(y\cdot1011=0\) appear, each near 500. The oracle is the CNOT construction of Section 1 with \(k=0\); since \(s_0=1\), the controlled XOR of \(s\) uses CNOTs from \(q_0\) to output qubits 4, 5 and 7.
n = 4
res = StatevectorSampler(seed=7).run([simon_circuit("1011")], shots=6).result()[0]
ys = res.data.c.get_bitstrings() # six runs, one string per run
print("observed y:", ys)
def dot(a, b): # a.b mod 2 for bit strings
return sum(int(u) & int(v) for u, v in zip(a, b)) % 2
candidates = [format(t, f"0{n}b") for t in range(1, 2 ** n)
if all(dot(format(t, f"0{n}b"), y) == 0 for y in ys)]
print("nonzero s consistent with every y:", candidates)
observed y: ['1010', '1110', '1101', '0011', '0100', '1101'] nonzero s consistent with every y: ['1011']
Checking all \(2^n-1\) candidates is fine for \(n=4\) and hopeless for \(n=128\); that is what Gaussian elimination over \(\mathbb F_2\) is for (HW5).
Next time: Grover's search algorithm, the quadratic speedup for unstructured search. HW5 is due at the start of Week 6.
all numbers and figures in these notes are computed in python (verify_L10.py) and follow the page theme