├── Notebooks ├── .gitignore ├── Day 0 Python Revision.ipynb ├── Day 1 Introduction to Quantum Circuits.ipynb ├── Day 2.1 Quantum Circuits Continued.ipynb ├── Day 2.2 Quantum TELIportation.ipynb ├── Day 3 Universality.ipynb ├── Day 4 Deutsch Josza algorithm.ipynb ├── Day 5 BB84 Protocol.ipynb ├── Day 6 Grover's algorithm.ipynb ├── Day 7 Quantum Fourier transform and its applications_ Part 1.ipynb ├── Day 7 Quantum Fourier transform and its applications_ Part 2.ipynb ├── Final Question 1 Discrete Logarithm.ipynb └── Final Question 2 Unitary Circuit Building.ipynb ├── README.md ├── Solutions ├── .gitignore ├── Day 1 solutions.ipynb ├── Day 2.1 Quantum Circuits Continued (Solutions).ipynb ├── Day 2.2 solutions.ipynb ├── Day 3 Universality Solutions.ipynb ├── Day 4 Deutsch Josza algorithm (Solution).ipynb ├── Day 5 BB84 Protocol Solutions.ipynb ├── Day 6 Grover's algorithm solutions.ipynb ├── Day 7 solutions.ipynb ├── Final Question 1 Discrete Logarithm Solutions.ipynb └── Final Question 2 Unitary Circuit Building Solutions.ipynb └── images ├── .gitignore ├── contabc.jpg ├── contv.jpg ├── dark_got_me_this.jpeg ├── geometrygrover.jpg ├── grover.jpg ├── hold.jpg ├── jozsa.jpg ├── swap.png ├── telep.jpeg ├── toffoli.png └── universal_irx.png /Notebooks/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnp-club/Quantum_Computing_Workshop_2020/20c287e220c000f2b00dbf87a103325498fa6263/Notebooks/.gitignore -------------------------------------------------------------------------------- /Notebooks/Day 0 Python Revision.ipynb: -------------------------------------------------------------------------------- 1 | {"nbformat":4,"nbformat_minor":0,"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.7.6"},"colab":{"name":"introduction.ipynb","provenance":[],"collapsed_sections":[]}},"cells":[{"cell_type":"markdown","metadata":{"id":"9WknV6SXYa5J","colab_type":"text"},"source":["# Introduction to Python and Jupyter notebooks\n","\n","This notebook just provides a small introduction to python which you will be requiring for this bootcamp. This is not a compulsory notebook and if you are already experienced with python you can jump right into the notebook for day 1.\n","\n","The main aim of this notebook is to familarize you with python syntax and also introduce the package numpy. If you are running this on colab you will not need any additional installations and this will also run if you are using Jupyter Anoconda. We do suggest using the IBM Q experience for future notebooks as you will require qiskit and its smoother to run it on IBM Q rather than all the installation. \n","To setup an IBM Q account refer https://quantum-computing.ibm.com/login. \n","For installing qiskit refer this https://qiskit.org/documentation/install.html\n","If you have any doubts, feel free to drop them on the telegram group.\n","You can find more support here https://quantum-computing.ibm.com/support"]},{"cell_type":"markdown","metadata":{"id":"akwjz2EcYa5K","colab_type":"text"},"source":["## Variables, types and declarations"]},{"cell_type":"code","metadata":{"id":"b5wehc6nYa5K","colab_type":"code","colab":{}},"source":["#Declaring variables\n","a = 2 \n","print(a)\n","print(type(a)) #This is an integer "],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"CdZgNOe9Ya5O","colab_type":"code","colab":{}},"source":["#True and False are special keywords in python\n","boolean = True\n","print(boolean)\n","print(type(boolean))"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"4FC2xjMdYa5Q","colab_type":"code","colab":{}},"source":["#Here 7e-2 is 7x10^-2 and the 1j actually is the square root of -1\n","b = 7e-2 + (8e-2)*1j\n","print(b)\n","print(type(b)) # This is a complex number"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"NqL0ltTYYa5S","colab_type":"code","colab":{}},"source":["#We will now try to re initiallize variable a and it will change its class\n","a = 7.2\n","print(a)\n","print(type(a)) #this is a float"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"C_t1yqZWYa5V","colab_type":"code","colab":{}},"source":["string = \"a word\"\n","print(string)\n","print(type(string))"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"KWkMTXM7Ya5X","colab_type":"code","colab":{}},"source":["#Type conversions can be done too\n","a_s = str(a)\n","bool3 = bool(a)\n","print(a_s)\n","print(type(a_s))\n","print(bool3)\n","print(type(bool3))"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"YirmSQWJYa5Z","colab_type":"code","colab":{}},"source":["#we also have lists, dictionaries and tuples\n","list1 = [1,2,3,4,5,6,\"word\"] # A list. Same as an array and is dynamic in length\n","dict1 = {1: 3, 'k':'f'} #A dictionary. This maps the first one as index to the second term as value\n","tuple1 = (1,2,4,5,0,-1,dict1) #A tuple. This is a collection of objects which get reordered are cannot be changed once assigned (immutable)\n","print(list1)\n","print(type(list1))\n","print(dict1)\n","print(type(dict1))\n","print(tuple1)\n","print(type(tuple1))"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"mntc2ZJxYa5b","colab_type":"text"},"source":["All of these are indexed exactly like arrays are but there is a special feature of python where it also takes negative indices and uses them to count backwards so list[-1] will have value \"a\". In dictionary the index has to be one of the terms present before the colon and that maps to the term after the colon and you will see how."]},{"cell_type":"code","metadata":{"id":"Q7ExVLTEYa5c","colab_type":"code","colab":{}},"source":["print(list1[-1])#Prints last index\n","print(dict1['k'])#Prints the mapped value to 'k'"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"e5HVIIYrYa5e","colab_type":"text"},"source":["A list of lists is essentially a 2 dimensional list and we can go on for as many dimensions as we like (as long as we dont run out of memory)."]},{"cell_type":"markdown","metadata":{"id":"d4BpiMaoYa5e","colab_type":"text"},"source":["## Conditional statements and loops\n","\n","We can use keywords like is, in and also and, or, not for analyzing the truth of a statement. The is tells us if two values are the same. The in tells us if a certain value is present in the other (like whether there is a certain value present in a list). The and, or, not are simply the bitwise operators of AND, OR, NOT."]},{"cell_type":"code","metadata":{"id":"d5OX87cEYa5f","colab_type":"code","colab":{}},"source":["#Using is\n","var1 = 78\n","tuple2 = (1,2,4,5,0,-1,dict1)\n","print(var1 is 78)#This will be true\n","print(tuple1 is tuple2)#This will be false since tuples,lists and dicts essentially are addresses\n"," #Hence even when they have same value, is requires also same addresses\n","print(tuple1 == tuple2)#This shows true since == checks equality in values\n","print(tuple1[-1] is tuple1[-1])#This is true since these both point to the same variable"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"n5Gh1ysTYa5h","colab_type":"code","colab":{}},"source":["var2 = 1\n","print(var2 in list1)#Shows true since var2 is infact in this list"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"JEFGVWwRYa5j","colab_type":"code","colab":{}},"source":["bool1 = True\n","bool2 = False\n","print(bool1 and bool2)\n","print(bool1 or bool2)\n","print(not bool2)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"RSTEawk_Ya5l","colab_type":"text"},"source":["if statements do a task if a certain statement is true and the following elif executes if thi statement wasnt true but another mentiones one is. If none of the statements come true the it will go to else."]},{"cell_type":"code","metadata":{"id":"5IsiruFwYa5m","colab_type":"code","colab":{}},"source":["if bool1:\n"," print('bool1 is true')#This one is executed as per our declarations \n","elif not bool2:\n"," print('bool2 is false and so is bool1')#This one isnt reached\n","else:\n"," print('bool1 is false but bool2 is true')#This one isnt reached"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"oaOO2hIdYa5o","colab_type":"text"},"source":["For looping we have the for loop which runs for a fixed number of iterations unless break is not mentioned anywhere. It iterates over a variable from a range or even from a list. There also is the while loop which executes as long as a certain statement is true."]},{"cell_type":"code","metadata":{"id":"dEJpv580Ya5o","colab_type":"code","colab":{}},"source":["for i in list1:\n"," print(i)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"-Wt9171aYa5q","colab_type":"code","colab":{}},"source":["for i in range(10):#iterates over numbers 0 to 9 both included\n"," print(i)\n","print(\" \")\n","for i in range(1,10):#iterates over numbers 1 to 9 both included\n"," print(i)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"Pt44rlt4Ya5s","colab_type":"code","colab":{}},"source":["for k,d in dict1.items():\n"," print(k,d)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"T8bYxIolYa5u","colab_type":"code","colab":{}},"source":["g = 100\n","while(g > 0):\n"," g = g//2#This returns the integer division of g\n"," print(g)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"_Nc1cjHJYa5w","colab_type":"text"},"source":["## Defining functions and using packages\n","\n","Functions take arguments, do a task and may or may not return a value. Functions help in keeping your code clean so its always better to use them as much as required.\n","\n","In this bootcamp you will be using the qiskit package which has various classes and functions in them which help us emulate a quantum computer and there are also ways by which we can call a backend to use one of their actual computers for an experiment or circuit that we have constructed. You will see this all in detail soon.\n","\n","One of the most important and useful package you wil be using is the numpy package."]},{"cell_type":"code","metadata":{"id":"KajSLwGvYa5x","colab_type":"code","colab":{}},"source":["def myfunc(a):#takes an input of a\n"," return a*2 #returns the double of a\n","\n","print(myfunc(2))#Calls function giving 2 as input\n","print(myfunc(3.4 + 4j))\n","print(myfunc(list1))"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"C9FJ4WpMYa5z","colab_type":"code","colab":{}},"source":["def myfunc2(*args):# we use *args when we do not know how many arguments will be sent\n"," s = 0\n"," for i in args:\n"," s = s + i\n"," return s\n","print(myfunc2(34))\n","print(myfunc2(34,35,36))"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"DWlODhoKYa51","colab_type":"code","colab":{}},"source":["def myfunc3(**kwargs):#we use **kwargs when we are to receive unkown number of keyword arguments\n"," #A keyword argument is where you provide a name to the variable as you pass it into the function.\n"," for i,j in kwargs.items():\n"," print(i,j)\n","myfunc3(x1 = 1, x2 = \"abcd\", x3 = list1)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"ibih8A3yYa53","colab_type":"text"},"source":["A lambda function is a small anonymous function.\n","A lambda function can take any number of arguments, but can only have one expression.\n","This makes them very powerful tools by themselves."]},{"cell_type":"code","metadata":{"id":"p7Thuu7WYa53","colab_type":"code","colab":{}},"source":["add = lambda a,b: a+b #A lamda function takes arguments and returns only one value\n","\n","print(add(list1,list1)) #essentially adds the list to itself"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"8iSgzvaeYa55","colab_type":"text"},"source":["We can import numpy using this"]},{"cell_type":"code","metadata":{"id":"aj_lDo0QYa55","colab_type":"code","colab":{}},"source":["import numpy as np #imports the package numpy and we have renamed it to np so our code looks cleaner\n","print(np.pi)#Good old pi\n","arr = np.array([[1,3],[4,2]])#Essentially an array and the argument given\n","print(arr)\n","print(arr.shape)#this gives the shape of the arr"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"sTXufaz1Ya57","colab_type":"code","colab":{}},"source":["arr2 = np.array([[0.3,8j],[9j,0]])\n","arr3 = np.matmul(arr2,arr)#this will give the matrix multiplication of arr2*arr\n","print(arr3)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"d30y3d6TYa5-","colab_type":"text"},"source":["You can find a really great python cheatsheet here for additional reference. Also do visit https://numpy.org/ where you can find a very good documentation of everything that you may need and also along with scipy package you can do as much linear algebra as you may need to using python."]}]} -------------------------------------------------------------------------------- /Notebooks/Day 1 Introduction to Quantum Circuits.ipynb: -------------------------------------------------------------------------------- 1 | {"nbformat":4,"nbformat_minor":0,"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.7.6"},"colab":{"name":"Day 1 Introduction to Quantum Circuits.ipynb","provenance":[],"collapsed_sections":[]}},"cells":[{"cell_type":"markdown","metadata":{"id":"DIDHS0IRFh4D","colab_type":"text"},"source":["# Introduction to quantum circuits"]},{"cell_type":"code","metadata":{"id":"L4-vwTslFh4E","colab_type":"code","colab":{}},"source":["from qiskit import *\n","from qiskit.tools.jupyter import *\n","from qiskit.visualization import *\n","import matplotlib.pyplot as plotter\n","import numpy as np\n","from IPython.display import display, Math, Latex\n","%matplotlib inline\n","# Loading your IBM Q account(s)\n","provider = IBMQ.load_account()"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"RHC3kxuKFh4I","colab_type":"text"},"source":["\n","Today we will learn some basic elements of qunatum computing which make it so special. You can brush up your [linear algebra](https://link.springer.com/content/pdf/bbm%3A978-1-4614-6336-8%2F1.pdf) (Only some initial sections from this link are revelant here) before seeing how it actually describes our physical states.\n","\n","*Quantum mechanics: Real Black Magic Calculus* \n","– Albert Einstein\n","\n","It does feel amazing to imagine how the quantum properties such as **quantum superposition** and **entanglement** work in real life. Though mathematically, it can be expressed very easily with the help of linear algebra. Like the famous $|dead\\rangle$ and $|alive\\rangle$ states of Schrödinger's cat can be used to describe the state of the cat $|\\psi\\rangle$ as it would be a linear combination of $|dead\\rangle$ and $|alive\\rangle$.\n","$$ |\\psi\\rangle = \\alpha|alive\\rangle + \\beta |dead\\rangle$$\n","where $|\\alpha|^2$ is the probability of it being alive and $|\\beta|^2$ of it being dead. This implies that $|\\beta|^2 + |\\alpha|^2 = 1$ \n","This is the same way a \"quantum bit\" or Qubit is described; but here the possible state we can measure (and we do it a lot) are the computational basis states i.e. $|0\\rangle$ and $|1\\rangle$ used in classical computation.\n","$$ |\\psi\\rangle = \\alpha|0\\rangle + \\beta |1\\rangle$$\n","where $|\\psi\\rangle$ is the state vector of the qubit. Here $\\alpha,\\beta \\in \\mathbb{C}$ and $|\\beta|^2 + |\\alpha|^2 = 1$. We can express a qubit on a **Bloch sphere** which gives us a nice idea of how we can visualize a qubit. Vectors to each pole (+z and -z) of the sphere represent the computational basis states. The state vector can lie anywhere on the Bloch sphere. \n","Here are some examples:"]},{"cell_type":"code","metadata":{"id":"x_ESkmGyFh4I","colab_type":"code","colab":{}},"source":["# The '0' state\n","plot_bloch_vector([0,0,1])"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"ROTLBzIMFh4K","colab_type":"code","colab":{}},"source":["# The '1' state\n","plot_bloch_vector([0,0,-1])"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"PZVnQmEzFh4N","colab_type":"text"},"source":["We represent the state vector as $\\left[ \\begin{matrix} \\alpha \\\\ \\beta \\end{matrix} \\right]$ and so we can think of an operation on the qubit (vector) by a quantum gate (matrix) analogous to logic gates in classical bits."]},{"cell_type":"code","metadata":{"id":"jcrtaoqxFh4O","colab_type":"code","colab":{}},"source":["qc1 = QuantumCircuit(2,2) # Initializing a quantum ciruit (2 qubits, 2 classical bits) \n","# All initialized to '0' by default.\n","qc1.draw(output='mpl') # Draws the circuit diagram"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"El_TyIVfFh4R","colab_type":"text"},"source":["The only contraint for a matrix to be a quantum gate is the it should be **unitary** which derives from the fact that the operated qubit also follows $|\\beta'|^2 + |\\alpha'|^2 = 1$ \n","For example $X = \\left[\\begin{matrix}\n","0 & 1\\\\1 & 0 \n","\\end{matrix}\\right]$ changes $\\alpha$ to $\\beta$ and $\\beta$ to $\\alpha$, which is similar to the classical NOT gate. We will first see some matrices (operations) which are already defined so we can directly used them."]},{"cell_type":"code","metadata":{"id":"tS2fI5AjFh4R","colab_type":"code","colab":{}},"source":["qc1.x(0) # Applying X gate to the first qubit\n","qc1.draw(output='mpl')\n"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"OG6umTEAFh4T","colab_type":"code","colab":{}},"source":["qc1.x(1) # Applying X gate to the second qubit\n","qc1.draw(output='mpl')"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"A6mQm5hGFh4V","colab_type":"text"},"source":["The X matrix we saw above is a part of an extremely important group of matrices called **Pauli matrices**.\n","Along with Pauli-X matrix we have Pauli-Y $= \\left[\\begin{matrix}0 & -\\iota\\\\ \\iota & 0 \\end{matrix}\\right]$ and Pauli-Z $ = \\left[\\begin{matrix}1 & 0\\\\0 & -1 \\end{matrix}\\right]$ and the Identity matrix $I$.\n"]},{"cell_type":"markdown","metadata":{"id":"9pDhPH2MFh4W","colab_type":"text"},"source":["Other useful gate is the Hadamard Gate\n","$H: H = \\frac{1}{\\sqrt{2}}\\left[\\begin{matrix} 1 & 1\\\\ 1 & -1\n","\\end{matrix}\\right]$\n","which makes the following operations:\n","$|0\\rangle \\to \\frac{|0\\rangle+|1\\rangle}{\\sqrt{2}}$ (commonly referred as $|+\\rangle$) and $|1\\rangle \\to \\frac{|0\\rangle-|1\\rangle}{\\sqrt{2}}$ (commonly referred as $|-\\rangle$). Also notice that $H^2 = I$.\n"]},{"cell_type":"code","metadata":{"id":"v4EXMmXqFh4W","colab_type":"code","colab":{}},"source":["# The '+' state\n","plot_bloch_vector([1,0,0])"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"gW4NT-VOFh4a","colab_type":"code","colab":{}},"source":["# The '-' state\n","plot_bloch_vector([-1,0,0])"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"54zS6PfiFh4c","colab_type":"code","colab":{}},"source":["qc2 = QuantumCircuit(2,2) # A new quantum circuit with 2 qubits and 2 classical bits \n","qc2.h(0) # Applying the Hadamard gate on first qubit\n","qc2.draw(output='mpl')"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"wlUcU9FkFh4f","colab_type":"text"},"source":["We are familiar with the 'if x then y' logic in our daily lives. For any 2 objects, this logic is useful as it describe their dependance on each other. So it is intuitive to think of a 2 qubit gate where in the fate of the second qubit is controlled by the first. We call these 'controlled operation' where a certain gate is applied to one qubit if the other qubit is in a particular state ( usually we consider when the $|1\\rangle$ state). \n","CX (CNOT) gate applies X gate to the 'target qubit' is the 'control qubit' is $|1\\rangle$. We call it controlled - X or controlled - NOT operation. It changes the state $|ab\\rangle$ to $|a (a\\oplus b)\\rangle$\n","\n"]},{"cell_type":"code","metadata":{"id":"Vr4QoOn9Fh4f","colab_type":"code","colab":{}},"source":["qc2.cx(0,1) # Applying CX gate ('control','target')\n","qc2.draw(output='mpl')"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"JAmmMVLTFh4j","colab_type":"text"},"source":["Now we are about to see a great example of \n"," \n"," \n","\n","\n","Quantum entanglement is a physical phenomenon in which the quantum state of a particle cannot be described independently of the state of the other. Which means that measuring one particle automatically reveals the state of other. This strange phenomenon is captured beautifully in qubits. For example if we could create a state which has equal probability of two qubits, either both $|0\\rangle$ or both $|1\\rangle$ or in other words $|\\psi\\rangle = \\frac{|00\\rangle + |11\\rangle }{\\sqrt{2}}$. Surprisingly, we already have such a structure! If you look carefully at qc2 and work out the opearations on pen and paper taking $q_0=q_1=0$, we will get the $|\\psi\\rangle$ mentioned above. We can measure the qubits in qc2 to verify this."]},{"cell_type":"code","metadata":{"id":"mjZjBSNAFh4k","colab_type":"code","colab":{}},"source":["qc2.measure(0,0) # Measure first qubit and store it in first classical bit \n","qc2.measure(1,1) # Measure second qubit and store it in second classical bit\n","# The code below plots a histogram of the measurement result. You can copy it for further use.\n","def run_circuit(qc):\n"," backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend\n"," result = execute(qc, backend, shots = 2000).result() # we run the simulation\n"," counts = result.get_counts() # we get the counts\n"," return counts\n","\n","counts = run_circuit(qc2)\n","print(counts)\n","plot_histogram(counts)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"K1XvrepTFh4n","colab_type":"text"},"source":["But there is no unique thing about choosing the initial qubits as $|0\\rangle$. For the 4 possible intial states $|00\\rangle,|01\\rangle,|10\\rangle,|11\\rangle$ we get 4 states, each of them show this property of entanglement. These are called **Bell states** or **EPR pairs** and as we saw they are formed after applying Hadamard gate to the first qubit and a CX gate keeping first qubit as control and second as target."]},{"cell_type":"markdown","metadata":{"id":"P2-ZsOZOFh4n","colab_type":"text"},"source":["## Your task\n","\n","1) Create a bell state such that the 2 qubits are always in opposite states after measurement.\n","Use the already developed qc1 earlier. Verify it by plotting a histogram. (**1 point**)"]},{"cell_type":"code","metadata":{"id":"aknCJW5gFh4o","colab_type":"code","colab":{}},"source":["#\n","#\n","# Your code here\n","#\n","#"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"mJEGBObLFh4q","colab_type":"text"},"source":["The **Toffoli gate** (also called controlled-controlled-not) can be considered similar to the CNOT gate but instead of 1 control qubit, we have 2 of them. It changes the state $|abc\\rangle$ to $|ab(a\\cdot b\\oplus c)\\rangle$. This gate is very useful and it can be used classically for all operations (universal). We can prove it by making a NAND gate from it by setting the variables as the control bits and '1' as target bit. Rest is simple boolean algebra.\n","\n"]},{"cell_type":"code","metadata":{"id":"c1kxbEWeFh4r","colab_type":"code","colab":{}},"source":["qc3 = QuantumCircuit(3,3)\n","qc3.x(range(3)) # Setting the all qubits to '1'\n","qc3.toffoli(0,1,2) # (control,control,target)\n","qc3.measure(0,0)\n","qc3.measure(1,1)\n","qc3.measure(2,2)\n","def run_circuit(qc3): \n"," backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend\n"," result = execute(qc3, backend, shots = 2000).result() # we run the simulation\n"," counts = result.get_counts() # we get the counts\n"," return counts\n","\n","counts = run_circuit(qc3)\n","print(counts)\n","plot_histogram(counts)\n","# The output should be '011' as the highest (q2) is flipped because the other two qubits were set to '1'"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"41qNX7v2Qw3U","colab_type":"text"},"source":["Some of the other important gates which you would appreciate later are the phase gate $S = \\left[\\begin{matrix} 1 & 0\\\\ 0 & \\iota\n","\\end{matrix}\\right]$ which changes the phase of the amplitude of $|1\\rangle$ and the $\\frac{\\pi}{8}$ gate $ T = \\left[\\begin{matrix} 1 & 0\\\\ 0 & e^{\\frac{\\iota \\pi}{4}}\n","\\end{matrix}\\right]$. We can see that $S=T^2$. \n","The beauty of quantum gates is the as they are unitary, their operation can be inverted just by applying their conjugate transpose gate.\n","$S^\\dagger S = \\left[\\begin{matrix} 1 & 0\\\\ 0 & -\\iota\n","\\end{matrix}\\right]\\left[\\begin{matrix} 1 & 0\\\\ 0 & \\iota\n","\\end{matrix}\\right] = I.$"]},{"cell_type":"markdown","metadata":{"id":"c32Zs1udFh4t","colab_type":"text"},"source":["## Your task\n","\n","2) Implement the swap operation as shown below and verify using histogram.\n","(**1 point**)\n","\n",""]},{"cell_type":"code","metadata":{"id":"2MyQFzL8Fh4u","colab_type":"code","colab":{}},"source":["qc4 = QuantumCircuit(2,2)\n","qc4.x() # Set any one qubit to '1' to see a noticable difference between input and output.\n","#\n","#\n","# Your code here\n","#\n","#"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"JH0DxLvXFh4x","colab_type":"text"},"source":["3) If you look closely, you will find that $HYH = -Y$ by simple matrix multiplication. Similarly prove that $HZH = X$ by applying those matrices on the left hand side to 2 qubits, set to $|0\\rangle$ and $|1\\rangle$ (as we can't determine a single qubit operation with only one measurement) and measuring it by plotting on a histogram. (**2 points**)\n"]},{"cell_type":"code","metadata":{"id":"4HE-xomUFh4z","colab_type":"code","colab":{}},"source":["qc5 = QuantumCircuit(2,2)\n","#\n","#\n","# Your code here\n","#\n","#"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"Z4WnxG6jFh41","colab_type":"text"},"source":["4) Create a circuit in which 2 qubits are swapped if the third qubit is in the state $\\frac{1}{\\sqrt{2}}|0\\rangle - \\frac{1 + \\iota}{2}|1\\rangle$. You can use any the gates we learned today.(**3 points**) \n","Hint: Try to think of this as a kind of controlled operation."]},{"cell_type":"code","metadata":{"id":"0HFyqc7xFh42","colab_type":"code","colab":{}},"source":["qc6 = QuantumCircuit(3,3)\n","qc6.initialize() # Set any one qubit of the 2 swapping qubits to '1' to see a noticable difference between input and output.\n","#\n","#\n","# Your code here\n","#\n","#\n","#"],"execution_count":null,"outputs":[]}]} -------------------------------------------------------------------------------- /Notebooks/Day 2.1 Quantum Circuits Continued.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "urE3_KHdCEpD" 8 | }, 9 | "source": [ 10 | "# Quantum circuits continued: Rotation operators, decompositions, controlled operations and more!" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "metadata": { 17 | "colab": {}, 18 | "colab_type": "code", 19 | "id": "ewxZplcrCEpE" 20 | }, 21 | "outputs": [], 22 | "source": [ 23 | "from qiskit import *\n", 24 | "from qiskit.compiler import transpile, assemble\n", 25 | "from qiskit.tools.jupyter import *\n", 26 | "from qiskit.visualization import *\n", 27 | "import matplotlib.pyplot as plotter\n", 28 | "import numpy as np\n", 29 | "from IPython.display import display, Math, Latex\n", 30 | "%matplotlib inline\n", 31 | "# Loading your IBM Q account(s)\n", 32 | "provider = IBMQ.load_account()" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": { 38 | "colab_type": "text", 39 | "id": "pOADMNPSCEpH" 40 | }, 41 | "source": [ 42 | "Congrats on completing Day 1! Let's start Day 2 by getting familiarised with a quantum gate, which you unknowingly utilised while solving yesterday's last question.\n", 43 | "\n", 44 | "## The Fredkin gate\n", 45 | "\n", 46 | "The Fredkin gate is the controlled swap gate, where the first qubit acts as the control qubit, meaning, it remains unchanged itself but swaps the other two \"target\" qubits if and only if the control qubit is set to $\\vert 1 \\rangle$." 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": { 53 | "colab": {}, 54 | "colab_type": "code", 55 | "id": "vv6UF-8HCEpI" 56 | }, 57 | "outputs": [], 58 | "source": [ 59 | "qc1 = QuantumCircuit(3,3)\n", 60 | "# All initialized to '0' by default.\n", 61 | "qc1.x(0) #This is for the purpose of setting the control qubit to '1'\n", 62 | "qc1.x(2) #As a result, the second target qubit becomes '1' while the first remains '0'. Now, lets's try swapping them.\n", 63 | "#Fredkin gate:\n", 64 | "def fredkin(qc):\n", 65 | " qc.toffoli(0,1,2)\n", 66 | " qc.toffoli(0,2,1)\n", 67 | " qc.toffoli(0,1,2)\n", 68 | "fredkin(qc1)\n", 69 | "qc1.draw('mpl')\n" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": { 75 | "colab_type": "text", 76 | "id": "3JvJj814CEpK" 77 | }, 78 | "source": [ 79 | "Now, let's observe the result obtained on measuring all three qubits by using a histogram." 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": null, 85 | "metadata": { 86 | "colab": {}, 87 | "colab_type": "code", 88 | "id": "5k0qT9P_CEpK" 89 | }, 90 | "outputs": [], 91 | "source": [ 92 | "#First let's measure all three qubits.\n", 93 | "#We're using the classical bits to store the result obtained on measuring each corresponding qubit. \n", 94 | "qc1.measure(0,0)\n", 95 | "qc1.measure(1,1)\n", 96 | "qc1.measure(2,2)\n", 97 | "#Now we use the same function we defined yesterday to run a quantum circuit\n", 98 | "def run_circuit(qc2):\n", 99 | " backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend\n", 100 | " result = execute(qc2, backend, shots = 2000).result() # we run the simulation\n", 101 | " counts = result.get_counts() # we get the counts\n", 102 | " return counts\n", 103 | "counts1=run_circuit(qc1)\n", 104 | "print(counts1)\n", 105 | "plot_histogram(counts1)\n" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "metadata": { 111 | "colab_type": "text", 112 | "id": "V0tqWk6_CEpN" 113 | }, 114 | "source": [ 115 | "Surprised to see the result to be $\\vert 011 \\rangle $ when it should be $\\vert 110 \\rangle $? Don't be. While representing the state of a multi-qubit system, the tensor order used in Qiskit is different than that used in most physics textbooks. Suppose there are $n$ qubits, and qubit $j$ is labelled as $Q_j$. Qiskit uses an ordering in which the $n$th qubit is on the left side of the tensor product, so that the basis vectors are labelled as $Q_{n-1}\\otimes \\cdots \\otimes Q_1\\otimes Q_0$.\n", 116 | "\n", 117 | "For example, if qubit zero is in state 1, qubit 1 is in state 1, and qubit 2 is in state 0, Qiskit would represent this state as $\\vert 011 \\rangle $, whereas many physics textbooks would represent it as $\\vert 110 \\rangle $.\n", 118 | "\n", 119 | "To observe the effect of the Fredkin gate, let's again make a similar circuit, this time initialising the control qubit to $\\vert 0 \\rangle$." 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": null, 125 | "metadata": { 126 | "colab": {}, 127 | "colab_type": "code", 128 | "id": "2a3h7krmCEpO" 129 | }, 130 | "outputs": [], 131 | "source": [ 132 | "qc2 = QuantumCircuit(3,3)\n", 133 | "# All initialized to '0' by default.\n", 134 | "qc2.x(2) #The second target qubit is initialised to '1'\n", 135 | "fredkin(qc2)\n", 136 | "qc2.measure(0,0)\n", 137 | "qc2.measure(1,1)\n", 138 | "qc2.measure(2,2)\n", 139 | "qc2.draw('mpl')\n" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": null, 145 | "metadata": { 146 | "colab": {}, 147 | "colab_type": "code", 148 | "id": "kTXAhz_0CEpQ" 149 | }, 150 | "outputs": [], 151 | "source": [ 152 | "counts2=run_circuit(qc2)\n", 153 | "print(counts2)\n", 154 | "plot_histogram(counts2)" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "metadata": { 160 | "colab_type": "text", 161 | "id": "T4xySn3BCEpS" 162 | }, 163 | "source": [ 164 | "This time the control qubit remains the same like last time, but so do the target qubits, because the control qubit was set to $\\vert 0 \\rangle$." 165 | ] 166 | }, 167 | { 168 | "cell_type": "markdown", 169 | "metadata": { 170 | "colab_type": "text", 171 | "id": "Ul6GSapECEpS" 172 | }, 173 | "source": [ 174 | "## The Rotation Operators\n", 175 | "\n", 176 | "To bring about rotations in the Bloch sphere, we have the rotation operators where we can specify the angle of rotation about each axis. These operators are defined for each Pauli matrix $P=\\{ X, Y, Z \\}$ as follows $$R_P(\\theta)\\equiv e^\\frac{-i\\theta P}{2}=\\cos\\frac{\\theta}{2}I-i\\sin\\frac{\\theta}{2}P$$\n", 177 | "\n", 178 | "Hence, the following are the Rotation operators about the $\\hat{x}, \\hat{y}$ and $\\hat{z}$ axes:\n", 179 | "\n", 180 | "$$R_x(\\theta)=\\begin{bmatrix}\n", 181 | "\\cos\\frac{\\theta}{2} & -i\\sin\\frac{\\theta}{2}\\\\\n", 182 | "-i\\sin\\frac{\\theta}{2} & \\cos\\frac{\\theta}{2}\n", 183 | "\\end{bmatrix} , \n", 184 | "R_y(\\theta)=\\begin{bmatrix}\n", 185 | "\\cos\\frac{\\theta}{2} & -\\sin\\frac{\\theta}{2}\\\\\n", 186 | "\\sin\\frac{\\theta}{2} & \\cos\\frac{\\theta}{2}\n", 187 | "\\end{bmatrix} , \n", 188 | "R_z(\\theta)=\\begin{bmatrix}\n", 189 | "e^\\frac{-i\\theta}{2} & 0\\\\\n", 190 | "0 & e^\\frac{i\\theta}{2}\n", 191 | "\\end{bmatrix}$$ \n", 192 | "\n", 193 | "If we want to bring about a rotation around an arbitrary axis, say, $\\hat{n} =(n_x,n_y,n_z)$ which is a real unit vector, then we define a rotation by an angle $\\theta$ about the $\\hat{n}$ axis by the operator\n", 194 | "$$R_{\\hat{n}}(\\theta)\\equiv \\exp(-i\\theta \\hat{n}\\cdot \\vec{\\sigma}/2)=\\cos\\left(\\frac{\\theta}{2}\\right)I-i\\sin\\left(\\frac{\\theta}{2}\\right)(n_x X+n_y Y+n_z Z)$$\n", 195 | "where $\\vec{\\sigma}$ denotes the three component vector $(X,Y,Z)$ of Pauli matrices.\n" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": null, 201 | "metadata": { 202 | "colab": {}, 203 | "colab_type": "code", 204 | "id": "cDu6MGTUCEpU" 205 | }, 206 | "outputs": [], 207 | "source": [ 208 | "qc = QuantumCircuit(1)\n", 209 | "#This is how we apply the rotation operators in Qiskit, mentioning the angle of rotation and qubit no. as parameters\n", 210 | "qc.rx(np.pi/2,0) \n", 211 | "qc.draw('mpl')\n" 212 | ] 213 | }, 214 | { 215 | "cell_type": "code", 216 | "execution_count": null, 217 | "metadata": { 218 | "colab": {}, 219 | "colab_type": "code", 220 | "id": "9H5OY1SNCEpX" 221 | }, 222 | "outputs": [], 223 | "source": [ 224 | "def final_vector(qc):\n", 225 | " backend = Aer.get_backend('statevector_simulator')\n", 226 | " job = execute(qc, backend)\n", 227 | " result = job.result()\n", 228 | " outputstate = result.get_statevector(qc, decimals=3)\n", 229 | " return outputstate\n", 230 | "print(final_vector(qc))\n", 231 | "# This prints the vector obtained on applying the above gate to the qubit state '0'" 232 | ] 233 | }, 234 | { 235 | "cell_type": "markdown", 236 | "metadata": { 237 | "colab_type": "text", 238 | "id": "-y1XNzfOCEpZ" 239 | }, 240 | "source": [ 241 | "## **Your Task:**\n", 242 | "1) Find a gate, say '$Q$', such that for any angle $\\theta$, $$QR_y(\\theta)Q=R_y(-\\theta).$$ (**2 points**)" 243 | ] 244 | }, 245 | { 246 | "cell_type": "code", 247 | "execution_count": null, 248 | "metadata": { 249 | "colab": {}, 250 | "colab_type": "code", 251 | "id": "0J9VyxXXCEpa" 252 | }, 253 | "outputs": [], 254 | "source": [ 255 | "qc3=QuantumCircuit(1)\n", 256 | "#your code here\n", 257 | "\n", 258 | "qc3.ry(#theta,0) #Enter an angle of your choice\n", 259 | "\n", 260 | "#enter code for your gate again\n", 261 | "\n", 262 | "print(final_vector(qc3))\n", 263 | "\n", 264 | "\n", 265 | "qc3=QuantumCircuit(1)\n", 266 | "qc3.ry(-#theta,0)\n", 267 | "print(final_vector(qc3))\n", 268 | "#Run this code for different values of theta and see if the two vectors printed are equal in each case" 269 | ] 270 | }, 271 | { 272 | "cell_type": "markdown", 273 | "metadata": { 274 | "colab_type": "text", 275 | "id": "hyon_FBiCEpc" 276 | }, 277 | "source": [ 278 | "The significance of the Rotation operators is further highlighted when we understand that any unitary operation for a single qubit can be decomposed into rotations about two of the coordinate axes by some angle each, along with a global phase factor, which is what's stated in the following theorem.\n", 279 | "\n", 280 | "### **Theorem:** $Z-Y$ decomposition for a single qubit\n", 281 | "**Suppose $U$ is a unitary operation on a single qubit. Then there exist real numbers $\\alpha, \\beta, \\gamma$ and $\\delta$ such that\n", 282 | "$$U=e^{i\\alpha}R_z(\\beta)R_y(\\gamma)R_z(\\delta).$$**\n", 283 | " **Proof:**\n", 284 | " Since $U$ is unitary, its rows and columns are orthonormal, so it can be expressed as follows\n", 285 | " $$U=\\begin{bmatrix}\n", 286 | "e^{i(\\alpha-\\beta/2-\\delta/2)}\\cos\\frac{\\gamma}{2} & -e^{i(\\alpha-\\beta/2+\\delta/2)}\\sin\\frac{\\gamma}{2}\\\\\n", 287 | "e^{i(\\alpha+\\beta/2-\\delta/2)}\\sin\\frac{\\gamma}{2} & e^{i(\\alpha+\\beta/2+\\delta/2)}\\cos\\frac{\\gamma}{2}\n", 288 | "\\end{bmatrix}$$\n", 289 | "By multiplying the matrices mentioned in the theorem, we obtain the exact form for $U$ as above.\n", 290 | "\n", 291 | "\n", 292 | "\n", 293 | "### **Corollary:**\n", 294 | "**Suppose $U$ is a unitary gate on a single qubit. Then there exist unitary operators $A,B,C$ on a single qubit such that $ABC = I$ and $U = e^{i\\alpha}AXBXC$, where $\\alpha$ is some overall phase factor.**\n", 295 | "\n", 296 | "**Proof:**\n", 297 | "Set $$A=R_z(\\beta)R_y(\\gamma/2) , B=R_y(-\\gamma/2)R_z(-(\\delta+\\beta)/2) , C=R_z((\\delta-\\beta)/2).$$\n", 298 | "This assignment satisfies all the identities mentioned in the corollary.\n", 299 | "\n" 300 | ] 301 | }, 302 | { 303 | "cell_type": "markdown", 304 | "metadata": { 305 | "colab_type": "text", 306 | "id": "uolK2oUJCEpc" 307 | }, 308 | "source": [ 309 | "## Your Task\n", 310 | "2) Find out $A, B, C$, and $\\alpha$ for the Hadamard gate and show that both representations are identical, by printing the final vectors on applying both transformations on the same initial state. (**2 points**)" 311 | ] 312 | }, 313 | { 314 | "cell_type": "code", 315 | "execution_count": null, 316 | "metadata": { 317 | "colab": {}, 318 | "colab_type": "code", 319 | "id": "pe34KpZ8CEpd" 320 | }, 321 | "outputs": [], 322 | "source": [ 323 | "#\n", 324 | "#\n", 325 | "#Your code for the decomposition here\n", 326 | "#\n", 327 | "#\n", 328 | "print(final_vector())\n", 329 | "\n", 330 | "# Apply the Hadamard gate and see if both operations are identical\n", 331 | "#\n", 332 | "print(final_vector())" 333 | ] 334 | }, 335 | { 336 | "cell_type": "markdown", 337 | "metadata": { 338 | "colab_type": "text", 339 | "id": "Mi9xauFDCEpf" 340 | }, 341 | "source": [ 342 | "## Controlled operations\n", 343 | "We will now see how to implement the controlled-$U$ operation for an arbitrary single qubit operation $U$, using only single qubit operations and the CNOT gate. \n", 344 | "\n", 345 | "A controlled $U$ operation is a two qubit operation, again with a control and a target qubit. If the control qubit is set to $\\vert 1 \\rangle$, then $U$ is applied to the target qubit, otherwise the target qubit is left alone.\n", 346 | "\n", 347 | "To achieve our goal, we first decompose it as done in the above corollary. The following is the circuit for implementing the controlled-$U$ operation.\n", 348 | "\n", 349 | "\n", 350 | "\n", 351 | "We now explain how the above circuit works. Suppose that the control qubit is set. Then the operation $U = e^{i\\alpha}AXBXC$ is applied to the second qubit. If, on the other hand, the control qubit is not set, then the operation $ABC = I$ is applied to the second qubit; that is, no change is made. Hence, this circuit implements the controlled-$U$ operation. \n" 352 | ] 353 | }, 354 | { 355 | "cell_type": "markdown", 356 | "metadata": { 357 | "colab_type": "text", 358 | "id": "gkNFupmaCEpf" 359 | }, 360 | "source": [ 361 | "## Your Task\n", 362 | "3) Using the decomposition from the second task, your work here is to simply type out the code for implementing the controlled Hadamard gate using only single qubit and CNOT gates. Plot a histogram for the result obtained after applying your gate to two qubits initialised to $\\vert 1\\rangle$ and $\\vert 0\\rangle$ respectively. (You should also change the control qubit to '0' and observe the result obtained in that case.) (**2 points**) " 363 | ] 364 | }, 365 | { 366 | "cell_type": "code", 367 | "execution_count": null, 368 | "metadata": { 369 | "colab": {}, 370 | "colab_type": "code", 371 | "id": "pzg74J2aCEpf" 372 | }, 373 | "outputs": [], 374 | "source": [ 375 | "#\n", 376 | "#\n", 377 | "#Your code here\n", 378 | "#\n", 379 | "#" 380 | ] 381 | }, 382 | { 383 | "cell_type": "markdown", 384 | "metadata": { 385 | "colab_type": "text", 386 | "id": "QbzDzB_rCEpi" 387 | }, 388 | "source": [ 389 | "## U3 gate\n", 390 | "U3 gate is a single qubit rotation gate, whose function takes the parameters ($\\theta, \\phi, \\lambda$, qubit). The matrix representation of the gate is:\n", 391 | "$$U3(\\theta, \\phi, \\lambda)=\\begin{bmatrix}\n", 392 | "\\cos\\frac{\\theta}{2} & -e^{i\\lambda}\\sin\\frac{\\theta}{2}\\\\\n", 393 | "e^{i\\phi}\\sin\\frac{\\theta}{2} & e^{i(\\phi+\\lambda)}\\cos\\frac{\\theta}{2}\n", 394 | "\\end{bmatrix}$$\n", 395 | "\n", 396 | "For example, we observe that $U3(\\theta, -\\pi/2, \\pi/2)=R_x(\\theta)$.\n", 397 | "\n", 398 | "Let us verify this by applying both gates on a state vector for $\\theta=\\pi/6$." 399 | ] 400 | }, 401 | { 402 | "cell_type": "code", 403 | "execution_count": null, 404 | "metadata": { 405 | "colab": {}, 406 | "colab_type": "code", 407 | "id": "zbP_aIKwCEpi" 408 | }, 409 | "outputs": [], 410 | "source": [ 411 | "qc_u3=QuantumCircuit(1)\n", 412 | "qc_u3.u3(np.pi/6,-np.pi/2,np.pi/2,0)\n", 413 | "print(final_vector(qc_u3))\n", 414 | "\n", 415 | "qc_rx=QuantumCircuit(1)\n", 416 | "qc_rx.rx(np.pi/6,0)\n", 417 | "print(final_vector(qc_rx))\n", 418 | "#Getting the same results will verify our observation stated above" 419 | ] 420 | }, 421 | { 422 | "cell_type": "markdown", 423 | "metadata": { 424 | "colab_type": "text", 425 | "id": "FHFvscfVCEpl" 426 | }, 427 | "source": [ 428 | "### Conditioning on multiple qubits\n", 429 | "Suppose we have $n + k$ qubits, and $U$ is a $k$ qubit unitary operator. Then we define the controlled operation $C^n(U)$ by the equation $$C^n(U)\\vert x_1x_2\\dots x_n\\rangle \\vert\\psi\\rangle=\\vert x_1x_2\\dots x_n\\rangle U^{x_1x_2\\cdots x_n}\\vert\\psi\\rangle.$$ This basically means the operation $U$ will be applied to the $k$ target qubits if and only if all the $n$ control qubits are set to '1'.\n", 430 | "\n", 431 | "Given below is the circuit for the $C^2(U)$ gate. $V$ is any unitary operator satisfying $V^2 = U$.\n", 432 | "" 433 | ] 434 | }, 435 | { 436 | "cell_type": "markdown", 437 | "metadata": { 438 | "colab_type": "text", 439 | "id": "BLXTz6CsCEpl" 440 | }, 441 | "source": [ 442 | "We encourage you to type out the explanation for the above circuit in this cell, just as we did in the case of controlled-$U$ operation.\n", 443 | "\n", 444 | "\n" 445 | ] 446 | }, 447 | { 448 | "cell_type": "markdown", 449 | "metadata": { 450 | "colab_type": "text", 451 | "id": "fwsWO6FGCEpl" 452 | }, 453 | "source": [ 454 | "## Your Task\n", 455 | "4) Implement the $C^4(H)$ gate using only single qubit and CNOT gates. \n", 456 | "\n", 457 | "**Hint:** Finding the gate $V$ satisfying $V^2=X$ **might** just prove to be useful, and you're free to use ancilla qubits (additional qubits used in intermediate stages), there is no restriction on the number of qubits to be used.\n", 458 | "\n", 459 | " (**4 points**)" 460 | ] 461 | }, 462 | { 463 | "cell_type": "code", 464 | "execution_count": null, 465 | "metadata": { 466 | "colab": {}, 467 | "colab_type": "code", 468 | "id": "jUTlPsvTCEpm" 469 | }, 470 | "outputs": [], 471 | "source": [ 472 | "#\n", 473 | "#\n", 474 | "#\n", 475 | "#\n", 476 | "#\n", 477 | "# Your code here\n", 478 | "#\n", 479 | "#\n", 480 | "#\n", 481 | "#\n", 482 | "#" 483 | ] 484 | } 485 | ], 486 | "metadata": { 487 | "colab": { 488 | "collapsed_sections": [], 489 | "name": "Day 2.1 Quantum Circuits Continued.ipynb", 490 | "provenance": [] 491 | }, 492 | "kernelspec": { 493 | "display_name": "Python 3", 494 | "language": "python", 495 | "name": "python3" 496 | }, 497 | "language_info": { 498 | "codemirror_mode": { 499 | "name": "ipython", 500 | "version": 3 501 | }, 502 | "file_extension": ".py", 503 | "mimetype": "text/x-python", 504 | "name": "python", 505 | "nbconvert_exporter": "python", 506 | "pygments_lexer": "ipython3", 507 | "version": "3.8.3" 508 | } 509 | }, 510 | "nbformat": 4, 511 | "nbformat_minor": 1 512 | } 513 | -------------------------------------------------------------------------------- /Notebooks/Day 2.2 Quantum TELIportation.ipynb: -------------------------------------------------------------------------------- 1 | {"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"Day 2.2 Quantum TELIportation.ipynb","provenance":[],"collapsed_sections":[]},"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.7.6"}},"cells":[{"cell_type":"markdown","metadata":{"colab_type":"text","id":"fe_pxf-Cii76"},"source":["# Quantum teleportation "]},{"cell_type":"code","metadata":{"colab_type":"code","id":"2bd7Epffii77","colab":{}},"source":["from qiskit import *\n","from qiskit.tools.jupyter import *\n","from qiskit.visualization import *\n","import matplotlib.pyplot as plotter\n","import numpy as np\n","from IPython.display import display, Math, Latex\n","%matplotlib inline\n","# Loading your IBM Q account(s)\n","provider = IBMQ.load_account()"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"colab_type":"text","id":"KNV3NPv-ii7_"},"source":["We already learned about the Bell states and how quantum entaglement works in case of quibts. But how to exploit this phenomenon to our advantage / how to do something cool? How about quantum teleportation? Today we will understand teleportation and also its truths - how efficient? faster than light?"]},{"cell_type":"markdown","metadata":{"colab_type":"text","id":"KPcwOby1ii7_"},"source":["Let's see the most basic example - teleporting a single qubit. In the Bell states, we saw that the 2 qubits were totally dependant on each other. Measuring one qubit instantly tells us about te other qubit, no matter how far they are, as long as they are entangled. \n","Alice and Bob (the world's most famous cryptographic couple) met long ago but now live far apart. While together they generated an EPR pair, each taking one qubit of the EPR pair when they separated. Many years later, Bob is in hiding, and Alice’s mission, should she choose to accept it, is to deliver a qubit $|\\psi\\rangle$ to Bob. She does not know the state of the qubit, and moreover can only send classical information to Bob.\n","So let's make an EPR pair and give it to Alice and Bob!"]},{"cell_type":"code","metadata":{"colab_type":"code","id":"FszVBZRnii8A","colab":{}},"source":["qr = QuantumRegister(3)\n","crz = ClassicalRegister(1)\n","crx = ClassicalRegister(2) # we will need seperates registers for using 'c_if' later.\n","qc = QuantumCircuit(qr,crz,crx)\n","qc.x(0)\n","qc.h(0) # 'psi' can't be unknown to us as we are creating it here. Let us take '-' state as our 'psi'\n","# We will verify later if the '-' is been teleported.\n","\n","qc.h(1)\n","qc.cx(1,2) # creating a bell state\n","qc.barrier() # Use barrier to separate steps, everything till this barrier is just intialisation."],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"colab_type":"text","id":"MQcZZFy5ii8C"},"source":["The state to be teleported is $|\\psi\\rangle=\\alpha|0\\rangle+\\beta|1\\rangle$, where $\\alpha$ and $\\beta$ are unknown amplitudes. The state input into the circuit $|\\psi_0\\rangle$ is \n","$$|\\psi_0\\rangle=|\\psi\\rangle|\\beta_{00}\\rangle$$\n","The notation $|\\beta_{00}\\rangle$ represents the bell state prepared with the intial qubit state $|00\\rangle$ that is $\\frac{|00\\rangle + |11\\rangle}{\\sqrt{2}}$.\n"," \n"," \n","The two top lines represent Alice’s system, while the bottom line is Bob’s system. The meters represent measurement, and the double lines coming out of them carry classical bits (recall that single lines denote qubits).\n","$$|\\psi_0\\rangle=\\frac{1}{\\sqrt{2}}\\left[\\alpha|0\\rangle(|00\\rangle+|11\\rangle) + \\beta|1\\rangle(|00\\rangle+|11\\rangle)\\right]$$\n","where we use the convention that the first two qubits (on the left) belong to Alice, and the third qubit to Bob. As we explained previously, Alice’s second qubit and Bob’s qubit start out in an entagled state. Alice sends her qubits through a CNOT gate, obtaining:\n","$$|\\psi_1\\rangle=\\frac{1}{\\sqrt{2}}\\left[\\alpha|0\\rangle(|00\\rangle+|11\\rangle) + \\beta|1\\rangle(|10\\rangle+|01\\rangle)\\right]$$\n"]},{"cell_type":"code","metadata":{"colab_type":"code","id":"vIcHUv9-ii8D","colab":{}},"source":["qc.cx(0,1) # '0' and '1' are with Alice and '2' is with Bob.\n","# psi_1 prepared."],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"colab_type":"text","id":"KF_Dwp9Fii8F"},"source":["She then sends the first qubit through a Hadamard gate, obtaining:\n","$$|\\psi_2\\rangle=\\frac{1}{2}\\left[\\alpha(|0\\rangle +|1\\rangle)(|00\\rangle+|11\\rangle) + \\beta(|0\\rangle-|1\\rangle)(|10\\rangle+|01\\rangle)\\right]$$\n","This state may be re-written in the following way, simply by regrouping terms: \n","$$|\\psi_2\\rangle=\\frac{1}{2}[|00\\rangle(\\alpha|0\\rangle+\\beta|1\\rangle)+|01\\rangle(\\alpha|1\\rangle+\\beta|0\\rangle)+|10\\rangle(\\alpha|0\\rangle-\\beta|1\\rangle+|11\\rangle(\\alpha|1\\rangle-\\beta|0\\rangle)]$$\n"]},{"cell_type":"code","metadata":{"colab_type":"code","id":"A29LqRIyii8G","colab":{}},"source":["qc.h(0)\n","# psi_2 prepared.\n","qc.barrier()"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"colab_type":"text","id":"a9_mc0g9ii8I"},"source":["This expression naturally breaks down into four terms. The first term has Alice’s qubits in the state $|00\\rangle$, and Bob’s qubit in the state $\\alpha|0\\rangle+\\beta|1\\rangle$ – which is the original state $|\\psi\\rangle$.\\\\\n"," If Alice performs a measurement and obtains the result 00 then Bob’s system will be in the state $|\\psi\\rangle$. Similarly, from the previous expression we can read off Bob’s post measurement state, given the result of Alice’s measurement:\n"," \n","$$00 \\mapsto |\\psi_3\\rangle \\equiv \\alpha|0\\rangle+\\beta|1\\rangle$$ \n","\n","$$01 \\mapsto |\\psi_3\\rangle \\equiv \\alpha|1\\rangle+\\beta|0\\rangle$$ \n","\n","$$10 \\mapsto |\\psi_3\\rangle \\equiv \\alpha|0\\rangle-\\beta|1\\rangle$$ \n","\n","$$11 \\mapsto |\\psi_3\\rangle \\equiv \\alpha|1\\rangle-\\beta|0\\rangle$$ "]},{"cell_type":"code","metadata":{"colab_type":"code","id":"hUmlXhMeii8I","colab":{}},"source":["qc.measure(0,0)\n","qc.measure(1,1)\n","qc.draw (output = 'mpl')"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"colab_type":"text","id":"SxtE3cJoii8L"},"source":["Bob can ‘fix up’ his state, recovering $|\\psi\\rangle$, by applying the appropriate quantum gate. For example, in the case where the measurement yields $00$, Bob doesn’t need to do anything. If the measurement is $01$ then Bob can fix up his state by applying the $X$ gate. If the measurement is $10$ then Bob can fix up his state by applying the $Z$ gate. If the measurement is $11$ then Bob can fix up his state by applying first an $X$ and then a $Z$ gate.\n","Summing up, Bob needs to apply the transformation $Z^{M_1}X^{M_2}$(note how time goes from left to right in circuit diagrams, but in matrix products terms on the right happen first) to his qubit, and he will recover the state $|\\psi\\rangle$. \n","\n","Here we got $ M_1 = 0$ and $M_2 = 1$. So lets do the final operation on the third state which Bob has to get $|\\psi\\rangle$. "]},{"cell_type":"code","metadata":{"colab_type":"code","id":"48EGahV2ii8M","colab":{}},"source":["qc.x(2).c_if(crx,1) # 'c_if' compares a classical register with a value (either 0 or 1) and performs the \n","qc.z(2).c_if(crz,1) # operation if they are equal.\n","\n","qc.draw('mpl')\n","# be careful of the order of applying X and Z!"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"colab_type":"text","id":"OL_wiKzOii8O"},"source":["Now we have got our state $|\\psi\\rangle$ teleported to Bob! But how do we check it? We started with $|\\psi\\rangle$ as $|-\\rangle$ state. If we apply H gate to $|\\psi\\rangle$, it will give us $|1\\rangle$ state. Then we can measure to verify if we got a $|1\\rangle$ by plotting a histogram. "]},{"cell_type":"code","metadata":{"colab_type":"code","id":"dz-C4Pfgii8P","colab":{}},"source":["qc.h(2)\n","qc.measure(2,crx[1])"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"colab_type":"code","id":"wSywG7v6ii8R","colab":{}},"source":["def run_circuit(qc):\n"," backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend\n"," result = execute(qc, backend, shots = 10000).result() # we run the simulation\n"," counts = result.get_counts() # we get the counts\n"," return counts\n","\n","counts = run_circuit(qc)\n","print(counts)\n","plot_histogram(counts)\n","# the output should be '1xx' if the teleportation is successful."],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"colab_type":"text","id":"qlsRSBF0ii8T"},"source":["Depending on Alice’s measurement outcome, Bob’s qubit will end up in one of these four possible states. Of course, to know which state it is in, Bob must be told the result of Alice’s measurement (**classically**) - which prevents teleportation from transmitting information faster than light. \n","That's the beauty of quantum mechanics - even though the state is decided instantly when the other qubit present in the EPR pair is measured, it would take a certain minimum time (restriction being the speed of light) for the 'spooky action at a distance' to help us teleport."]},{"cell_type":"markdown","metadata":{"colab_type":"text","id":"OurPgXvfii8U"},"source":["## Your task\n"," \n","(Hints follow)\n","You may think this is such a narrow topic; only one algorithm, nothing else to discuss. \n","\n"," \n","And if you think this was an **incorrect use** of the meme you should definitely try this question. \n","No teleportation, nothing. Home alone, Alice has 2 qubits as an EPR pair ( let's take $|\\beta_{00}\\rangle$). Her neighbour Skyler arrives at the door with an evil intention to apply a Pauli gate to one of her qubits. She leaves with a smirk which increases Alice's worry, as she knew Skyler definitely didn't come by just to discuss about **global warming**. She is paranoid and wants to know if Skyler messed with her EPR pair and if she did, which pauli gate she applied. However, she can measure only once because, of course, the qubits would no longer be in Bell state after that. She also doesn't have any extra qubits with her. Write a code which determines the Pauli gate Skyler applied exactly. You can plot histogram for measurement.\n","\n","One last suggestion - 'It doesn't matter if the process serves its initial purpose, as long as it serves some other.'\n","\n"," (**6 points**)"]},{"cell_type":"code","metadata":{"colab_type":"code","id":"J9P1wNuZii8U","scrolled":true,"colab":{}},"source":["qc1 = QuantumCircuit( , )\n","#\n","#\n","# \n","# Your code here\n","#\n","#\n","#\n","#"],"execution_count":null,"outputs":[]}]} 2 | -------------------------------------------------------------------------------- /Notebooks/Day 3 Universality.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "G7GMBKReugEV" 8 | }, 9 | "source": [ 10 | "# Universality in Quantum Computers\n", 11 | "\n", 12 | "As you may know, in classical computers using NAND gates alone one can implement all boolean functions. Similarly we can also implement any arbitrary unitary evolution using just CNOT, Hadamard, Phase and $\\pi/8$ gates upto an arbitrary level of accuracy. It is important to understand that one cannot represent every unitary evolution since they exist over a continuous set of variables hence could require an infinite number of these gates to execute." 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": { 19 | "colab": {}, 20 | "colab_type": "code", 21 | "id": "3_nTydiyugEW" 22 | }, 23 | "outputs": [], 24 | "source": [ 25 | "%matplotlib inline\n", 26 | "# Importing standard Qiskit libraries and configuring account\n", 27 | "from qiskit import *\n", 28 | "from qiskit.compiler import *\n", 29 | "from qiskit.tools.jupyter import *\n", 30 | "from qiskit.visualization import *\n", 31 | "import numpy as np\n", 32 | "import qiskit.quantum_info as qi\n", 33 | "# Loading your IBM Q account(s)\n", 34 | "provider = IBMQ.load_account()" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": { 40 | "colab_type": "text", 41 | "id": "EquhXKCFvvcM" 42 | }, 43 | "source": [ 44 | "## Asymptotic Notation\n", 45 | "Before we go any further with explaining universality, let us first get familiarised with the asymptotic notation, which comes in handy while trying to understand the magnitude of speed-up of algorithms.\n", 46 | "\n", 47 | "The $O$ notation is used to set *upper* bounds on the behaviour of a function. Suppose $f(n)$ and $g(n)$ are two functions on non-negative integers. $f(n)$ is said to be $O(g(n))$, if there are constants $c$ and $n_0$ such that for all values of $n > n_0$, $f(n) \\leq cg(n)$.\n", 48 | "\n", 49 | "The $\\Omega$ notation is used to set *lower* bounds on the behaviour of a function. Suppose $f(n)$ and $g(n)$ are two functions on non-negative integers. $f(n)$ is said to be $\\Omega(g(n))$, if there are constants $c$ and $n_0$ such that for all values of $n > n_0$, $cg(n)\\leq f(n)$.\n", 50 | "\n", 51 | "Finally, the $\\Theta$ notation is used to indicate that $f(n)$ behaves the same as $g(n)$ asymptotically, up to unimportant constant factors. That is, we say $f(n)$ is $\\Theta(g(n))$ if it is both $O(g(n))$ and $\\Omega(g(n))$." 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": { 57 | "colab_type": "text", 58 | "id": "MmFMqYHRugEZ" 59 | }, 60 | "source": [ 61 | "## Two level unitary operations\n", 62 | "\n", 63 | "A two level unitary matrix is a which affects only two or fewer of the components of the vector it acts on. A $d$ dimensional unitary matrix can be written as a product of at most $d(d-1)/2$ two level unitary matrices. The steps in finding this unitary decomposition is to keep making the diagonal elements of $U$ equal to 1 and make the rest of the row and column zero and at each stage we will get a two level unitary evolution to multiply. repeat till we don't end up with a two level unitary matrix.\n", 64 | "\n", 65 | "A two level unitary acts only on two binary sequences say $|g_1\\rangle$ and $|g_m\\rangle$ and these are connected by the gray code $|g_2\\rangle, |g_3\\rangle,\\ldots|g_{m-1}\\rangle$. A gray code is a sequence where each term differs from the last by just a single bit. Lets say the $|g_{m-1}\\rangle$ and $|g_m\\rangle$ differ in their $j^{th}$ bit, then we have to construct a controlled operation such that it acts the unitary $\\tilde{U}$ on the $j^{th}$ qubit and is controlled by the rest of the cubits. Here $\\tilde{U}$ is the sub matrix of $U$ which makes it differ from an identity matrix. However this must be applied after we interchange the $|g_1\\rangle$ state with $|g_{m-1}\\rangle$\n", 66 | "\n", 67 | "We are now going to try our hand at executing a simple two level unitary given by $U$\n", 68 | "$$U = \\begin{bmatrix}\n", 69 | " a & 0 & 0 & 0 & 0 & 0 & 0 & c\\\\\n", 70 | " 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n", 71 | " 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\\\\n", 72 | " 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\\\\n", 73 | " 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\\\\n", 74 | " 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\\\\n", 75 | " 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\\\\n", 76 | " b & 0 & 0 & 0 & 0 & 0 & 0 & d\\\\\n", 77 | " \\end{bmatrix}$$\n", 78 | "\n", 79 | "We can see that here $\\tilde{U} = \\begin{bmatrix}a & c\\\\b & d\\end{bmatrix}$ in the cell below. For the sake of this example we will use $R_y(\\theta)$ for $\\tilde{U}$ and $\\theta = \\pi/4$ so we write $\\tilde{U} = \\begin{bmatrix}\\cos(\\pi/8) & -\\sin(\\pi/8)\\\\\\sin(\\pi/8) & \\cos(\\pi/8)\\end{bmatrix}$" 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": null, 85 | "metadata": { 86 | "colab": {}, 87 | "colab_type": "code", 88 | "id": "zRMixz5dugEZ" 89 | }, 90 | "outputs": [], 91 | "source": [ 92 | "#This cell is just for seeing what the U tilde matrix looks like\n", 93 | "a, b, c, d = np.cos(np.pi/8), np.sin(np.pi/8), -np.sin(np.pi/8), np.cos(np.pi/8)\n", 94 | "u_tilde = np.array([[a,c],[b,d]]).reshape(2,2)\n", 95 | "print(u_tilde)" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": null, 101 | "metadata": { 102 | "colab": {}, 103 | "colab_type": "code", 104 | "id": "ef4YXvuiugEb" 105 | }, 106 | "outputs": [], 107 | "source": [ 108 | "#This cell defines the controlled variant of the U tilde matrix\n", 109 | "qc_for_u = QuantumCircuit(1)\n", 110 | "qc_for_u.ry(np.pi/4, 0)\n", 111 | "qc_for_u.name = \"U\"\n", 112 | "controlled_u_tilde = qc_for_u.to_gate().control(2)" 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "metadata": { 118 | "colab_type": "text", 119 | "id": "-7n0YcXYugEe" 120 | }, 121 | "source": [ 122 | "The two states $U$ affects is $|000\\rangle$ and $|111\\rangle$. The gray code sequence between them is $|000\\rangle\\xrightarrow{}|001\\rangle\\xrightarrow{}|011\\rangle\\xrightarrow{}|111\\rangle$. Here the last two terms differ in their first qubit and we must exchange the state $|000\\rangle$ with $|011\\rangle$. We can achieve this by using toffolis to sequentially change $|000\\rangle$ to $|011\\rangle$ and then use a controlled $\\tilde{U}$ which is controlled by the second and third qubit and acts on the first qubit. We will now demonstrate this on a circuit." 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": null, 128 | "metadata": { 129 | "colab": {}, 130 | "colab_type": "code", 131 | "id": "3XvgukVdugEf" 132 | }, 133 | "outputs": [], 134 | "source": [ 135 | "qc1 = QuantumCircuit(3)\n", 136 | "\n", 137 | "qc1.x(0)\n", 138 | "qc1.x(1)\n", 139 | "qc1.toffoli(0,1,2)#Flipping the third bit if the first two are zero\n", 140 | "qc1.x(0) #Essentially 000 -> 001\n", 141 | "qc1.x(1)\n", 142 | "\n", 143 | "qc1.x(0)\n", 144 | "qc1.toffoli(0,2,1)#Flipping the second bit if the first bit is zero and the third is one\n", 145 | "qc1.x(0) #Essentially 001 -> 011\n", 146 | "\n", 147 | "qc1.append(controlled_u_tilde, [1, 2, 0])\n", 148 | "\n", 149 | "qc1.x(0)\n", 150 | "qc1.toffoli(0,2,1)#Undoing the flip from before\n", 151 | "qc1.x(0)\n", 152 | "\n", 153 | "qc1.x(0)\n", 154 | "qc1.x(1)\n", 155 | "qc1.toffoli(0,1,2)#Undoing the flip from before\n", 156 | "qc1.x(0)\n", 157 | "qc1.x(1)\n", 158 | "\n", 159 | "qc1.draw('mpl')\n" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": { 165 | "colab_type": "text", 166 | "id": "6nJpM2gpugEh" 167 | }, 168 | "source": [ 169 | "Now before you point out that some of these $X$ gates are unnecessary, I have just added them for clarity regarding which toffoli does what.\n", 170 | "\n", 171 | "Now for being sure let's find the unitary of this circuit and compare it with the $U$ which we wished to emulate." 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": null, 177 | "metadata": { 178 | "colab": {}, 179 | "colab_type": "code", 180 | "id": "tWrt6AIKugEh" 181 | }, 182 | "outputs": [], 183 | "source": [ 184 | "U_circ = qi.Operator(qc1).data\n", 185 | "print(U_circ)" 186 | ] 187 | }, 188 | { 189 | "cell_type": "markdown", 190 | "metadata": { 191 | "colab_type": "text", 192 | "id": "UV6IfiIJugEj" 193 | }, 194 | "source": [ 195 | "So it does turn out to be correct! Now here is your task.\n", 196 | "\n", 197 | "## Your task\n", 198 | "1) You have to execute the following unitary given by $M$\n", 199 | "$$M = \\begin{bmatrix}\n", 200 | " a & 0 & 0 & 0 & 0 & 0 & 0 & c\\\\\n", 201 | " 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n", 202 | " b & 0 & 0 & 0 & 0 & 0 & 0 & d\\\\\n", 203 | " 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\\\\n", 204 | " 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\\\\n", 205 | " 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\\\\n", 206 | " 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\\\\n", 207 | " 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\\\\n", 208 | " \\end{bmatrix}$$\n", 209 | "So first off, this is actually a multiplication of the a two level matrix with the previously defined $U$. We can verify the following.\n", 210 | "$$V = \\begin{bmatrix}\n", 211 | " 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n", 212 | " 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n", 213 | " 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1\\\\\n", 214 | " 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\\\\n", 215 | " 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\\\\n", 216 | " 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\\\\n", 217 | " 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\\\\n", 218 | " 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\\\\n", 219 | " \\end{bmatrix}$$\n", 220 | "\n", 221 | "$$M = V * U$$\n", 222 | "\n", 223 | "So you just have to append the circuit for $V$ to the circuit that is already defined for qc1. You are free to use any apporach as long as you only use $X$ gates and toffolis and the one exception of conntrolled_u_tilde. Also no ancilla bits allowed.\n", 224 | "\n", 225 | " (**3 points**)" 226 | ] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "execution_count": null, 231 | "metadata": { 232 | "colab": {}, 233 | "colab_type": "code", 234 | "id": "dqPMk4BrugEk" 235 | }, 236 | "outputs": [], 237 | "source": [ 238 | "qc2 = QuantumCircuit(3)\n", 239 | "#\n", 240 | "#\n", 241 | "#Add code for executing U\n", 242 | "#\n", 243 | "#\n", 244 | "\n", 245 | "#\n", 246 | "#\n", 247 | "#Add code for executing V\n", 248 | "#\n", 249 | "#\n", 250 | "\n", 251 | "qc2.draw('mpl')" 252 | ] 253 | }, 254 | { 255 | "cell_type": "markdown", 256 | "metadata": { 257 | "colab_type": "text", 258 | "id": "VvxRSmFCugEn" 259 | }, 260 | "source": [ 261 | "You can verify the correctness of this by running the cell below and seeing if it is accurate. On a sidenote, it is possible that you may have some phase differences in your circuit but in theory they are the same since we ignore global phase so we have the function without_global_phase for removing global phase. (Mostly your circuit wouldnt have a global phase since you are going to use only $X$ and toffoli and even $R_y$ doesnt have phase)." 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": null, 267 | "metadata": { 268 | "colab": {}, 269 | "colab_type": "code", 270 | "id": "uwrN4oqzugEn" 271 | }, 272 | "outputs": [], 273 | "source": [ 274 | "def without_global_phase(matrix: np.ndarray, atol: float = 1e-8) :\n", 275 | " phases1 = np.angle(matrix[abs(matrix) > atol].ravel(order='F'))\n", 276 | " if len(phases1) > 0:\n", 277 | " matrix = np.exp(-1j * phases1[0]) * matrix\n", 278 | " return matrix\n", 279 | "V_circ = without_global_phase(qi.Operator(qc2).data)\n", 280 | "print(V_circ)" 281 | ] 282 | }, 283 | { 284 | "cell_type": "markdown", 285 | "metadata": { 286 | "colab_type": "text", 287 | "id": "r8UiNgyEugEp" 288 | }, 289 | "source": [ 290 | "For a two level unitary evolution we can see that it takes complexity of $O(n^2)$ on breaking down into CNOTs and single qubit gates. If we were to extend this to an arbitrary unitary evolution we can see that it will become $O(n^{2}2^{2n})$ since an arbitrary unitary evolution can be represented as a product of $O((2^n)^2)$ since the operator is $2^n$ dimensional and the number of two level matrices required is in $n^2$ complexity." 291 | ] 292 | }, 293 | { 294 | "cell_type": "markdown", 295 | "metadata": { 296 | "colab_type": "text", 297 | "id": "otBfxTwiugEr" 298 | }, 299 | "source": [ 300 | "## Approximating single qubit unitary gates\n", 301 | "\n", 302 | "Now here's the thing. Notice how we so freely used any arbitrary single qubit gate? Well we often just cannot come up with any arbitrary unitary operation over a single qubit so easily.\n", 303 | "\n", 304 | "We will now define a quantity $E(U,V)$ where $U$ and $V$ are unitary operators of the same state space.\n", 305 | "$$E(U,V) = max||(U - V)|\\psi\\rangle||$$\n", 306 | "\n", 307 | "The max represents the maximum over all possible $|\\psi\\rangle$ in the state space. Suppose a quantum system starts in the state $|\\psi\\rangle$, and we perform either the unitary operation $U$, or the unitary operation $V$ . Following this, we perform a measurement. Let $M$ be a POVM element associated with the measurement, and let $P_U$ (or $P_V$) be the probability of obtaining the corresponding measurement outcome if the operation $U$ (or $V$) was performed. We then have\n", 308 | "$$|P_U - P_V| = |\\langle\\psi|U^{\\dagger}MU|\\psi\\rangle - \\langle\\psi|V^{\\dagger}MV|\\psi\\rangle|$$\n", 309 | "If we substitute $|\\Delta\\rangle = (U - V)|\\psi\\rangle$ we get the following from Cauchy-Schwarz inequality\n", 310 | "$$|P_U - P_V| = |\\langle\\psi|U^{\\dagger}M|\\Delta\\rangle + \\langle\\Delta|MV|\\psi\\rangle|$$\n", 311 | "$$|P_U - P_V| \\leq |||\\Delta\\rangle|| + |||\\Delta\\rangle||$$\n", 312 | "$$|P_U - P_V| \\leq 2E(U,V)$$\n", 313 | "\n", 314 | "Hence if $E(U,V)$ is small, the measurement results of $U$ and $V$ will also be very close. Note that if we use sequenced gates, the error value will add up linearly and so wouldn't actually cause big problems\n", 315 | "$$E(U_{2}U_{1} - V_{2}V_{1}) = ||(U_{2}U_{1} - V_{2}V_{1})|\\psi\\rangle||$$\n", 316 | "$$E(U_{2}U_{1} - V_{2}V_{1}) = ||(U_{2}U_{1} - V_{2}U_{1})|\\psi\\rangle + (V_{2}U_{1} - V_{2}V_{1})|\\psi\\rangle||$$\n", 317 | "$$E(U_{2}U_{1} - V_{2}V_{1}) = ||(U_{2}U_{1} - V_{2}U_{1})|\\psi\\rangle + (V_{2}U_{1} - V_{2}V_{1})|\\psi\\rangle||$$\n", 318 | "Using triangle inequality we get\n", 319 | "$$E(U_{2}U_{1} - V_{2}V_{1}) \\leq ||(U_{2} - V_{2})U_{1}|\\psi\\rangle|| + ||U_{1} - V_{1})V_{2}|\\psi\\rangle||$$\n", 320 | "$$E(U_{2}U_{1} - V_{2}V_{1}) \\leq E(U_2,V_2) + E(U_1,V_1)$$\n", 321 | "This can be extended to any number of operations using induction.\n", 322 | "\n", 323 | "Consider the operations $T$ and $HTH$. $T$ gives a rotation about $Z$ axis by an angle of $\\pi/4$. $HTH$ gives a rotation about $X$ axis by an angle of $\\pi/4$. Combining these both we get\n", 324 | "\n", 325 | "$$\n", 326 | "\\exp(-\\iota\\dfrac{\\pi}{8}Z)\\exp(-\\iota\\dfrac{\\pi}{8}X) = \\Big[\\cos\\left(\\dfrac{\\pi}{8}\\right)I - {\\iota}\\sin\\left(\\dfrac{\\pi}{8}\\right)Z\\Big]\\Big[\\cos\\left(\\dfrac{\\pi}{8}\\right)I - {\\iota}\\sin\\left(\\dfrac{\\pi}{8}\\right)X\\Big]\n", 327 | "$$\n", 328 | "\n", 329 | "$$\n", 330 | "\\exp(-\\iota\\dfrac{\\pi}{8}Z)\\exp(-\\iota\\dfrac{\\pi}{8}X) = {\\cos}^2\\left(\\dfrac{\\pi}{8}\\right)I - \\iota\\Big[\\cos\\left(\\dfrac{\\pi}{8}\\right)(X + Z) + \\sin\\left(\\dfrac{\\pi}{8}\\right)Y\\Big]\\sin\\left(\\dfrac{\\pi}{8}\\right)\n", 331 | "$$\n", 332 | "\n", 333 | "As one can see this obtains us a rotation by an $\\theta$ such that $\\cos\\left(\\dfrac{\\theta}{2}\\right) = {\\cos}^2\\left(\\dfrac{\\pi}{8}\\right)$ about the vector $\\vec{n} = \\left(\\cos\\dfrac{\\pi}{8},\\sin\\dfrac{\\pi}{8},\\cos\\dfrac{\\pi}{8}\\right)$. We can see that this $\\theta$ is an irrational multiple of $2\\pi$. Hence suppose we wish to make $R_{\\vec{n}}(\\alpha)$ for some arbitrary $\\alpha$ we can replicate to an arbitrary accuracy using repeated iteration $R_{\\vec{n}}(\\theta)$\n", 334 | "\n", 335 | "Lets say we wish to have it to some accuracy $\\delta$ and $N$ is an integer larger than $2\\pi/\\delta$. Lets define a sequence $\\theta_k = (k\\theta)$mod $2\\pi$. From pigeonhole principle we can see that there exists distinct $j,k \\in{1,2,.....N}$ such that $|\\theta_j - \\theta_k| \\leq 2\\pi/N < \\delta$ hence we can use $\\theta_{l(j-k)}$ to attain any value in $[0, 2\\pi)$ with accuracy of $\\delta$.\n", 336 | "\n", 337 | "Now we can see that $HR_{\\vec{n}}(\\theta)H = R_{\\vec{m}}(\\theta)$ where $\\vec{m} = \\left(\\cos\\dfrac{\\pi}{8},-\\sin\\dfrac{\\pi}{8},\\cos\\dfrac{\\pi}{8}\\right)$. Now if we recall eq. 3.2, we can see that all unitary operators can be represented as the following subject to a certain level of accuracy.\n", 338 | "\n", 339 | "$$\n", 340 | "U = e^{i\\alpha}{R_{\\vec{n}}(\\theta)}^{n_1}H{R_{\\vec{n}}(\\theta)}^{n_2}H{R_{\\vec{n}}(\\theta)}^{n_3}\n", 341 | "$$\n", 342 | "\n", 343 | "Now here is the important question. How efficient is this approach? Say we have $m$ gate circuit we wish to replicate to closeness of $\\epsilon$ (we define this closeness as $E(U,V) < \\epsilon$). This will give us a $\\Omega(m2^{(m/\\epsilon)})$ which is not really good however with the $\\theta_k$ approach we can see that the range of angles gets filled up fairly uniformly so we can consider a complexity of some $\\Theta(m^2/\\epsilon)$. This gives us a fairly good approach however it can get even better. According to the Solovay-Kitaev theorem one can achieve a complexity of $O(mlog(m/\\epsilon))$" 344 | ] 345 | }, 346 | { 347 | "cell_type": "markdown", 348 | "metadata": { 349 | "colab_type": "text", 350 | "id": "rpaOp394ugEs" 351 | }, 352 | "source": [ 353 | "TL;DR we can create any single qubit unitary just using some good old $T$ and $H$ gates or anything which can help us find a rotation which of any irrational angle. Now we will just explore some simple examples for that but first we will define a function for finding the norm difference between two unitaries barring the global phase so we can check validation." 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": null, 359 | "metadata": { 360 | "colab": {}, 361 | "colab_type": "code", 362 | "id": "r_52XNbEugEs" 363 | }, 364 | "outputs": [], 365 | "source": [ 366 | "#Function just returns norm ignoring global phase between unitaries\n", 367 | "def norm(unitary_a: np.ndarray, unitary_b: np.ndarray) :\n", 368 | " return np.linalg.norm(without_global_phase(unitary_b)-without_global_phase(unitary_a), ord=2)" 369 | ] 370 | }, 371 | { 372 | "cell_type": "markdown", 373 | "metadata": { 374 | "colab_type": "text", 375 | "id": "-bMjA6bgugEu" 376 | }, 377 | "source": [ 378 | "## Your task\n", 379 | "2) While one can try to break their head around making very complex unitaries using this approach, we are going to give some relatively simpler ones which should be possible to do with not a very large number of $T$ and $H$ gates. (**2 points**)" 380 | ] 381 | }, 382 | { 383 | "cell_type": "code", 384 | "execution_count": null, 385 | "metadata": { 386 | "colab": {}, 387 | "colab_type": "code", 388 | "id": "XR4_B-mcugEu" 389 | }, 390 | "outputs": [], 391 | "source": [ 392 | "#Make the Pauli Y gate\n", 393 | "qcy = QuantumCircuit(1)\n", 394 | "#\n", 395 | "#\n", 396 | "#Your code here\n", 397 | "#\n", 398 | "#\n", 399 | "Y_circ = qi.Operator(qcy).data\n", 400 | "Y = np.array([[0,-1j],[1j,0]])\n", 401 | "print(norm(Y_circ,Y))" 402 | ] 403 | }, 404 | { 405 | "cell_type": "markdown", 406 | "metadata": { 407 | "colab_type": "text", 408 | "id": "2A7GwZZpugEw" 409 | }, 410 | "source": [ 411 | "## Bonus question (not compulsory but encouraged)\n", 412 | "\n", 413 | "This next task is for a unitary that you are given and is stored in uni_q. You have to make this unitary purely using $H$ and $T$ gates. \n", 414 | "\n", 415 | "So firstly here are some hints, this unitary will require no more or less than 5 $H$ gates and that is all you know. The rest of the required gates are obviously $T$. An obvious approach is to try multiple combinations till the norm difference reaches a very small amount and for that at every trial you can use the good old qi.Operator(circuit).data to get the unitary of the circuit at that moment. If you want to re initiallize your circuit i.e. clear out all the gates, then simply do qc3 = QuantumCircuit(1) again.\n", 416 | "\n", 417 | "Another note: $T^8 = I$ and also the hint doesn't really describe an optimal approach so it might take some time to execute but it is possible to approach the answer quicker using some randomness but of course that would depend on the initialized seed.\n", 418 | "\n", 419 | " (**6 points**)" 420 | ] 421 | }, 422 | { 423 | "cell_type": "code", 424 | "execution_count": null, 425 | "metadata": { 426 | "colab": {}, 427 | "colab_type": "code", 428 | "id": "fRXyRK-augEw" 429 | }, 430 | "outputs": [], 431 | "source": [ 432 | "uni_q = np.array([[-0.25+0.60355339j, 0.60355339+0.45710678j],\n", 433 | " [0.60355339-0.45710678j, 0.25+0.60355339j]]).reshape(2,2)\n", 434 | "\n", 435 | "qc3 = QuantumCircuit(1)\n", 436 | "#\n", 437 | "#\n", 438 | "#Your code here\n", 439 | "#\n", 440 | "#\n", 441 | "uni_q_circ = qi.Operator(qc3).data\n", 442 | "print(norm(uni_q_circ,uni_q))" 443 | ] 444 | }, 445 | { 446 | "cell_type": "markdown", 447 | "metadata": { 448 | "colab_type": "text", 449 | "id": "j4lrbNgLugEz" 450 | }, 451 | "source": [ 452 | "You would mostly be getting extremely small errors (mostly much smaller than 1e-8). The reason we are getting extremely small errors is mostly because the qi.Operator(circuit).data makes certain floating point errors but these are clearly insignificant." 453 | ] 454 | }, 455 | { 456 | "cell_type": "markdown", 457 | "metadata": { 458 | "colab_type": "text", 459 | "id": "QtnaDUpougEz" 460 | }, 461 | "source": [ 462 | "## Bonus question (not compulsory but encouraged)\n", 463 | "\n", 464 | "The following gate is also a universal gate by itself (of course you will require ancilla bits but it can do the job).\n", 465 | "\n", 466 | "Here $\\alpha$ is an irrational number. For the sake of this example we will take $\\pi\\alpha = \\cos^{-1}(3/5)$ and it is trivial to prove that this would give us an irrational $\\alpha$. Now your task is to somehow get a $|+\\rangle$ state on a qubit using this and you may use as many qubits and these gates as you want. Since it is rather trivial that we can execute $X$ gates and CNOTs using this gate, you are also allowed to use them.\n", 467 | "\n", 468 | "Hints: imagine you start from a two-qubit state $|00\\rangle$, and apply a $R_x(\\pi/2)$ to the first qubit. Then apply a controlled-not controlled off the first qubit and targeting the second qubit. Next, apply the inverse of the first rotation. Finally, measure the first qubit. If you get answer $|1\\rangle$, the second qubit is in the $|-\\rangle$ state and if you get the result $|0\\rangle$, the second qubit will be in state $|+\\rangle$.\n", 469 | "\n", 470 | "You are of course free to just ignore the hint and do whatsoever you wish to as long as you reach $|+\\rangle$" 471 | ] 472 | }, 473 | { 474 | "cell_type": "code", 475 | "execution_count": null, 476 | "metadata": { 477 | "colab": {}, 478 | "colab_type": "code", 479 | "id": "QMkfNzmvugE0" 480 | }, 481 | "outputs": [], 482 | "source": [ 483 | "#Defining the gate as controlled_irx\n", 484 | "from qiskit.extensions import *\n", 485 | "pi_alpha = np.arccos(0.6)\n", 486 | "qc_for_irx = QuantumCircuit(1)\n", 487 | "irx = np.array([[1j*np.cos(pi_alpha/2),np.sin(pi_alpha/2)],[np.sin(pi_alpha/2),1j*np.cos(pi_alpha/2)]]).reshape(2,2)\n", 488 | "g_irx = UnitaryGate(data=irx,label=None)\n", 489 | "controlled_irx = g_irx.control(2)" 490 | ] 491 | }, 492 | { 493 | "cell_type": "code", 494 | "execution_count": null, 495 | "metadata": { 496 | "colab": {}, 497 | "colab_type": "code", 498 | "id": "h37J4N4-ugE2" 499 | }, 500 | "outputs": [], 501 | "source": [ 502 | "n = 4\n", 503 | "qcb = QuantumCircuit(n,n)\n", 504 | "qcb.x(0)#Ancilla\n", 505 | "qcb.x(1)#Ancilla You can use these two as control to use the controlled_irx gate\n", 506 | "#\n", 507 | "#\n", 508 | "#\n", 509 | "#\n", 510 | "#Your code here\n", 511 | "#\n", 512 | "#\n", 513 | "#\n", 514 | "#\n", 515 | "#Get state of qubit which should have the |+> state using the backend simulator\n", 516 | "i = None#Index for the qubit at |+> state\n", 517 | "qcb.h(i)#Puts the |+> state to |0>\n", 518 | "qcb.measure(i, i)\n", 519 | "def run_circuit(qcb):\n", 520 | " backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend\n", 521 | " result = execute(qcb, backend, shots = 2000).result() # we run the simulation\n", 522 | " counts = result.get_counts() # we get the counts\n", 523 | " return counts\n", 524 | "\n", 525 | "counts = run_circuit(qcb)\n", 526 | "print(counts)\n", 527 | "plot_histogram(counts)" 528 | ] 529 | }, 530 | { 531 | "cell_type": "markdown", 532 | "metadata": { 533 | "colab_type": "text", 534 | "id": "wU4x6EsrugE3" 535 | }, 536 | "source": [ 537 | "If your answer is correct then you should see nearly all shots on $|0000\\rangle$ because you have only chosen the ith qubit for measurement so it will just assign a default zero to the rest. \n", 538 | "Should you choose to measure all the qubits, you may see a different answer depending on your approach. If you follow the hints approach you will get an equal distribution between $|0011\\rangle$ and $|0111\\rangle$" 539 | ] 540 | } 541 | ], 542 | "metadata": { 543 | "colab": { 544 | "collapsed_sections": [], 545 | "name": "Day 3 Universality.ipynb", 546 | "provenance": [] 547 | }, 548 | "kernelspec": { 549 | "display_name": "Python 3", 550 | "language": "python", 551 | "name": "python3" 552 | }, 553 | "language_info": { 554 | "codemirror_mode": { 555 | "name": "ipython", 556 | "version": 3 557 | }, 558 | "file_extension": ".py", 559 | "mimetype": "text/x-python", 560 | "name": "python", 561 | "nbconvert_exporter": "python", 562 | "pygments_lexer": "ipython3", 563 | "version": "3.7.4" 564 | } 565 | }, 566 | "nbformat": 4, 567 | "nbformat_minor": 1 568 | } 569 | -------------------------------------------------------------------------------- /Notebooks/Day 4 Deutsch Josza algorithm.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "fCQC6rLzMAsk" 8 | }, 9 | "source": [ 10 | "# The Deutsch-Josza Algorithm\n", 11 | "The Deutsch-Josza algorithm is useful in solving the Deutsch's problem described as follows:\n", 12 | "Alice selects a number $x$ from $0$ to $2^n-1$ and sends it to Bob who calculates $f(x)$ and sends it back to Alice. The function $f(x)$ attains either the value 0 or 1 for each $x$ and satisfies either one of the following two properties:\n", 13 | "1. $f(x)$ is *constant* for all values of $x$.\n", 14 | "2. $f(x)$ is *balanced*, i,e., it attains the value 0 for exactly half of all possible $x$ and 1 for the other half.\n", 15 | "\n", 16 | "Alice's job is to determine whether the function is constant or balanced. In the classical situation, where Alice sends n bits to represent her number each time, at worst it would take $\\frac{2^n}{2}+1$ trials for Alice to determine, since a function could attain the same value for the first half of trials and the next trial could still give a different value, if it were balanced.\n", 17 | "\n", 18 | "\n", 19 | "If Alice and Bob use qubits, and Bob uses the unitary operator $U_f$ to calculate $f(x)$ then Alice's number is stored in the $n$ qubit query register and Bob uses the single qubit answer register to store the result of his operator. Alice then follows the described algorithm to carry out the task.\n", 20 | "\n", 21 | " \n", 22 | "\n", 23 | "The input state is $$\\vert \\psi_0 \\rangle = \\vert 0 \\rangle^{\\otimes n}\\vert 1\\rangle.$$\n", 24 | "After applying the Hadamard transform on all the qubits, the state is:\n", 25 | "\\begin{align*}\n", 26 | "\\vert \\psi_1 \\rangle &=\\left(\\frac{\\vert 0 \\rangle + \\vert 1 \\rangle}{\\sqrt{2}}\\right)^{\\otimes n}\\left(\\frac{\\vert 0 \\rangle -\\vert 1\\rangle}{\\sqrt{2}}\\right)\\\\\n", 27 | "&=\\sum\\limits_{x\\in\\{0,1\\}^n}\\frac{\\vert x \\rangle}{\\sqrt{2^n}}\\left(\\frac{\\vert 0 \\rangle -\\vert 1\\rangle}{\\sqrt{2}}\\right)\n", 28 | "\\end{align*}\n", 29 | "If we apply $U_f$ gate to state $\\vert x \\rangle \\left( \\frac{\\vert 0\\rangle - \\vert 1\\rangle}{\\sqrt{2}}\\right)$, we get the following state\n", 30 | " \n", 31 | "\\begin{align*}\n", 32 | "\\vert x \\rangle\\left(\\frac{\\vert f(x)\\rangle -\\vert 1\\oplus f(x)\\rangle}{\\sqrt{2}}\\right)\n", 33 | "&=\\begin{cases}\n", 34 | "\t\t\\vert x \\rangle\\left(\\frac{\\vert 0\\rangle -\\vert 1\\rangle}{\\sqrt{2}}\\right) & \\text{if $f(x)=0$}\\\\\n", 35 | "\t\t\\vert x \\rangle\\left(\\frac{\\vert 1\\rangle -\\vert 0\\rangle}{\\sqrt{2}}\\right) & \\text{if $f(x)=1$}\\\\\n", 36 | "\t\\end{cases}\\\\\n", 37 | "&=(-1)^{f(x)}\\vert x \\rangle \\left(\\frac{\\vert 0\\rangle -\\vert 1\\rangle}{\\sqrt{2}}\\right)\n", 38 | "\\end{align*} So, on applying $U_f$ to $\\vert \\psi_1 \\rangle$, we get:\n", 39 | "$$\\vert \\psi_2 \\rangle=\\sum\\limits_{x}\\frac{(-1)^{f(x)}\\vert x \\rangle}{\\sqrt{2^n}}\\left(\\frac{\\vert 0 \\rangle -\\vert 1\\rangle}{\\sqrt{2}}\\right).$$\n", 40 | "For a single qubit $\\vert x \\rangle,$ $$H\\vert x \\rangle=\\sum\\limits_{z}\\frac{(-1)^{xz}\\vert z \\rangle}{\\sqrt{2}}.$$\n", 41 | "For $n$ qubits $$H^{\\otimes n}\\vert x_1,...x_n \\rangle=\\sum\\limits_{z_1,...z_n}\\frac{(-1)^{x_1z_1+...x_nz_n}\\vert z_1,...z_n \\rangle}{\\sqrt{2^n}}$$\n", 42 | "i.e., $$H^{\\otimes n}\\vert x \\rangle=\\sum\\limits_{z}\\frac{(-1)^{x\\cdot z}\\vert z \\rangle}{\\sqrt{2^n}}$$ where $x\\cdot z$ represents the bitwise inner product of $x$ and $z$, modulo 2.\n", 43 | "\n", 44 | "Applying Hadamard transform on the query register, we obtain\n", 45 | "$$\\vert \\psi_3 \\rangle=\\sum\\limits_z\\sum\\limits_{x}\\frac{(-1)^{x.z+f(x)}\\vert z \\rangle}{2^n}\\left(\\frac{\\vert 0 \\rangle -\\vert 1\\rangle}{\\sqrt{2}}\\right).$$\n", 46 | "\n", 47 | "The amplitude for the state $\\vert 0 \\rangle^{\\otimes n}$ is $\\displaystyle{\\sum\\limits_{x}\\frac{(-1)^{f(x)}}{2^n}}$.\n", 48 | "\n", 49 | "If $f$ is constant, the amplitude will be $\\pm 1$, which means all other amplitudes will be 0, so an observation will yield 0s for all qubits in the query register. If $f$ is balanced, then the positive and negative contributions to the amplitude of the state $\\vert 0 \\rangle^{\\otimes n}$ cancel, leaving an amplitude of 0, and a measurement must yield a result other than 0 on at least one qubit in the query register.\n", 50 | "\n", 51 | "Therefore, if on measuring the query register, Alice obtains all 0s then the function is constant; otherwise the function is balanced. \n", 52 | "\n", 53 | "Hence, a quantum computer can solve Deutsch’s problem with one evaluation of the function $f$ compared to the classical requirement for $\\frac{2^n}{2}+1$ evaluations." 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "metadata": { 60 | "colab": {}, 61 | "colab_type": "code", 62 | "id": "VUmAdMH4MAsg" 63 | }, 64 | "outputs": [], 65 | "source": [ 66 | "%matplotlib inline\n", 67 | "# Importing standard Qiskit libraries and configuring account\n", 68 | "from qiskit import QuantumCircuit, execute, Aer, IBMQ\n", 69 | "from qiskit.compiler import transpile, assemble\n", 70 | "from qiskit.tools.jupyter import *\n", 71 | "from qiskit.visualization import *\n", 72 | "import numpy as np\n", 73 | "from fractions import Fraction as frac \n", 74 | "import qiskit.quantum_info as qi\n", 75 | "# Loading your IBM Q account(s)\n", 76 | "provider = IBMQ.load_account()" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": { 82 | "colab_type": "text", 83 | "id": "__27cO-5MAsl" 84 | }, 85 | "source": [ 86 | "## Creating Quantum Oracles\n", 87 | "Now, we will attempt to construct a circuit in order to demonstrate the above algorithm for a particular case. Before that let's have a look at the internal circuitry of the quantum oracles $U_f$ used to calculate $f(x)$.\n", 88 | "\n", 89 | "For a constant function, it is simple:\n", 90 | "\n", 91 | " 1. if $f(x)$ = 0, then $\\vert y \\oplus f(x)\\rangle = \\vert y\\rangle$ so apply the $I$ gate to the qubit in register 2 (i.e., no need to apply any gates).\n", 92 | " 2. if $f(x)$ = 1, then $\\vert y \\oplus f(x)\\rangle = \\vert y \\oplus 1\\rangle$ so apply the $X$ gate to the qubit in register 2.\n", 93 | " \n", 94 | "A simple way to create a balanced function is by applying the CNOT gate for each qubit in register 1, with the qubit in register 2 as the target. Shown below is an example of a quantum oracle for a balanced function to be applied to a 3-qubit system:" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": { 101 | "colab": {}, 102 | "colab_type": "code", 103 | "id": "sIV_EugkMAsl" 104 | }, 105 | "outputs": [], 106 | "source": [ 107 | "qc=QuantumCircuit(4)\n", 108 | "\n", 109 | "#In this particular oracle, the last qubit is storing the value of the function\n", 110 | "\n", 111 | "qc.cx(0,3)\n", 112 | "qc.cx(1,3)\n", 113 | "qc.cx(2,3)\n", 114 | "#The last qubit is 1 if there are odd no. of 1s in the other 3 qubits\n", 115 | "#and 0 otherwise\n", 116 | "#Hence it is a balanced function\n", 117 | "\n", 118 | "qc.draw('mpl')" 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "metadata": { 124 | "colab_type": "text", 125 | "id": "6e-lCePnMAsq" 126 | }, 127 | "source": [ 128 | "## Example of a quantum circuit for the Deutsch-Josza Algorithm:\n", 129 | "Here we will take the simple case wherein the number chosen lies in the range 0 to 3 (i.e. $2^2 - 1$ ) and the function is balanced. The part of the quantum circuit between the barriers is one possible representation of the quantum oracle for this function." 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "metadata": { 136 | "colab": {}, 137 | "colab_type": "code", 138 | "id": "z83gT6zcMAsr" 139 | }, 140 | "outputs": [], 141 | "source": [ 142 | "qc1=QuantumCircuit(3)\n", 143 | "qc1.x(2)\n", 144 | "qc1.h(0)\n", 145 | "qc1.h(1)\n", 146 | "qc1.h(2)\n", 147 | "\n", 148 | "qc1.barrier(range(3))\n", 149 | "qc1.cx(0,2)\n", 150 | "qc1.cx(1,2)\n", 151 | "qc1.barrier(range(3))\n", 152 | "qc1.h(0)\n", 153 | "qc1.h(1)\n", 154 | "\n", 155 | "meas = QuantumCircuit(3, 2)\n", 156 | "meas.measure(range(2),range(2))\n", 157 | "\n", 158 | "# The Qiskit circuit object supports composition using\n", 159 | "# the addition operator.\n", 160 | "circ = qc1+meas\n", 161 | "\n", 162 | "circ.draw('mpl')" 163 | ] 164 | }, 165 | { 166 | "cell_type": "markdown", 167 | "metadata": { 168 | "colab_type": "text", 169 | "id": "8G95o93ZMAsv" 170 | }, 171 | "source": [ 172 | "The following is the result obtained on measuring the query register:" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": null, 178 | "metadata": { 179 | "colab": {}, 180 | "colab_type": "code", 181 | "id": "ZBLhhq4SMAsw" 182 | }, 183 | "outputs": [], 184 | "source": [ 185 | "backend_sim = Aer.get_backend('qasm_simulator')\n", 186 | "\n", 187 | "job_sim = execute(circ, backend_sim, shots=1000)\n", 188 | "\n", 189 | "result_sim = job_sim.result()\n", 190 | "counts = result_sim.get_counts(circ)\n", 191 | "print(counts)\n", 192 | "plot_histogram(counts)" 193 | ] 194 | }, 195 | { 196 | "cell_type": "markdown", 197 | "metadata": { 198 | "colab_type": "text", 199 | "id": "jW80PBRMMAsy" 200 | }, 201 | "source": [ 202 | "Since we do not get an output $\\vert 00\\rangle$, the function must be balanced." 203 | ] 204 | }, 205 | { 206 | "cell_type": "markdown", 207 | "metadata": { 208 | "colab_type": "text", 209 | "id": "MSLCES23MAsz" 210 | }, 211 | "source": [ 212 | "## Your Task:\n", 213 | "**(5 points)**\n", 214 | "\n", 215 | "Your task is to make a quantum circuit (the rest is done for you, you just need to construct the oracle for the below function) for a 4-qubit query resgister. The function is balanced with outputs for each $x$ as given below:\n", 216 | "\n", 217 | "|        **$x$ :**         |        **$f(x)$**        |        **$x$ :**       |        **$f(x)$**        |\n", 218 | "| :-------------: | :----------: | :-------------------: | :----------: |\n", 219 | "| $\\vert 0000 \\rangle $ | 1 | $\\vert 1000 \\rangle $ | 0 |\n", 220 | "| $\\vert 0001 \\rangle $ | 0 | $\\vert 1001 \\rangle $ | 1 |\n", 221 | "| $\\vert 0010 \\rangle $ | 0 | $\\vert 1010 \\rangle $ | 1 |\n", 222 | "| $\\vert 0011 \\rangle $ | 1 | $\\vert 1011 \\rangle $ | 0 |\n", 223 | "| $\\vert 0100 \\rangle $ | 0 | $\\vert 1100 \\rangle $ | 1 |\n", 224 | "| $\\vert 0101 \\rangle $ | 1 | $\\vert 1101 \\rangle $ | 0 |\n", 225 | "| $\\vert 0110 \\rangle $ | 1 | $\\vert 1110 \\rangle $ | 0 |\n", 226 | "| $\\vert 0111 \\rangle $ | 0 | $\\vert 1111 \\rangle $ | 1 |\n" 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": null, 232 | "metadata": { 233 | "colab": {}, 234 | "colab_type": "code", 235 | "id": "tG-dN9-HMAsz" 236 | }, 237 | "outputs": [], 238 | "source": [ 239 | "qc2=QuantumCircuit(5)\n", 240 | "qc2.x(4)\n", 241 | "qc2.h(0)\n", 242 | "qc2.h(1)\n", 243 | "qc2.h(2)\n", 244 | "qc2.h(3)\n", 245 | "qc2.h(4)\n", 246 | "\n", 247 | "qc2.barrier(range(5))\n", 248 | "#Your code for the oracle here\n", 249 | "#YOU ARE PERMITTED TO USE ONLY SINGLE QUBIT GATES AND CNOT(cx) GATES\n", 250 | "\n", 251 | "\n", 252 | "qc2.barrier(range(5))\n", 253 | "qc2.h(0)\n", 254 | "qc2.h(1)\n", 255 | "qc2.h(2)\n", 256 | "qc2.h(3)\n", 257 | "\n", 258 | "meas2 = QuantumCircuit(5, 4)\n", 259 | "meas2.measure(range(4),range(4))\n", 260 | "\n", 261 | "circ2 = qc2+meas2\n", 262 | "circ2.draw('mpl')\n" 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "metadata": { 268 | "colab_type": "raw", 269 | "id": "hi0NJle7MAs2" 270 | }, 271 | "source": [ 272 | "For demonstrating the values of the function for different inputs, we will use the following circuit. Add and remove $X$ gates as you please, in order to check $f(x)$ for different values of $x$. In this case, $q_4$ will directly give $f(x)$:" 273 | ] 274 | }, 275 | { 276 | "cell_type": "code", 277 | "execution_count": null, 278 | "metadata": { 279 | "colab": {}, 280 | "colab_type": "code", 281 | "id": "GAMumA-wMAs2" 282 | }, 283 | "outputs": [], 284 | "source": [ 285 | "#verification cell, reinitialising the circuit so that it's easier for you to copy-paste the oracle\n", 286 | "qc2=QuantumCircuit(5)\n", 287 | "#Add X gates HERE as required to change the input state\n", 288 | "qc2.barrier(range(5))\n", 289 | "\n", 290 | "#Copy and paste your code for the oracle from the above cell here\n", 291 | "\n", 292 | "\n", 293 | "qc2.barrier(range(5))\n", 294 | "\n", 295 | "measv = QuantumCircuit(5, 1)\n", 296 | "measv.measure(4,0)\n", 297 | "\n", 298 | "circv = qc2+measv\n", 299 | "circv.draw('mpl')\n", 300 | "\n" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": null, 306 | "metadata": { 307 | "colab": {}, 308 | "colab_type": "code", 309 | "id": "VW5CPwMEMAs5" 310 | }, 311 | "outputs": [], 312 | "source": [ 313 | "#DO NOT RUN THIS CELL WITHOUT EDITING THE ABOVE ONE AS DESIRED\n", 314 | "#The following code will give you f(x) for the value of x you chose in the above cell\n", 315 | "backend_sim2 = Aer.get_backend('qasm_simulator')\n", 316 | "\n", 317 | "job_sim2 = execute(circv, backend_sim2, shots=1000)\n", 318 | "\n", 319 | "result_sim2 = job_sim2.result()\n", 320 | "counts2 = result_sim2.get_counts(circv)\n", 321 | "print(counts2)\n", 322 | "plot_histogram(counts2)" 323 | ] 324 | } 325 | ], 326 | "metadata": { 327 | "colab": { 328 | "collapsed_sections": [], 329 | "name": "Day 4 Deutsch Josza algorithm.ipynb", 330 | "provenance": [] 331 | }, 332 | "kernelspec": { 333 | "display_name": "Python 3", 334 | "language": "python", 335 | "name": "python3" 336 | }, 337 | "language_info": { 338 | "codemirror_mode": { 339 | "name": "ipython", 340 | "version": 3 341 | }, 342 | "file_extension": ".py", 343 | "mimetype": "text/x-python", 344 | "name": "python", 345 | "nbconvert_exporter": "python", 346 | "pygments_lexer": "ipython3", 347 | "version": "3.8.3" 348 | } 349 | }, 350 | "nbformat": 4, 351 | "nbformat_minor": 1 352 | } 353 | -------------------------------------------------------------------------------- /Notebooks/Day 5 BB84 Protocol.ipynb: -------------------------------------------------------------------------------- 1 | {"nbformat":4,"nbformat_minor":0,"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.7.4"},"colab":{"name":"Day 5 BB84 Protocol.ipynb","provenance":[],"collapsed_sections":[]}},"cells":[{"cell_type":"markdown","metadata":{"id":"SRuLyIYAPT4S","colab_type":"text"},"source":["# Quantum Cryptography using BB84\n","This third exercise focuses on **[BB84](https://en.wikipedia.org/wiki/BB84)**, a cryptography protocol developed in 1984 by one of the most famous IBMers, Charles Bennett, together with his colleague Gilles Brassard. This scheme was realized five years later in the first demonstration of [quantum key distribution](https://en.wikipedia.org/wiki/Quantum_key_distribution) by Bennett and colleague John Smolin at IBM [C. H. Bennett, F. Bessette, G. Brassard, L. Salvail, and J. Smolin, J. of Cryptography **5**, 3 (1992) ]. Both Charles and John are still members of the IBM Quantum team.\n","\n","\n","\n","\n","
Charles Bennett and John Smolin working on the first demonstration of quantum key distribution, IBM T.J. Watson Research Center.
\n","\n","\n","\n","The goal of the BB84 protocol is to create a secret key between two parties, Alice and Bob, that can then be used by both parties to encrypt and decrypt a hidden message. In this exercise we will guide you through the different steps of the protocol to create such a secret key to decrypt our encrypted message.\n","\n","\n","## BB84 protocol\n","\n","Let's walk through the steps of the BB84 protocol:\n","\n","1. In the first step, Alice chooses two random bit strings, $k$ and $b$, that each consist of $n$ bits. Her bit string $k$ contains the actual bits she wants to encode (out of which the key will later be formed), while $b$ determines the bases in which she will encode her bits. For $b_i=0$ (i.e., if the $i^{th}$ bit is zero), she encodes the $i^{th}$ qubit in the standard $\\{|0\\rangle, |1\\rangle \\}$ basis, while for $b_i=1$, she encodes it in the $\\{|+\\rangle, |-\\rangle \\}$ basis, where $|+\\rangle:=\\frac{1}{\\sqrt{2}}(|0\\rangle +|1\\rangle)$, $|-\\rangle:=\\frac{1}{\\sqrt{2}}(|0\\rangle -|1\\rangle)$. \n","This becomes more illustrative when representing each basis by two perpendicular arrows, where the two different bases are rotated by $45^\\circ$.\n","The encoding of each qubit $q_i$ would therefore look like the following:\n","\n","\"drawing\"\n","\n","2. After encoding her $n$ qubits, Alice sends these qubits to Bob. Bob also chooses a random bit string $\\tilde{b}$ consisting of $n$ bits that determines in which bases he is going to perform measurements. He stores the outcomes of his measurements $\\tilde{k_i}$ together with the corresponding basis bits $\\tilde{b_i}$ in a table.\n","\n","3. Next, Alice and Bob compare their basis bits $b_i$ and $\\tilde{b}_i$. Whenever $b_i \\neq \\tilde{b}_i$, Bob measured in a different basis than Alice's qubit was encoded in, so he gets each outcome with probability $\\frac{1}{2}$. Alice and Bob therefore discard all key bits corresponding to these basis bits. If $b_i = \\tilde{b}_i$, however, they prepared and measured the qubit in the same basis, so (unless someone eavesdropped) Bob will get the key bit that Alice encoded, $\\tilde{k}_i = k_i$. These outcomes then compose the key.\n","\n","## An illustrated example\n","\n","Suppose Alice's random bit strings are $k=`0111001`$ and $b=`1101000`$ and Bob's random bit string is $\\tilde{b}=`1001101`$. Try to understand the other entries in the table below. Note that in the case where the basis bits are different, Bob has a 50% chance to get each outcome, so here one of them was chosen randomly.\n","\n","\"drawing\"\n","\n","The key produced in this example would be '0110'. To make sure that the key is secret and correct, Alice and Bob would \"sacrifice\" some of their key bits to check that no one eavesdropped. If someone had measured a qubit on the way, this could have changed the state of that qubit and with probability $\\frac{1}{4}$, Bob's and Alice's key bits will be different. By checking $m$ bits, the probability to not notice an eavesdropper decreases as $\\left(\\frac{3}{4}\\right)^m$. Thus, if they check enough bits and they are all the same, they can assume that no one eavesdropped and their key is secret. However, to keep things simple, we will not perfom these tests in this excercise. Instead, all bits of the key will be used.\n","\n","### Message encrpytion\n","Once a secret key is distributed, Alice can encrypt her message by using the so-called [one-time pad](https://en.wikipedia.org/wiki/One-time_pad) technique: she simply adds the key bits on top of her secret message bits that she wants to send. Using the example above, her key is $\\text{key}=`0110`$. If her secret message bit string is $m=`1100`$, the encrypted message will be $c=m\\oplus \\text{key} \\mod 2 = `1010`$. Bob can then decrypt the message by adding his key on that encrypted message, $m=c\\oplus \\text{key} \\mod 2$.\n","\n","## Your task \n","\n","In the following tasks, you play the role of Bob. You will create such a secret key with Alice and use it to decrypt the encrypted message from her.\n","\n","(**2 points**)\n"]},{"cell_type":"code","metadata":{"id":"tf9C08ykPT4t","colab_type":"code","colab":{}},"source":["%matplotlib inline\n","\n","# Importing standard Qiskit libraries\n","import random\n","from qiskit import *\n","from qiskit.tools.jupyter import *\n","from qiskit.visualization import *\n","\n","#Get the library to check the answers\n","%pip install -I git+https://github.com/mnp-club/MnP_QC_Workshop.git\n","from mnp_qc_workshop_2020.bb84 import *\n","\n","# Configuring account\n","provider = IBMQ.load_account()\n","backend = provider.get_backend('ibmq_qasm_simulator') # with this simulator it wouldn't work \\\n","\n","# Initial setup\n","random.seed(64) # do not change this seed, otherwise you will get a different key"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"BzRs6563PT44","colab_type":"text"},"source":["\n","\n","### Simulating this protocol in Qiskit\n","\n","(**8 points**)\n","\n","In this exercise, there are three steps. Each of these three steps is completed $n=16$ times. Before the protocol begins, Alice will choose two random bit strings, $k$ and $b$.\n","\n","1. Alice will prepare each qubit using the function `Alice_prepare_qubit`, which is already written for you.\n","\n","2. Bob measures his qubit using a specific set of bases, which we have given you in a variable called `bases`. **You will supply the procedure that Bob takes in the function `Bob_measure_qubit`.**\n","\n","3. A quantum circuit for this sequence of operations is created. It will be called `this_qubit_circuit` for each qubit, and all such circuits are collected together in an array called `all_qubit_circuits`. We have supplied the code to do this.\n","\n","Finally, we run `all_qubit_circuits` on the IBM high-performance cloud simulator called `ibmq_qasm_simulator` and collect the results of the measurements into a bitstring called `bits`. We have supplied the code to do this.\n","\n","You will then follow the protocol to decrypt a message using the extracted key from the BB84 protocol.\n","\n","\n","### i) Execute step 2 of the BB84 protocol to get your bitstring\n","To do so, you need a random bit string $\\tilde{b}$ that determines the bases in which you should perform the measurements. In order to reproduce the key that we used to encrypt our secret message, we provide you with this \"random\" `bases` bit string of 16 bits that uses seed \"64\".\n","**Perform measurements in the bases corresponding to the given bit string and return the outcome as a bit string in the form '$\\tilde{b}_0\\tilde{b}_1...\\tilde{b}_{n-1}$'.** Note that Qiskit returns outcomes always in reverse order, so if $|\\tilde{q}_0\\rangle = |0 \\rangle $ and $|\\tilde{q}_1\\rangle = |\\tilde{q}_2\\rangle = |1 \\rangle $, it will return the outcome '110'. You can check whether your bit string is correct by `check_bits(bitstring)`."]},{"cell_type":"code","metadata":{"id":"p_rBUf_RPT46","colab_type":"code","colab":{}},"source":["# This is your 'random' bit string that determines your bases\n","numqubits = 16\n","bob_bases = str('{0:016b}'.format(random.getrandbits(numqubits)))\n","\n","def bb84():\n"," print('Bob\\'s bases:', bob_bases)\n","\n"," # Now Alice will send her bits one by one...\n"," all_qubit_circuits = []\n"," for qubit_index in range(numqubits):\n","\n"," # This is Alice creating the qubit\n"," thisqubit_circuit = alice_prepare_qubit(qubit_index)\n","\n"," # This is Bob finishing the protocol below\n"," bob_measure_qubit(bob_bases, qubit_index, thisqubit_circuit)\n"," \n"," # We collect all these circuits and put them in an array\n"," all_qubit_circuits.append(thisqubit_circuit)\n"," \n"," \n"," # Now execute all the circuits for each qubit\n"," results = execute(all_qubit_circuits, backend=backend, shots=1).result()\n"," # And combine the results\n"," bits = ''\n"," for qubit_index in range(numqubits):\n"," bits += [measurement for measurement in results.get_counts(qubit_index)][0]\n"," \n"," return bits\n","\n","# Here is your task\n","def bob_measure_qubit(bob_bases, qubit_index, qubit_circuit):\n"," #\n"," #\n"," #Your code here\n"," #\n"," #\n"," \n","bits = bb84() \n","print('Bob\\'s bits: ', bits)\n","check_bits(bits)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"q5KORnjcPT5C","colab_type":"text"},"source":["After you performed your measurements, Alice announces her bases bits which are stored in alice_bases in the next cell\n","\n","### ii) Execute step 3 of the BB84 protocol to extract the key\n","To do so, compare Alice's bases bits $b$ to your bases bits $\\tilde{b}$ (given in the previous step) and extract the key by keeping only the outcomes when your bases were the same.\n","You can check whether your key is correct by `check_key(key)`."]},{"cell_type":"code","metadata":{"id":"69m4Px15PT5F","colab_type":"code","colab":{}},"source":["alice_bases = '0100000101011100' # Alice's bases bits\n","key = ''\n","#\n","#\n","#Your code here\n","#\n","#\n","print(key)\n","check_key(key)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"xzHVo1R_PT5N","colab_type":"text"},"source":["Using this key, Alice can now send you her private encrypted message. While for full security a key would only be used once, she will now use her 8-bit-long key 43 times in a row to encrypt her 344-bit-long message. Her encrypted message is stored in message variable of the next cell.\n","\n","A small note I would like to make is that generally the key is kept to be of a larger length since this ratio is actually very skewed but for demonstration purposes of the concept in play we will use it in this way.\n","\n","### iii) Decrypt the message using the extracted key\n","The message encryption section in the introduction describes how to do so. You can check whether your decrypted message bit string is correct by check_decrypted(decrypted)."]},{"cell_type":"code","metadata":{"id":"wi3f0W8tPT5O","colab_type":"code","colab":{}},"source":["message = get_message()# encrypted message\n","#\n","#\n","#Your code here\n","#\n","#\n","print(decrypted)\n","check_decrypted(decrypted)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"Jee1GdjiPT5a","colab_type":"text"},"source":["Now you would have obtained the decrypted message. You have a pleasent surprise waiting for you after you convert this using ASCII notation. In the cell below, convert every of the forty three 8 bit pieces of the string to a character using the regular ASCII notation. The check_message function will tell you whether you have correctly converted it or not."]},{"cell_type":"code","metadata":{"id":"42slKb6aPT5b","colab_type":"code","colab":{}},"source":["decrypted_to_string_ASCII = ''\n","#\n","#\n","#Your code here\n","#\n","#\n","check_message(decrypted_to_string_ASCII)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"mhi2TD2EPT5i","colab_type":"text"},"source":["The documentation in this notebook has been taken mainly from here. Kudos to the qiskit team for documenting this so well."]}]} -------------------------------------------------------------------------------- /Notebooks/Day 6 Grover's algorithm.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "A0-Pin4YMLhi" 8 | }, 9 | "source": [ 10 | "# Grover's Algorithm\n", 11 | "\n", 12 | "\n", 13 | "If we have a database of $n$ entries and are looking for one particular entry among these, a classical computer will require $O(N)$ operations to find the entry we're looking for. A quantum computer, on the other hand, can achieve this task using only $O(\\sqrt{N})$ operations. The quantum search algorithm used for this purpose is known as Grover’s algorithm, which provides a quadratic speed-up over classical search algorithms. " 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": null, 19 | "metadata": { 20 | "colab": {}, 21 | "colab_type": "code", 22 | "id": "DnwXu6gFMLhj" 23 | }, 24 | "outputs": [], 25 | "source": [ 26 | "from qiskit import *\n", 27 | "from qiskit.compiler import *\n", 28 | "from qiskit.tools.jupyter import *\n", 29 | "from qiskit.visualization import *\n", 30 | "import matplotlib.pyplot as plotter\n", 31 | "import numpy as np\n", 32 | "from IPython.display import display, Math, Latex\n", 33 | "%matplotlib inline\n", 34 | "# Loading your IBM Q account(s)\n", 35 | "provider = IBMQ.load_account()" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": { 41 | "colab_type": "text", 42 | "id": "wnA9Ts2KMLhm" 43 | }, 44 | "source": [ 45 | "We will start by introducing an *oracle*.\n", 46 | "\n", 47 | "## The oracle\n", 48 | "We wish to search through a search space of $N$ elements, and let us assume $N=2^n$ so that the index can be stored in $n$ bits. Suppose the search problem has $M$ solutions with $1\\leq M \\leq N$. We define a function $f$, which takes as input an integer $x$, in the range $0$ to $N-1$. By definition, $f(x) = 1$ if $x$ is a solution to the search problem, and f(x) = 0, otherwise. Suppose we have a quantum oracle, a black box, with the ability to recognize solutions to the search problem. The oracle is a unitary operator, $O$, which may make use of an additional *oracle qubit* for recognising solutions, so that its action is defined as follows:\n", 49 | "$$\\vert x\\rangle \\vert q\\rangle \\overset{O}{\\longrightarrow}\\vert x\\rangle \\vert q\\oplus f(x) \\rangle,$$ where $\\vert x\\rangle$ is the index register and the oracle qubit $\\vert q\\rangle$ is a single qubit which is flipped if $f(x) = 1$, and is unchanged otherwise. It is convenient to initially set the oracle qubit to $(\\vert 0\\rangle-\\vert 1\\rangle)/\\sqrt{2}$ since the action of the oracle becomes:\n", 50 | "$$\\vert x\\rangle \\left(\\frac{\\vert 0\\rangle-\\vert 1\\rangle}{\\sqrt2}\\right) \\overset{O}{\\longrightarrow}(-1)^{f(x)}\\vert x\\rangle\\left(\\frac{\\vert 0\\rangle-\\vert 1\\rangle}{\\sqrt2}\\right) $$\n", 51 | "Since the oracle qubit remains the same effectively, the action of the oracle may be written as:\n", 52 | "$$\\vert x\\rangle \\overset{O}{\\longrightarrow}(-1)^{f(x)}\\vert x\\rangle.$$\n", 53 | "The quantum search algorithm begins with the computer in the state $\\vert 0\\rangle^{\\otimes n}$. The Hadamard transform is used to put the computer in the equal superposition state,\n", 54 | "$$\\vert \\psi\\rangle=\\frac{1}{\\sqrt{N}}\\sum\\limits_{x=0}^{N-1} \\vert x\\rangle$$\n", 55 | "\n", 56 | "Now let's utilise Qiskit to help design some of these mysterious oracles for a few simple cases.\n", 57 | "\n", 58 | "Suppose we have $N$=4 and only one of the four states happens to be the sought after solution. Let the solution state be denoted by $\\vert w\\rangle$.\n", 59 | "\n", 60 | "We will first see what the oracle looks like in case $\\vert w\\rangle = \\vert 11\\rangle$. \n", 61 | "\n", 62 | "The way the oracle $O$ is expected to behave is as follows: $$O \\frac{1}{2}(\\vert 00\\rangle +\\vert 01\\rangle +\\vert 10\\rangle +\\vert 11\\rangle)=\\frac{1}{2}(\\vert 00\\rangle +\\vert 01\\rangle +\\vert 10\\rangle -\\vert 11\\rangle)$$" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": { 69 | "colab": {}, 70 | "colab_type": "code", 71 | "id": "URMRnOeZMLhn" 72 | }, 73 | "outputs": [], 74 | "source": [ 75 | "qc1=QuantumCircuit(2)\n", 76 | "qc1.h(0)\n", 77 | "qc1.h(1)\n", 78 | "qc1.barrier() #The part between the barriers is our oracle\n", 79 | "qc1.cz(0,1)\n", 80 | "#We are using a controlled-Z gate which flips the sign of the second qubit when both qubits are set to '1'\n", 81 | "qc1.barrier()\n", 82 | "qc1.draw('mpl')" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": { 89 | "colab": {}, 90 | "colab_type": "code", 91 | "id": "GKRV2LTQMLhq" 92 | }, 93 | "outputs": [], 94 | "source": [ 95 | "def final_vector(qc):\n", 96 | " backend = Aer.get_backend('statevector_simulator')\n", 97 | " job = execute(qc, backend)\n", 98 | " result = job.result()\n", 99 | " outputstate = result.get_statevector(qc, decimals=3)\n", 100 | " return outputstate\n", 101 | "print(final_vector(qc1)) #We can see that the desired state has been obtained\n" 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "metadata": { 107 | "colab_type": "text", 108 | "id": "ex34w2a5MLht" 109 | }, 110 | "source": [ 111 | "## Your Task\n", 112 | "1) Make an oracle for $N=8$ and $\\vert w \\rangle=\\vert 010 \\rangle$, and run it as done above." 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": null, 118 | "metadata": { 119 | "colab": {}, 120 | "colab_type": "code", 121 | "id": "kpQD8SelMLht" 122 | }, 123 | "outputs": [], 124 | "source": [ 125 | "#\n", 126 | "#\n", 127 | "#Your code here\n", 128 | "#\n", 129 | "#\n", 130 | "#draw the circuit" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "metadata": {}, 137 | "outputs": [], 138 | "source": [ 139 | "#print the final vector\n" 140 | ] 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "metadata": { 145 | "colab_type": "text", 146 | "id": "e3rnUFAvMLhw" 147 | }, 148 | "source": [ 149 | "## The Grover iteration\n", 150 | "\n", 151 | "The Grover iteration or Grover operator, denoted by $G$, consists of the following four steps, and it is applied repeatedly throughout the search algorithm.\n", 152 | "\n", 153 | "1) Apply the oracle $O$.\n", 154 | "\n", 155 | "2) Apply the Hadamard transform $H^{\\otimes n}$.\n", 156 | "\n", 157 | "3) Perform a conditional phase shift on the computer, with every computational basis state except $\\vert 0\\rangle$ receiving a phase shift of -1,\n", 158 | "$$\\vert x\\rangle\\longrightarrow -(-1)^{\\delta_{x0}}\\vert x\\rangle.$$ \n", 159 | "\n", 160 | "4) Apply the Hadamard transform $H^{\\otimes n}$.\n", 161 | "\n", 162 | "The combined effect of steps 2, 3, and 4 is\n", 163 | "$$H^{\\otimes n}(2\\vert 0\\rangle\\langle 0\\vert -1) H^{\\otimes n}=2\\vert \\psi\\rangle\\langle \\psi\\vert -I,$$ where $\\vert \\psi\\rangle$is the equally weighted superposition of states.\n", 164 | "\n", 165 | "Thus the Grover iteration may be written as\n", 166 | "$$G=(2\\vert \\psi\\rangle\\langle \\psi\\vert -I)O.$$\n", 167 | "\n", 168 | "The following is the circuit for Grover iteration, $G$:\n", 169 | "\n", 170 | "\n", 171 | "\n", 172 | "### Geometric visualisation\n", 173 | "\n", 174 | "Let $\\sideset{}{'}\\sum\\limits_x $ indicate a sum over all $x$ which are solutions to the search problem and $\\sideset{}{''}\\sum\\limits_x$ indicate a sum over all x which are not solutions to the search problem. We define normalized states\n", 175 | "$$\\vert\\alpha\\rangle=\\frac{1}{\\sqrt{N-M}}\\sideset{}{''}\\sum\\limits_x \\vert x\\rangle$$\n", 176 | "$$\\vert\\beta\\rangle=\\frac{1}{\\sqrt{M}}\\sideset{}{'}\\sum\\limits_x\\vert x\\rangle$$\n", 177 | "So, the initial state can be expressed as \n", 178 | "$$\\vert\\psi\\rangle=\\sqrt{\\frac{N-M}{N}}\\vert\\alpha\\rangle+\\sqrt{\\frac{M}{N}}\\vert\\beta\\rangle.$$\n", 179 | "The oracle operation $O$ performs a reflection about the vector $\\vert\\alpha\\rangle$ in the plane defined by $\\vert\\alpha\\rangle$ and $\\vert\\beta\\rangle$. That is, $$O(a\\vert\\alpha\\rangle + b\\vert\\beta\\rangle)=a\\vert\\alpha\\rangle-b\\vert\\beta\\rangle.$$ Similarly, $2\\vert \\psi\\rangle\\langle \\psi\\vert -I$ also performs a reflection in the plane defined by $\\vert\\alpha\\rangle$ and $\\vert\\beta\\rangle$, about the vector $\\vert\\psi\\rangle$. (To understand this, consider any unit vector $\\vert\\phi_1\\rangle$ parallel to $\\vert\\psi\\rangle$, on applying this operation, it remains the same, while any vector $\\vert\\phi_2\\rangle$ perpendicular to $\\vert\\psi\\rangle$ gets its sign flipped.)\n", 180 | "\n", 181 | "The product of two reflections is a rotation. \n", 182 | "Let $\\cos(\\theta/2)=\\sqrt{(N-M)/N}$, so that $$\\vert\\psi\\rangle = \\cos(\\theta/2)\\vert\\alpha\\rangle+ \\sin(\\theta/2)\\vert\\beta\\rangle.$$ \n", 183 | "The two reflections which comprise $G$ take $\\vert\\psi\\rangle$ to \n", 184 | "$$G\\vert\\psi\\rangle = \\cos\\left(\\frac{3\\theta}{2}\\right)\\vert\\alpha\\rangle+ \\sin\\left(\\frac{3\\theta}{2}\\right)\\vert\\beta\\rangle,$$\n", 185 | "so the rotation angle of operator $G$ in the space spanned by $\\vert\\alpha\\rangle$ and $\\vert\\beta\\rangle$ is $\\theta$. \n", 186 | "\n", 187 | "The following figure shows the action of a single Grover iteration, $G$.\n", 188 | "\n", 189 | "\n", 190 | "Continued application of $G$ takes the state to \n", 191 | "$$G^k\\vert\\psi\\rangle = \\cos\\left(\\frac{2k+1}{2}\\theta\\right)\\vert\\alpha\\rangle+ \\sin\\left(\\frac{2k+1}{2}\\theta\\right)\\vert\\beta\\rangle.$$\n", 192 | "Repeated application of the Grover iteration rotates the state vector close to $\\vert\\beta\\rangle$! Since $\\vert\\beta\\rangle$ is the superposition of all the solution states, getting as close as we can to $\\vert\\beta\\rangle$ is what we aim to achieve." 193 | ] 194 | }, 195 | { 196 | "cell_type": "markdown", 197 | "metadata": { 198 | "colab_type": "text", 199 | "id": "_SrL-1qSMLhw" 200 | }, 201 | "source": [ 202 | "## Quantum search algorithm and its performance\n", 203 | "Rotating through $\\arccos(\\sqrt{M/N})$ radians takes the system to $\\vert\\beta\\rangle$.Let CI($x$) denote the integer closest to the real number $x$, where by convention we round halves down, CI(3.5)=3, for example. Then repeating the Grover iteration \n", 204 | "$$R=CI\\frac{\\arccos\\sqrt{M/N}}{\\theta},$$\n", 205 | "times rotates $\\vert\\psi\\rangle$ to within an angle $\\theta/2 \\leq \\pi/4$ of $\\vert\\beta\\rangle$. Observing the state then yields a solution to the search problem with probability at least 1/2.\\\\\n", 206 | " Assuming for the moment that $M \\leq N/2$, we have \n", 207 | " $$\\frac{\\theta}{2}\\leq\\sin\\left(\\frac{\\theta}{2}\\right)=\\sqrt{\\frac{M}{N}}$$\n", 208 | " Since $R\\leq\\lceil\\pi/2\\theta\\rceil$, so we obtain an upper bound on the number of iterations required\n", 209 | " $$R\\leq\\left\\lceil\\frac{\\pi}{4}\\sqrt{\\frac{N}{M}}\\right\\rceil$$\n", 210 | "Thus $R=O\\sqrt{N/M})$ Grover iterations (and thus oracle calls) must be performed in order to obtain a solution to the search problem with high probability. \n", 211 | "\n", 212 | "\n", 213 | "## Algorithm: Quantum search (summarized for the case $M = 1$)\n", 214 | "### Inputs:\n", 215 | "**1.** A black box oracle $O$ which performs the transformation $O\\vert x\\rangle\\vert q\\rangle=\\vert x\\rangle\\vert q\\oplus f(x)\\rangle$ where $f(x) = 0$ for all $0 \\leq x\\leq 2^n$ except $x_0$, for which $f(x_0) = 1$\n", 216 | "\n", 217 | "**2.** $n +1$ qubits in the state $\\vert 0\\rangle$. \n", 218 | "\n", 219 | "### Outputs:\n", 220 | "$x_0$.\n", 221 | "\n", 222 | "### Runtime:\n", 223 | "$O(\\sqrt{2^n})$ operations. Succeeds with probability $O(1)$. \n", 224 | "\n", 225 | "### Procedure:\n", 226 | "\n", 227 | "**1.** $\\vert 0\\rangle^{\\otimes n}\\vert 0\\rangle$ \n", 228 | "\n", 229 | "*initial state*\n", 230 | "\n", 231 | "**2.** $\\displaystyle{\\longrightarrow \\frac{1}{\\sqrt{2^n}}\\sum_{x=0}^{2^n-1}\\vert x\\rangle\\left[\\frac{\\vert 0\\rangle-\\vert 1\\rangle}{\\sqrt{2}}\\right]}$ \n", 232 | "\n", 233 | "*apply $H^{\\otimes n}$ to the first $n$ qubits, and $HX$ to the last qubit*\n", 234 | "\n", 235 | "**3.**$\\displaystyle{\\longrightarrow [(2\\vert \\psi\\rangle\\langle \\psi\\vert -I)O]^R \\frac{1}{\\sqrt{2^n}}\\sum_{x=0}^{2^n-1}\\vert x\\rangle\\left[\\frac{\\vert 0\\rangle-\\vert 1\\rangle}{\\sqrt{2}}\\right]}$ \n", 236 | "$\\displaystyle{\\approx \\vert x_0\\rangle\\left[\\frac{\\vert 0\\rangle-\\vert 1\\rangle}{\\sqrt{2}}\\right]}$ \n", 237 | " \n", 238 | " *apply the Grover iteration $R\\approx[\\pi \\sqrt{2^n}/4]$times*\n", 239 | "\n", 240 | "**4.**$\\longrightarrow x_0$ \n", 241 | "\n", 242 | "*measure the first $n$ qubits*\n", 243 | "\n", 244 | "\n", 245 | "\n", 246 | "From the expression $\\theta = \\arcsin\\left(\\frac{2\\sqrt{M(N-M)}}{N}\\right)$, we see that the angle $\\theta$ gets smaller as $M$ varies from $N/2$ to $N$, hence increasing the number of iterations needed by the search algorithm with M, for $M \\geq N/2$, which is counter-intuitive.\n", 247 | "\n", 248 | "One way of solving this problem is if $M$ is known in advance to be larger than $N/2$ then we can just randomly pick an item from the search space and check if it is a solution using the oracle. This approach has a success probability at least one-half, and only requires one consultation with the oracle.\n", 249 | "\n", 250 | "In the case where it isn't known whether $M \\geq N/2$, the approach is to double the number of elements in the search space by adding a single qubit $\\vert q\\rangle$ to the search index which adds $N$ extra items to the search space, none of which are solutions. So, less than half the items in the new search space are solutions. A new augmented oracle $O'$ is constructed. using one call to $O$, which marks an item only if it is a solution to the search problem **and** the extra bit is set to zero. At most $R = \\pi/4\\sqrt{2N/M}$ calls to $O'$ are required, and it follows that $O(\\sqrt{N/M})$ calls to $O$ are required to perform the search." 251 | ] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": { 256 | "colab_type": "text", 257 | "id": "TlX0b2NNMLhx" 258 | }, 259 | "source": [ 260 | "## Implementation using Qiskit\n", 261 | "We will now go through the example of Grover's algorithm for 3 qubits with two solution states $\\vert 101\\rangle$ and $\\vert 110\\rangle$. So here, we have $M=2$ and $N=8$.\n", 262 | "\n", 263 | "Observe that in this case the above-defined state $\\vert \\psi\\rangle$ will make an angle $\\theta/2=\\arccos\\left(\\sqrt{\\frac{8-2}{8}}\\right)=\\pi/6$ with the $\\vert\\alpha\\rangle$ axis, and an angle $\\pi/3$ with the $\\vert\\beta\\rangle$ axis, which in this case happens to be $\\theta$, so one Grover iteration will give us the solutions states! This is portrayed below." 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": null, 269 | "metadata": { 270 | "colab": {}, 271 | "colab_type": "code", 272 | "id": "S7PK-iMQMLhx" 273 | }, 274 | "outputs": [], 275 | "source": [ 276 | "def ccz_gate(qc,a,b,c):\n", 277 | " qc.h(c)\n", 278 | " qc.ccx(a,b,c)\n", 279 | " qc.h(c)\n", 280 | " \n", 281 | " " 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": null, 287 | "metadata": { 288 | "colab": {}, 289 | "colab_type": "code", 290 | "id": "nm7YTLSxMLh0" 291 | }, 292 | "outputs": [], 293 | "source": [ 294 | "#Let's create an oracle which will mark the states 101 and 110\n", 295 | "#(This particular oracle won't be using ancilla qubits)\n", 296 | "def phase_oracle(circuit):\n", 297 | " circuit.cz(0, 2)\n", 298 | " circuit.cz(0, 1)\n", 299 | "n=3\n", 300 | "qc2=QuantumCircuit(n,n)\n", 301 | "for i in range(0,n):\n", 302 | " qc2.h(i)\n", 303 | "qc2.barrier([0,1,2])\n", 304 | "#This creates a superposition of all states\n", 305 | "\n", 306 | "#We will now perform the Grover iteration\n", 307 | "phase_oracle(qc2)\n", 308 | "qc2.barrier([0,1,2])\n", 309 | "for i in range(0,n):\n", 310 | " qc2.h(i)\n", 311 | " \n", 312 | "#Performing a conditional phase shift\n", 313 | "qc2.x(0)\n", 314 | "qc2.x(1)\n", 315 | "qc2.x(2)\n", 316 | "ccz_gate(qc2,0,1,2)\n", 317 | "qc2.x(0)\n", 318 | "qc2.x(1)\n", 319 | "qc2.x(2)\n", 320 | "for i in range(0,n):\n", 321 | " qc2.h(i)\n", 322 | "#The Grover iteration is now complete\n", 323 | "\n", 324 | "qc2.barrier([0,1,2])\n", 325 | "qc2.draw('mpl')\n" 326 | ] 327 | }, 328 | { 329 | "cell_type": "markdown", 330 | "metadata": { 331 | "colab_type": "text", 332 | "id": "qlRmf6WsMLh3" 333 | }, 334 | "source": [ 335 | "To verify our above reasoning of obtaining the solution states $\\vert 101\\rangle$ and $\\vert 110\\rangle$ after applying the above circuit, let us measure the qubits." 336 | ] 337 | }, 338 | { 339 | "cell_type": "code", 340 | "execution_count": null, 341 | "metadata": { 342 | "colab": {}, 343 | "colab_type": "code", 344 | "id": "9k5tz6KUMLh3" 345 | }, 346 | "outputs": [], 347 | "source": [ 348 | "qc2.measure(0,0)\n", 349 | "qc2.measure(1,1)\n", 350 | "qc2.measure(2,2)\n", 351 | "qc2.draw('mpl')" 352 | ] 353 | }, 354 | { 355 | "cell_type": "code", 356 | "execution_count": null, 357 | "metadata": { 358 | "colab": {}, 359 | "colab_type": "code", 360 | "id": "febj9AVbMLh5" 361 | }, 362 | "outputs": [], 363 | "source": [ 364 | "def counts_circ(circ):\n", 365 | " backend_sim = Aer.get_backend('qasm_simulator')\n", 366 | " job_sim = execute(circ, backend_sim, shots=2000)\n", 367 | " result_sim = job_sim.result()\n", 368 | " counts = result_sim.get_counts(circ)\n", 369 | " return(counts)\n", 370 | "plot_histogram(counts_circ(qc2))\n" 371 | ] 372 | }, 373 | { 374 | "cell_type": "markdown", 375 | "metadata": { 376 | "colab_type": "text", 377 | "id": "8uj-SQlaMLh8" 378 | }, 379 | "source": [ 380 | "As you can see, we have succeeded in obtaining the solution states!\n", 381 | "\n", 382 | "\n", 383 | "## Multiple-Control Toffoli gate\n", 384 | "Before we give you your next task on Grover's algorithm, we would like to introduce the very useful Multiple-Control Toffoli gate, denoted by **MCT** in Qiskit. The control qubits are specified in the form of a list as the first argument in the **mct** function, while the target qubit is entered as the second argument.\n", 385 | "\n", 386 | "Let us implement it and see what it does.\n" 387 | ] 388 | }, 389 | { 390 | "cell_type": "code", 391 | "execution_count": null, 392 | "metadata": { 393 | "colab": {}, 394 | "colab_type": "code", 395 | "id": "67YmXfDLMLh8" 396 | }, 397 | "outputs": [], 398 | "source": [ 399 | "qc_mct=QuantumCircuit(5,5)\n", 400 | "for i in range(0,4):\n", 401 | " qc_mct.x(i)\n", 402 | "qc_mct.mct([0,1,2,3],4)\n", 403 | "qc_mct.draw('mpl')" 404 | ] 405 | }, 406 | { 407 | "cell_type": "markdown", 408 | "metadata": { 409 | "colab_type": "text", 410 | "id": "D8El2qUtMLh-" 411 | }, 412 | "source": [ 413 | "This is what a multi-control Toffoli gate looks like. It reverses the target qubit if and only if all the control qubits are set to '1'. In the above example the MCT gate is applied to the state $\\vert 11110\\rangle$. Run the following cell to see the obtained state upon measurement." 414 | ] 415 | }, 416 | { 417 | "cell_type": "code", 418 | "execution_count": null, 419 | "metadata": { 420 | "colab": {}, 421 | "colab_type": "code", 422 | "id": "CD5h7ObAMLh_" 423 | }, 424 | "outputs": [], 425 | "source": [ 426 | "qc_mct.measure(0,0)\n", 427 | "qc_mct.measure(1,1)\n", 428 | "qc_mct.measure(2,2)\n", 429 | "qc_mct.measure(3,3)\n", 430 | "qc_mct.measure(4,4)\n", 431 | "plot_histogram(counts_circ(qc_mct))" 432 | ] 433 | }, 434 | { 435 | "cell_type": "markdown", 436 | "metadata": { 437 | "colab_type": "text", 438 | "id": "PXsa32QFMLiD" 439 | }, 440 | "source": [ 441 | "## Your Task\n", 442 | "2) Construct a circuit to carry out the Grover's algorithm for 4 qubits with these solution states: $\\vert 1010\\rangle, \\vert 0010\\rangle, \\vert 1001\\rangle$ and $\\vert 1000\\rangle$.\n", 443 | "\n", 444 | "You are free to use any of the functions defined above. You are required to display both the circuit and the histogram showing the end-result." 445 | ] 446 | }, 447 | { 448 | "cell_type": "code", 449 | "execution_count": null, 450 | "metadata": { 451 | "colab": {}, 452 | "colab_type": "code", 453 | "id": "j9jri4E5MLiE" 454 | }, 455 | "outputs": [], 456 | "source": [ 457 | "#\n", 458 | "#\n", 459 | "#Your code here\n", 460 | "#\n", 461 | "#\n", 462 | "# Display the circuit here finally" 463 | ] 464 | }, 465 | { 466 | "cell_type": "code", 467 | "execution_count": null, 468 | "metadata": { 469 | "colab": {}, 470 | "colab_type": "code", 471 | "id": "rOdI_LPUvK2Q" 472 | }, 473 | "outputs": [], 474 | "source": [ 475 | "#\n", 476 | "#\n", 477 | "#code for the histogram here\n", 478 | "#\n", 479 | "#" 480 | ] 481 | } 482 | ], 483 | "metadata": { 484 | "colab": { 485 | "collapsed_sections": [], 486 | "name": "Day 5 Grover's algorithm.ipynb", 487 | "provenance": [] 488 | }, 489 | "kernelspec": { 490 | "display_name": "Python 3", 491 | "language": "python", 492 | "name": "python3" 493 | }, 494 | "language_info": { 495 | "codemirror_mode": { 496 | "name": "ipython", 497 | "version": 3 498 | }, 499 | "file_extension": ".py", 500 | "mimetype": "text/x-python", 501 | "name": "python", 502 | "nbconvert_exporter": "python", 503 | "pygments_lexer": "ipython3", 504 | "version": "3.8.3" 505 | } 506 | }, 507 | "nbformat": 4, 508 | "nbformat_minor": 1 509 | } 510 | -------------------------------------------------------------------------------- /Notebooks/Day 7 Quantum Fourier transform and its applications_ Part 1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "Day 7 Quantum Fourier transform and its applications: Part 1.ipynb", 7 | "provenance": [], 8 | "collapsed_sections": [] 9 | }, 10 | "kernelspec": { 11 | "display_name": "Python 3", 12 | "language": "python", 13 | "name": "python3" 14 | }, 15 | "language_info": { 16 | "codemirror_mode": { 17 | "name": "ipython", 18 | "version": 3 19 | }, 20 | "file_extension": ".py", 21 | "mimetype": "text/x-python", 22 | "name": "python", 23 | "nbconvert_exporter": "python", 24 | "pygments_lexer": "ipython3", 25 | "version": "3.7.6" 26 | } 27 | }, 28 | "cells": [ 29 | { 30 | "cell_type": "markdown", 31 | "metadata": { 32 | "colab_type": "text", 33 | "id": "iU0wkViC72Jx" 34 | }, 35 | "source": [ 36 | "# Quantum Fourier transform and its applications: Part 1\n", 37 | "\n", 38 | "*A good idea has a way of becoming simpler and solving problems other than that for which it was intended.* \n", 39 | "– Robert Tarjan" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "metadata": { 45 | "colab_type": "code", 46 | "id": "b4txSM5S72Jy", 47 | "scrolled": true, 48 | "colab": {} 49 | }, 50 | "source": [ 51 | "from qiskit import *\n", 52 | "from qiskit.compiler import *\n", 53 | "from qiskit.tools.jupyter import *\n", 54 | "from qiskit.visualization import *\n", 55 | "import matplotlib.pyplot as plotter\n", 56 | "import scipy\n", 57 | "import numpy as np\n", 58 | "from IPython.display import display, Math, Latex\n", 59 | "import qiskit.quantum_info as qi\n", 60 | "%matplotlib inline\n", 61 | "# Loading your IBM Q account(s)\n", 62 | "provider = IBMQ.load_account()" 63 | ], 64 | "execution_count": null, 65 | "outputs": [] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": { 70 | "colab_type": "text", 71 | "id": "_6eNQ3QD72J2" 72 | }, 73 | "source": [ 74 | "You may heard about the **discrete Fourier transform (DFT)**. In the usual mathematical notation, the discrete Fourier transform takes as input a vector of complex numbers, $x_0,\\ldots,x_{N-1}$ where the length $N$ of the vector is a fixed parameter. It outputs the transformed data, a vector of complex numbers $y_0,\\ldots,y_{N-1}$, defined by\n", 75 | "$$ y_k \\equiv \\frac{1}{\\sqrt{N}} \\sum^{N-1}_{j=0} x_j e^{\\frac{2\\pi i j k }{N}} $$ \n", 76 | "\n", 77 | "The **quantum Fourier transform** is exactly the same transformation, although the conventional notation for the quantum Fourier transform is somewhat different. The quantum Fourier transform on an orthonormal basis $|0\\rangle,\\ldots, |N-1\\rangle$ is defined to be a linear operator with the following action on the basis states,\n", 78 | "$$|j\\rangle \\to \\frac{1}{\\sqrt{N}} \\sum^{N-1}_{k=0} e^{\\frac{2\\pi i j k}{N}} |k\\rangle$$\n", 79 | "Equivalently, the action on an arbitrary state may be written,\n", 80 | "$$ \\sum_{j=0}^{N-1} x_j |j\\rangle \\to \\sum_{k=0}^{N-1} y_k |k\\rangle $$\n", 81 | "where the amplitudes $y_k$ are the discrete Fourier transform of the amplitudes $x_j$.\n", 82 | "We will show later that this is indeed an unitary transformation. \n", 83 | "\n", 84 | "Now we will aim to derive the circuit for quantum Fourier transfom. But for that we can use the alternate representation of the transform. It is very helpful to write the state $|j\\rangle$ using the binary representation $j = j_1 j_2 \\ldots j_n$. More formally, $j = j_1 2^{n-1} + j_2 2^{n-2}+\\ldots+j_n 2^0$. It is also convenient to adopt the notation $0 . j_l j_{l+1} \\ldots j_m$ to represent the binary fraction $j_l/2 + j_{l+1}/4+ \\ldots + j_m/2^{m-l+1}$. using a little algebra the quantum Fourier transform can be given the following useful product representation: \n", 85 | "$$|j_1\\ldots j_n\\rangle \\to \\frac{(|0\\rangle + e^{2\\pi i 0.j_n}|1\\rangle)(|0\\rangle + e^{2\\pi i 0.j_{n-1}j_n}|1\\rangle) \\ldots (|0\\rangle + e^{2\\pi i 0.j_1j_2 \\ldots j_n}|1\\rangle)}{2^{\\frac{n}{2}}}$$\n", 86 | "We won't go into the derivation of this form, though you can verify it for some terms. This product representation is so useful that you may even wish to consider this to be the definition of the quantum Fourier transform. The product representation makes it easy to derive an efficient circuit for the quantum Fourier transform. Let us define an unitary transformation $R_k$ used in the circuit diagram figure:\n", 87 | "$$R_ k \\equiv \\left[ \\begin{matrix} 1 & 0\\\\\n", 88 | "0 & e^{\\frac{2 \\pi i}{2^k}}\n", 89 | "\\end{matrix} \\right]$$ \n", 90 | "\n", 91 | "We can see the circuit diagram below for quantum fourier transform. we will apply the gates as shown to get the product representation mentioned above which implies the this is indeed a circuit for quantum fourier transform.\n", 92 | " \n", 93 | "\n", 94 | "Do keep in mind that the swap gates at the end of the circuit which reverse the order of the qubits and normalization factors of $\\frac{1}{\\sqrt{2}}$ in the output are not shown in the figure to improve the readability.\n", 95 | " \n", 96 | "So let's start to apply gates by following the figure.\n", 97 | "\n", 98 | "Notice that applying the Hadamard gate to the first bit produces the state:\n", 99 | "$$\\frac{1}{2^{\\frac{1}{2}}}(|0\\rangle + e^{2\\pi i 0.j_1}|1\\rangle)|j_2 \\ldots j_n\\rangle$$\n", 100 | "Applying the controlled-$R_2$ gate produces the state:\n", 101 | "$$\\frac{1}{2^{\\frac{1}{2}}}(|0\\rangle + e^{2\\pi i 0.j_1j_2}|1\\rangle)|j_2 \\ldots j_n\\rangle$$\n", 102 | "We continue applying the controlled-$R_3, R_4$ through $R_n$ gates, each of which adds an extra bit to the phase of the co-efficient of the first $|1\\rangle$. At the end of this procedure we have the state: \n", 103 | "$$\\frac{1}{2^{\\frac{1}{2}}}(|0\\rangle + e^{2\\pi i 0.j_1j_2 \\cdots j_n}|1\\rangle)|j_2 \\ldots j_n\\rangle$$\n", 104 | "Next, we perform a similar procedure on the second qubit and after applying Hadamard and controlled-$R_2$ through $R_{n-1}$ gates yield the state:\n", 105 | "$$\\frac{1}{2^{\\frac{2}{2}}}(|0\\rangle + e^{2\\pi i 0.j_1j_2 \\cdots j_n}|1\\rangle)(|0\\rangle + e^{2\\pi i 0.j_2 \\cdots j_n}|1\\rangle)|j_3 \\ldots j_n\\rangle$$\n", 106 | "We continue in this fashion for each qubit, giving a final state :\n", 107 | "$$\\frac{1}{2^{\\frac{n}{2}}}(|0\\rangle + e^{2\\pi i 0.j_1j_2 \\cdots j_n}|1\\rangle)(|0\\rangle + e^{2\\pi i 0.j_2 \\cdots j_n}|1\\rangle) \\cdots (|0\\rangle + e^{2\\pi i 0.j_n}|1\\rangle)$$\n", 108 | "Swap operations which are omitted from Figure for clarity, are then used to reverse the order of the qubits. After the swap operations, the state of the qubits is:\n", 109 | "$$ \\frac{1}{2^{\\frac{n}{2}}}(|0\\rangle + e^{2\\pi i 0.j_n}|1\\rangle)(|0\\rangle + e^{2\\pi i 0.j_{n-1}j_n}|1\\rangle)\\cdots (|0\\rangle + e^{2\\pi i 0.j_1j_2 \\cdots j_n}|1\\rangle)$$\n", 110 | "\n", 111 | "\n", 112 | "Comparing with the earlier equation, we see that this is the desired output from the quantum Fourier transform. This construction also proves that the quantum Fourier transform is **unitary**, since each gate in the circuit is unitary.\n", 113 | "\n", 114 | "\n", 115 | "So this is a quite long process. Hence we will use qft_rotations and swap_registers to make qft, for your use. Go through the code to understand how it works.\n", 116 | " " 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "metadata": { 122 | "colab_type": "code", 123 | "id": "-IYxDzpn72J2", 124 | "colab": {} 125 | }, 126 | "source": [ 127 | "\n", 128 | "def qft_rotations(circuit, n):\n", 129 | " \"\"\"Performs qft on the first n qubits in circuit (without swaps)\"\"\"\n", 130 | " if n == 0:\n", 131 | " return circuit\n", 132 | " n -= 1\n", 133 | " circuit.h(n)\n", 134 | " for qubit in range(n):\n", 135 | " circuit.cu1(np.pi/2**(n-qubit), qubit, n)\n", 136 | " # At the end of our function, we call the same function again on\n", 137 | " # the next qubits (we reduced n by one earlier in the function)\n", 138 | " qft_rotations(circuit, n)\n", 139 | "def swap_registers(circuit, n):\n", 140 | " for qubit in range(n//2):\n", 141 | " circuit.swap(qubit, n-qubit-1)\n", 142 | " return circuit\n", 143 | "\n", 144 | "def qft(circuit, n):\n", 145 | " \"\"\"QFT on the first n qubits in circuit\"\"\"\n", 146 | " qft_rotations(circuit, n)\n", 147 | " swap_registers(circuit, n)\n", 148 | " return circuit\n", 149 | "\n", 150 | "# Let's see how it looks:\n", 151 | "qc = QuantumCircuit(4,4)\n", 152 | "qft(qc,4)\n", 153 | "qc.draw(output = 'mpl')" 154 | ], 155 | "execution_count": null, 156 | "outputs": [] 157 | }, 158 | { 159 | "cell_type": "markdown", 160 | "metadata": { 161 | "colab_type": "text", 162 | "id": "FY5qfFie72J5" 163 | }, 164 | "source": [ 165 | "Inverse of the quantum Fourier transform $QFT^\\dagger$ is perhaps more important than the $QFT$ in terms of its applications. We will see its use in very important problems which quantum computing solves exponentially faster than classical comupters." 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "metadata": { 171 | "colab_type": "code", 172 | "id": "lysTRO5m72J5", 173 | "colab": {} 174 | }, 175 | "source": [ 176 | "def inverse_qft(circuit, n):\n", 177 | " \"\"\"Does the inverse QFT on the first n qubits in circuit\"\"\"\n", 178 | " # First we create a QFT circuit of the correct size:\n", 179 | " qft_circ = qft(QuantumCircuit(n), n)\n", 180 | " # Then we take the inverse of this circuit\n", 181 | " invqft_circ = qft_circ.inverse()\n", 182 | " # And add it to the first n qubits in our existing circuit\n", 183 | " circuit.append(invqft_circ, circuit.qubits[:n])\n", 184 | " return circuit.decompose() # .decompose() allows us to see the individual gates\n", 185 | "\n", 186 | "qc = inverse_qft(qc,4)\n", 187 | "\n", 188 | "qc.measure(range(4),range(4))\n", 189 | "def run_circuit(qc):\n", 190 | " backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend\n", 191 | " result = execute(qc, backend, shots = 10000).result() # we run the simulation\n", 192 | " counts = result.get_counts() # we get the counts\n", 193 | " return counts\n", 194 | "\n", 195 | "counts = run_circuit(qc)\n", 196 | "print(counts)\n", 197 | "plot_histogram(counts)\n", 198 | "# we should get the intial state '0000'" 199 | ], 200 | "execution_count": null, 201 | "outputs": [] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": { 206 | "colab_type": "text", 207 | "id": "ox09CZEL72J7" 208 | }, 209 | "source": [ 210 | "## Your task\n", 211 | "\n", 212 | "1) Given $QFT^n|x\\rangle = |x\\rangle$. Figure out $n$ by applying $QFT$ different number of times. Be careful and try for different initial values of qubits as sometimes a particular state may be the eigenstate for a lesser $n$ and thus misleading. Print $n$. " 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "metadata": { 218 | "colab_type": "code", 219 | "id": "Kt_g8UuD72J8", 220 | "colab": {} 221 | }, 222 | "source": [ 223 | "qc1 = QuantumCircuit(3,3)\n", 224 | "qc1.h(0)\n", 225 | "qc1.h(1)\n", 226 | "qc1.h(2) # try for different initial values \n", 227 | "\n", 228 | "for i in range(None):# choose the number of times you want to do qft)\n", 229 | " qft(qc1,3)\n", 230 | "\n", 231 | "qc1.measure(0,0) \n", 232 | "qc1.measure(1,1)\n", 233 | "qc1.measure(2,2)\n", 234 | "def run_circuit(qc1):\n", 235 | " backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend\n", 236 | " result = execute(qc1, backend, shots = 10000).result() # we run the simulation\n", 237 | " counts = result.get_counts() # we get the counts\n", 238 | " return counts\n", 239 | "\n", 240 | "counts = run_circuit(qc1)\n", 241 | "print(counts)\n", 242 | "plot_histogram(counts)" 243 | ], 244 | "execution_count": null, 245 | "outputs": [] 246 | }, 247 | { 248 | "cell_type": "markdown", 249 | "metadata": { 250 | "colab_type": "text", 251 | "id": "g0-92LPY72J-" 252 | }, 253 | "source": [ 254 | "2) Analyze the states and their amplitudes for different cases and find the untiary matrix associated with $QFT^2$. Try to use states whose amplitudes have a different modulus (unlike $\\frac{|0\\rangle + |1\\rangle}{\\sqrt{2}}$), otherwise you may not see a change. Do this for 2 qubits i.e. print the $4\\times4$ matrix as output." 255 | ] 256 | }, 257 | { 258 | "cell_type": "code", 259 | "metadata": { 260 | "colab_type": "code", 261 | "id": "-l6ziaCp72J_", 262 | "colab": {} 263 | }, 264 | "source": [ 265 | "#\n", 266 | "#\n", 267 | "#\n", 268 | "#\n", 269 | "# Your code here\n", 270 | "#\n", 271 | "#\n", 272 | "#\n", 273 | "#" 274 | ], 275 | "execution_count": null, 276 | "outputs": [] 277 | }, 278 | { 279 | "cell_type": "markdown", 280 | "metadata": { 281 | "colab_type": "text", 282 | "id": "kIdaWNwO72KB" 283 | }, 284 | "source": [ 285 | "## The intuition\n", 286 | "\n", 287 | "The quantum Fourier transform (QFT) transforms between two bases, the computational ($Z$) basis, and the Fourier basis. The H-gate is the single-qubit QFT, and it transforms between the $Z$-basis states $|0\\rangle$ and $|1\\rangle$ to the X-basis states $|+\\rangle$ and $|−\\rangle$ . In the same way, all multi-qubit states in the computational basis have corresponding states in the Fourier basis. The QFT is simply the function that transforms between these bases.\n", 288 | "\n", 289 | "$$|\\text{State in Computational Basis}\\rangle \\quad\\underrightarrow{QFT} \\quad{}|\\text{State in Fourier Basis}\\rangle$$\n", 290 | " \n", 291 | "$$QFT|x\\rangle=|\\tilde{x}\\rangle$$\n", 292 | "(We often note states in the Fourier basis using the tilde).\n", 293 | "\n", 294 | "In the computational basis, we store numbers in binary using the states $|0\\rangle$ and $|1\\rangle$:\n", 295 | "\n", 296 | " \n", 297 | "\n", 298 | "Note the frequency with which the different qubits change; the leftmost qubit flips with every increment in the number, the next with every $2$ increments, the third with every $4$ increments, and so on. In the Fourier basis, we store numbers using different rotations around the $Z$-axis:\n", 299 | "\n", 300 | " \n", 301 | "\n", 302 | "The number we want to store dictates the angle at which each qubit is rotated around the $Z$-axis. In the state $|\\tilde{0}\\rangle$ , all qubits are in the state $|+\\rangle$ . As seen in the example above, to encode the state $|\\tilde{5}\\rangle$ on $3$ qubits, we rotated the leftmost qubit by $\\frac{5}{2^n}= \\frac{5}{8}$ full turns ( $\\frac{5}{8} × 2 \\pi$ radians). The next qubit is turned double this ( $\\frac{10}{8} × 2 \\pi$ radians, or $\\frac{10}{8}$ full turns), this angle is then doubled for the qubit after, and so on.\n", 303 | "\n", 304 | "Again, note the frequency with which each qubit changes. The leftmost qubit (qubit $0$) in this case has the lowest frequency, and the rightmost the highest.\n", 305 | "\n" 306 | ] 307 | }, 308 | { 309 | "cell_type": "markdown", 310 | "metadata": { 311 | "colab_type": "text", 312 | "id": "hxgwW41B72KC" 313 | }, 314 | "source": [ 315 | "## Your task\n", 316 | "\n", 317 | "3) Create a circuit in a function which adds $1$ to all the states from $|0000\\rangle$ to $|1110\\rangle$ and changes the state $|1111\\rangle$ to $|0000\\rangle$. Check the output for values $|1000\\rangle$, $|1101\\rangle$ and $|1111\\rangle$. Unofortunately, no ancilla qubits allowed. \n", 318 | "Hint: Change the basis. Do the work. Change it back." 319 | ] 320 | }, 321 | { 322 | "cell_type": "code", 323 | "metadata": { 324 | "colab_type": "code", 325 | "id": "dQO4XFXL72KC", 326 | "colab": {} 327 | }, 328 | "source": [ 329 | "def Modulo_increment(qc)\n", 330 | "#\n", 331 | "#\n", 332 | "#\n", 333 | "# Your code here\n", 334 | "#\n", 335 | "#\n", 336 | "return qc" 337 | ], 338 | "execution_count": null, 339 | "outputs": [] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "metadata": { 344 | "colab_type": "code", 345 | "id": "RaG4808gEQPv", 346 | "colab": {} 347 | }, 348 | "source": [ 349 | "# checking for the case of '1000'\n", 350 | "qc2 = QuantumCircuit(4,4)\n", 351 | "qc2.x(3)\n", 352 | "qc2 = Modulo_increment(qc2)\n", 353 | "qc2.measure(range(4),range(4))\n", 354 | "def run_circuit(qc2):\n", 355 | " backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend\n", 356 | " result = execute(qc2, backend, shots = 10000).result() # we run the simulation\n", 357 | " counts = result.get_counts() # we get the counts\n", 358 | " return counts\n", 359 | "\n", 360 | "counts = run_circuit(qc2)\n", 361 | "print(counts)\n", 362 | "plot_histogram(counts)" 363 | ], 364 | "execution_count": null, 365 | "outputs": [] 366 | }, 367 | { 368 | "cell_type": "code", 369 | "metadata": { 370 | "colab_type": "code", 371 | "id": "aVptPS9lEQdM", 372 | "colab": {} 373 | }, 374 | "source": [ 375 | "# checking for the case of '1101'\n", 376 | "qc3 = QuantumCircuit(4,4)\n", 377 | "qc3.x(3)\n", 378 | "qc3.x(2)\n", 379 | "qc3.x(0)\n", 380 | "qc3 = Modulo_increment(qc2)\n", 381 | "qc3.measure(range(4),range(4))\n", 382 | "def run_circuit(qc3):\n", 383 | " backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend\n", 384 | " result = execute(qc3, backend, shots = 10000).result() # we run the simulation\n", 385 | " counts = result.get_counts() # we get the counts\n", 386 | " return counts\n", 387 | "\n", 388 | "counts = run_circuit(qc3)\n", 389 | "print(counts)\n", 390 | "plot_histogram(counts)" 391 | ], 392 | "execution_count": null, 393 | "outputs": [] 394 | }, 395 | { 396 | "cell_type": "code", 397 | "metadata": { 398 | "colab_type": "code", 399 | "id": "y3cAifPQEQpB", 400 | "colab": {} 401 | }, 402 | "source": [ 403 | "# checking for the case of '1111'\n", 404 | "qc4 = QuantumCircuit(4,4)\n", 405 | "qc4.x(range(4))\n", 406 | "qc4 = Modulo_increment(qc4)\n", 407 | "qc4.measure(range(4),range(4))\n", 408 | "def run_circuit(qc4):\n", 409 | " backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend\n", 410 | " result = execute(qc4, backend, shots = 10000).result() # we run the simulation\n", 411 | " counts = result.get_counts() # we get the counts\n", 412 | " return counts\n", 413 | "\n", 414 | "counts = run_circuit(qc4)\n", 415 | "print(counts)\n", 416 | "plot_histogram(counts)" 417 | ], 418 | "execution_count": null, 419 | "outputs": [] 420 | } 421 | ] 422 | } -------------------------------------------------------------------------------- /Notebooks/Day 7 Quantum Fourier transform and its applications_ Part 2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "WyqeWtBKMXBW" 8 | }, 9 | "source": [ 10 | "# Quantum Fourier transform and its applications: Part 2\n", 11 | "\n", 12 | "Suppose $f$ is a periodic function producing a single bit as output and such that $f(x + r)=f(x)$, for some unknown $0 " 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": { 114 | "colab_type": "text", 115 | "id": "Z3udG3dpMXBi" 116 | }, 117 | "source": [ 118 | "3. We will now also make this into the state $\\frac{1}{2^t} \\sum^{2^t-1}_{x=0} |x\\rangle|1\\rangle$ since our oracle does multiplication instead of addition. Note that this is the same thing since all we need is the state. After we apply $O$ which is the multiplying oracle, we get,\n", 119 | "$$\\to \\frac{1}{2^t} \\sum_{x=0}^{2^t-1} |x\\rangle|f(x)\\rangle \\approx \\frac{1}{\\sqrt{r 2^t}} \\sum_{l=0}^{r-1} \\sum_{x=0}^{2^t-1} e^{2 \\pi i l x/r} |x\\rangle |\\hat{f}(l)\\rangle$$ \n", 120 | "\n", 121 | "Here we are taking $f(x) = 7^x \\text{ mod } 15$. Looking closely at it, it will reveal that it is designed to give a period of $4$. We will verify it. So the Oracle function is given below. Notice that even though we can take many other functions but finding the Oracle can be extremely difficult." 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": null, 127 | "metadata": { 128 | "colab": {}, 129 | "colab_type": "code", 130 | "id": "DNO4AHiVMXBj" 131 | }, 132 | "outputs": [], 133 | "source": [ 134 | "# Oracle for the f(x) mentioned above\n", 135 | "def Oracle(qc):\n", 136 | " for q in range(3):\n", 137 | " qc.append(__c_amod15(2**q), [q] + [i+3 for i in range(4)])\n", 138 | "\n", 139 | "Oracle(qc)" 140 | ] 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "metadata": { 145 | "colab_type": "text", 146 | "id": "Tjnr61uNMXBl" 147 | }, 148 | "source": [ 149 | "4. Now, by applying the inverse Fourier transform to the first register, we get:\n", 150 | "$$\\to \\frac{1}{\\sqrt{r}} \\sum_{l=0}^{r-1} |\\widetilde{l/r}\\rangle|\\hat{f}(l)\\rangle$$\n", 151 | "\n", 152 | "5. Then, measure the first register:\n", 153 | "$$ \\to \\widetilde{l/r}$$\n", 154 | "\n", 155 | "Now this has some abuse of notation happening here. So first off, the actual state would not be $\\widetilde{l/r}$ but rather would be $\\widetilde{l/r}*(2^t)\\mod(2^t)$ as the state stored in that register so on measurement you would have to analyze the results keeping this in mind. Using the results keeping this in mind, you can analyze the fractions of their phases of the eigen values of the oracle (the logic of phase estimation) and that would be $(\\widetilde{l/r}*(2^t)\\mod(2^t))/(2^t)$." 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": null, 161 | "metadata": { 162 | "colab": {}, 163 | "colab_type": "code", 164 | "id": "x2fCtwMZMXBl" 165 | }, 166 | "outputs": [], 167 | "source": [ 168 | "qc.append(qft_dagger(t),range(t)) # inverse quantum fourier transform only of the register (first 4 qubits)\n", 169 | "qc.measure(range(t), range(t))\n", 170 | "def run_circuit(qc):\n", 171 | " backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend\n", 172 | " result = execute(qc, backend, shots = 100000).result() # we run the simulation\n", 173 | " counts = result.get_counts() # we get the counts\n", 174 | " return counts\n", 175 | "\n", 176 | "counts = run_circuit(qc)\n", 177 | "print(counts)\n", 178 | "plot_histogram(counts)" 179 | ] 180 | }, 181 | { 182 | "cell_type": "markdown", 183 | "metadata": { 184 | "colab_type": "text", 185 | "id": "oV7BnZB3MXBp" 186 | }, 187 | "source": [ 188 | "6. These results can now give us the phase estimations from their actual values and the next few cells will be to analyze these results so you can figure out the period. On a sidenote one can also see that since we have four equally possible results this would imply a period of four but for the sake of formality we must analyze this more.\n" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": null, 194 | "metadata": { 195 | "colab": {}, 196 | "colab_type": "code", 197 | "id": "5NwzAcUEMXBp" 198 | }, 199 | "outputs": [], 200 | "source": [ 201 | "rows, eigenvalues = [], []\n", 202 | "for output in counts:\n", 203 | " decimal = int(output, 2)\n", 204 | " eigenvalue = decimal/(2**t)\n", 205 | " eigenvalues.append(eigenvalue)\n", 206 | " rows.append([\"%s(bin) = %i(dec)\" % (output, decimal), \"%i/%i = %.2f\" % (decimal, 2**t, eigenvalue)])\n", 207 | "print(tabulate(rows, headers=[\"Register Output\", \"Phase\"]))" 208 | ] 209 | }, 210 | { 211 | "cell_type": "markdown", 212 | "metadata": { 213 | "colab_type": "text", 214 | "id": "WfIc-w06MXBt" 215 | }, 216 | "source": [ 217 | "We can use built-in Python functionality `.as_integer_ratio()` to convert these phases to fractions:" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": null, 223 | "metadata": { 224 | "colab": {}, 225 | "colab_type": "code", 226 | "id": "fsQ-hjwfMXBt" 227 | }, 228 | "outputs": [], 229 | "source": [ 230 | "0.333333333.as_integer_ratio()" 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": null, 236 | "metadata": { 237 | "colab": {}, 238 | "colab_type": "code", 239 | "id": "Hm0vWFqFMXBw" 240 | }, 241 | "outputs": [], 242 | "source": [ 243 | "rows = []\n", 244 | "for eigenvalue in eigenvalues:\n", 245 | " numerator, denominator = eigenvalue.as_integer_ratio()\n", 246 | " rows.append([eigenvalue, \"%i/%i\" % (numerator, denominator), denominator])\n", 247 | "print(tabulate(rows, headers=[\"Phase\", \"Fraction\", \"Guess for r\"], colalign=('right','right','right')))" 248 | ] 249 | }, 250 | { 251 | "cell_type": "markdown", 252 | "metadata": { 253 | "colab_type": "text", 254 | "id": "vwkxnrRzMXBy" 255 | }, 256 | "source": [ 257 | "$2$ of our guesses for $r$ are $4$. This implies the period of function is $4$." 258 | ] 259 | } 260 | ], 261 | "metadata": { 262 | "colab": { 263 | "collapsed_sections": [], 264 | "name": "Day 6.2 Quantum Fourier transform and its applications: Part 2.ipynb", 265 | "provenance": [] 266 | }, 267 | "kernelspec": { 268 | "display_name": "Python 3", 269 | "language": "python", 270 | "name": "python3" 271 | }, 272 | "language_info": { 273 | "codemirror_mode": { 274 | "name": "ipython", 275 | "version": 3 276 | }, 277 | "file_extension": ".py", 278 | "mimetype": "text/x-python", 279 | "name": "python", 280 | "nbconvert_exporter": "python", 281 | "pygments_lexer": "ipython3", 282 | "version": "3.7.4" 283 | } 284 | }, 285 | "nbformat": 4, 286 | "nbformat_minor": 1 287 | } 288 | -------------------------------------------------------------------------------- /Notebooks/Final Question 1 Discrete Logarithm.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "TGif5OgHa65Z" 8 | }, 9 | "source": [ 10 | "# The Discrete Logarithm problem\n", 11 | "\n", 12 | "The discrete logarithm problem is that if given $a$ and $b = a^s$, we have to determine $s$. Say we have the function $f(x_1,x_2) = a^{sx_1 + x_2}\\mod N$ and let say $r$ is the smallest positive integer $a^r\\mod N = 1$. This function's period is a tuple $(l,-ls)$. Say we have a unitary $U|{x_1}\\rangle|{x_2}\\rangle|{y}\\rangle = |{x_1}\\rangle|{x_2}\\rangle|{y \\oplus f(x_1,x_2)}\\rangle$ we can actually find its period in a similar manner to the period finding algorithm described previously.\n", 13 | "\n", 14 | "So we just start with the state $|{0}\\rangle|{0}\\rangle|{0}\\rangle$ over three registers where the first two have $t = O(\\text{ceil}(\\log r + \\log(1/\\epsilon)))$ qubits and the third one stores the function and then create the superposition states for the first two register. We then apply the unitary on the state and that can be decomposed in a similar way as shown in step 3 of the period finding algorithms procedure to \n", 15 | "$$\\dfrac{1}{2^t}\\sum_{x_1=0}^{2^t-1}\\sum_{x_2=0}^{2^t-1}|{x_1}\\rangle|{x_2}\\rangle|{f(x_1,x_2)}\\rangle$$ \n", 16 | "$$\\approx \\dfrac{1}{2^t\\sqrt{r}}\\sum_{l_2=0}^{r-1}\\sum_{x_1=0}^{2^t-1}\\sum_{x_2=0}^{2^t-1}e^{2\\pi\\iota(sl_2x_1 + l_2x_2)/r}|{x_1}\\rangle|{x_2}\\rangle|{\\hat{f}(sl_2,l_1)}\\rangle$$\n", 17 | "\\begin{equation}= \\dfrac{1}{2^t\\sqrt{r}}\\sum_{l_2=0}^{r-1}\\left[\\sum_{x_1=0}^{2^t-1}e^{2\\pi\\iota(sl_2x_1)/r}\\right]\\left[\\sum_{x_2=0}^{2^t-1}e^{2\\pi\\iota(l_2x_2)/r}\\right]|{x_1}\\rangle|{x_2}\\rangle|{\\hat{f}(sl_2,l_1)}\\rangle\\end{equation}\n", 18 | "\n", 19 | "We can see that applying the inverse Fourier transform and see that we will be left with a state to be this\n", 20 | "$$\\dfrac{1}{\\sqrt{r}}\\sum_{l_2 = 0}^{r - 1}|sl_2/r\\rangle|l_2/r\\rangle|{\\hat{f}(sl_2,l_2)}\\rangle$$\n", 21 | "Now this has some abuse of notation happening here. So first off, the actual state would not be $|sl_2/r\\rangle$ but rather would be $|sl_2/r*(2^t)\\mod(2^t)\\rangle$ as the state stored in that register so on measurement you would have to analyze the results keeping this in mind. Using the results keeping this in mind, you can analyze the fractions of their phases of the eigen values of the oracle (the logic of phase estimation) and that would be $(sl_2/r*(2^t)\\mod(2^t))/(2^t)$.\n", 22 | "\n", 23 | "Note that $|{\\hat{f}(l_1,l_2)}\\rangle = \\dfrac{1}{\\sqrt{r}}\\sum_{j=0}^{r-1}e^{-2\\pi\\iota{l_2j}/r}|{f(0,j)}\\rangle$ which is the Fourier transform of $f(x_1,x_2)$. Note that $l_1,l_2$ must satisfy $\\sum_{k=0}^{r-1}e^{2\\pi\\iota{k(l_1/s - l_2)}/r} = r$ else the amplitude of $f(l_1,l_2)$ would go to zero.\n", 24 | "\n", 25 | "## Corrections made:\n", 26 | "The oracle actually does the operation $O|x_1\\rangle|x_2\\rangle|y\\rangle = |x_1\\rangle|x_2\\rangle|y * f(x_1,x_2)\\rangle$ and $f(x_1,x_2) = a^{x_1}b^{x_2}\\mod{15}$ whereas in the uncorrected version it was written as $O|x_1\\rangle|x_2\\rangle|y\\rangle = |x_1\\rangle|x_2\\rangle|y * f(x_1,x_2)\\rangle$ and $f(x_1,x_2) = b^{x_1}a^{x_2}\\mod{15}$. This doesn't change any of the underlying concept since this just switches the position of the two registers otherwise it is the exact same. We are sorry for any inconveniences caused.\n" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": null, 32 | "metadata": { 33 | "colab": {}, 34 | "colab_type": "code", 35 | "id": "6CE9PWH0a65a" 36 | }, 37 | "outputs": [], 38 | "source": [ 39 | "!pip install tabulate\n", 40 | "%matplotlib inline\n", 41 | "# Importing standard Qiskit libraries and configuring account\n", 42 | "from qiskit import QuantumCircuit, execute, Aer, IBMQ\n", 43 | "from qiskit.compiler import transpile, assemble\n", 44 | "from qiskit.tools.jupyter import *\n", 45 | "from qiskit.visualization import *\n", 46 | "from tabulate import tabulate\n", 47 | "# Loading your IBM Q account(s)\n", 48 | "provider = IBMQ.load_account()\n", 49 | "#Get the library to check the answers\n", 50 | "%pip install -I git+https://github.com/mnp-club/MnP_QC_Workshop.git\n", 51 | "from mnp_qc_workshop_2020.discrete_logarithm import qft_dagger, oracle, check_answer" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": { 57 | "colab_type": "text", 58 | "id": "u1DMBsY2a65e" 59 | }, 60 | "source": [ 61 | "A thing we can note is that we do not neccessarily need to construct the oracle for $U|x_1\\rangle|x_2\\rangle|y\\rangle = |x_1\\rangle|x_2\\rangle|y \\oplus f(x_1,x_2)\\rangle$ we can also use the oracle for $U|x_1\\rangle|x_2\\rangle|y\\rangle = |x_1\\rangle|x_2\\rangle|y * f(x_1,x_2)\\rangle$ and just set $|y\\rangle = |1\\rangle$.\n", 62 | "\n", 63 | "## Your task\n", 64 | "\n", 65 | "So you are now given an oracle which does the following operation $O|x_1\\rangle|x_2\\rangle|y\\rangle = |x_1\\rangle|x_2\\rangle|y * f(x_1,x_2)\\rangle$ and $f(x_1,x_2) = a^{x_1}b^{x_2}\\mod{15}$ and here $|x_1\\rangle$ and $|x_2\\rangle$ are 3 qubit states and quite trivially the state of $|y * f(x_1,x_2)\\rangle$ will be stored using 4 qubits. Your task is to find the values of $a$ and $b$ using the discrete logarithm setup described above to find the minimum $s$ (which is an integer) for $a^s = b$ and you are free to perform additional analysis using the oracle to find out the values of $a$ and $b$. An obvious clue that you can see is that both $a$ and $b$ are coprime with 15. Also assume that $a,b < 15$\n", 66 | "\n", 67 | "For performing inverse fourier transform over some $n$ qubits from the circuit just use QuantumCircuit.append(qft_dagger(n), qubits) and for attaching the oracle use QuantumCircuit.append(oracle(), qubits).\n", 68 | "\n", 69 | "**Note:** use the qiskit notation everywhere except for the third register so if you want to put the thrd register into the state $|1\\rangle$ you have to apply an $X$ gate at the q_9 which is the first qubit of the third register.\n", 70 | "\n", 71 | "There is a possibility that you would have reached the answer pretty easily but I would urge you to reach it with as much rigor as possible. This question is anyway in the bonus category so you need not think about the points you will receive." 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": null, 77 | "metadata": { 78 | "colab": {}, 79 | "colab_type": "code", 80 | "id": "pDomYpOFa65e" 81 | }, 82 | "outputs": [], 83 | "source": [ 84 | "n_qubits = 3 #Number of qubits for the registers of |x1> and |x2>\n", 85 | "#For figuring out you would just need 2*n_qubits classical bits\n", 86 | "qc_disc_log = QuantumCircuit(4+n_qubits+n_qubits, n_qubits+n_qubits)\n", 87 | "#\n", 88 | "#\n", 89 | "#Your code here\n", 90 | "#\n", 91 | "#\n", 92 | "qc_disc_log.draw('text')" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": { 98 | "colab_type": "text", 99 | "id": "-9Pbav4xa65h" 100 | }, 101 | "source": [ 102 | "Now that you have made the required circuit (hopefully it is correct) let us go ahead and run it and check our results. If done correctly you can expect four values to only be reached." 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": null, 108 | "metadata": { 109 | "colab": {}, 110 | "colab_type": "code", 111 | "id": "W7KowxVoa65h" 112 | }, 113 | "outputs": [], 114 | "source": [ 115 | "backend = Aer.get_backend('qasm_simulator')\n", 116 | "results = execute(qc_disc_log, backend, shots=8192).result()\n", 117 | "counts = results.get_counts()\n", 118 | "plot_histogram(counts)" 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "metadata": { 124 | "colab_type": "text", 125 | "id": "_Js1caj-a65k" 126 | }, 127 | "source": [ 128 | "Let us now analyze these results. Do refer the explanation in the beginning to properly analyze the results and infer a value of $s$ from there." 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": null, 134 | "metadata": { 135 | "colab": {}, 136 | "colab_type": "code", 137 | "id": "lmV9ckEFa65k" 138 | }, 139 | "outputs": [], 140 | "source": [ 141 | "rows_x_1, eigenvalues_x_1 = [], []\n", 142 | "for output in counts:\n", 143 | " decimal = int(output, 2)//8\n", 144 | " eigenvalue = decimal/(2**3)\n", 145 | " eigenvalues_x_1.append(eigenvalue)\n", 146 | " rows_x_1.append([\"%s(bin) = %i(dec)\" % (output[0:3], decimal), \"%i/%i = %.2f\" % (decimal, 2**3, eigenvalue)])\n", 147 | "print(tabulate(rows_x_1, headers=[\"Register Output\", \"Phase\"]))" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "metadata": { 154 | "colab": {}, 155 | "colab_type": "code", 156 | "id": "xfo-sZHfa65n" 157 | }, 158 | "outputs": [], 159 | "source": [ 160 | "rows_x_2, eigenvalues_x_2 = [], []\n", 161 | "for output in counts:\n", 162 | " decimal = int(output, 2) - ((int(output, 2)//8)*8)\n", 163 | " eigenvalue = decimal/(2**3)\n", 164 | " eigenvalues_x_2.append(eigenvalue)\n", 165 | " rows_x_2.append([\"%s(bin) = %i(dec)\" % (output[3:6], decimal), \"%i/%i = %.2f\" % (decimal, 2**3, eigenvalue)])\n", 166 | "print(tabulate(rows_x_2, headers=[\"Register Output\", \"Phase\"]))" 167 | ] 168 | }, 169 | { 170 | "cell_type": "markdown", 171 | "metadata": { 172 | "colab_type": "text", 173 | "id": "ceXYqpjQa65q" 174 | }, 175 | "source": [ 176 | "You have hopefully got the value of $s$ from these analysis. Go ahead and store it in the variable s in the next cell." 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": null, 182 | "metadata": { 183 | "colab": {}, 184 | "colab_type": "code", 185 | "id": "w30ayU2Ta65q" 186 | }, 187 | "outputs": [], 188 | "source": [ 189 | "#Store your value for s here\n", 190 | "s = None" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": { 196 | "colab_type": "text", 197 | "id": "KKk8FQBPa65s" 198 | }, 199 | "source": [ 200 | "Now you can go ahead and analyze the oracle for getting an idea of the value of $a$ and $b$. Another clue for figuring that out is that in the results for discrete logarithm, you should only get four possibilities and this should tell you the order of $a$ with respect to $15$." 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": null, 206 | "metadata": { 207 | "colab": {}, 208 | "colab_type": "code", 209 | "id": "8WqxaxZwa65t" 210 | }, 211 | "outputs": [], 212 | "source": [ 213 | "#Do analysis of oracle here" 214 | ] 215 | }, 216 | { 217 | "cell_type": "markdown", 218 | "metadata": { 219 | "colab_type": "text", 220 | "id": "R82CKHHza65v" 221 | }, 222 | "source": [ 223 | "Now if you have figured out the values of $a$ and $b$ you can go ahead and store them in the variables a, b in the next cell" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "metadata": { 230 | "colab": {}, 231 | "colab_type": "code", 232 | "id": "0Rnrf4w-a65w" 233 | }, 234 | "outputs": [], 235 | "source": [ 236 | "#Store values of a and b here\n", 237 | "a, b = None" 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": { 243 | "colab_type": "text", 244 | "id": "6xevyTDca65y" 245 | }, 246 | "source": [ 247 | "You can now get your answers checked by running the below cell" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": null, 253 | "metadata": { 254 | "colab": {}, 255 | "colab_type": "code", 256 | "id": "oLOG4Lx_a65y" 257 | }, 258 | "outputs": [], 259 | "source": [ 260 | "check_answer(s,a,b)" 261 | ] 262 | } 263 | ], 264 | "metadata": { 265 | "colab": { 266 | "collapsed_sections": [], 267 | "name": "Final Question 1: Discrete Logarithm.ipynb", 268 | "provenance": [] 269 | }, 270 | "kernelspec": { 271 | "display_name": "Python 3", 272 | "language": "python", 273 | "name": "python3" 274 | }, 275 | "language_info": { 276 | "codemirror_mode": { 277 | "name": "ipython", 278 | "version": 3 279 | }, 280 | "file_extension": ".py", 281 | "mimetype": "text/x-python", 282 | "name": "python", 283 | "nbconvert_exporter": "python", 284 | "pygments_lexer": "ipython3", 285 | "version": "3.7.4" 286 | } 287 | }, 288 | "nbformat": 4, 289 | "nbformat_minor": 1 290 | } 291 | -------------------------------------------------------------------------------- /Notebooks/Final Question 2 Unitary Circuit Building.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "o4QmdyVMK1LG" 8 | }, 9 | "source": [ 10 | "# Circuit Decomposition\n", 11 | "\n", 12 | "You may recall from your quantum mechanics course that quantum theory is unitary. The aim of a quantum computer essentially boils down to executing unitaries and as you have seen previously we also explored universality in quantum computing\n", 13 | "\n", 14 | "**\"A set of quantum gates is said to be universal if any unitary transformation of the quantum data can be efficiently approximated arbitrarily well as a sequence of gates in the set.\"** (https://qiskit.org/textbook/ch-algorithms/defining-quantum-circuits.html)\n", 15 | "\n", 16 | "Every gate you run on the IBM Quantum Experience is transpiled into single qubit rotations and CNOT (CX) gates. We know that these constitute a universal gate set, which implies that any unitary can be implemented using only these gates. However, in general it is not easy to find a good decomposition for an arbitrary unitary. Your task is to find such a decomposition for given unitary." 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "colab_type": "text", 23 | "id": "SpKn-fs6K1LH" 24 | }, 25 | "source": [ 26 | "First off let us import the required functions for checking circuit and supplying the unitary." 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": null, 32 | "metadata": { 33 | "colab": {}, 34 | "colab_type": "code", 35 | "id": "m8y54EWQK1LH" 36 | }, 37 | "outputs": [], 38 | "source": [ 39 | "# Importing standard Qiskit libraries and configuring account\n", 40 | "from qiskit import *\n", 41 | "from qiskit.compiler import *\n", 42 | "from qiskit.tools.jupyter import *\n", 43 | "from qiskit.visualization import *\n", 44 | "import matplotlib.pyplot as plotter\n", 45 | "import scipy\n", 46 | "import numpy as np\n", 47 | "from IPython.display import display, Math, Latex\n", 48 | "import qiskit.quantum_info as qi\n", 49 | "%matplotlib inline\n", 50 | "%pip install -I git+https://github.com/mnp-club/MnP_QC_Workshop.git\n", 51 | "from mnp_qc_workshop_2020.unitary_circuit import *" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": { 57 | "colab_type": "text", 58 | "id": "_CVsHXouK1LL" 59 | }, 60 | "source": [ 61 | "

Now you can have a look at the unitary. We are just going to plot some graphs to give an idea of what it looks like.

" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "metadata": { 68 | "colab": {}, 69 | "colab_type": "code", 70 | "id": "08zbPBmbK1LL" 71 | }, 72 | "outputs": [], 73 | "source": [ 74 | "U = get_unitary()\n", 75 | "print(U.shape)\n", 76 | "fig, (ax1, ax2) = plotter.subplots(nrows = 1, ncols = 2, figsize=(12,6))\n", 77 | "ax1.imshow(np.real(U)) #plot real parts of each element\n", 78 | "ax2.imshow(np.imag(U)) #plot imaginary parts of each element\n", 79 | "fig, (ax3, ax4) = plotter.subplots(nrows = 1, ncols = 2, figsize=(12,6))\n", 80 | "ax3.imshow(np.abs(U)) #plot the absolute values of each element\n", 81 | "ax4.imshow(np.angle(U)) #plot the phase angles of each element" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": { 87 | "colab_type": "text", 88 | "id": "c1TY8cvgK1LO" 89 | }, 90 | "source": [ 91 | "

Although the unitary doesnt look all that pleasant, it is symmetric and also has some interesting properties which it would disclose if you happen to do some modifications to it. You might find it to show some cleaner shape if you happen to multiply certain things to it.

" 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "metadata": { 97 | "colab_type": "text", 98 | "id": "6-HpII9xK1LO" 99 | }, 100 | "source": [ 101 | "## Your task\n", 102 | "\n", 103 | "**Using only single qubit rotations and CNOT gates, find a quantum circuit that approximates that unitary $U$ by a unitary $V$ up to an error $\\varepsilon = 0.01$, such that $\\lVert U - V\\rVert_2 \\leq \\varepsilon$ !** \n", 104 | "\n", 105 | "Note that the norm we are using here is the spectral norm, $\\qquad \\lVert A \\rVert_2 = \\max_{\\lVert \\psi \\rVert_2= 1} \\lVert A \\psi \\rVert$.\n", 106 | "\n", 107 | "This can be seen as the largest scaling factor that the matrix $A$ has on any initial (normalized) state $\\psi$. One can show that this norm corresponds to the largest singular value of $A$, i.e., the square root of the largest eigenvalue of the matrix $A^\\dagger A$, where $A^{\\dagger}$ denotes the conjugate transpose of $A$.\n", 108 | "\n", 109 | "**When you submit a circuit, we remove the global phase of the corresponding unitary $V$ before comparing it with $U$ using the spectral norm. For example, if you submit a circuit that generates $V = \\text{e}^{i\\theta}U$, we remove the global phase $\\text{e}^{i\\theta}$ from $V$ before computing the norm, and you will have a successful submission. As a result, you do not have to worry about matching the desired unitary, $U$, up to a global phase.**\n", 110 | "\n", 111 | "The cost function that we have defined is something like this\n", 112 | "$$\n", 113 | "\\qquad \\text{cost} = 10 \\cdot n_{cx} + n_{u3}\n", 114 | "$$\n", 115 | "\n", 116 | "**Note that you will need to ensure that your circuit is composed only of $u3$ and $cx$ gates. The exercise is considered correctly solved if your cost is smaller than 1200.**\n", 117 | "\n", 118 | "In the cell below you can go ahead and try to execute this in a circuit. Note that we have added a transpile function in the cell below since we are going to calculate the cost after your circuit has been transpiled into only CNOT gates and U3 gates. You can find the documentation for the transpile function here.\n", 119 | "\n", 120 | "Another function that might come in handy for starting with is the QuantumCircuit.unitary(obj, qubits[, label]) which simply applies the unitary gate obj to the given qubits." 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": null, 126 | "metadata": { 127 | "colab": {}, 128 | "colab_type": "code", 129 | "id": "l88Q-3mwK1LP" 130 | }, 131 | "outputs": [], 132 | "source": [ 133 | "qc = QuantumCircuit(4)\n", 134 | "#\n", 135 | "#\n", 136 | "#Your code here\n", 137 | "#\n", 138 | "#\n", 139 | "qc = transpile(qc,basis_gates=['cx','u3'],optimization_level=3)\n", 140 | "qc.draw('mpl')" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": null, 146 | "metadata": { 147 | "colab": {}, 148 | "colab_type": "code", 149 | "id": "4E3w6VTiK1LS", 150 | "scrolled": true 151 | }, 152 | "outputs": [], 153 | "source": [ 154 | "#Run this cell for getting your circuit checked\n", 155 | "check_circuit(qc)" 156 | ] 157 | } 158 | ], 159 | "metadata": { 160 | "colab": { 161 | "collapsed_sections": [], 162 | "name": "Final Question 2: Unitary Circuit Building.ipynb", 163 | "provenance": [] 164 | }, 165 | "kernelspec": { 166 | "display_name": "Python 3", 167 | "language": "python", 168 | "name": "python3" 169 | }, 170 | "language_info": { 171 | "codemirror_mode": { 172 | "name": "ipython", 173 | "version": 3 174 | }, 175 | "file_extension": ".py", 176 | "mimetype": "text/x-python", 177 | "name": "python", 178 | "nbconvert_exporter": "python", 179 | "pygments_lexer": "ipython3", 180 | "version": "3.7.4" 181 | } 182 | }, 183 | "nbformat": 4, 184 | "nbformat_minor": 1 185 | } 186 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quantum Computing Workshop 2020 2 | 3 | Welcome to our Quantum Computing workshop. The main aim of this workshop will be to introduce some concepts of quantum computing and implement them using **Qiskit**, an open source python library developed by IBM for simulation of quantum computers. This is a very fun and useful library for anyone wanting to run quantum computing workshops and it is also quite accessible. 4 | 5 | As of now, the workshop is closed and completed. We would like to thank all of you for your participation and submissions which have made this workshop a success. We surely hope that you enjoyed this workshop and learnt something new :)
6 | The certificates will be awarded on the basis of the answers in the submissions which were received. 7 | 8 | ## Starting up 9 | 10 | So to start off you need an IBM id so that you can use the IBM Quantum Experience. 11 | 12 | You can find the IBM Quantum Experience [here](https://quantum-computing.ibm.com/). 13 | 14 | If you are familiar with github or have github desktop we recommend cloning the repo to your computer or if you arent familiar with github then you can simply download as zip by clicking at the green **Code** button above and select **Download ZIP**. You will have to of course download it again as we update more notebooks in here. 15 | To download the notebooks individually you have to select **Raw** button and then save as on that page and you will have to choose the save as in *all files* and it would automatically come as Notebook_name.ipynb.txt so you have to save it as just Notebook_name.ipynb by removing the .txt from the extension. In case your computer doesnt allow you to actually change extensions, you can open it in notepad or any text editor and save as in *all files* and keep the .ipynb extension and you will be done.
16 | Alternatively you may also choose to open [google colab](https://colab.research.google.com/) and here select the **Github** tab and enter the link of this repo and it will list all the notebooks in this repo and you can then proceed to download from there however we will strongly recommend you to not use google colab as you may face errors. 17 | 18 | Once you have set up your account you can use the notebooks provided to you by going on the side tab on [this](https://quantum-computing.ibm.com/) page and selecting **Qiskit Notebooks** under the Tools section and selecting **Import** and simply uploading the notebooks that you have downloaded from here. 19 | If you wish to run these notebooks locally you will require **Anaconda** and **Jupyter** which comes by default along with **Anaconda**. 20 | 21 | You will also require the packages **numpy**, **scipy**, **matplotlib** and **os** but these come included with **Jupyter** anyway.
22 | To install qiskit follow [this guide](https://qiskit.org/documentation/install.html). Note that you will also need to store your IBM credentials locally as a .json file to run these notebooks locally. 23 | 24 | Once you are done with all this you can start with the notebooks :) 25 | 26 | ## Material of the Workshop 27 | 28 | [Day 0 Python Revision](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Notebooks/Day%200%20Python%20Revision.ipynb)
29 | [Day 1 Introduction to Quantum Circuits](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Notebooks/Day%201%20Introduction%20to%20Quantum%20Circuits.ipynb) | [Solutions](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Solutions/Day%201%20solutions.ipynb)
30 | [Day 2.1 Quantum Circuits Continued](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Notebooks/Day%202.1%20Quantum%20Circuits%20Continued.ipynb) | [Solutions](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Solutions/Day%202.1%20Quantum%20Circuits%20Continued%20(Solutions).ipynb)
31 | [Day 2.2 Quantum TELIportation](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Notebooks/Day%202.2%20Quantum%20TELIportation.ipynb) (*sic*) | [Solutions](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Solutions/Day%202.2%20solutions.ipynb)
32 | [Day 3 Universality](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Notebooks/Day%203%20Universality.ipynb) | [Solutions](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Solutions/Day%203%20Universality%20Solutions.ipynb)
33 | [Day 4 Deutsch Josza algorithm](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Notebooks/Day%204%20Deutsch%20Josza%20algorithm.ipynb) | [Solutions](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Solutions/Day%204%20Deutsch%20Josza%20algorithm%20(Solution).ipynb)
34 | [Day 5 BB84 Protocol](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Notebooks/Day%205%20BB84%20Protocol.ipynb) | [Solutions](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Solutions/Day%205%20BB84%20Protocol%20Solutions.ipynb)
35 | [Day 6 Grover's algorithm](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Notebooks/Day%206%20Grover's%20algorithm.ipynb) | [Solutions](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Solutions/Day%206%20Grover's%20algorithm%20solutions.ipynb)
36 | [Day 7 Quantum Fourier transform and its applications Part 1](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Notebooks/Day%207%20Quantum%20Fourier%20transform%20and%20its%20applications_%20Part%201.ipynb) | [Solutions](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Solutions/Day%207%20solutions.ipynb)
37 | [Day 7 Quantum Fourier transform and its applications Part 2](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Notebooks/Day%207%20Quantum%20Fourier%20transform%20and%20its%20applications_%20Part%202.ipynb)
38 | 39 | These are the final questions of this workshop which are at a slightly more challenging level.
40 | [Final Question 1 Discrete Logarithm](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Notebooks/Final%20Question%201%20Discrete%20Logarithm.ipynb) | [Solutions](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Solutions/Final%20Question%201%20Discrete%20Logarithm%20Solutions.ipynb)
41 | [Final Question 2 Unitary Circuit Building](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Notebooks/Final%20Question%202%20Unitary%20Circuit%20Building.ipynb) | [Solutions](https://github.com/mnp-club/Quantum_Computing_Workshop_2020/blob/master/Solutions/Final%20Question%202%20Unitary%20Circuit%20Building%20Solutions.ipynb)
42 | 43 | **Note:** the markdown texts may look a little messed up on the github preview but on downloading it will be completely fine. 44 | 45 | If you are facing troubles you can ask your doubts on the [telegram group](https://t.me/joinchat/R8Z5FhfzigvZWYIxTrz6Zw).
46 | For additional references you will find the [Qiskit Textbook](https://qiskit.org/textbook/preface.html) to be quite handy and also [this youtube playlist](https://www.youtube.com/watch?v=X2q1PuI2RFI&list=PL1826E60FD05B44E4) does a pretty good job at explaining some basic concepts for quantum computing. 47 | 48 | You can find the recordings of our sessions and all the slides [here](https://drive.google.com/drive/folders/1gXw4SgpOakgBAudCDcvfo1-zJEBiE-v0) 49 | 50 | We had two talks from guest lecturers:
51 | Sandesh Kalantre, Graduate Student, University of Maryland on *Quantum Computing: The Present and The Future* at 7 PM, 26th July
52 | Apoorva D. Patel, Professor at the Centre for High Energy Physics, Indian Institute of Science, Bangalore at 7 PM, 2nd August 53 | 54 | We hope you have fun and learn something new :) 55 | 56 | 57 | ## References 58 | 59 | *Quantum Computation and Quantum Information* by Michael Nielsen and Isaac Chuang
60 | [Qiskit Textbook](https://qiskit.org/textbook/preface.html)
61 | [IBM May 4 challenge repo](https://github.com/qiskit-community/may4_challenge)
62 | [IBM May 4 challenge excercises](https://github.com/qiskit-community/may4_challenge_exercises)
63 | -------------------------------------------------------------------------------- /Solutions/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnp-club/Quantum_Computing_Workshop_2020/20c287e220c000f2b00dbf87a103325498fa6263/Solutions/.gitignore -------------------------------------------------------------------------------- /Solutions/Day 1 solutions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# DAY 1 MODULE SOLUTIONS" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "from qiskit import *\n", 17 | "from qiskit.tools.jupyter import *\n", 18 | "from qiskit.visualization import *\n", 19 | "import matplotlib.pyplot as plotter\n", 20 | "import numpy as np\n", 21 | "from IPython.display import display, Math, Latex\n", 22 | "%matplotlib inline\n", 23 | "# Loading your IBM Q account(s)\n", 24 | "provider = IBMQ.load_account()\n", 25 | "\n", 26 | "def run_circuit(qc):\n", 27 | " backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend\n", 28 | " result = execute(qc, backend, shots = 200000).result() # we run the simulation\n", 29 | " counts = result.get_counts() # we get the counts\n", 30 | " return counts" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "TASK 1:" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "# qc1 was already initialised to |1>|1>.\n", 47 | "qc1.h(0)\n", 48 | "# qc1 => (|0>|1> + |1>|1>)/sqrt(2)\n", 49 | "qc1.cx(0,1)\n", 50 | "# qc1 => (|0>|1> + |1>|0>)/sqrt(2)\n", 51 | "\n", 52 | "#measure\n", 53 | "qc1.measure([0,1],[0,1])\n", 54 | "counts = run_circuit(qc1)\n", 55 | "print(counts)\n", 56 | "plot_histogram(counts)" 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "metadata": {}, 62 | "source": [ 63 | "TASK 2:" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "metadata": {}, 70 | "outputs": [], 71 | "source": [ 72 | "qc_swap = QuantumCircuit(2,2)\n", 73 | "qc_swap.x(0)\n", 74 | "#initial state => |1>|0>\n", 75 | "\n", 76 | "# applying swap\n", 77 | "qc_swap.cx(0,1)\n", 78 | "qc_swap.cx(1,0)\n", 79 | "qc_swap.cx(0,1) #Following from the circuit diagram.\n", 80 | "# There is also qc_swap.swap(0,1)\n", 81 | "\n", 82 | "# measure\n", 83 | "qc_swap.measure([0,1],[0,1])\n", 84 | "counts = run_circuit(qc_swap)\n", 85 | "print(counts)\n", 86 | "plot_histogram(counts)" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "TASK 3:" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": null, 99 | "metadata": {}, 100 | "outputs": [], 101 | "source": [ 102 | "qc5 = QuantumCircuit(2,2)\n", 103 | "qc5.x(1)\n", 104 | "# initial state is |0>|1> SO THAT WE KNOW WHAT BOTH |0> AND |1> MAP TO.\n", 105 | "# A lot of people did a mistake here in not understanding why we are using |0> and |1>\n", 106 | "\n", 107 | "#apply HZH on both qubits.\n", 108 | "qc5.h([0,1])\n", 109 | "qc5.z([0,1])\n", 110 | "qc5.h([0,1])\n", 111 | "\n", 112 | "# measure\n", 113 | "qc5.measure([0,1],[0,1])\n", 114 | "\n", 115 | "counts = run_circuit(qc5)\n", 116 | "print(counts)\n", 117 | "plot_histogram(counts)\n", 118 | "# final state would be |1>|0> = X(|0>|1>)\n", 119 | "# Hence they are equal (Actually not. We can only conclude that they agree only upto a global phase. I think now you can appreciate this now.)" 120 | ] 121 | }, 122 | { 123 | "cell_type": "markdown", 124 | "metadata": {}, 125 | "source": [ 126 | "TASK 4:" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": null, 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "qc6 = QuantumCircuit(3,3)\n", 136 | "qc6.x(0) # Set any one qubit of the 2 swapping qubits to '1' to see a noticable difference between input and output.\n", 137 | "qc6.initialize([1/np.sqrt(2), -complex(1,1)/2],2) # Optional. Initializing the 3rd qubit.\n", 138 | "\n", 139 | "# convert 3rd qubit into |1>\n", 140 | "qc6.tdg(2) \n", 141 | "qc6.h(2)\n", 142 | "# OR\n", 143 | "#qc6.t(2)\n", 144 | "#qc6.s(2)\n", 145 | "#qc6.h(2)\n", 146 | "#qc6.x(2)\n", 147 | "# You can check that they are equivalent.\n", 148 | "\n", 149 | "# As you know now, it's a Fredkin gate.\n", 150 | "qc6.toffoli(2,1,0)\n", 151 | "qc6.toffoli(2,0,1)\n", 152 | "qc6.toffoli(2,1,0)\n", 153 | "\n", 154 | "#reset the third qubit to its intial state\n", 155 | "qc6.h(2)\n", 156 | "qc6.t(2)\n", 157 | "\n", 158 | "# Measure\n", 159 | "qc6.measure([0,1,2],[0,1,2])\n", 160 | "counts = run_circuit(qc6)\n", 161 | "print(counts)\n", 162 | "plot_histogram(counts)" 163 | ] 164 | } 165 | ], 166 | "metadata": { 167 | "kernelspec": { 168 | "display_name": "Python 3", 169 | "language": "python", 170 | "name": "python3" 171 | }, 172 | "language_info": { 173 | "codemirror_mode": { 174 | "name": "ipython", 175 | "version": 3 176 | }, 177 | "file_extension": ".py", 178 | "mimetype": "text/x-python", 179 | "name": "python", 180 | "nbconvert_exporter": "python", 181 | "pygments_lexer": "ipython3", 182 | "version": "3.7.6" 183 | } 184 | }, 185 | "nbformat": 4, 186 | "nbformat_minor": 4 187 | } 188 | -------------------------------------------------------------------------------- /Solutions/Day 2.2 solutions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# DAY 2.2 MODULE SOLUTIONS" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "from qiskit import *\n", 17 | "from qiskit.tools.jupyter import *\n", 18 | "from qiskit.visualization import *\n", 19 | "import matplotlib.pyplot as plotter\n", 20 | "import numpy as np\n", 21 | "from IPython.display import display, Math, Latex\n", 22 | "%matplotlib inline\n", 23 | "# Loading your IBM Q account(s)\n", 24 | "provider = IBMQ.load_account()\n", 25 | "\n", 26 | "def run_circuit(qc):\n", 27 | " backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend\n", 28 | " result = execute(qc, backend, shots = 200000).result() # we run the simulation\n", 29 | " counts = result.get_counts() # we get the counts\n", 30 | " return counts" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": null, 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "THE ONLY TASK" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "qc1 = QuantumCircuit(2,2)\n", 49 | "# preparing 'a' bell pair which is |beta_00> here\n", 50 | "qc1.h(0)\n", 51 | "qc1.cx(0,1)\n", 52 | "qc1.barrier([0,1])\n", 53 | "\n", 54 | "# Apply any pauli gate here\n", 55 | "a = input(\"BE SKYLER. APPLY ANY OF THE FOUR PAULI GATES:\")\n", 56 | "if a == 'X':\n", 57 | " qc1.x(1) \n", 58 | "if a == 'Y':\n", 59 | " qc1.y(1)\n", 60 | "if a == 'Z':\n", 61 | " qc1.z(1)\n", 62 | "# if it's not any of them, it's I. So we don't need to do anything.\n", 63 | "qc1.barrier([0,1])\n", 64 | "\n", 65 | "# Change the state into standard states: |00>, |01>, |10>, |11> depending on the gate applied\n", 66 | "qc1.cx(0,1)\n", 67 | "qc1.h(0)\n", 68 | "\n", 69 | "#Measure\n", 70 | "qc1.measure([0,1],[0,1])\n", 71 | "backend = Aer.get_backend('qasm_simulator')\n", 72 | "result = execute(qc1, backend, shots = 1024).result()\n", 73 | "counts = result.get_counts()\n", 74 | "plot_histogram(counts)\n", 75 | "\n", 76 | "# On observation we get this mapping\n", 77 | "print('Skyler applied this gate: ')\n", 78 | "if '00' in counts.keys():\n", 79 | " print('I') # She bluffed. # Don't worry if you haven't considered this case. Happens.\n", 80 | "elif '10' in counts.keys():\n", 81 | " print('X')\n", 82 | "elif '11' in counts.keys():\n", 83 | " print('Y')\n", 84 | "else:\n", 85 | " print('Z')\n", 86 | "plot_histogram(counts)" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [] 95 | } 96 | ], 97 | "metadata": { 98 | "kernelspec": { 99 | "display_name": "Python 3", 100 | "language": "python", 101 | "name": "python3" 102 | }, 103 | "language_info": { 104 | "codemirror_mode": { 105 | "name": "ipython", 106 | "version": 3 107 | }, 108 | "file_extension": ".py", 109 | "mimetype": "text/x-python", 110 | "name": "python", 111 | "nbconvert_exporter": "python", 112 | "pygments_lexer": "ipython3", 113 | "version": "3.7.6" 114 | } 115 | }, 116 | "nbformat": 4, 117 | "nbformat_minor": 4 118 | } 119 | -------------------------------------------------------------------------------- /Solutions/Day 5 BB84 Protocol Solutions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "SRuLyIYAPT4S" 8 | }, 9 | "source": [ 10 | "# Quantum Cryptography using BB84\n", 11 | "This third exercise focuses on **[BB84](https://en.wikipedia.org/wiki/BB84)**, a cryptography protocol developed in 1984 by one of the most famous IBMers, Charles Bennett, together with his colleague Gilles Brassard. This scheme was realized five years later in the first demonstration of [quantum key distribution](https://en.wikipedia.org/wiki/Quantum_key_distribution) by Bennett and colleague John Smolin at IBM [C. H. Bennett, F. Bessette, G. Brassard, L. Salvail, and J. Smolin, J. of Cryptography **5**, 3 (1992) ]. Both Charles and John are still members of the IBM Quantum team.\n", 12 | "\n", 13 | "\n", 14 | "\n", 15 | "\n", 16 | "
Charles Bennett and John Smolin working on the first demonstration of quantum key distribution, IBM T.J. Watson Research Center.
\n", 17 | "\n", 18 | "\n", 19 | "\n", 20 | "The goal of the BB84 protocol is to create a secret key between two parties, Alice and Bob, that can then be used by both parties to encrypt and decrypt a hidden message. In this exercise we will guide you through the different steps of the protocol to create such a secret key to decrypt our encrypted message.\n", 21 | "\n", 22 | "\n", 23 | "## BB84 protocol\n", 24 | "\n", 25 | "Let's walk through the steps of the BB84 protocol:\n", 26 | "\n", 27 | "1. In the first step, Alice chooses two random bit strings, $k$ and $b$, that each consist of $n$ bits. Her bit string $k$ contains the actual bits she wants to encode (out of which the key will later be formed), while $b$ determines the bases in which she will encode her bits. For $b_i=0$ (i.e., if the $i^{th}$ bit is zero), she encodes the $i^{th}$ qubit in the standard $\\{|0\\rangle, |1\\rangle \\}$ basis, while for $b_i=1$, she encodes it in the $\\{|+\\rangle, |-\\rangle \\}$ basis, where $|+\\rangle:=\\frac{1}{\\sqrt{2}}(|0\\rangle +|1\\rangle)$, $|-\\rangle:=\\frac{1}{\\sqrt{2}}(|0\\rangle -|1\\rangle)$. \n", 28 | "This becomes more illustrative when representing each basis by two perpendicular arrows, where the two different bases are rotated by $45^\\circ$.\n", 29 | "The encoding of each qubit $q_i$ would therefore look like the following:\n", 30 | "\n", 31 | "\"drawing\"\n", 32 | "\n", 33 | "2. After encoding her $n$ qubits, Alice sends these qubits to Bob. Bob also chooses a random bit string $\\tilde{b}$ consisting of $n$ bits that determines in which bases he is going to perform measurements. He stores the outcomes of his measurements $\\tilde{k_i}$ together with the corresponding basis bits $\\tilde{b_i}$ in a table.\n", 34 | "\n", 35 | "3. Next, Alice and Bob compare their basis bits $b_i$ and $\\tilde{b}_i$. Whenever $b_i \\neq \\tilde{b}_i$, Bob measured in a different basis than Alice's qubit was encoded in, so he gets each outcome with probability $\\frac{1}{2}$. Alice and Bob therefore discard all key bits corresponding to these basis bits. If $b_i = \\tilde{b}_i$, however, they prepared and measured the qubit in the same basis, so (unless someone eavesdropped) Bob will get the key bit that Alice encoded, $\\tilde{k}_i = k_i$. These outcomes then compose the key.\n", 36 | "\n", 37 | "## An illustrated example\n", 38 | "\n", 39 | "Suppose Alice's random bit strings are $k=`0111001`$ and $b=`1101000`$ and Bob's random bit string is $\\tilde{b}=`1001101`$. Try to understand the other entries in the table below. Note that in the case where the basis bits are different, Bob has a 50% chance to get each outcome, so here one of them was chosen randomly.\n", 40 | "\n", 41 | "\"drawing\"\n", 42 | "\n", 43 | "The key produced in this example would be '0110'. To make sure that the key is secret and correct, Alice and Bob would \"sacrifice\" some of their key bits to check that no one eavesdropped. If someone had measured a qubit on the way, this could have changed the state of that qubit and with probability $\\frac{1}{4}$, Bob's and Alice's key bits will be different. By checking $m$ bits, the probability to not notice an eavesdropper decreases as $\\left(\\frac{3}{4}\\right)^m$. Thus, if they check enough bits and they are all the same, they can assume that no one eavesdropped and their key is secret. However, to keep things simple, we will not perfom these tests in this excercise. Instead, all bits of the key will be used.\n", 44 | "\n", 45 | "### Message encrpytion\n", 46 | "Once a secret key is distributed, Alice can encrypt her message by using the so-called [one-time pad](https://en.wikipedia.org/wiki/One-time_pad) technique: she simply adds the key bits on top of her secret message bits that she wants to send. Using the example above, her key is $\\text{key}=`0110`$. If her secret message bit string is $m=`1100`$, the encrypted message will be $c=m\\oplus \\text{key} \\mod 2 = `1010`$. Bob can then decrypt the message by adding his key on that encrypted message, $m=c\\oplus \\text{key} \\mod 2$.\n", 47 | "\n", 48 | "## Your task \n", 49 | "\n", 50 | "In the following four tasks, you play the role of Bob. You will create such a secret key with Alice and use it to decrypt the encrypted message from her.\n" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 1, 56 | "metadata": { 57 | "colab": {}, 58 | "colab_type": "code", 59 | "id": "tf9C08ykPT4t" 60 | }, 61 | "outputs": [ 62 | { 63 | "name": "stdout", 64 | "output_type": "stream", 65 | "text": [ 66 | "Collecting git+https://github.com/mnp-club/MnP_QC_Workshop.git\n", 67 | " Cloning https://github.com/mnp-club/MnP_QC_Workshop.git to c:\\users\\mahade~1\\appdata\\local\\temp\\pip-req-build-xz7apflf\n", 68 | "Building wheels for collected packages: mnp-qc-workshop-2020\n", 69 | " Building wheel for mnp-qc-workshop-2020 (setup.py): started\n", 70 | " Building wheel for mnp-qc-workshop-2020 (setup.py): finished with status 'done'\n", 71 | " Created wheel for mnp-qc-workshop-2020: filename=mnp_qc_workshop_2020-0.1-cp37-none-any.whl size=6924 sha256=3faa00b7d62eabbaa68d91a2594ddcbf2dcb04432bff94da55698ea0715d5fc7\n", 72 | " Stored in directory: C:\\Users\\MAHADE~1\\AppData\\Local\\Temp\\pip-ephem-wheel-cache-k3p87ynv\\wheels\\36\\2c\\cf\\a4cccefc10da21f2e37567d1a568955da0f91923b9263c9b4b\n", 73 | "Successfully built mnp-qc-workshop-2020\n", 74 | "Installing collected packages: mnp-qc-workshop-2020\n", 75 | "Successfully installed mnp-qc-workshop-2020-0.1\n", 76 | "Note: you may need to restart the kernel to use updated packages.\n" 77 | ] 78 | }, 79 | { 80 | "name": "stderr", 81 | "output_type": "stream", 82 | "text": [ 83 | " Running command git clone -q https://github.com/mnp-club/MnP_QC_Workshop.git 'C:\\Users\\MAHADE~1\\AppData\\Local\\Temp\\pip-req-build-xz7apflf'\n" 84 | ] 85 | } 86 | ], 87 | "source": [ 88 | "%matplotlib inline\n", 89 | "\n", 90 | "# Importing standard Qiskit libraries\n", 91 | "import random\n", 92 | "from qiskit import execute, Aer, IBMQ\n", 93 | "from qiskit.tools.jupyter import *\n", 94 | "from qiskit.visualization import *\n", 95 | "\n", 96 | "#Get the library to check the answers\n", 97 | "%pip install -I git+https://github.com/mnp-club/MnP_QC_Workshop.git\n", 98 | "from mnp_qc_workshop_2020.bb84 import *\n", 99 | "\n", 100 | "# Configuring account\n", 101 | "provider = IBMQ.load_account()\n", 102 | "backend = provider.get_backend('ibmq_qasm_simulator') # with this simulator it wouldn't work \\\n", 103 | "\n", 104 | "# Initial setup\n", 105 | "random.seed(64) # do not change this seed, otherwise you will get a different key" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "metadata": { 111 | "colab_type": "text", 112 | "id": "BzRs6563PT44" 113 | }, 114 | "source": [ 115 | "\n", 116 | "\n", 117 | "### Simulating this protocol in Qiskit\n", 118 | "\n", 119 | "In this exercise, there are three steps. Each of these three steps is completed $n=16$ times. Before the protocol begins, Alice will choose two random bit strings, $k$ and $b$.\n", 120 | "\n", 121 | "1. Alice will prepare each qubit using the function `Alice_prepare_qubit`, which is already written for you.\n", 122 | "\n", 123 | "2. Bob measures his qubit using a specific set of bases, which we have given you in a variable called `bases`. **You will supply the procedure that Bob takes in the function `Bob_measure_qubit`.**\n", 124 | "\n", 125 | "3. A quantum circuit for this sequence of operations is created. It will be called `this_qubit_circuit` for each qubit, and all such circuits are collected together in an array called `all_qubit_circuits`. We have supplied the code to do this.\n", 126 | "\n", 127 | "Finally, we run `all_qubit_circuits` on the IBM high-performance cloud simulator called `ibmq_qasm_simulator` and collect the results of the measurements into a bitstring called `bits`. We have supplied the code to do this.\n", 128 | "\n", 129 | "You will then follow the protocol to decrypt a message using the extracted key from the BB84 protocol.\n", 130 | "\n", 131 | "\n", 132 | "### i) Execute step 2 of the BB84 protocol to get your bitstring\n", 133 | "To do so, you need a random bit string $\\tilde{b}$ that determines the bases in which you should perform the measurements. In order to reproduce the key that we used to encrypt our secret message, we provide you with this \"random\" `bases` bit string of 16 bits that uses seed \"64\".\n", 134 | "**Perform measurements in the bases corresponding to the given bit string and return the outcome as a bit string in the form '$\\tilde{b}_0\\tilde{b}_1...\\tilde{b}_{n-1}$'.** Note that Qiskit returns outcomes always in reverse order, so if $|\\tilde{q}_0\\rangle = |0 \\rangle $ and $|\\tilde{q}_1\\rangle = |\\tilde{q}_2\\rangle = |1 \\rangle $, it will return the outcome '110'. You can check whether your bit string is correct by `check_bits(bitstring)`." 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": 2, 140 | "metadata": { 141 | "colab": {}, 142 | "colab_type": "code", 143 | "id": "p_rBUf_RPT46" 144 | }, 145 | "outputs": [ 146 | { 147 | "name": "stdout", 148 | "output_type": "stream", 149 | "text": [ 150 | "Bob's bases: 0111100111100101\n", 151 | "Bob's bits: 1000001010101000\n", 152 | "So far, so good 🎉! You got the right bits!\n" 153 | ] 154 | } 155 | ], 156 | "source": [ 157 | "# This is your 'random' bit string that determines your bases\n", 158 | "numqubits = 16\n", 159 | "bob_bases = str('{0:016b}'.format(random.getrandbits(numqubits)))\n", 160 | "\n", 161 | "def bb84():\n", 162 | " print('Bob\\'s bases:', bob_bases)\n", 163 | "\n", 164 | " # Now Alice will send her bits one by one...\n", 165 | " all_qubit_circuits = []\n", 166 | " for qubit_index in range(numqubits):\n", 167 | "\n", 168 | " # This is Alice creating the qubit\n", 169 | " thisqubit_circuit = alice_prepare_qubit(qubit_index)\n", 170 | "\n", 171 | " # This is Bob finishing the protocol below\n", 172 | " bob_measure_qubit(bob_bases, qubit_index, thisqubit_circuit)\n", 173 | " \n", 174 | " # We collect all these circuits and put them in an array\n", 175 | " all_qubit_circuits.append(thisqubit_circuit)\n", 176 | " \n", 177 | " \n", 178 | " # Now execute all the circuits for each qubit\n", 179 | " results = execute(all_qubit_circuits, backend=backend, shots=1).result()\n", 180 | " # And combine the results\n", 181 | " bits = ''\n", 182 | " for qubit_index in range(numqubits):\n", 183 | " bits += [measurement for measurement in results.get_counts(qubit_index)][0]\n", 184 | " \n", 185 | " return bits\n", 186 | "\n", 187 | "# Here is your task\n", 188 | "def bob_measure_qubit(bob_bases, qubit_index, qubit_circuit):\n", 189 | " if(bob_bases[qubit_index] == '1'):\n", 190 | " qubit_circuit.h(0)#Just change basis to +,- and then measure\n", 191 | " qubit_circuit.measure(0,0)\n", 192 | " \n", 193 | "bits = bb84() \n", 194 | "print('Bob\\'s bits: ', bits)\n", 195 | "check_bits(bits)" 196 | ] 197 | }, 198 | { 199 | "cell_type": "markdown", 200 | "metadata": { 201 | "colab_type": "text", 202 | "id": "q5KORnjcPT5C" 203 | }, 204 | "source": [ 205 | "After you performed your measurements, Alice announces her bases bits which are stored in alice_bases in the next cell\n", 206 | "\n", 207 | "### ii) Execute step 3 of the BB84 protocol to extract the key\n", 208 | "To do so, compare Alice's bases bits $b$ to your bases bits $\\tilde{b}$ (given in the previous step) and extract the key by keeping only the outcomes when your bases were the same.\n", 209 | "You can check whether your key is correct by `check_key(key)`." 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": 3, 215 | "metadata": { 216 | "colab": {}, 217 | "colab_type": "code", 218 | "id": "69m4Px15PT5F" 219 | }, 220 | "outputs": [ 221 | { 222 | "name": "stdout", 223 | "output_type": "stream", 224 | "text": [ 225 | "10010000\n", 226 | "So far, so good 🎉! You got the right key!\n" 227 | ] 228 | } 229 | ], 230 | "source": [ 231 | "alice_bases = '0100000101011100' # Alice's bases bits\n", 232 | "key = ''\n", 233 | "for i in range(16):\n", 234 | " if alice_bases[i]==bob_bases[i]:\n", 235 | " key = key + bits[i]#The key is only added to for the case where the bases are the same\n", 236 | "print(key)\n", 237 | "check_key(key)" 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": { 243 | "colab_type": "text", 244 | "id": "xzHVo1R_PT5N" 245 | }, 246 | "source": [ 247 | "Using this key, Alice can now send you her private encrypted message. While for full security a key would only be used once, she will now use her 8-bit-long key 43 times in a row to encrypt her 344-bit-long message. Her encrypted message is stored in message variable of the next cell.\n", 248 | "\n", 249 | "A small note I would like to make is that generally the key is kept to be of a larger length since this ratio is actually very skewed but for demonstration purposes of the concept in play we will use it in this way.\n", 250 | "\n", 251 | "### iii) Decrypt the message using the extracted key\n", 252 | "The message encryption section in the introduction describes how to do so. You can check whether your decrypted message bit string is correct by check_decrypted(decrypted)." 253 | ] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": 4, 258 | "metadata": { 259 | "colab": {}, 260 | "colab_type": "code", 261 | "id": "wi3f0W8tPT5O" 262 | }, 263 | "outputs": [ 264 | { 265 | "name": "stdout", 266 | "output_type": "stream", 267 | "text": [ 268 | "01101000011101000111010001110000011100110011101000101111001011110111011101110111011101110010111001111001011011110111010101110100011101010110001001100101001011100110001101101111011011010010111101110111011000010111010001100011011010000011111101110110001111010111010101100010001110000011001001011000011000100011000101000011001110000110111101110011\n", 269 | "So far, so good 🎉! You got the right decrypted message!\n" 270 | ] 271 | } 272 | ], 273 | "source": [ 274 | "message = get_message()# encrypted message\n", 275 | "decrypted = ''\n", 276 | "for i in range(344):\n", 277 | " if key[i%8] == '1':\n", 278 | " if message[i] == '0':\n", 279 | " m = '1'\n", 280 | " else:\n", 281 | " m = '0'\n", 282 | " decrypted = decrypted + m\n", 283 | " else:\n", 284 | " decrypted = decrypted + message[i]\n", 285 | "#The key is sort of applied like a mask over each of the 43 8 bit sequences \n", 286 | "#of the 344 bitstring and its just a xor operation here\n", 287 | "print(decrypted)\n", 288 | "check_decrypted(decrypted)" 289 | ] 290 | }, 291 | { 292 | "cell_type": "markdown", 293 | "metadata": { 294 | "colab_type": "text", 295 | "id": "Jee1GdjiPT5a" 296 | }, 297 | "source": [ 298 | "Now you would have obtained the decrypted message. You have a pleasent surprise waiting for you after you convert this using ASCII notation. In the cell below, convert every of the forty three 8 bit pieces of the string to a character using the regular ASCII notation. The check_message function will tell you whether you have correctly converted it or not." 299 | ] 300 | }, 301 | { 302 | "cell_type": "code", 303 | "execution_count": 5, 304 | "metadata": { 305 | "colab": {}, 306 | "colab_type": "code", 307 | "id": "42slKb6aPT5b" 308 | }, 309 | "outputs": [ 310 | { 311 | "name": "stdout", 312 | "output_type": "stream", 313 | "text": [ 314 | "https://www.youtube.com/watch?v=ub82Xb1C8os\n", 315 | "Congratulations 🎉! You have succesfully decrypted the message which is https://www.youtube.com/watch?v=ub82Xb1C8os. Do go ahead and try the link. Its worth it ;)\n" 316 | ] 317 | } 318 | ], 319 | "source": [ 320 | "decrypted_to_string_ASCII = ''\n", 321 | "\n", 322 | "for i in range(43):\n", 323 | " decrypted_to_string_ASCII += str(chr(int(decrypted[(i*8):(i*8)+8],2)))#sequentially taking the 8 bit sequences\n", 324 | "print(decrypted_to_string_ASCII)\n", 325 | "check_message(decrypted_to_string_ASCII)" 326 | ] 327 | }, 328 | { 329 | "cell_type": "markdown", 330 | "metadata": { 331 | "colab_type": "text", 332 | "id": "mhi2TD2EPT5i" 333 | }, 334 | "source": [ 335 | "The documentation in this notebook has been taken mainly from here. Kudos to the qiskit team for documenting this so well." 336 | ] 337 | } 338 | ], 339 | "metadata": { 340 | "colab": { 341 | "collapsed_sections": [], 342 | "name": "Day 4.2 BB84 Protocol.ipynb", 343 | "provenance": [] 344 | }, 345 | "kernelspec": { 346 | "display_name": "Python 3", 347 | "language": "python", 348 | "name": "python3" 349 | }, 350 | "language_info": { 351 | "codemirror_mode": { 352 | "name": "ipython", 353 | "version": 3 354 | }, 355 | "file_extension": ".py", 356 | "mimetype": "text/x-python", 357 | "name": "python", 358 | "nbconvert_exporter": "python", 359 | "pygments_lexer": "ipython3", 360 | "version": "3.7.4" 361 | } 362 | }, 363 | "nbformat": 4, 364 | "nbformat_minor": 1 365 | } 366 | -------------------------------------------------------------------------------- /Solutions/Day 7 solutions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | " # DAY 7.1 MODULE SOLUTIONS" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "!pip install qiskit\n", 17 | "from qiskit import *\n", 18 | "from qiskit.tools.jupyter import *\n", 19 | "from qiskit.visualization import *\n", 20 | "import matplotlib.pyplot as plotter\n", 21 | "import numpy as np\n", 22 | "from IPython.display import display, Math, Latex\n", 23 | "import qiskit.quantum_info as qi\n", 24 | "%matplotlib inline\n", 25 | "# Loading your IBM Q account(s)\n", 26 | "provider = IBMQ.load_account()\n", 27 | "\n", 28 | "def run_circuit(qc):\n", 29 | " backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend\n", 30 | " result = execute(qc, backend, shots = 200000).result() # we run the simulation\n", 31 | " counts = result.get_counts() # we get the counts\n", 32 | " return counts\n", 33 | "\n", 34 | "def qft_rotations(circuit, n):\n", 35 | " \"\"\"Performs qft on the first n qubits in circuit (without swaps)\"\"\"\n", 36 | " if n == 0:\n", 37 | " return circuit\n", 38 | " n -= 1\n", 39 | " circuit.h(n)\n", 40 | " for qubit in range(n):\n", 41 | " circuit.cu1(np.pi/2**(n-qubit), qubit, n)\n", 42 | " # At the end of our function, we call the same function again on\n", 43 | " # the next qubits (we reduced n by one earlier in the function)\n", 44 | " qft_rotations(circuit, n)\n", 45 | "def swap_registers(circuit, n):\n", 46 | " for qubit in range(n//2):\n", 47 | " circuit.swap(qubit, n-qubit-1)\n", 48 | " return circuit\n", 49 | "\n", 50 | "def qft(circuit, n):\n", 51 | " \"\"\"QFT on the first n qubits in circuit\"\"\"\n", 52 | " qft_rotations(circuit, n)\n", 53 | " \n", 54 | "def inverse_qft(circuit, n):\n", 55 | " \"\"\"Does the inverse QFT on the first n qubits in circuit\"\"\"\n", 56 | " # First we create a QFT circuit of the correct size:\n", 57 | " qft_circ = qft(QuantumCircuit(n), n)\n", 58 | " # Then we take the inverse of this circuit\n", 59 | " invqft_circ = qft_circ.inverse()\n", 60 | " # And add it to the first n qubits in our existing circuit\n", 61 | " circuit.append(invqft_circ, circuit.qubits[:n])\n", 62 | " return circuit.decompose() # .decompose() allows us to see the individual gates\n", 63 | "\n", 64 | " swap_registers(circuit, n)\n", 65 | " return circuit" 66 | ] 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "metadata": {}, 71 | "source": [ 72 | "TASK 1" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": null, 78 | "metadata": {}, 79 | "outputs": [], 80 | "source": [ 81 | "# Basically change the values of n starting from 1,2(you know which aren't I),3,... We will start with 3\n", 82 | "a = 0\n", 83 | "n = 3\n", 84 | "while a==0:\n", 85 | " c = ClassicalRegister(5)\n", 86 | " q = QuantumRegister(5)\n", 87 | " qc = QuantumCircuit(q,c)\n", 88 | " for i in range(n):\n", 89 | " qft(qc,5)\n", 90 | " qc.measure(range(5),range(5))\n", 91 | " counts = run_circuit(qc)\n", 92 | " if len(counts.keys()) == 1:\n", 93 | " if '00000' in counts.keys():\n", 94 | " a=1\n", 95 | " print(n)\n", 96 | " n = n+1\n", 97 | "\n", 98 | "# Actually due to qiskit using reverse order for bit strings sometimes produces weird results when it comes to such matrices repeatedly.\n", 99 | "# Nevertheless, you can search up on the internet for QFT matrix and you can calculate that QFT^4 indeed equals I.\n", 100 | " \n" 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "metadata": {}, 106 | "source": [ 107 | "TASK 2" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "metadata": {}, 114 | "outputs": [], 115 | "source": [ 116 | "# The matrix can be easily solved theoretically and can be found on the internet, Here we will get the matrix according to the qiskit nomenclature.\n", 117 | "\n", 118 | "qc = QuantumCircuit(2)\n", 119 | "qft(qc, 2)\n", 120 | "qft(qc, 2)\n", 121 | "with np.printoptions(precision=3, suppress=True):\n", 122 | " print(qi.Operator(qc).data)\n" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "TASK 3" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "# The required function. Also recall that T^2 = S, S^2 = Z.\n", 139 | "def Modulo_increment(qc): # 4 bit circuit where q3 is MSB\n", 140 | " qft(qc,4)\n", 141 | " qc.rz(np.pi/8,0)\n", 142 | " qc.t(1)\n", 143 | " qc.s(2)\n", 144 | " qc.z(3)\n", 145 | " inverse_qft(qc,4)\n", 146 | " return qc\n", 147 | "\n", 148 | "# Then you can check for different values." 149 | ] 150 | } 151 | ], 152 | "metadata": { 153 | "kernelspec": { 154 | "display_name": "Python 3", 155 | "language": "python", 156 | "name": "python3" 157 | }, 158 | "language_info": { 159 | "codemirror_mode": { 160 | "name": "ipython", 161 | "version": 3 162 | }, 163 | "file_extension": ".py", 164 | "mimetype": "text/x-python", 165 | "name": "python", 166 | "nbconvert_exporter": "python", 167 | "pygments_lexer": "ipython3", 168 | "version": "3.7.6" 169 | } 170 | }, 171 | "nbformat": 4, 172 | "nbformat_minor": 4 173 | } 174 | -------------------------------------------------------------------------------- /images/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnp-club/Quantum_Computing_Workshop_2020/20c287e220c000f2b00dbf87a103325498fa6263/images/.gitignore -------------------------------------------------------------------------------- /images/contabc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnp-club/Quantum_Computing_Workshop_2020/20c287e220c000f2b00dbf87a103325498fa6263/images/contabc.jpg -------------------------------------------------------------------------------- /images/contv.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnp-club/Quantum_Computing_Workshop_2020/20c287e220c000f2b00dbf87a103325498fa6263/images/contv.jpg -------------------------------------------------------------------------------- /images/dark_got_me_this.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnp-club/Quantum_Computing_Workshop_2020/20c287e220c000f2b00dbf87a103325498fa6263/images/dark_got_me_this.jpeg -------------------------------------------------------------------------------- /images/geometrygrover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnp-club/Quantum_Computing_Workshop_2020/20c287e220c000f2b00dbf87a103325498fa6263/images/geometrygrover.jpg -------------------------------------------------------------------------------- /images/grover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnp-club/Quantum_Computing_Workshop_2020/20c287e220c000f2b00dbf87a103325498fa6263/images/grover.jpg -------------------------------------------------------------------------------- /images/hold.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnp-club/Quantum_Computing_Workshop_2020/20c287e220c000f2b00dbf87a103325498fa6263/images/hold.jpg -------------------------------------------------------------------------------- /images/jozsa.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnp-club/Quantum_Computing_Workshop_2020/20c287e220c000f2b00dbf87a103325498fa6263/images/jozsa.jpg -------------------------------------------------------------------------------- /images/swap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnp-club/Quantum_Computing_Workshop_2020/20c287e220c000f2b00dbf87a103325498fa6263/images/swap.png -------------------------------------------------------------------------------- /images/telep.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnp-club/Quantum_Computing_Workshop_2020/20c287e220c000f2b00dbf87a103325498fa6263/images/telep.jpeg -------------------------------------------------------------------------------- /images/toffoli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnp-club/Quantum_Computing_Workshop_2020/20c287e220c000f2b00dbf87a103325498fa6263/images/toffoli.png -------------------------------------------------------------------------------- /images/universal_irx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnp-club/Quantum_Computing_Workshop_2020/20c287e220c000f2b00dbf87a103325498fa6263/images/universal_irx.png --------------------------------------------------------------------------------