Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
302 changes: 302 additions & 0 deletions examples/bernstein_vazirani_tutorial.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Bernstein-Varzirani Algorithm\n",
"The Bernstein-Varzirani algorithm solves the following problem:\n",
"1. Given an n-bit secret number x\n",
"2. There is an oracle holding the secret key that one can query with a number y, and the oracle will return $x \\bullet y$, the bit-wise dot product modulo 2 of the two numbers. For example, given a 4-bit secret number $x=13$ with binary representation 1101 and $y=15$ with binary representation 1111, then $x \\bullet y =$ 3 mod 2, which is 1\n",
"3. The question is: how many oracle queries does one need to figure out x?\n",
"\n",
"With the classical approach, if we query the oracle with 1, 2(10), 4(100) and 8(1000), then we can figure out each bit of the secret key and thus we need four queries. For a general n-bit number, we need n queries. Surprisingly, in the quantum computing paradigm, the Bernstein-Varzirani algorithm needs only one measurement, regardless of the size of the secret number"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import projectq\n",
"from projectq.backends import CircuitDrawer\n",
"from projectq.ops import H, Z, All, Measure, Barrier\n",
"\n",
"from pathlib import Path"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"n = 3 #No. of qubits"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## The classical approach"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def query_oracle(y):\n",
" \"\"\"\n",
" The oracle returns the bit-wise dot product modulo 2 \n",
" of the secret key and y\n",
" \"\"\"\n",
" x = 6 #secret key\n",
" s = bin(x & y).count('1') % 2\n",
" \n",
" return s"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Secret key is 6 after 3 tries\n"
]
}
],
"source": [
"b = ''.join([str(query_oracle(2**(k))) for k in reversed(range(n))])\n",
"print(\"Secret key is {} after {} tries\".format(int(b, 2), n))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## The quantum computing approach\n",
"There are five steps in the Bernstein-Varzirani circuit construction:\n",
"1. Initialize all qubits to 0 (This is the default value anyway)\n",
"2. Apply Hadamard transform to all qubits\n",
"3. Implement the oracle \n",
"4. Apply Hadamard transform to all qubits\n",
"5. Measure the circuit\n",
"\n",
"The workings of the Bernstein-Varzirani algorithm can only be understood by working through the mathematics. After step 2, the application of the Hadamard transform to all qubits results in\n",
"$$\\begin{align} H^{\\otimes n} |0\\rangle = \\frac{1}{\\sqrt{N}} \\sum_{y=0}^{N-1} | y \\rangle \\end{align}$$\n",
"where $N = 2^n$\n",
"\n",
"The oracle seeks to implement the following operation in step 3:\n",
"$$\\begin{align} \\frac{1}{\\sqrt{N}} \\sum_{y=0}^{N-1} | y \\rangle \\rightarrow \\frac{1}{\\sqrt{N}} \\sum_{y=0}^{N-1} (-1)^{x \\bullet y} | y \\rangle \\end{align}$$\n",
"The oracle effectively multiplies each superposition state $|y \\rangle$ with -1 raised to the power of $x \\bullet y$, the bit-wise inner product modulo 2. But why does the oracle implement this operation?\n",
"\n",
"Using the identity (proof given below)\n",
"$$\\begin{align} H^{\\otimes n} |x\\rangle = \\frac{1}{\\sqrt{N}} \\sum_{y=0}^{N-1} (-1)^{x \\bullet y} | y \\rangle \\end{align}$$\n",
"If we further apply another Hadamard transform to all qubits in step 4, then $$ H^{\\otimes n} H^{\\otimes n} |x\\rangle = H^{\\otimes n} \\frac{1}{\\sqrt{N}} \\sum_{y=0}^{N-1} (-1)^{x \\bullet y} | y \\rangle $$\n",
"i.e. $$ |x\\rangle = H^{\\otimes n} \\frac{1}{\\sqrt{N}} \\sum_{y=0}^{N-1} (-1)^{x \\bullet y} | y \\rangle $$\n",
"since the Hadamard transform is symmetric and unitary (meaning that the transform is its own inverse). Thus measuring the circuit in step 5 will give the secret key.\n",
"\n",
"Summarizing,\n",
"$$ H^{\\otimes n} |0\\rangle ^{\\otimes n} \\rightarrow \\frac{1}{\\sqrt{N}} \\sum_{y=0}^{N-1} | y \\rangle \\rightarrow \\frac{1}{\\sqrt{N}} \\sum_{y=0}^{N-1} (-1)^{x \\bullet y} | y \\rangle \\rightarrow H^{\\otimes n} \\frac{1}{\\sqrt{N}} \\sum_{y=0}^{N-1} (-1)^{x \\bullet y} | y \\rangle = |x\\rangle$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### The oracle implementation\n",
"First note that in implementing\n",
"$$ \\frac{1}{\\sqrt{N}} \\sum_{y=0}^{N-1} (-1)^{x \\bullet y} | y \\rangle = \\frac{1}{\\sqrt{N}} \\sum_{y=0}^{N-1} (-1)^{(x_{n-1} y_{n-1} \\oplus \\ldots \\oplus x_0 y_0)} | y_{n-1}\\ldots y_0\\rangle $$\n",
"$$ = \\frac{1}{\\sqrt{N}} \\sum_{y=0}^{N-1} (-1)^{x_{n-1} y_{n-1}} (-1)^{x_{n-2} y_{n-2}} \\ldots (-1)^{x_0 y_0}) | y_{n-1}\\ldots y_0\\rangle $$\n",
"the individual product ${x_{n-1} y_{n-1}, \\ldots, x_0 y_0}$ only needs to be evaluated for bits in x that are 1. \n",
"\n",
"For the bits in x that are 1, we only introduce a minus sign for the bits in y that are 1 i.e.\n",
"$ |0 \\rangle \\rightarrow |0 \\rangle$ and $ |1 \\rangle \\rightarrow -|1 \\rangle$. This is precisely what the Z gate does. Thus the oracle can be implemented as: if a bit in x is 1, apply the Z gate to the corresponding bit in y."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def make_bv_circuit(engine, n):\n",
" circuit = engine.allocate_qureg(n)\n",
" All(H) | circuit\n",
" Barrier | circuit\n",
" \n",
" # Oracle\n",
" x = 6 # secret key\n",
" for i in range(n):\n",
" if x & 1: # Only apply Z if the current bit of the secret key x is 1\n",
" Z | circuit[i]\n",
" x >>= 1 # Move the next bit to the 1 position\n",
" Barrier | circuit\n",
" \n",
" All(H) | circuit\n",
" All(Measure) | circuit\n",
" engine.flush()\n",
"\n",
" return circuit"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Draw circuit diagram"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"drawing_engine = CircuitDrawer()\n",
"main_engine = projectq.MainEngine(drawing_engine)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"circuit = make_bv_circuit(main_engine, n)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<wand.image.Image: 6a735ea 'PDF' (285x129)>"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"p = Path('diagram')\n",
"if not p.exists(): #if the diagram directory doesn't exist, create it\n",
" p.mkdir()\n",
"with open('diagram/bv.tex', 'w') as f:\n",
" latex = drawing_engine.get_latex() #get circuit diagram as latex\n",
" f.write(latex) \n",
"#Change the pdf scale to 1.8 from 0.8 to have better visual effect\n",
"!sed -i 's@tikzpicture\\}\\[scale=0.8@tikzpicture\\}\\[scale=1.8@g' diagram/bv.tex\n",
"!cd diagram; pdflatex bv.tex > /dev/null #convert tex to latex, piping to /dev/null to silent output \n",
"\n",
"#Wand package needed to convert pdf to image\n",
"from wand.image import Image as WImage\n",
"img = WImage(filename='diagram/bv.pdf')\n",
"img"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Run circuit"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Measurement outcome: 110\n",
"Secret key: 6\n"
]
}
],
"source": [
"main_engine = projectq.MainEngine()\n",
"circuit = make_bv_circuit(main_engine, n)\n",
"measurement = ''.join(str(int(c)) for c in reversed(circuit))\n",
"print('Measurement outcome:', measurement)\n",
"print('Secret key:', int(measurement, 2))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Proof of the key identity"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$$\\begin{align} H^{\\otimes n} |x\\rangle = \\frac{1}{\\sqrt{N}} \\sum_{y=0}^{N-1} (-1)^{x \\bullet y} | y \\rangle \\end{align}$$ where $ N = 2^n $ and the $ \\bullet$ operator denotes bitwise dot product modulo 2 i.e. $ x \\bullet y = x_0y_0 \\oplus x_1y_1 \\oplus \\ldots x_{N-1}y_{N-1}$ where $\\oplus$ denotes the sum modulo 2 operation or the XOR operation. We prove this identity by induction.\n",
"\n",
"Base case, n = 1:\n",
"$$ H |x_0\\rangle = \\frac{1}{\\sqrt{2}} (|0\\rangle + (-1)^{x_0}|1\\rangle) = \\frac{1}{\\sqrt{2}} \\sum_{y=0}^{1} (-1)^{x_0y} | y \\rangle$$\n",
"\n",
"Assuming n-1 (n > 1) is true i.e. $$ H^{\\otimes (n-1)} |x_{n-2} \\ldots x_0\\rangle = \\frac{1}{\\sqrt{2^{n-1}}} \\sum_{y=0}^{2^{n-1} - 1} (-1)^{(x_{n-2} \\ldots x_0) \\bullet (y_{n-2} \\ldots y_0)} | y \\rangle$$ \n",
"\n",
"$$ = \\frac{1}{\\sqrt{2^{n-1}}} \\sum_{y=0}^{2^{n-1} - 1} (-1)^{(x_{n-2} y_{n-2} \\oplus \\ldots \\oplus x_0 y_0)} | y \\rangle$$ \n",
"\n",
"where $y = y_{n-2} \\ldots y_0$ is the binary representation\n",
"\n",
"\n",
"Then $$ H^{\\otimes n} |x_{n-1} \\ldots x_0 \\rangle = \\frac{1}{\\sqrt{2}} (|0\\rangle + (-1)^{x_{n-1}}|1\\rangle) \\times \\frac{1}{\\sqrt{2^{n-1}}} \\sum_{y=0}^{2^{n-1} - 1} (-1)^{(x_{n-2} y_{n-2} \\oplus \\ldots \\oplus x_0 y_0)} | y \\rangle $$\n",
"\n",
"$$ \\begin{align} = \\frac{1}{\\sqrt{2^n}} \\sum_{y=0}^{2^{n-1} - 1} (-1)^{(x_{n-2} y_{n-2} \\oplus \\ldots \\oplus x_0 y_0)} |0\\rangle| y \\rangle + \\frac{1}{\\sqrt{2^n}} \\sum_{y=0}^{2^{n-1} - 1} (-1)^{x_{n-1}} (-1)^{(x_{n-2} y_{n-2} \\oplus \\ldots \\oplus x_0 y_0)} |1\\rangle| y \\rangle \\end{align} $$\n",
"\n",
"$$ \\begin{align} = \\frac{1}{\\sqrt{2^n}} \\sum_{y=0}^{2^{n-1} - 1} (-1)^{(x_{n-1}0 \\oplus x_{n-2}y_{n-2} \\oplus \\ldots \\oplus x_0y_0)} | y \\rangle + \\frac{1}{\\sqrt{2^n}} \\sum_{y=2^{n-1}}^{2^{n} - 1} (-1)^{(x_{n-1}1 \\oplus x_{n-2}y_{n-2}\\oplus \\ldots \\oplus x_0y_0)} | y \\rangle \\end{align}$$\n",
"\n",
"$$ = \\frac{1}{\\sqrt{2^n}} \\sum_{y=0}^{2^{n-1} - 1} (-1)^{(x_{n-1}x_{n-2} \\ldots x_0) \\bullet (0y_{n-2} \\ldots y_0)} | y \\rangle + \\frac{1}{\\sqrt{2^n}} \\sum_{y=2^{n-1}}^{2^{n} - 1} (-1)^{(x_{n-1} \\ldots x_0) \\bullet (1y_{{n}-2} \\ldots y_0)} | y \\rangle $$\n",
"\n",
"$$ = \\frac{1}{\\sqrt{2^{n}}} \\sum_{y=0}^{2^{n} - 1} (-1)^{(x_{n-1} \\ldots x_0) \\bullet (y_{n-1} \\ldots y_0)} | y \\rangle $$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "projectq",
"language": "python",
"name": "projectq"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.8"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Loading