scaffolding-2026

This commit is contained in:
PFmix
2026-08-22 13:40:45 +02:00
parent f8cb596470
commit caf13eb166
140 changed files with 88 additions and 3 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,402 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "c35cb2a3-1285-480c-ab04-5ca6119caea5",
"metadata": {},
"source": [
"# Fun with Oracles: The Bernstein-Vazirani Algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "aa400782-06a3-4a80-9745-ea370dbc0d04",
"metadata": {},
"outputs": [],
"source": [
"from qiskit import *\n",
"%matplotlib inline\n",
"from qiskit.visualization import plot_histogram\n",
"from qiskit_aer import AerSimulator\n",
"\n",
"# import image module\n",
"from IPython.display import Image\n"
]
},
{
"cell_type": "markdown",
"id": "650b3002-863f-4501-af8e-8d96c97031e0",
"metadata": {},
"source": [
"In this exercise, we will construct an oracle that lets us guess a secret number (the bitstring $a$) in a single query to a quantum computer. We begin by posing the problem classically. Suppose we have access to a function $f_a(x)$ that takes a bitstring $a$ as input and returns $a\\cdot x$, modulo 2:\n",
"\n",
"$$ f_a(x)=a\\cdot x\\quad (\\mathrm{mod}\\; 2).$$\n",
"\n",
"As an example, if $a=001$ and $x=101$, then $f_a(x)=0\\times 1 + 0\\times 0 + 1\\times 1=1$. As a classical circuit, we can represent the Bernstein-Vazirani oracle like this (image stolen from Pennylane):\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "8ad6ec84-ff10-45ae-b530-29e8b3b3a069",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<img src=\"classical.png\" width=\"400\" height=\"400\"/>"
],
"text/plain": [
"<IPython.core.display.Image object>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Image(url=\"classical.png\", width=400, height=400)"
]
},
{
"cell_type": "markdown",
"id": "6f214d9d-fb7c-4eea-9181-6412f042bcf9",
"metadata": {},
"source": [
"More generally, we can write the action of this oracle as \n",
"\n",
"$$\n",
"\\begin{equation}\n",
"U_f\\vert x\\rangle\\vert s\\rangle=\\vert x\\rangle \\vert s\\oplus f_a(x)\\rangle,\\quad s=0,1,\n",
"\\end{equation}\n",
"$$\n",
"\n",
"where $\\oplus$ denotes addition modulo 2.\n",
"\n",
"To find $a$ classically, we would have to query this oracle $N=\\mathrm{len}(a)$ times with inputs $x_0=0\\dots 01$, $x_1=0\\dots 10$,..., $x_{N-1}=10\\dots0$. (Each query allows us to determine one bit of $a$.) Given the oracle $U_f$, the Bernstein-Vazirani algorithm allows us to find $a$ in a single shot using the following circuit:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7acb0d7d-28c2-4f8b-b4fb-688c6cc1c88a",
"metadata": {},
"outputs": [],
"source": [
"Image(url=\"quantum.png\", width=400, height=400)"
]
},
{
"cell_type": "markdown",
"id": "1db253b7-ce69-4ae3-a5aa-bd68236eecf1",
"metadata": {},
"source": [
"The steps of the algorithm are as follows:\n",
"\n",
"1. Initialize the register qubits to the $|0\\rangle^{\\otimes n}$ state, and the ancilla qubit to $|{-}\\rangle$\n",
"2. Apply Hadamard gates to the input register\n",
"3. Query the oracle\n",
"4. Apply Hadamard gates to the input register\n",
"5. Measure\n",
"\n",
"\n",
"Let's walk through the steps one-by-one. If we start with all register qubits initialized in $\\vert a\\rangle$ and apply a Hadamard to each one, then we have prepared the state\n",
"\n",
"$$\\vert a \\rangle\\stackrel{H^{\\otimes N}}{\\longrightarrow} \\frac{1}{\\sqrt{2^N}}\\sum_{x=0}^{2^N-1}(-1)^{a\\cdot x}\\vert x\\rangle.$$\n",
"\n",
"\n",
"Since the register qubits are all initialized in $\\vert 0\\rangle$, the input state on which the oracle acts is therefore given by\n",
"\n",
"$$\\vert \\psi_0\\rangle =\\frac{1}{\\sqrt{2^{N+1}}}\\sum_{x=0}^{2^N-1}\\vert x\\rangle(\\vert 0\\rangle-\\vert 1\\rangle).$$\n",
"\n",
"Let's start by preparing this state. First, we choose a secret number:"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "c3c33752-30db-45fe-a7a2-3cd3e353fac5",
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"Enter a binary number: 0101\n"
]
}
],
"source": [
"secretnumber = input('Enter a binary number:')\n",
"l = len(secretnumber)"
]
},
{
"cell_type": "markdown",
"id": "5fbe7426-0bf0-4d6b-930f-0c685dc9c251",
"metadata": {},
"source": [
"The next code cell is for you to complete. It should realize Steps #1 and #2 in the list above:"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "91a691a1-6d0a-49ed-b921-a322ae585167",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"word-wrap: normal;white-space: pre;background: #fff0;line-height: 1.1;font-family: &quot;Courier New&quot;,Courier,monospace\"> ┌───┐ ░ \n",
"q_0: ┤ H ├──────░─\n",
" ├───┤ ░ \n",
"q_1: ┤ H ├──────░─\n",
" ├───┤ ░ \n",
"q_2: ┤ H ├──────░─\n",
" ├───┤ ░ \n",
"q_3: ┤ H ├──────░─\n",
" ├───┤┌───┐ ░ \n",
"q_4: ┤ X ├┤ H ├─░─\n",
" └───┘└───┘ ░ \n",
"c: 4/═════════════\n",
" </pre>"
],
"text/plain": [
" ┌───┐ ░ \n",
"q_0: ┤ H ├──────░─\n",
" ├───┤ ░ \n",
"q_1: ┤ H ├──────░─\n",
" ├───┤ ░ \n",
"q_2: ┤ H ├──────░─\n",
" ├───┤ ░ \n",
"q_3: ┤ H ├──────░─\n",
" ├───┤┌───┐ ░ \n",
"q_4: ┤ X ├┤ H ├─░─\n",
" └───┘└───┘ ░ \n",
"c: 4/═════════════\n",
" "
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"qc = QuantumCircuit(l+1, l)\n",
"\n",
"qc.h(range(l))\n",
"qc.x(l)\n",
"qc.h(l)\n",
"\n",
"qc.barrier()\n",
"qc.draw()"
]
},
{
"cell_type": "markdown",
"id": "cc91b15d-1cfe-4b9f-b6cb-7d5d3c4c0f2e",
"metadata": {},
"source": [
"Next, let's see what happens when we apply the oracle $U_f$ to $\\vert \\psi_0\\rangle$. Using the equation given above, we have\n",
"\n",
"$$\n",
" \\begin{aligned}\n",
" U_f\\lvert \\psi_0 \\rangle \n",
" & = \\frac{1}{\\sqrt{2^{N+1}}}\\sum_{x=0}^{2^N-1} \\vert x\\rangle (\\vert f_a(x)\\rangle - \\vert 1 \\oplus f_a(x)\\rangle) \\\\&= \\frac{1}{\\sqrt{2^{N+1}}}\\sum_{x=0}^{2^N-1}(-1)^{f_a(x)}|x\\rangle ( |0\\rangle - |1\\rangle ) .\n",
" \\end{aligned}\n",
" $$\n",
"\n",
"Neglecting the ancilla qubit, the action of Steps #1-3 above can therefore be summarized as\n",
"\n",
"$$|00\\dots 0\\rangle \\xrightarrow{H^{\\otimes N}} \\frac{1}{\\sqrt{2^N}} \\sum_{x} |x\\rangle \\xrightarrow{U_f} \\frac{1}{\\sqrt{2^N}} \\sum_{x} (-1)^{a\\cdot x}|x\\rangle.$$\n",
"\n",
"Since the Hadamard is its own inverse, the secret number $a$ can be recovered by once again applying Hadamards to all register qubits:\n",
"\n",
"$$ \\frac{1}{\\sqrt{2^N}}\\sum_{x=0}^{2^N-1}(-1)^{a\\cdot x}\\vert x\\rangle\\stackrel{H^{\\otimes N}}{\\longrightarrow}\\vert a \\rangle.$$\n"
]
},
{
"cell_type": "markdown",
"id": "2a41cbc7-774a-493c-9a44-9e7d7c4c3cf5",
"metadata": {},
"source": [
"We will now construct the oracle by applying CNOTs between the ancilla (in position ```l```) and every bit of the secret number equal to 1. (The target qubit should be ```l```.)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "12f33b9c-e0fe-44a2-9e3c-84f04d0d3240",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"word-wrap: normal;white-space: pre;background: #fff0;line-height: 1.1;font-family: &quot;Courier New&quot;,Courier,monospace\"> ┌───┐ ░ \n",
"q_0: ┤ H ├──────░───■───────\n",
" ├───┤ ░ │ \n",
"q_1: ┤ H ├──────░───┼───────\n",
" ├───┤ ░ │ \n",
"q_2: ┤ H ├──────░───┼────■──\n",
" ├───┤ ░ │ │ \n",
"q_3: ┤ H ├──────░───┼────┼──\n",
" ├───┤┌───┐ ░ ┌─┴─┐┌─┴─┐\n",
"q_4: ┤ X ├┤ H ├─░─┤ X ├┤ X ├\n",
" └───┘└───┘ ░ └───┘└───┘\n",
"c: 4/═══════════════════════\n",
" </pre>"
],
"text/plain": [
" ┌───┐ ░ \n",
"q_0: ┤ H ├──────░───■───────\n",
" ├───┤ ░ │ \n",
"q_1: ┤ H ├──────░───┼───────\n",
" ├───┤ ░ │ \n",
"q_2: ┤ H ├──────░───┼────■──\n",
" ├───┤ ░ │ │ \n",
"q_3: ┤ H ├──────░───┼────┼──\n",
" ├───┤┌───┐ ░ ┌─┴─┐┌─┴─┐\n",
"q_4: ┤ X ├┤ H ├─░─┤ X ├┤ X ├\n",
" └───┘└───┘ ░ └───┘└───┘\n",
"c: 4/═══════════════════════\n",
" "
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"\n",
"#Building the oracle\n",
"for i, digit in enumerate(reversed(secretnumber)):\n",
" if digit == '1':\n",
" qc.cx(i,l)\n",
" \n",
"qc.draw()"
]
},
{
"cell_type": "markdown",
"id": "40135256-76f5-43ff-ba73-0d2d27ceee2b",
"metadata": {},
"source": [
"Finally, we apply Hadamards and measure all register qubits in the $Z$ basis. You can visualized your circuit using ```qc.draw()```."
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "5176744b-a527-42c7-b52b-e8a320119097",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"word-wrap: normal;white-space: pre;background: #fff0;line-height: 1.1;font-family: &quot;Courier New&quot;,Courier,monospace\"> ┌───┐ ░ ┌───┐ ┌─┐ \n",
"q_0: ┤ H ├──────░───■──┤ H ├─────────────┤M├───\n",
" ├───┤ ░ │ ├───┤ ┌─┐ └╥┘ \n",
"q_1: ┤ H ├──────░───┼──┤ H ├─────┤M├──────╫────\n",
" ├───┤ ░ │ └───┘ └╥┘┌───┐ ║ ┌─┐\n",
"q_2: ┤ H ├──────░───┼─────────■───╫─┤ H ├─╫─┤M├\n",
" ├───┤ ░ │ ┌───┐ │ ║ └┬─┬┘ ║ └╥┘\n",
"q_3: ┤ H ├──────░───┼──┤ H ├──┼───╫──┤M├──╫──╫─\n",
" ├───┤┌───┐ ░ ┌─┴─┐└───┘┌─┴─┐ ║ └╥┘ ║ ║ \n",
"q_4: ┤ X ├┤ H ├─░─┤ X ├─────┤ X ├─╫───╫───╫──╫─\n",
" └───┘└───┘ ░ └───┘ └───┘ ║ ║ ║ ║ \n",
"c: 4/═════════════════════════════╩═══╩═══╩══╩═\n",
" 1 3 0 2 </pre>"
],
"text/plain": [
" ┌───┐ ░ ┌───┐ ┌─┐ \n",
"q_0: ┤ H ├──────░───■──┤ H ├─────────────┤M├───\n",
" ├───┤ ░ │ ├───┤ ┌─┐ └╥┘ \n",
"q_1: ┤ H ├──────░───┼──┤ H ├─────┤M├──────╫────\n",
" ├───┤ ░ │ └───┘ └╥┘┌───┐ ║ ┌─┐\n",
"q_2: ┤ H ├──────░───┼─────────■───╫─┤ H ├─╫─┤M├\n",
" ├───┤ ░ │ ┌───┐ │ ║ └┬─┬┘ ║ └╥┘\n",
"q_3: ┤ H ├──────░───┼──┤ H ├──┼───╫──┤M├──╫──╫─\n",
" ├───┤┌───┐ ░ ┌─┴─┐└───┘┌─┴─┐ ║ └╥┘ ║ ║ \n",
"q_4: ┤ X ├┤ H ├─░─┤ X ├─────┤ X ├─╫───╫───╫──╫─\n",
" └───┘└───┘ ░ └───┘ └───┘ ║ ║ ║ ║ \n",
"c: 4/═════════════════════════════╩═══╩═══╩══╩═\n",
" 1 3 0 2 "
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"qc.h(range(l))\n",
"qc.measure(range(l), range(l))\n",
"\n",
"qc.draw()"
]
},
{
"cell_type": "markdown",
"id": "52f182ee-ea26-465c-b012-0e372e716d4c",
"metadata": {},
"source": [
"We can finally run our circuit. Only a single shot is required since the outcome is deterministic in the absence of other errors. If you've done everything correctly, the following code block should return the secret number you specified at the beginning of this notebook."
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "d4e33e1d-5b8a-4de5-8f16-92462c59e651",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'0101': 1}\n"
]
}
],
"source": [
"backend = AerSimulator()\n",
"result = backend.run(qc, shots=1).result()\n",
"counts = result.get_counts()\n",
"print(counts)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1a21d874-a981-498f-9340-8e535f75220a",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"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.13.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+299
View File
@@ -0,0 +1,299 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "62eb0b66-e94e-4370-8c2c-cdf78aff67ee",
"metadata": {},
"source": [
"# Fun with Oracles: The Bernstein-Vazirani Algorithm"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "aea3cb24-6b7f-4f3a-83f6-bd54782d0cd2",
"metadata": {},
"outputs": [],
"source": [
"from qiskit import *\n",
"%matplotlib inline\n",
"from qiskit.visualization import plot_histogram\n",
"from qiskit_aer import AerSimulator\n",
"\n",
"# import image module\n",
"from IPython.display import Image\n"
]
},
{
"cell_type": "markdown",
"id": "8781dcaa-068c-4a70-b72e-df35b0ac2869",
"metadata": {},
"source": [
"In this exercise, we will construct an oracle that lets us guess a secret number (the bitstring $a$) in a single query to a quantum computer. We begin by posing the problem classically. Suppose we have access to a function $f_a(x)$ that takes a bitstring $a$ as input and returns $a\\cdot x$, modulo 2:\n",
"\n",
"$$ f_a(x)=a\\cdot x\\quad (\\mathrm{mod}\\; 2).$$\n",
"\n",
"As an example, if $a=001$ and $x=101$, then $f_a(x)=0\\times 1 + 0\\times 0 + 1\\times 1=1$. As a classical circuit, we can represent the Bernstein-Vazirani oracle like this (image stolen from Pennylane):\n"
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "6765ec1d-0134-486e-973f-2325058323de",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<img src=\"classical.png\" width=\"400\" height=\"400\"/>"
],
"text/plain": [
"<IPython.core.display.Image object>"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Image(url=\"classical.png\", width=400, height=400)"
]
},
{
"cell_type": "markdown",
"id": "8cd8eada-56cc-45d5-ad65-ec602044dc35",
"metadata": {},
"source": [
"More generally, we can write the action of this oracle as \n",
"\n",
"$$\n",
"\\begin{equation}\n",
"U_f\\vert x\\rangle\\vert s\\rangle=\\vert x\\rangle \\vert s\\oplus f_a(x)\\rangle,\\quad s=0,1,\n",
"\\end{equation}\n",
"$$\n",
"\n",
"where $\\oplus$ denotes addition modulo 2.\n",
"\n",
"To find $a$ classically, we would have to query this oracle $N=\\mathrm{len}(a)$ times with inputs $x_0=0\\dots 01$, $x_1=0\\dots 10$,..., $x_{N-1}=10\\dots0$. Given the oracle $U_f$, the Bernstein-Vazirani algorithm allows us to find $a$ in a single shot using the following circuit:"
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "95991dbc-3881-4ea4-9e04-c9e1073b7b7c",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<img src=\"quantum.png\" width=\"400\" height=\"400\"/>"
],
"text/plain": [
"<IPython.core.display.Image object>"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Image(url=\"quantum.png\", width=400, height=400)"
]
},
{
"cell_type": "markdown",
"id": "95ca75e1-eba3-474b-8c52-7667cd78f988",
"metadata": {},
"source": [
"The steps of the algorithm are as follows:\n",
"\n",
"1. Initialize the register qubits to the $|0\\rangle^{\\otimes n}$ state, and the ancilla qubit to $|{-}\\rangle$\n",
"2. Apply Hadamard gates to the input register\n",
"3. Query the oracle\n",
"4. Apply Hadamard gates to the input register\n",
"5. Measure\n",
"\n",
"\n",
"Let's walk through the steps one-by-one. If we start with all register qubits initialized in $\\vert a\\rangle$ and apply a Hadamard to each one, then we have prepared the state\n",
"\n",
"$$\\vert a \\rangle\\stackrel{H^{\\otimes N}}{\\longrightarrow} \\frac{1}{\\sqrt{2^N}}\\sum_{x=0}^{2^N-1}(-1)^{a\\cdot x}\\vert x\\rangle.$$\n",
"\n",
"\n",
"Since the register qubits are all initialized in $\\vert 0\\rangle$, the input state on which the oracle acts is therefore given by\n",
"\n",
"$$\\vert \\psi_0\\rangle =\\frac{1}{\\sqrt{2^{N+1}}}\\sum_{x=0}^{2^N-1}\\vert x\\rangle(\\vert 0\\rangle-\\vert 1\\rangle).$$\n",
"\n",
"Let's start by preparing this state. First, we choose a secret number:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "c85ed1b3-5b84-4199-960e-f9b4c9e5ba68",
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"Enter a binary number: 101\n"
]
}
],
"source": [
"secretnumber = input('Enter a binary number:')\n",
"l = len(secretnumber)"
]
},
{
"cell_type": "markdown",
"id": "8c533af7-633b-41f1-b8d6-43fae31f81d4",
"metadata": {},
"source": [
"The next code cell is for you to complete. It should realize Steps #1 and #2 in the list above:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2a2eec4d-2d1b-42c4-b966-4b52d9d20831",
"metadata": {},
"outputs": [],
"source": [
"qc = QuantumCircuit(l+1, l)\n",
"\n",
"### YOUR CODE GOES HERE ###\n",
"\n",
"qc.barrier()\n"
]
},
{
"cell_type": "markdown",
"id": "5ec9584b-98ae-44bf-9d60-4e8a4bf65470",
"metadata": {},
"source": [
"Next, let's see what happens when we apply the oracle $U_f$ to $\\vert \\psi_0\\rangle$. Using the equation given above, we have\n",
"\n",
"$$\n",
" \\begin{aligned}\n",
" U_f\\lvert \\psi_0 \\rangle \n",
" & = \\frac{1}{\\sqrt{2^{N+1}}}\\sum_{x=0}^{2^N-1} \\vert x\\rangle (\\vert f_a(x)\\rangle - \\vert 1 \\oplus f_a(x)\\rangle) \\\\&= \\frac{1}{\\sqrt{2^{N+1}}}\\sum_{x=0}^{2^N-1}(-1)^{f_a(x)}|x\\rangle ( |0\\rangle - |1\\rangle ) .\n",
" \\end{aligned}\n",
" $$\n",
"\n",
"Neglecting the ancilla qubit, the action of Steps #1-3 above can therefore be summarized as\n",
"\n",
"$$|00\\dots 0\\rangle \\xrightarrow{H^{\\otimes N}} \\frac{1}{\\sqrt{2^N}} \\sum_{x} |x\\rangle \\xrightarrow{U_f} \\frac{1}{\\sqrt{2^N}} \\sum_{x} (-1)^{a\\cdot x}|x\\rangle.$$\n",
"\n",
"Since the Hadamard is its own inverse, the secret number $a$ can be recovered by once again applying Hadamards to all register qubits:\n",
"\n",
"$$ \\frac{1}{\\sqrt{2^N}}\\sum_{x=0}^{2^N-1}(-1)^{a\\cdot x}\\vert x\\rangle\\stackrel{H^{\\otimes N}}{\\longrightarrow}\\vert a \\rangle.$$\n"
]
},
{
"cell_type": "markdown",
"id": "6353c87e-7806-4482-84d6-7b5e4862de86",
"metadata": {},
"source": [
"We will now construct the oracle by applying CNOTs between the ancilla (in position ```l```) and every bit of the secret number equal to 1. (The target qubit should be ```l```.)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2c702a6a-83e5-43a6-9ff3-f529fd1babc6",
"metadata": {},
"outputs": [],
"source": [
"\n",
"#Building the oracle\n",
"for i, digit in enumerate(reversed(secretnumber)):\n",
" if digit == '1':\n",
" ### YOUR CODE GOES HERE ###\n",
" \n",
"qc.barrier()\n"
]
},
{
"cell_type": "markdown",
"id": "15420105-f5b8-492d-a5ad-442ae9d1a944",
"metadata": {},
"source": [
"Finally, we apply Hadamards and measure all register qubits in the $Z$ basis. You can visualized your circuit using ```qc.draw()```."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e3b4e4f6-02a3-4024-83ab-bba3a5e61e6d",
"metadata": {},
"outputs": [],
"source": [
"qc.h(range(l))\n",
"qc.measure(range(l), range(l))\n",
"\n",
"qc.draw()"
]
},
{
"cell_type": "markdown",
"id": "b8807b89-399b-40fe-b361-7b8f68093cd0",
"metadata": {},
"source": [
"We can finally run our circuit. Only a single shot is required since the outcome is deterministic in the absence of other errors. If you've done everything correctly, the following code block should return the secret number you specified at the beginning of this notebook."
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "c5f6feff-88bf-41bd-b9dd-5d532c30c9c1",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'101': 1}\n"
]
}
],
"source": [
"backend = AerSimulator()\n",
"result = backend.run(qc, shots=1).result()\n",
"counts = result.get_counts()\n",
"print(counts)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "83f46f4a-0b46-4a0b-8e31-785d384a7bf2",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"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.13.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+483
View File
@@ -0,0 +1,483 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "9979b6b6-97df-48ec-9efb-f7b97bb00e23",
"metadata": {},
"source": [
"# Quantum teleportation and dense coding with GHZ and W states"
]
},
{
"cell_type": "markdown",
"id": "8897e34e",
"metadata": {},
"source": [
"*Each task requires you to do use the code proposed below, or, more directly, use Qiskit*"
]
},
{
"cell_type": "markdown",
"id": "f911d904-7146-4bc0-9d41-0eed75b5cd75",
"metadata": {},
"source": [
"## 1. Can standard teleportation be efficiently done with GHZ state?"
]
},
{
"cell_type": "markdown",
"id": "8121fa9f-af9a-444c-8e8b-9e585cadda29",
"metadata": {},
"source": [
"In standard quantum teleportation, Alice and Bob share a Bell pair, and Alice can teleport an unknown qubit state to Bob using two classical bits.\n",
"\n",
"In this exercise, you will explore how teleportation can (or cannot) be achieved using a **GHZ state** shared among three parties — Alice, Bob, and Charlie.\n",
"\n",
"A 3-qubit GHZ state is defined as:\n",
"\n",
"$ |\\text{GHZ}\\rangle = \\frac{1}{\\sqrt{2}} (|000\\rangle + |111\\rangle). $\n",
"\n",
"### Tasks:\n",
"1. Prepare a GHZ state.\n",
"2. Build the state with Qubit layout: $ |\\psi\\rangle |\\text{ghz}_a\\,\\, \\text{ghz}_c\\, \\,\\text{ghz}_b\\rangle $. $|\\psi\\rangle=\\alpha|0\\rangle + \\beta|1\\rangle$ (qubit 0) is the state Alice wants to send. We place GHZ on qubits 1,2,3 where Alice holds qubit 1, Charlie qubit 2, Bob qubit 3. We give Alice access to $|\\psi\\rangle$ and her GHZ qubit ($|\\psi\\rangle$ and $|\\text{ghz}_a\\rangle$), while Bob holds $|\\text{ghz}_b\\rangle$. Charlie holds $|\\text{ghz}_c\\rangle$ but we will consider the case where Charlie does **not** cooperate.\n",
"3. Let Alice perform a Bell-basis measurement on ($|\\psi\\rangle$, $|\\text{ghz}_a\\rangle$) — same as in standard teleportation.\n",
"4. Condition on Alice's measurement outcome, allow Bob to apply the usual Pauli corrections **based only on Alice's classical bits**, but *without* any action from Charlie.\n",
"5. Examine Bob's reduced state and compute fidelity with the original $|\\psi\\rangle$.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e5d3f31f-11d5-4c66-957e-fb8642da273d",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"from numpy import sqrt, exp, pi\n",
"from qiskit.quantum_info import Statevector, partial_trace, DensityMatrix\n",
"from itertools import product"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9da80a8a-d643-429f-8541-1d134ee39c6f",
"metadata": {},
"outputs": [],
"source": [
"# single qubit state |psi> parameterized by angles (theta, phi)\n",
"def single_qubit_state(theta=0.7, phi=0.3):\n",
" return np.array([np.cos(theta/2), np.exp(1j*phi)*np.sin(theta/2)], dtype=complex)\n",
"\n",
"# prepare initial global state: |psi> (q0) ⊗ |GHZ(1,2,3)>\n",
"def prepare_global_state(psi_vec):\n",
" # GHZ on qubits 1,2,3: (|000> + |111>)/sqrt(2)\n",
" ghz = (1/sqrt(2)) * np.array([1,0,0,0,0,0,0,1], dtype=complex) # 3 qubits\n",
" #global_state = kronecker product the two parts to have 1 ⊗ 3 = 4 qubits total\n",
" return global_state"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3e0e2f56-9389-45b6-8157-01f66f36873e",
"metadata": {},
"outputs": [],
"source": [
"# Pauli matrices and Bell basis projectors\n",
"X = np.array([[0,1],[1,0]], dtype=complex)\n",
"# define Y, Z, I similarly\n",
"\n",
"# Bell states\n",
"phi_plus = (1/sqrt(2)) * np.array([1,0,0,1], dtype=complex)\n",
"#define phi_minus, psi_plus and psi_minus similarly\n",
"bells = [phi_plus, phi_minus, psi_plus, psi_minus]"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ecd5af9d-f80c-41d4-950f-9a7102953a31",
"metadata": {},
"outputs": [],
"source": [
"#define an arbitrary single qubit state and the resultant 4 qubit resource\n",
"# psi_vec = single_qubit_state(?)\n",
"# global_state = prepare_global_state(?)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "aacb07b5-418f-4cad-92b9-ec8bd8f25777",
"metadata": {},
"outputs": [],
"source": [
"# projector onto Bell state for qubits (0,1) in a 4-qubit system: build as kron(P_bell, I_2, I_2)\n",
"def bell_projector_on_01(bvec):\n",
" P = np.outer(bvec.conj(), bvec)\n",
" # embed into 4-qubit space: qubits 0 and 1 are the Bell qubits\n",
" return np.kron(P, np.kron(I, I))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "736d0e3d-58e2-479e-bd3c-91e70caf3055",
"metadata": {},
"outputs": [],
"source": [
"# fidelity between pure psi and density matrix rho on a single qubit\n",
"def fidelity_single_qubit(psi_vec, rho):\n",
" # psi_vec is vector, rho is 2x2 density matrix\n",
" return np.real(np.vdot(psi_vec, rho @ psi_vec))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "eac4cd58-a8a9-4b8b-9aff-10f221dcf783",
"metadata": {},
"outputs": [],
"source": [
"results = []\n",
"for idx, bvec in enumerate(bells):\n",
" P = bell_projector_on_01(bvec)\n",
" # Project global state onto Bell outcome (unnormalized)\n",
" proj_state = P @ global_state\n",
" prob = np.vdot(proj_state, proj_state).real\n",
" if prob < 1e-12:\n",
" continue\n",
" # Normalize post-measurement global state\n",
" proj_state = proj_state / sqrt(prob)\n",
" # After Alice's measurement outcome idx, usually Bob would apply a correction depending on idx:\n",
" # Mapping: phi_plus -> I, phi_minus -> Z, psi_plus -> X, psi_minus -> XZ (up to phase)\n",
" if idx == 0: # hint: phi_plus -> I (no correction)\n",
" corr = np.kron(np.eye(1), np.kron(np.eye(1), np.eye(2))) # placeholder (unused)\n",
" # For 4-qubit vector, we will apply correction on qubit 3 (Bob's qubit)\n",
" corr_on_bob = ?\n",
" elif idx == 1: \n",
" corr_on_bob = ?\n",
" elif idx == 2: \n",
" corr_on_bob = ?\n",
" else: \n",
" corr_on_bob = ?\n",
"\n",
" # Apply correction on qubit 3: form operator I(0) ⊗ I(1) ⊗ I(2) ⊗ corr_on_bob\n",
" Ucorr = np.kron(np.eye(8), corr_on_bob) # 4x4 ⊗ 2x2 = 8x8\n",
" state_after_corr = Ucorr @ proj_state\n",
"\n",
" # Now compute Bob's reduced state by tracing out qubits 0,1,2 -> keep qubit 3\n",
" # reshape to 4-qubit density and partial trace\n",
" rho = np.outer(state_after_corr, state_after_corr.conj())\n",
" # trace out qubits 0,1,2 by reshaping\n",
" rho_bob = np.zeros((2,2), dtype=complex)\n",
" # indices: total 4 qubits -> size 16; iterate basis states and sum over traced subsystems\n",
" for i in range(16):\n",
" for j in range(16):\n",
" # bitstrings for i and j\n",
" bi = format(i, '04b')\n",
" bj = format(j, '04b')\n",
" # keep only elements where first three bits equal (we are tracing them)\n",
" if bi[0:3] == bj[0:3]:\n",
" # accumulate contributions to bob density matrix element (last bit)\n",
" ib = int(bi[3])\n",
" jb = int(bj[3])\n",
" rho_bob[ib, jb] += rho[i, j]\n",
" F = fidelity_single_qubit(psi_vec, rho_bob)\n",
" results.append((idx, prob, F))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cdc0f4c0-34bf-4373-a52b-bc63680ac547",
"metadata": {},
"outputs": [],
"source": [
"# Print results\n",
"print(\"Bell outcome index, probability, fidelity of Bob's reduced state with original |psi>\")\n",
"for r in results:\n",
" print(r)\n",
"# compute average fidelity over outcomes (weighted)\n",
"avgF = sum(prob * F for (_, prob, F) in results) / sum(prob for (_, prob, F) in results)\n",
"print(\"\\nAverage fidelity (with no Charlie cooperation) = {:.6f}\".format(avgF))"
]
},
{
"cell_type": "markdown",
"id": "9b50ec18-7e2c-4466-82db-3d5ab45ef500",
"metadata": {},
"source": [
"6. why is the fidelity <1 without Charlie's intervention?\n",
"7. If instead of Charlie, the third qubit of the GHZ state actually modelizes the entanglement of the qubits a and b with the environment (and therefore, this qubit is not accessible to any operator), conclude on the qualitative effect of this entanglement (i.e. a kind of noise) on the teleportation protocol."
]
},
{
"cell_type": "markdown",
"id": "af6f34ca-e8c7-458a-9d0d-7c5c6452a869",
"metadata": {},
"source": [
"## 2. Can superdense coding (particularly the case of two classical bits sent by sharing one qubit) be efficiently done with GHZ and W states?"
]
},
{
"cell_type": "markdown",
"id": "bb713949-6169-4386-81b3-7ba5c25dcb56",
"metadata": {},
"source": [
"Examining the original superdense coding architecture, the basic principle is that Alice, who wants to transmit two classical bits of information, performs local unitaries on her qubit and four orthogoonal states (Bell states) are produced. We'll explicitly test whether Alice's local unitaries on her single qubit can produce 4 mutually orthogonal global states when three qubit GHZ and W states are used as resources. \n",
"\n",
"The W state is defined as:\n",
"$ |W\\rangle = \\frac{1}{\\sqrt{3}} (|100\\rangle + |010\\rangle + |001\\rangle) $\n",
"\n",
"### Tasks:\n",
"\n",
"1. Build the GHZ and W states on 3 qubits (labelled 0, 1, 2).\n",
"2. On Alice's qubit (qubit 0) apply the four usual encoding unitaries: $I, X, Z, Y=(iXZ)$ on both the GHZ and W states.\n",
"3. Compute the eight resulting global statevectors and form the Gram matrix of overlaps to test orthogonality (for W state, repeat and observe that the states are not mutually orthogonal).\n",
"4. Conclude."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4c3e23c3-8041-41ed-8aa0-ff7a65f7f86d",
"metadata": {},
"outputs": [],
"source": [
"# Build GHZ state with ordering |q0 q1 q2> where index = q0*4 + q1*2 + q2\n",
"ghz = np.zeros(8, dtype=complex)\n",
"ghz[0] = 1.0\n",
"ghz[7] = 1.0\n",
"ghz = ghz / np.linalg.norm(ghz) # (|000> + |111>)/sqrt(2)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7d46234d-983a-4ab4-b43b-13d9e15ce4ce",
"metadata": {},
"outputs": [],
"source": [
"# Build W state similar to above (|100> + |010> + |001>)/sqrt(3)\n",
"#w = np.zeros(...)\n",
"#...\n",
"#...\n",
"#...\n",
"w = w / np.linalg.norm(w)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "afb9a1ce-616b-422d-b1f7-fea8c798f2b3",
"metadata": {},
"outputs": [],
"source": [
"# Alice has qubit 0; Bob have qubits 1 and 2\n",
"# Define encoding unitaries on Alice's qubit: I, X, Z, XZ\n",
"encs = [I, X, Z, X @ Z]\n",
"enc_names = ['I','X','Z','XZ']"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a54dcbed-5db8-43e3-8b95-471aaaf9c653",
"metadata": {},
"outputs": [],
"source": [
"def apply_on_alice(U, state):\n",
" # apply U on qubit 0 of 3-qubit state: U ⊗ I ⊗ I\n",
" return np.kron(U, np.kron(I, I)) @ state\n",
"\n",
"def gram_matrix(states):\n",
" n = len(states)\n",
" G = np.zeros((n,n), dtype=complex)\n",
" for i in range(n):\n",
" for j in range(n):\n",
" G[i,j] = np.vdot(states[i], states[j])\n",
" return G"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9c382467-de08-4e1e-aa48-9deaa1b51b41",
"metadata": {},
"outputs": [],
"source": [
"# GHZ case\n",
"#ghz_states =?... apply single qubit unitaries U on qubit 0 and identities on qubit 1 and 2\n",
"#G_ghz = ?... calculate the Gram matrix\n",
"print(\"GHZ Gram matrix (absolute values):\\n\", np.round(np.abs(G_ghz), 6))\n",
"\n",
"# Check if GHZ states are mutually orthogonal\n",
"orth_ghz = np.allclose(G_ghz, np.diag(np.diag(G_ghz)), atol=1e-8)\n",
"print(\"GHZ: are encoded states orthogonal?\", orth_ghz)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cf918757-50b0-4a2c-9417-9b4af1064e7c",
"metadata": {},
"outputs": [],
"source": [
"# W case\n",
"# go through the same steps as above, are the encoded states orthogonal?"
]
},
{
"cell_type": "markdown",
"id": "fa73f58f-e008-481a-8719-1fb0d0da86a3",
"metadata": {},
"source": [
"## 3. Can superdense coding of three classical bits be done by sharing two qubits with the GHZ state?"
]
},
{
"cell_type": "markdown",
"id": "f28e9220-0d48-49b3-995f-d4ca311e35bb",
"metadata": {},
"source": [
"If Alice now has access to two qubits of the GHZ state (the 0th and 1st qubit) and Bob holds the last qubit (2nd), then Alice can send three classical bits by encoding them in an orthogonal set of 8 states.​ Conceptually, this means Alice can performs any two qubit unitaries from $\\{I,X,Y,Z\\}_0\\otimes \\{I,X,Y,Z\\}_1$ (or any 1 and 2 qubits gates, but we restrict ourselves to Pauli in this exercise) on her qubits and the result should contain 8 orthogonal states. \n",
"\n",
"1. Apply the all the Pauli unitaries above to the GHZ state."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "17fad278-4ee5-4ad2-9dd2-7de7ac1e6d0c",
"metadata": {},
"outputs": [],
"source": [
"# kronecker for two-qubit operator acting on qubits (0,1) with qubit order q0 q1 q2.\n",
"def apply_on_alice(U_ab, state):\n",
" # U_ab is 4x4 acting on qubits 0 and 1; full operator is U_ab ⊗ I (Bob qubit is last)\n",
" #return [?]"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "16795955-84a7-4e27-99c8-85638e0edf70",
"metadata": {},
"outputs": [],
"source": [
"# Build two-qubit operator list: all tensor products of single-qubit Paulis\n",
"single_paulis = [('I',I), ('X',X), ('Y',Y), ('Z',Z)]\n",
"op_list = []\n",
"op_names = []\n",
"for (n1, M1), (n2, M2) in product(single_paulis, repeat=2):\n",
" name = f\"{n1}⊗{n2}\"\n",
" op = np.kron(M1, M2)\n",
" op_list.append(op)\n",
" op_names.append(name)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5bd76511-b934-462a-8f25-7a61360235d1",
"metadata": {},
"outputs": [],
"source": [
"# Compute encoded states\n",
"encoded_states = [apply_on_alice(U, ghz) for U in op_list]\n",
"# Ensure normalization (should be unitary applied to normalized state)\n",
"for v in encoded_states:\n",
" assert np.allclose(np.linalg.norm(v), 1.0, atol=1e-9)\n",
"\n",
"# Compute Gram matrix\n",
"G = np.array([[np.vdot(sj, si) for sj in encoded_states] for si in encoded_states], dtype=complex)\n",
"absG = np.round(np.abs(G), 8)\n",
"print(\"Absolute Gram matrix (|<psi_i|psi_j>|):\")\n",
"print(absG)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e8d11f30-3f84-4950-b0f8-17bfdf45a7d5",
"metadata": {},
"outputs": [],
"source": [
"# Rank (how many orthogonal states)\n",
"# Use SVD tolerance\n",
"u,s,vt = np.linalg.svd(G)\n",
"rank = np.sum(s > 1e-8)\n",
"print(\"\\n Numeric rank of Gram matrix (max # orthogonal states):\", rank)"
]
},
{
"cell_type": "markdown",
"id": "2c02eb77-1751-45af-a145-57ff3b756b05",
"metadata": {},
"source": [
"Understandably, not all 16 combinations are needed, and 8 are enough. \n",
"\n",
"2. Find such a set."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c9deee2d-6260-4ed8-90b7-4b970f42c034",
"metadata": {},
"outputs": [],
"source": [
"# pick an orthogonal subset\n",
"orth_indices = []\n",
"tol = 1e-8\n",
"for i in range(len(encoded_states)):\n",
" v = encoded_states[i]\n",
" ok = True\n",
" for j in orth_indices:\n",
" ov = abs(np.vdot(encoded_states[j], v))\n",
" if ov > tol:\n",
" ok = False\n",
" break\n",
" if ok:\n",
" orth_indices.append(i)\n",
" if len(orth_indices) >= rank:\n",
" break\n",
"\n",
"print(\"\\n Found orthogonal subset of size\", len(orth_indices))\n",
"for k in orth_indices:\n",
" print(\" -\", op_names[k])"
]
},
{
"cell_type": "markdown",
"id": "4f66cbad",
"metadata": {},
"source": [
"3. Conclude"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"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.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 448 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 KiB