scaffolding-2026
@@ -0,0 +1 @@
|
||||
\relax
|
||||
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 83 KiB |
@@ -0,0 +1,625 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Exercise 2: Quantum Logic Gates"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Prerequisite\n",
|
||||
"- [Qiskit basics](https://qiskit.org/documentation/tutorials/circuits/1_getting_started_with_qiskit.html)\n",
|
||||
"- [Ch.1.2 The Atoms of Computation](https://qiskit.org/textbook/ch-states/atoms-computation.html)\n",
|
||||
"\n",
|
||||
"Other relevant materials\n",
|
||||
"- [Access IBM Quantum Systems](https://qiskit.org/documentation/install.html#access-ibm-quantum-systems)\n",
|
||||
"- [IBM Quantum Systems Configuration](https://quantum-computing.ibm.com/docs/manage/backends/configuration)\n",
|
||||
"- [Transpile](https://qiskit.org/documentation/apidoc/transpiler.html)\n",
|
||||
"- [IBM Quantum account](https://quantum-computing.ibm.com/docs/manage/account/ibmq)\n",
|
||||
"- [Quantum Circuits](https://qiskit.org/documentation/apidoc/circuit.html) "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from qiskit import *\n",
|
||||
"from qiskit.visualization import plot_histogram\n",
|
||||
"import numpy as np"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## **Exercise 1**\n",
|
||||
"\n",
|
||||
"See 'Part 1' below, and find the circuits required for the:\n",
|
||||
"* (a) `XOR` gate;\n",
|
||||
"* (b) `AND` gate;\n",
|
||||
"* (c) `NAND` gate;\n",
|
||||
"* (d) `OR` gate.\n",
|
||||
"\n",
|
||||
"## **Exercise 2**\n",
|
||||
"\n",
|
||||
"See 'Part 2' below, and find a `layout` for which the AND gate compiles to 6 non-local gates for `ibmqx2`. Note that there is some randomness in the compiling process. So you might need to try a few times."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<h2 style=\"font-size:24px;\">Part 1: Classical logic gates with quantum circuits</h2>\n",
|
||||
"\n",
|
||||
"<br>\n",
|
||||
"\n",
|
||||
"An implementation of the `NOT` gate is provided as an example."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def NOT(inp):\n",
|
||||
" \"\"\"An NOT gate.\n",
|
||||
" \n",
|
||||
" Parameters:\n",
|
||||
" inp (str): Input, encoded in qubit 0.\n",
|
||||
" \n",
|
||||
" Returns:\n",
|
||||
" QuantumCircuit: Output NOT circuit.\n",
|
||||
" str: Output value measured from qubit 0.\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" qc = QuantumCircuit(1, 1) # A quantum circuit with a single qubit and a single classical bit\n",
|
||||
" qc.reset(0)\n",
|
||||
" \n",
|
||||
" # We encode '0' as the qubit state |0⟩, and '1' as |1⟩\n",
|
||||
" # Since the qubit is initially |0⟩, we don't need to do anything for an input of '0'\n",
|
||||
" # For an input of '1', we do an x to rotate the |0⟩ to |1⟩\n",
|
||||
" if inp=='1':\n",
|
||||
" qc.x(0)\n",
|
||||
" \n",
|
||||
" # barrier between input state and gate operation \n",
|
||||
" qc.barrier()\n",
|
||||
" \n",
|
||||
" # Now we've encoded the input, we can do a NOT on it using x\n",
|
||||
" qc.x(0)\n",
|
||||
" \n",
|
||||
" #barrier between gate operation and measurement\n",
|
||||
" qc.barrier()\n",
|
||||
" \n",
|
||||
" # Finally, we extract the |0⟩/|1⟩ output of the qubit and encode it in the bit c[0]\n",
|
||||
" qc.measure(0,0)\n",
|
||||
" qc.draw('mpl')\n",
|
||||
" \n",
|
||||
" # We'll run the program on a simulator\n",
|
||||
" backend = Aer.get_backend('qasm_simulator')\n",
|
||||
" # Since the output will be deterministic, we can use just a single shot to get it\n",
|
||||
" job = execute(qc, backend, shots=1, memory=True)\n",
|
||||
" output = job.result().get_memory()[0]\n",
|
||||
" \n",
|
||||
" return qc, output"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## Test the function\n",
|
||||
"for inp in ['0', '1']:\n",
|
||||
" qc, out = NOT(inp)\n",
|
||||
" print('NOT with input',inp,'gives output',out)\n",
|
||||
" display(qc.draw())\n",
|
||||
" print('\\n')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<h3 style=\"font-size: 20px\">📓 XOR gate</h3>\n",
|
||||
"\n",
|
||||
"Takes two binary strings as input and gives one as output.\n",
|
||||
"\n",
|
||||
"The output is '0' when the inputs are equal and '1' otherwise."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def XOR(inp1,inp2):\n",
|
||||
" \"\"\"An XOR gate.\n",
|
||||
" \n",
|
||||
" Parameters:\n",
|
||||
" inpt1 (str): Input 1, encoded in qubit 0.\n",
|
||||
" inpt2 (str): Input 2, encoded in qubit 1.\n",
|
||||
" \n",
|
||||
" Returns:\n",
|
||||
" QuantumCircuit: Output XOR circuit.\n",
|
||||
" str: Output value measured from qubit 1.\n",
|
||||
" \"\"\"\n",
|
||||
" \n",
|
||||
" qc = QuantumCircuit(2, 1) \n",
|
||||
" qc.reset(range(2))\n",
|
||||
" \n",
|
||||
" if inp1=='1':\n",
|
||||
" qc.x(0)\n",
|
||||
" if inp2=='1':\n",
|
||||
" qc.x(1)\n",
|
||||
" \n",
|
||||
" # barrier between input state and gate operation \n",
|
||||
" qc.barrier()\n",
|
||||
" \n",
|
||||
" # this is where your program for quantum XOR gate goes\n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" # barrier between input state and gate operation \n",
|
||||
" qc.barrier()\n",
|
||||
" \n",
|
||||
" qc.measure(1,0) # output from qubit 1 is measured\n",
|
||||
" \n",
|
||||
" #We'll run the program on a simulator\n",
|
||||
" backend = Aer.get_backend('qasm_simulator')\n",
|
||||
" #Since the output will be deterministic, we can use just a single shot to get it\n",
|
||||
" job = execute(qc, backend, shots=1, memory=True)\n",
|
||||
" output = job.result().get_memory()[0]\n",
|
||||
" \n",
|
||||
" return qc, output"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## Test the function\n",
|
||||
"for inp1 in ['0', '1']:\n",
|
||||
" for inp2 in ['0', '1']:\n",
|
||||
" qc, output = XOR(inp1, inp2)\n",
|
||||
" print('XOR with inputs',inp1,inp2,'gives output',output)\n",
|
||||
" display(qc.draw())\n",
|
||||
" print('\\n')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<h3 style=\"font-size: 20px\">📓 AND gate</h3>\n",
|
||||
"\n",
|
||||
"Takes two binary strings as input and gives one as output.\n",
|
||||
"\n",
|
||||
"The output is `'1'` only when both the inputs are `'1'`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def AND(inp1,inp2):\n",
|
||||
" \"\"\"An AND gate.\n",
|
||||
" \n",
|
||||
" Parameters:\n",
|
||||
" inpt1 (str): Input 1, encoded in qubit 0.\n",
|
||||
" inpt2 (str): Input 2, encoded in qubit 1.\n",
|
||||
" \n",
|
||||
" Returns:\n",
|
||||
" QuantumCircuit: Output XOR circuit.\n",
|
||||
" str: Output value measured from qubit 2.\n",
|
||||
" \"\"\"\n",
|
||||
" qc = QuantumCircuit(3, 1) \n",
|
||||
" qc.reset(range(2))\n",
|
||||
" \n",
|
||||
" if inp1=='1':\n",
|
||||
" qc.x(0)\n",
|
||||
" if inp2=='1':\n",
|
||||
" qc.x(1)\n",
|
||||
" \n",
|
||||
" qc.barrier()\n",
|
||||
"\n",
|
||||
" # this is where your program for quantum AND gate goes\n",
|
||||
"\n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
"\n",
|
||||
" qc.barrier()\n",
|
||||
" qc.measure(2, 0) # output from qubit 2 is measured\n",
|
||||
" \n",
|
||||
" # We'll run the program on a simulator\n",
|
||||
" backend = Aer.get_backend('qasm_simulator')\n",
|
||||
" # Since the output will be deterministic, we can use just a single shot to get it\n",
|
||||
" job = execute(qc, backend, shots=1, memory=True)\n",
|
||||
" output = job.result().get_memory()[0]\n",
|
||||
" \n",
|
||||
" return qc, output"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## Test the function\n",
|
||||
"for inp1 in ['0', '1']:\n",
|
||||
" for inp2 in ['0', '1']:\n",
|
||||
" qc, output = AND(inp1, inp2)\n",
|
||||
" print('AND with inputs',inp1,inp2,'gives output',output)\n",
|
||||
" display(qc.draw())\n",
|
||||
" print('\\n')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<h3 style=\"font-size: 20px\">📓 NAND gate</h3>\n",
|
||||
"\n",
|
||||
"Takes two binary strings as input and gives one as output.\n",
|
||||
"\n",
|
||||
"The output is `'0'` only when both the inputs are `'1'`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def NAND(inp1,inp2):\n",
|
||||
" \"\"\"An NAND gate.\n",
|
||||
" \n",
|
||||
" Parameters:\n",
|
||||
" inpt1 (str): Input 1, encoded in qubit 0.\n",
|
||||
" inpt2 (str): Input 2, encoded in qubit 1.\n",
|
||||
" \n",
|
||||
" Returns:\n",
|
||||
" QuantumCircuit: Output NAND circuit.\n",
|
||||
" str: Output value measured from qubit 2.\n",
|
||||
" \"\"\"\n",
|
||||
" qc = QuantumCircuit(3, 1) \n",
|
||||
" qc.reset(range(3))\n",
|
||||
" \n",
|
||||
" if inp1=='1':\n",
|
||||
" qc.x(0)\n",
|
||||
" if inp2=='1':\n",
|
||||
" qc.x(1)\n",
|
||||
" \n",
|
||||
" qc.barrier()\n",
|
||||
" \n",
|
||||
" # this is where your program for quantum NAND gate goes\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" qc.barrier()\n",
|
||||
" qc.measure(2, 0) # output from qubit 2 is measured\n",
|
||||
" \n",
|
||||
" # We'll run the program on a simulator\n",
|
||||
" backend = Aer.get_backend('qasm_simulator')\n",
|
||||
" # Since the output will be deterministic, we can use just a single shot to get it\n",
|
||||
" job = execute(qc,backend,shots=1,memory=True)\n",
|
||||
" output = job.result().get_memory()[0]\n",
|
||||
" \n",
|
||||
" return qc, output"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## Test the function\n",
|
||||
"for inp1 in ['0', '1']:\n",
|
||||
" for inp2 in ['0', '1']:\n",
|
||||
" qc, output = NAND(inp1, inp2)\n",
|
||||
" print('NAND with inputs',inp1,inp2,'gives output',output)\n",
|
||||
" display(qc.draw())\n",
|
||||
" print('\\n')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<h3 style=\"font-size: 20px\">📓 OR gate</h3>\n",
|
||||
"\n",
|
||||
"Takes two binary strings as input and gives one as output.\n",
|
||||
"\n",
|
||||
"The output is '1' if either input is '1'."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def OR(inp1,inp2):\n",
|
||||
" \"\"\"An OR gate.\n",
|
||||
" \n",
|
||||
" Parameters:\n",
|
||||
" inpt1 (str): Input 1, encoded in qubit 0.\n",
|
||||
" inpt2 (str): Input 2, encoded in qubit 1.\n",
|
||||
" \n",
|
||||
" Returns:\n",
|
||||
" QuantumCircuit: Output XOR circuit.\n",
|
||||
" str: Output value measured from qubit 2.\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" qc = QuantumCircuit(3, 1) \n",
|
||||
" qc.reset(range(3))\n",
|
||||
" \n",
|
||||
" if inp1=='1':\n",
|
||||
" qc.x(0)\n",
|
||||
" if inp2=='1':\n",
|
||||
" qc.x(1)\n",
|
||||
" \n",
|
||||
" qc.barrier()\n",
|
||||
" \n",
|
||||
" # this is where your program for quantum OR gate goes\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" qc.barrier()\n",
|
||||
" qc.measure(2, 0) # output from qubit 2 is measured\n",
|
||||
" \n",
|
||||
" # We'll run the program on a simulator\n",
|
||||
" backend = Aer.get_backend('qasm_simulator')\n",
|
||||
" # Since the output will be deterministic, we can use just a single shot to get it\n",
|
||||
" job = execute(qc,backend,shots=1,memory=True)\n",
|
||||
" output = job.result().get_memory()[0]\n",
|
||||
" \n",
|
||||
" return qc, output"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## Test the function\n",
|
||||
"for inp1 in ['0', '1']:\n",
|
||||
" for inp2 in ['0', '1']:\n",
|
||||
" qc, output = OR(inp1, inp2)\n",
|
||||
" print('OR with inputs',inp1,inp2,'gives output',output)\n",
|
||||
" display(qc.draw())\n",
|
||||
" print('\\n')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<h2 style=\"font-size:24px;\">Part 2: AND gate on Quantum Computer</h2>\n",
|
||||
"<br>\n",
|
||||
"\n",
|
||||
"Real quantum computers are not able to implement arbitary gates directly. Instead, everything needs to be compiled (or 'transpiled') to the set of basic gates that the device can use. This usually consists of a set of single qubit rotations, as well as two qubit gates like `cx`.\n",
|
||||
"\n",
|
||||
"There are also limits on which `cx` gates can be used directly: only some pairs of control and target qubits are possible. To implement other `cx` gates, tricks such as using `swap` gates to effectively move information around must be used. The possible pairs of qubits on which `cx` gates can be applied is known as the 'connectivity' of the device.\n",
|
||||
"\n",
|
||||
"We'll now look at some examples. To make sure you don't end up in a queue for a busy device, we'll be using mock backends. These are designed to act exactly like real backends."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from qiskit.test.mock import FakeYorktown\n",
|
||||
"backend = FakeYorktown()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Upon executing the following cell you will be presented with a widget that displays all of the information about your choice of the backend. You can obtain information that you need by clicking on the tabs. For example, backend status, number of qubits and the connectivity are under `configuration` tab, where as the `Error Map` tab will reveal the latest noise information for the system. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import qiskit.tools.jupyter\n",
|
||||
"\n",
|
||||
"backend"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The two system we are using (or at least pretending to) is `ibmqx2` (also known as `ibmq_yorktown`).\n",
|
||||
"\n",
|
||||
"Here's a circuit that applies an `AND` gate, compiled into single and two qubit gates (assuming full connectivity)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"qc_and = QuantumCircuit(3)\n",
|
||||
"qc_and.ccx(0,1,2)\n",
|
||||
"print('AND gate')\n",
|
||||
"display(qc_and.draw())\n",
|
||||
"print('\\n\\nTranspiled AND gate with all the required connectiviy')\n",
|
||||
"qc_and.decompose().draw()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This ideal transpilation requires 6 `cx` gates.\n",
|
||||
"\n",
|
||||
"There are often optimizations that the transpiler can perform that reduce the overall gate count, and thus total length of the input circuits. Note that the addition of swaps to match the device topology, and optimizations for reducing the length of a circuit are at odds with each other. In what follows we will make use of `initial_layout` that allows us to pick the qubits on a device used for the computation and `optimization_level`, an argument that allows selecting from internal defaults for circuit swap mapping and optimization methods to perform.\n",
|
||||
"\n",
|
||||
"You can learn more about transpile function in depth [here](https://qiskit.org/documentation/apidoc/transpiler.html)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Rather than actually running the AND function, let's just look at the transpiled circuits. The following function does this for a given set of inputs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# run the cell to define AND gate for real quantum system\n",
|
||||
"\n",
|
||||
"def AND(inp1, inp2, backend, layout):\n",
|
||||
" \n",
|
||||
" qc = QuantumCircuit(3, 1) \n",
|
||||
" qc.reset(range(3))\n",
|
||||
" \n",
|
||||
" if inp1=='1':\n",
|
||||
" qc.x(0)\n",
|
||||
" if inp2=='1':\n",
|
||||
" qc.x(1)\n",
|
||||
" \n",
|
||||
" qc.barrier()\n",
|
||||
" qc.ccx(0, 1, 2) \n",
|
||||
" qc.barrier()\n",
|
||||
" qc.measure(2, 0) \n",
|
||||
" \n",
|
||||
" qc_trans = transpile(qc, backend, initial_layout=layout, optimization_level=3)\n",
|
||||
" \n",
|
||||
" return qc_trans"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<h4 style=\"font-size: 16px\">Three qubits on <code>ibmqx2</code> with the triangle connectivity</h4>\n",
|
||||
" \n",
|
||||
"First, examine `ibmqx2` using the widget introduced earlier. Find the best set of three qubits to use for the `AND` gate, making best use of the connectivity."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# run this cell for the widget\n",
|
||||
"backend"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<p>📓 Assign your choice of layout to the list variable <code>layout</code> in the cell below</p>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Assign your choice of the initial_layout to the variable layout1 as a list \n",
|
||||
"# ex) layout = [0,2,4]\n",
|
||||
"layout = "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Compile the `AND` gate on `ibmqx2` by running the cell below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"for input1 in ['0','1']:\n",
|
||||
" for input2 in ['0','1']:\n",
|
||||
" qc_trans1 = AND(input1, input2, backend, layout)\n",
|
||||
" \n",
|
||||
" print('For input '+input1+input2)\n",
|
||||
" print('# of nonlocal gates =',qc_trans1.num_nonlocal_gates())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.9.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "9ae539e2",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Exercise 3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "fe84364c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 1. Mutually unbiased bases\n",
|
||||
"Show that the $X$, $Y$ and $Z$ bases are all unbiased with respect to each other: each\n",
|
||||
"state for one basis results in a completely random result for the other two bases."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7e5f84c8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 2. Shifting certainty\n",
|
||||
"\n",
|
||||
"The state of a single qubit is characterized by three numbers, $\\langle \\sigma^x \\rangle$, $\\langle \\sigma^y \\rangle$ and $\\langle \\sigma^z \\rangle$,\n",
|
||||
"defined as\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"\\langle \\sigma^\\alpha \\rangle = p^\\alpha_0 - p^\\alpha_1,\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"where $p^z_0$ is the probability of the outcome \\texttt{0} for a $Z$ measurement, and so on.\n",
|
||||
"\n",
|
||||
"For any single qubit superposition $|\\psi\\rangle = c_0 |0\\rangle + c_1 |1\\rangle$,\n",
|
||||
"\n",
|
||||
"$\n",
|
||||
"\\langle \\sigma^x \\rangle^2 + \\langle \\sigma^y \\rangle^2 + \\langle \\sigma^z \\rangle^2 = 1\n",
|
||||
"$\n",
|
||||
"\n",
|
||||
"Verify this for the following states.\n",
|
||||
"\n",
|
||||
"* (a) $|\\psi\\rangle = \\cos \\theta \\, |0\\rangle + \\sin \\theta \\, |1\\rangle$\n",
|
||||
"* (b) $|\\psi\\rangle = \\frac{1}{\\sqrt{2}} \\, \\left( |0\\rangle \\,+\\, e^{i \\phi} |1\\rangle \\right)$\n",
|
||||
"\n",
|
||||
"## 3. A useful matrix\n",
|
||||
"\n",
|
||||
"Find the $2\\times2$ matrix $M$ such that\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"p^z_0 - p^z_1 = \\langle \\psi | M | \\psi \\rangle \\,\\,\\, \\forall |\\psi\\rangle\n",
|
||||
"$$"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ac918ed2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.9.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Exercise 4\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## 1. Alternative Pauli Basis States\n",
|
||||
"\n",
|
||||
"There are an infinite number of possible single qubit states. From a theoretical stand-point, the one we choose to label $| 0 \\rangle$ is arbitrary. So let’s consider the following alternative.\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"| \\bar 0 \\rangle = \\cos(\\theta) \\, | 0 \\rangle + \\sin(\\theta) \\, | 1 \\rangle.\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"For this $| \\bar 0 \\rangle$:\n",
|
||||
"\n",
|
||||
"(a) Find a corresponding orthogonal state $| \\bar 1 \\rangle$;\n",
|
||||
"\n",
|
||||
"(b) For this basis $| \\bar 0 \\rangle$, $| \\bar 1 \\rangle$, find mutually unbiased basis states $| \\bar + \\rangle$ and $| \\bar - \\rangle$;\n",
|
||||
"\n",
|
||||
"## 2. Properties of the Pauli Matrices\n",
|
||||
"\n",
|
||||
"Note: Sometimes the Pauli matrices are written as $X$, $Y$ and $Z$, and sometimes as $\\sigma_x$, $\\sigma_y$ and $\\sigma_z$. For the most part, the convention is an arbitrary choice. Once you’ve used them enough, you’ll hardly notice the difference (to the great annoyance of your students!).\n",
|
||||
"\n",
|
||||
"The Pauli matrices are defined\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"X = \n",
|
||||
"\\begin{pmatrix} \n",
|
||||
"0 & 1 \\\\\n",
|
||||
"1 & 0 \\\\\n",
|
||||
"\\end{pmatrix}, \\,\\,\n",
|
||||
"Y = \n",
|
||||
"\\begin{pmatrix} \n",
|
||||
"0 & -i \\\\\n",
|
||||
"i & 0 \\\\\n",
|
||||
"\\end{pmatrix}, \\,\\,\n",
|
||||
"Z = \n",
|
||||
"\\begin{pmatrix} \n",
|
||||
"1 & 0 \\\\\n",
|
||||
"0 & -1 \\\\\n",
|
||||
"\\end{pmatrix}, \\,\\,\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"(a) Show that each squares to the identity matrix.\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"I = \n",
|
||||
"\\begin{pmatrix} \n",
|
||||
"1 & 0 \\\\\n",
|
||||
"0 & 1 \\\\\n",
|
||||
"\\end{pmatrix}\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"(b) Show that $P_1 P_2 = - P_2 P_1$ for any pair of Paulis $P_1$ and $P_2$.\n",
|
||||
"\n",
|
||||
"(c) Show that $P_1 P_2 \\sim P_3$ for any pair of Paulis $P_1$ and $P_2$, where $P_3$ is the remaining Pauli.\n",
|
||||
"\n",
|
||||
"(d) Find the eigenvectors and eigenvalues of each Pauli.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## 3. The Hadamard\n",
|
||||
"\n",
|
||||
"The Hadamard matrix can be expressed\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"H = \\frac{1}{\\sqrt{2}}\n",
|
||||
"\\begin{pmatrix} \n",
|
||||
"1 & 1 \\\\\n",
|
||||
"1 & -1 \\\\\n",
|
||||
"\\end{pmatrix}\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"(a) Find the eigenvectors and eigenvalues of this matrix.\n",
|
||||
"\n",
|
||||
"(b) Show that $H$ also squares to identity.\n",
|
||||
"\n",
|
||||
"(c) Show that $H P_1 H^\\dagger \\sim P_2$ for Paulis $P_1$ and $P_2$. \n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## 4. Two-qubit Paulis\n",
|
||||
"\n",
|
||||
"For two qubits we can define a set of matrices $X_0$, $Y_0$ and $Z_0$ that behave as Paulis (i.e. they have the same properties as in 3a, 3b and 3c above). We can also define another separate set of matrices $X_1$, $Y_1$ and $Z_1$ that also behave as Paulis. Furthermore, any matrix from one of these sets will commute with any matrix from the other\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"P_0 P_1 = P_1 P_0, \\,\\, \\forall \\,\\, P_j \\, \\in \\, \\{X_j, Y_j, Z_j\\}.\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"Usually we define these sets in a very simple way, using the Paulis of one qubit for one set, and the Paulis of the other qubit for the other set,\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"X_0 = X \\otimes I, \\, X_1 = I \\otimes X, \\, \\rm{etc}.\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"But since this is an exercise, let's do something a bit more interesting! Consider the following choice of the first set,\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"X_0 = X \\otimes X, \\, Y_0 = Y \\otimes X,\\, Z_0 = Z \\otimes I\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"Find a corresponding $X_1$, $Y_1$ and $Z_1$."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.9.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Exercise 5\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## 1. Clifford Gates and Paulis\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"(a) For $n$ qubits, there are $4^n$ possible tensor products of Paulis (one of which is the $n$-qubit identity). Show that (up to a phase) these can be expressed as a product of $2n$ $n$-qubit Paulis.\n",
|
||||
"\n",
|
||||
"(b) If $U$ is a Clifford gate, the following property holds\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"U P U^\\dagger \\sim P' \\,\\,\\,\\,\\, \\forall P,\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"where $P$ and $P'$ are Paulis and $\\sim$ denotes equality up to a factor of $\\pm 1$ or $\\pm i$. If this relation holds for the $2n$ Pauli generators of part (a), show that it also holds for all $n$-qubit Paulis.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## 2. Single-Qubit Clifford Gates\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"(a) Show that the Paulis are Cliffords themselves.\n",
|
||||
"\n",
|
||||
"(b) Show that $H$, $S$ and $S^\\dagger$ are Clifford gates.\n",
|
||||
"\n",
|
||||
"(c) Show that $T=S^{1/2}$ is not a Clifford gate.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## 3. Two-Qubit Clifford Gates\n",
|
||||
"\n",
|
||||
"For more than one qubit, Clifford gates map between tensor products of Pauli operators.\n",
|
||||
"\n",
|
||||
"For two qubits\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"U \\,( P \\otimes Q )\\, U^\\dagger \\sim P' \\otimes Q' \\,\\,\\,\\,\\, \\forall P,Q\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"where $P$, $Q$, $P'$ and $Q'$ are all Paulis and $\\sim$ denotes equality up to a factor of $\\pm 1$ or $\\pm i$.\n",
|
||||
"\n",
|
||||
"(a) Show that the controlled-NOT is a Clifford gate.\n",
|
||||
"\n",
|
||||
"(b) Show that the controlled-Hadamard is not a Clifford gate.\n",
|
||||
"\n",
|
||||
"## 4. Three-Qubit Clifford Gates\n",
|
||||
"\n",
|
||||
"(a) Provide an example of a three-qubit Clifford gate, and show that it is indeed a Clifford. This should be a truly three qubit gate, and therefore not one that can be expressed purely as a tensor product of single- and two-qubit gates.\n",
|
||||
"\n",
|
||||
"(b) Show that the Toffoli gate is not Clifford."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.9.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"colab_type": "text",
|
||||
"id": "pmm5uV8cQQN6"
|
||||
},
|
||||
"source": [
|
||||
"# Exercise 7\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## 1\n",
|
||||
"\n",
|
||||
"Given the ability to perform `cx` and `h` gates, and given a supply of auxiliary qubits in state $|0\\rangle$, show that it is possible to perform `x` gates with arbitrarily high probability of success."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"colab_type": "text",
|
||||
"id": "CJyxxSDUQQN9"
|
||||
},
|
||||
"source": [
|
||||
"## 2\n",
|
||||
"\n",
|
||||
"A general $n$-qubit state can be written\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"| \\psi \\rangle = \\sum_x c_x |x\\rangle = \\sum_x \\Re (c_x) |x\\rangle + i \\Im (c_x) |x\\rangle,\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"where the $|x\\rangle$ here denote the $n$-qubit Z basis states.\n",
|
||||
"\n",
|
||||
"For each such state we can write an equivalent state for which all amplitudes are real. Since the complex nature of the amplitudes effectively adds an extra degree of superposition to the state, we need to add an extra qubit to the system to encode the same information. The $n+1$ qubit state $| \\tilde \\psi \\rangle$ equivalent to $| \\psi \\rangle$ is then\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"| \\tilde \\psi \\rangle = \\sum_x \\Re (c_x) |x\\rangle \\otimes |0\\rangle + \\Im (c_x) |x\\rangle \\otimes |1\\rangle.\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"Note that the $i$ in $| \\psi \\rangle$ is replaced by the $|1\\rangle$ state on the extra qubit in $| \\tilde \\psi \\rangle$.\n",
|
||||
"\n",
|
||||
"a) For each $n$ qubit unitary $U$ we can define an equivalent $n+1$ qubit unitary $\\tilde U$, such that\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"\\tilde U (|x\\rangle \\otimes |0\\rangle) = \\widetilde{U |x\\rangle}, \\,\\, \\forall x\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"Write the effects of $\\tilde U (|x\\rangle \\otimes |0\\rangle)$ and $\\tilde U (|x\\rangle \\otimes |1\\rangle)$ in terms of $U$.\n",
|
||||
"\n",
|
||||
"b) Show that any two gates $\\tilde U$ and $\\tilde V$ will combine equivalently to their counterparts $U$ and $V$, i.e.,\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"\\tilde U \\tilde V = \\widetilde{U V}.\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"c) The controlled-$S$ gate is a two qubit gate which applies a phase of $i$ when the two qubits are in the state $|11\\rangle$ and acts trivially otherwise. Find the equivalent gate acting on real states, and express it in terms of gates that we have seen already during the course."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 3\n",
|
||||
"\n",
|
||||
"a) Given all Clifford gates and any an additional non-Clifford, universal quantum computation can be perfomed. Given this fact, show that the controlled-$S$ and Hadamard are a universal gate set.\n",
|
||||
"\n",
|
||||
"b) Show that the Toffoli and Hadamard can simulate universal quantum computation on real states."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"collapsed_sections": [],
|
||||
"name": "Exercise_4.ipynb",
|
||||
"provenance": [],
|
||||
"version": "0.3.2"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.9.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 1
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Exercise 8\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 1. Delayed measurement with Clifford only circuits\n",
|
||||
"\n",
|
||||
"In general, we can consider quantum circuits with classically-controlled operations: this is when measurements are made throught the circuit, with the outcomes being used to determine what gates are subsequently applied.\n",
|
||||
"\n",
|
||||
"Nevertheless, when reasoning about what kind of computations we can do with quantum circuits, it is simplest to consider only circuits of the form represented below.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Here all measurements are deferred to the end, and so there are no classically-controlled operations.\n",
|
||||
"\n",
|
||||
"Remarkably, this comes with no loss of generality. This is because all classically-controlled operations (with measurement) can be replaced by fully quantum controlled gates (unitary and without measurement). For example\n",
|
||||
"\n",
|
||||
"<div>\n",
|
||||
"<img src=\"delayed.png\" width=\"500\"/>\n",
|
||||
"</div>\n",
|
||||
"\n",
|
||||
"Any circuit with classically-controlled gates can therefore be replaced by an equivalent one with only fully quantum gates and with all measurements deferred to the end. However, the gate set required for the latter will typically need to be more powerful that the set of unitary gates used in the former."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"To show this, consider circuits for which classically-control is allowed, but for which the unitary part of all gates must be Clifford. Show that, in general, the equivalent circuit without classically-controlled gates requires non-Clifford gates."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 2. Unitarity of the order-finding operator\n",
|
||||
"\n",
|
||||
"For integers $x$, $N$ and $L$ with $x < N \\leq 2^L-1$ and $\\rm { gcd} (x,N) = 1$, consider the following operation,\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"U = \\sum_{y=0}^{2^L-1} \\left| \\, f(y) \\, \\right\\rangle \\left\\langle \\,y \\, \\right|,\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"Where $f(y) = x \\times y \\,\\,\\, {\\rm mod}\\, N$ for $0 \\leq y <N$ and $f(y)=y$ otherwise. Show that $U$ is unitary."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 3. Eigenstates of the order-finding operator\n",
|
||||
"\n",
|
||||
"* (a) Show that the following states are eigenstates of $U$,\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"\\left| u_s \\right\\rangle = \\frac{1}{\\sqrt{r}} \\sum_{k=0}^{r-1} \\exp \\left[ \\frac{-2 \\pi i s k}{r} \\right] \\left| x^k \\,\\,\\, {\\rm mod}\\, N \\right\\rangle.\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"Here $0 \\leq s \\leq r-1$, where $r$ is the smallest integer such that $x^r = 1 \\,\\,\\, {\\rm mod}\\, N$. Show also that the corresponding eigenvalues are $u_s = \\exp ( {2 \\pi i s}/{r} )$.\n",
|
||||
"\n",
|
||||
"* (b) There are also many states with eigenvalue $1$. What are these?\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.9.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Exercise 9\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 1. Grover's algorithm for two qubits\n",
|
||||
"\n",
|
||||
"(a) Implement an oracle and diffusion operator to search all strings of two bits to find `01`.\n",
|
||||
"\n",
|
||||
"(b) Run the algorithm, and determine how many iterations are required to output `01` with high probability."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 2. Grover's algorithm for three qubits\n",
|
||||
"\n",
|
||||
"(a) Implement an oracle and diffusion operator to search all strings of three bits to find `100`.\n",
|
||||
"\n",
|
||||
"(b) Run the algorithm, and determine how many iterations are required to output `100` with high probability."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.9.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,247 @@
|
||||
from qiskit import QuantumCircuit, transpile
|
||||
from qiskit_ibm_runtime.fake_provider import FakeYorktownV2
|
||||
import numpy as np
|
||||
|
||||
def test_xor(XOR):
|
||||
# Test XOR gate outputs using assert statements
|
||||
expected_outputs = {
|
||||
('0', '0'): '0',
|
||||
('0', '1'): '1',
|
||||
('1', '0'): '1',
|
||||
('1', '1'): '0'
|
||||
}
|
||||
for inp1 in ['0', '1']:
|
||||
for inp2 in ['0', '1']:
|
||||
_, output = XOR(inp1, inp2)
|
||||
assert output == expected_outputs[(inp1, inp2)], f"❌ XOR({inp1}, {inp2}) = {output}, expected {expected_outputs[(inp1, inp2)]}"
|
||||
print("✅ All XOR gate tests passed.")
|
||||
|
||||
def test_and(AND):
|
||||
# Test AND gate outputs using assert statements
|
||||
expected_outputs = {
|
||||
('0', '0'): '0',
|
||||
('0', '1'): '0',
|
||||
('1', '0'): '0',
|
||||
('1', '1'): '1'
|
||||
}
|
||||
for inp1 in ['0', '1']:
|
||||
for inp2 in ['0', '1']:
|
||||
_, output = AND(inp1, inp2)
|
||||
assert output == expected_outputs[(inp1, inp2)], f"❌ AND({inp1}, {inp2}) = {output}, expected {expected_outputs[(inp1, inp2)]}"
|
||||
print("✅ All AND gate tests passed.")
|
||||
|
||||
def test_nand(NAND):
|
||||
# Test NAND gate outputs using assert statements
|
||||
expected_outputs = {
|
||||
('0', '0'): '1',
|
||||
('0', '1'): '1',
|
||||
('1', '0'): '1',
|
||||
('1', '1'): '0'
|
||||
}
|
||||
for inp1 in ['0', '1']:
|
||||
for inp2 in ['0', '1']:
|
||||
_, output = NAND(inp1, inp2)
|
||||
assert output == expected_outputs[(inp1, inp2)], f"❌ NAND({inp1}, {inp2}) = {output}, expected {expected_outputs[(inp1, inp2)]}"
|
||||
print("✅ All NAND gate tests passed.")
|
||||
|
||||
|
||||
def test_or(OR):
|
||||
# Test OR gate outputs using assert statements
|
||||
expected_outputs = {
|
||||
('0', '0'): '0',
|
||||
('0', '1'): '1',
|
||||
('1', '0'): '1',
|
||||
('1', '1'): '1'
|
||||
}
|
||||
for inp1 in ['0', '1']:
|
||||
for inp2 in ['0', '1']:
|
||||
_, output = OR(inp1, inp2)
|
||||
assert output == expected_outputs[(inp1, inp2)], f"❌ OR({inp1}, {inp2}) = {output}, expected {expected_outputs[(inp1, inp2)]}"
|
||||
print("✅ All OR gate tests passed.")
|
||||
|
||||
def test_compilation(layout):
|
||||
|
||||
backend = FakeYorktownV2()
|
||||
|
||||
def AND(inp1, inp2, backend, layout):
|
||||
|
||||
qc = QuantumCircuit(3, 1)
|
||||
qc.reset(range(3))
|
||||
|
||||
if inp1=='1':
|
||||
qc.x(0)
|
||||
if inp2=='1':
|
||||
qc.x(1)
|
||||
|
||||
qc.barrier()
|
||||
qc.ccx(0, 1, 2)
|
||||
qc.barrier()
|
||||
qc.measure(2, 0)
|
||||
|
||||
qc_trans = transpile(qc, backend, initial_layout=layout, optimization_level=3)
|
||||
|
||||
return qc_trans
|
||||
|
||||
for input1 in ['0','1']:
|
||||
for input2 in ['0','1']:
|
||||
qc_trans1 = AND(input1, input2, backend, layout)
|
||||
num_gates = qc_trans1.num_nonlocal_gates()
|
||||
|
||||
print('For input '+input1+input2)
|
||||
assert num_gates==6, f"❌ Toffoli transpiled to {num_gates} two qubit gates rather than 6."
|
||||
print("✅ Ideal transpilation acheived.")
|
||||
|
||||
def test_alt(
|
||||
find_orthogonal_state,
|
||||
find_mutually_unbiased_basis_plus,
|
||||
find_mutually_unbiased_basis_minus
|
||||
):
|
||||
ket_0 = np.array([[1], [0]], dtype=complex)
|
||||
ket_1 = np.array([[0], [1]], dtype=complex)
|
||||
|
||||
# (a) Test for orthogonality
|
||||
theta = np.pi / 4
|
||||
ket_zero_bar = np.cos(theta) * ket_0 + np.sin(theta) * ket_1
|
||||
ket_one_bar = find_orthogonal_state(ket_zero_bar, theta)
|
||||
|
||||
# Check if the inner product is close to zero
|
||||
inner_product_orthogonal = np.vdot(ket_zero_bar, ket_one_bar)
|
||||
assert np.isclose(inner_product_orthogonal, 0), "❌ Test (a) failed: The states |ψ0⟩ and |ψ1⟩ are not orthogonal."
|
||||
print("✅ Test (a) passed: |ψ0⟩ and |ψ1⟩ are orthogonal.")
|
||||
|
||||
# (b) Test for mutually unbiased bases
|
||||
ket_plus_bar = find_mutually_unbiased_basis_plus(ket_zero_bar, ket_one_bar)
|
||||
ket_minus_bar = find_mutually_unbiased_basis_minus(ket_zero_bar, ket_one_bar)
|
||||
|
||||
# Check orthogonality of the MUB states
|
||||
inner_product_mub = np.vdot(ket_plus_bar, ket_minus_bar)
|
||||
assert np.isclose(inner_product_mub, 0), "❌ Test (b) failed: The states |ψ+⟩ and |ψ-⟩ are not orthogonal."
|
||||
|
||||
# Check the unbiased condition
|
||||
inner_product_plus_zero = np.abs(np.vdot(ket_plus_bar, ket_zero_bar))**2
|
||||
inner_product_plus_one = np.abs(np.vdot(ket_plus_bar, ket_one_bar))**2
|
||||
inner_product_minus_zero = np.abs(np.vdot(ket_minus_bar, ket_zero_bar))**2
|
||||
inner_product_minus_one = np.abs(np.vdot(ket_minus_bar, ket_one_bar))**2
|
||||
|
||||
assert np.isclose(inner_product_plus_zero, 0.5), "❌ Test (b) failed: The bases are not mutually unbiased for |<ψ+|ψ0>|^2."
|
||||
assert np.isclose(inner_product_plus_one, 0.5), "❌ Test (b) failed: The bases are not mutually unbiased for |<ψ+|ψ1>|^2."
|
||||
assert np.isclose(inner_product_minus_zero, 0.5), "❌ Test (b) failed: The bases are not mutually unbiased for |<ψ-|ψ0>|^2."
|
||||
assert np.isclose(inner_product_minus_one, 0.5), "❌ Test (b) failed: The bases are not mutually unbiased for |<ψ-|ψ1>|^2."
|
||||
|
||||
print("✅ Test (b) passed: The bases are mutually unbiased.")
|
||||
|
||||
print("\nCongratulations! All tests passed!")
|
||||
|
||||
def test_paulis(
|
||||
I,
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
test_square_to_identity,
|
||||
test_anticommutation,
|
||||
test_product_relation,
|
||||
get_eigenvalues_and_eigenvectors,
|
||||
):
|
||||
|
||||
I = np.array([
|
||||
[1, 0],
|
||||
[0, 1]
|
||||
]) # Identity matrix
|
||||
|
||||
paulis = {'X': X, 'Y': Y, 'Z': Z, 'I':I}
|
||||
assert all(p is not None for p in [X, Y, Z, I]), "❌ FAIL: At least one matrix is not defined."
|
||||
|
||||
# (a)
|
||||
for i in paulis:
|
||||
assert np.allclose(test_square_to_identity(paulis[i]) , I), "❌ FAIL (a): Your function did not correctly verify that all Pauli matrices square to the identity."
|
||||
print("✅ PASS (a): Your function `test_square_to_identity` works correctly.")
|
||||
|
||||
# (b)
|
||||
for i in ['X', 'Y', 'Z']:
|
||||
for j in ['X', 'Y', 'Z']:
|
||||
if i!=j:
|
||||
assert np.allclose(paulis[i]@paulis[j],test_anticommutation(paulis[i],paulis[j])), "❌ FAIL (b): Your function did not correctly verify the anticommutation relations."
|
||||
print("✅ PASS (b): Your function `test_anticommutation` works correctly.")
|
||||
|
||||
# (c)
|
||||
assert np.allclose(np.array(test_product_relation()), np.array([X@Y,Y@Z,Z@X])), "❌ FAIL (c): Your function did not correctly verify the Pauli product relations."
|
||||
print("✅ PASS (c): Your function `test_product_relation` works correctly.")
|
||||
|
||||
# (d)
|
||||
eigenvalues, eigenvectors = get_eigenvalues_and_eigenvectors()
|
||||
assert isinstance(eigenvalues, dict) and isinstance(eigenvectors, dict), "❌ FAIL (d): Function must return two dictionaries."
|
||||
assert all(k in eigenvalues for k in ['X', 'Y', 'Z']), "❌ FAIL (d): Eigenvalues dictionary is missing keys."
|
||||
assert all(k in eigenvectors for k in ['X', 'Y', 'Z']), "❌ FAIL (d): Eigenvectors dictionary is missing keys."
|
||||
|
||||
print("✅ PASS (d): Your function `get_eigenvalues_and_eigenvectors` has the correct return type and keys.")
|
||||
|
||||
for name in ['X', 'Y', 'Z']:
|
||||
# Check eigenvalues
|
||||
e_vals = eigenvalues[name]
|
||||
assert np.allclose(sorted(np.real(e_vals)), [-1, 1]), f"❌ FAIL (d): Eigenvalues for {name} are incorrect. Expected [-1, 1]."
|
||||
|
||||
# Check eigenvector equation: P * v = lambda * v
|
||||
p_matrix = paulis[name]
|
||||
e_vecs = eigenvectors[name]
|
||||
for i in range(len(e_vals)):
|
||||
lambda_val = e_vals[i]
|
||||
v_vec = e_vecs[:, i].reshape(2, 1)
|
||||
assert np.allclose(p_matrix @ v_vec, lambda_val * v_vec), f"❌ FAIL (d): Eigenvector equation Pv=λv does not hold for {name}."
|
||||
|
||||
print("✅ PASS (d): Eigenvalues and eigenvectors are correct for all Pauli matrices.")
|
||||
|
||||
print("\nCongratulations! All tests passed!")
|
||||
|
||||
def test_hadamard(
|
||||
H,
|
||||
get_hadamard_eigen_system,
|
||||
test_hadamard_squares_to_identity,
|
||||
test_hadamard_pauli_transformation
|
||||
):
|
||||
|
||||
print("--- Running Verification ---")
|
||||
|
||||
I = np.array([
|
||||
[1, 0],
|
||||
[0, 1]
|
||||
])
|
||||
|
||||
# Check that matrices are defined before proceeding
|
||||
assert H is not None, "❌ FAIL: H matrix is not defined."
|
||||
|
||||
# (a)
|
||||
eigen_system = get_hadamard_eigen_system()
|
||||
assert isinstance(eigen_system, tuple) and len(eigen_system) == 2, "❌ FAIL (a): Function must return a tuple of (eigenvalues, eigenvectors)."
|
||||
eigenvalues, eigenvectors = eigen_system
|
||||
|
||||
# Check eigenvalues are correct (+1 and -1)
|
||||
assert np.allclose(sorted(np.real(eigenvalues)), [-1, 1]), f"❌ FAIL (a): Eigenvalues for H are incorrect. Expected [-1, 1]."
|
||||
|
||||
# Check eigenvector equation H * v = lambda * v
|
||||
for i in range(len(eigenvalues)):
|
||||
lambda_val = eigenvalues[i]
|
||||
v_vec = eigenvectors[:, i].reshape(2, 1)
|
||||
assert np.allclose(H @ v_vec, lambda_val * v_vec), f"❌ FAIL (a): Eigenvector equation Hv=λv does not hold."
|
||||
|
||||
print("✅ PASS (a): Your function `get_hadamard_eigen_system` works correctly.")
|
||||
|
||||
# (b)
|
||||
assert np.allclose(test_hadamard_squares_to_identity() , I), "❌ FAIL (b): Your function did not correctly verify that H^2 = I."
|
||||
print("✅ PASS (b): Your function `test_hadamard_squares_to_identity` works correctly.")
|
||||
answers = ["Z", "-Y", "X"]
|
||||
X = np.array([[0, 1], [1, 0]], dtype=complex)
|
||||
Y = np.array([[0, -1j], [1j, 0]], dtype=complex)
|
||||
I = np.array([[1,0],[0,1]])
|
||||
Z = np.array([[1,0],[0,-1]])
|
||||
|
||||
# (c)
|
||||
things = [ '1. H X H†',
|
||||
'2. H Y H†',
|
||||
'3. H Z H†']
|
||||
|
||||
for i,val in enumerate(test_hadamard_pauli_transformation()):
|
||||
assert val == answers[i], f"❌ FAIL (c): Your function did not correctly verify the Hadamard-Pauli transformation {things[i]}."
|
||||
print("✅ PASS (c): Your answers are correct.")
|
||||
|
||||
|
||||
print("\nCongratulations! All tests passed!")
|
||||
@@ -0,0 +1,27 @@
|
||||
5 a
|
||||
|
||||
From lecture: We are secure as long as H(K) >= H(M)
|
||||
|
||||
A collection of N messages m_j, each of n characters, is equivalent to one message of N*n characters
|
||||
|
||||
M=[]
|
||||
for j in range(N):
|
||||
M.append( m[j] )
|
||||
|
||||
This can have a Shannon entropy of H(M) = O(1) (some finite value).
|
||||
|
||||
If the same key, k, is used for each m_j, then the key for the meta message is
|
||||
|
||||
K = k * N
|
||||
|
||||
The repetition does not increase the information content, so K will have the same Kolmogorov complexity as k. But since the Shannon entropy averages this over all characters
|
||||
|
||||
H(K) = H(k)/N = O(1/N)
|
||||
|
||||
The fact that H(M) can have a finite value conflicts with the inevitable decay of H(K), ensuring that the security condition cannot hold in general for arbitrarily large N.
|
||||
|
||||
|
||||
|
||||
5 b
|
||||
|
||||
Create a frequency table for the first character in each message. Whichever character is most common is most likely the encoding of E, since E is the most common letter in English text. The next most common is most likely T, and so on. Do the same for all characters. Decode all messages accordingly. Find successes and failures and use them to improve the decoding until all messages become sensible English text. The decoding is the key (or the inverse of it, at least).
|
||||
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 2.7 KiB |