Skip to content

Commit e00f6d3

Browse files
committed
feat: ✨ add Bernstein-Vazirani algorithm explanation, implementation details, and example code
1 parent 9bde241 commit e00f6d3

1 file changed

Lines changed: 176 additions & 0 deletions

File tree

‎blog/2026/03/30/iqc-006.md‎

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,179 @@ measure q[n-1] -> c[n-1];
274274
```
275275

276276
## Bernstein-Vazirani Algorithm
277+
278+
- Given $f_s: \{0,1\}^n \to \{0,1\}$ defined as $f_s(x) = s \cdot x \mod 2$
279+
- where $s$ is an unknown $n$-bit string and $x$ is the input.
280+
- The goal is to determine the hidden string $s$ as few queries as possible.
281+
- Classically: query $f$ with input $e_0 = 00 \cdots 01$, $e_1 = 00 \cdots 10$, ..., $e_{n-1} = 10 \cdots 00$ to get each bit of $s$.
282+
- Total $n$ queries.
283+
- Quantum: use the exact same circuit as Deutsch-Jozsa.
284+
- $U_{f_s} \ket{x} = (-1)^{s \cdot x} \ket{x}$.
285+
- where we can ignore the output register in the $\ket{-}$ state.
286+
- For $n =1$, the state after the oracle before the final $H$ gate is:
287+
- $\frac{1}{\sqrt{2}} \left( \ket{0} + (-1)^{s} \ket{1} \right)$
288+
- which is $\ket{+}$ if $s=0$ and $\ket{-}$ if $s=1$.
289+
- Applying $H$ to this state returns $s$:
290+
- $H \frac{1}{\sqrt{2}} \left( \ket{0} + (-1)^{s} \ket{1} \right) = \ket{s}$.
291+
- The measurement deterministically reveals $s$.
292+
293+
### n=2 case
294+
295+
- $s = s_1s_0$ and $x = x_1 x_0$
296+
- $\rightarrow s\cdot x = s_1x_1 + s_0 x_0$
297+
298+
$$ \begin{array}{rrrr}
299+
\frac{1}{2}\big(&(-1) ^{s_0\cdot 0+s_1\cdot 0} \ket{00} + &(-1) ^{s_0\cdot 1+s_1\cdot 0} \ket{01}+&(-1) ^{s_0\cdot 0+s_1\cdot 1} \ket{10}+&(-1) ^{s_0\cdot 1+s_1\cdot 1} \ket{11}\big)\\
300+
=\frac1{2}\big(& \ket{00} + &(-1) ^{s_0} \ket{01}+&(-1) ^{s_1} \ket{10}+&(-1) ^{s_1+s_0} \ket{11}\big).
301+
\end{array}
302+
$$
303+
304+
this factorized into:
305+
306+
$$\frac{1}{\sqrt{2}}\big(\ket 0 + (-1)^{s_1}\ket 1\big)\otimes \frac{1}{\sqrt{2}}\big(\ket 0 + (-1)^{s_0}\ket 1\big).$$
307+
308+
this reduces the same agument as $n=1$ for each qubit where after the final $H$ gates, the state becomes:
309+
310+
$$
311+
\ket{s_1}\otimes \ket {s_0} \equiv \ket{s_1 s_0}.
312+
$$
313+
314+
### Implemnting Bernstein-Vazirani
315+
316+
$$
317+
U_{f_s} \ket{x} \ket y = \ket{x} \ket {y\oplus (s\cdot x)}
318+
$$
319+
320+
$$
321+
y\oplus (s\cdot x) = y\oplus s_0 x_0 \oplus s_1 x_1 \oplus \cdots \oplus s_{n-1} x_{n-1}
322+
$$
323+
324+
$$
325+
U_{f_s} \ket{x} \ket y = \ket{x} \ket {y\oplus s x}
326+
$$
327+
328+
- $\mathbb{I}$ if $s = 0$ and $CNOT$ if $s = 1$.
329+
- To implement $f_{s}(x) = s \cdot x$, we can use a CNOT from qubit $i$ to the scratch qubit if $s_i = 1$.
330+
331+
```py
332+
import numpy as np
333+
from qiskit import QuantumCircuit, transpile
334+
from qiskit_aer import AerSimulator
335+
336+
337+
def bv_query(n, secret=None):
338+
# Build oracle for f_s(x) = s · x
339+
# q[0]..q[n-1] = input register
340+
# q[n] = scratch/output qubit
341+
342+
if secret is None:
343+
value = np.random.randint(0, 2 ** n)
344+
secret = format(value, f"0{n}b")
345+
else:
346+
secret = secret.zfill(n)
347+
348+
oracle = QuantumCircuit(n + 1, name="Uf")
349+
for index, bit in enumerate(reversed(secret)):
350+
if bit == "1":
351+
oracle.cx(index, n)
352+
353+
return oracle, secret
354+
355+
356+
def bernstein_vazirani_circuit(n, secret=None):
357+
# Build full Bernstein-Vazirani circuit
358+
oracle, secret = bv_query(n, secret)
359+
360+
qc = QuantumCircuit(n + 1, n)
361+
362+
# Prepare scratch qubit in |->
363+
qc.x(n)
364+
365+
# Apply Hadamards to all qubits
366+
for i in range(n + 1):
367+
qc.h(i)
368+
369+
# Apply oracle
370+
qc.compose(oracle, inplace=True)
371+
372+
# Apply final Hadamards to input register
373+
for i in range(n):
374+
qc.h(i)
375+
376+
# Measure input register
377+
for i in range(n):
378+
qc.measure(i, i)
379+
380+
return qc, secret
381+
382+
383+
def run_bernstein_vazirani(n, secret=None, shots=1):
384+
qc, secret = bernstein_vazirani_circuit(n, secret)
385+
386+
simulator = AerSimulator()
387+
compiled = transpile(qc, simulator)
388+
result = simulator.run(compiled, shots=shots).result()
389+
counts = result.get_counts()
390+
391+
measured = max(counts, key=counts.get)
392+
recovered = measured[::-1]
393+
394+
return qc, secret, recovered, counts
395+
396+
397+
# Example
398+
qc, secret, recovered, counts = run_bernstein_vazirani(5, "01101")
399+
print(qc.draw())
400+
print("Secret :", secret)
401+
print("Measured :", recovered)
402+
print("Counts :", counts)
403+
```
404+
405+
```qasm
406+
OPENQASM 2.0;
407+
qreg q[6];
408+
creg c[5];
409+
410+
x q[5];
411+
h q[0];
412+
h q[1];
413+
h q[2];
414+
h q[3];
415+
h q[4];
416+
h q[5];
417+
418+
cx q[0],q[5];
419+
cx q[2],q[5];
420+
cx q[3],q[5];
421+
422+
h q[0];
423+
h q[1];
424+
h q[2];
425+
h q[3];
426+
h q[4];
427+
428+
measure q[0] -> c[0];
429+
measure q[1] -> c[1];
430+
measure q[2] -> c[2];
431+
measure q[3] -> c[3];
432+
measure q[4] -> c[4];
433+
```
434+
435+
## Summary
436+
437+
- There are $2^{2^n}$ possible Boolean functions from $\{0,1\}^n$ to $\{0,1\}$.
438+
- A Boolean function is balanced if it outputs 1 on exactly half of the inputs, that is, on $2^{n-1}$ out of the $2^n$ possible inputs.
439+
- In Deutsch’s algorithm with $n=1$, only **1 quantum query** is needed to distinguish a constant function from a balanced function.
440+
- A quantum oracle for $f$ is defined as the unitary
441+
$U_f:\ |x\rangle|y\rangle \mapsto |x\rangle|y\oplus f(x)\rangle.$
442+
- Every XOR-based function of the form $f(x)=x_j\oplus x_k\oplus\cdots$ that depends on at least one input bit is balanced.
443+
- Using multi-controlled $X$ gates, together with anti-controls when needed, we can implement an oracle for any Boolean function.
444+
- The phase kickback multiplies the state by the phase factor $(-1)^{f(x)}$, so the phase changes exactly when $f(x)=1$.
445+
- If these questions were stored in a Python list called `questions`, then the 8th question would be `questions[7]`.
446+
- Applying Hadamard gates to $n$ qubits initialized in $|0\rangle$ produces an equal superposition over all $2^n$ computational basis states.
447+
- In Deutsch–Jozsa for $n>1$, measuring all input qubits as 0 means that $f$ is constant.
448+
- Bernstein–Vazirani solves the hidden-string problem $f(x)=s\cdot x$ with **1 quantum query**.
449+
- A multi-controlled $X$ gate with 3 controls can be implemented using scratch qubits and **4 Toffoli gates**.
450+
- Applying $U_{f_1}$ followed by $U_{f_2}$ yields an oracle whose action on the scratch qubit corresponds to $f_1(x)\oplus f_2(x)$.
451+
- Uncomputation resets scratch qubits to their initial states by applying the inverse of the computation, which means reversing the order of the steps.
452+
- Multi-controlled gates with more than one control can be decomposed into simpler gates, but in general this requires more than just $CX$ and $X$; single-qubit gates are also needed.

0 commit comments

Comments
 (0)