├── .github └── FUNDING.yml ├── Intro.ipynb ├── README.md ├── _config.yml ├── lecture_10_quantum_gates.ipynb ├── lecture_2_complex_numbers.ipynb ├── lecture_3_vectors.ipynb ├── lecture_4_inner_products.ipynb ├── lecture_5_bloch_sphere.ipynb ├── lecture_6_tensor_products.ipynb ├── lecture_7_preparing_basis_states.ipynb ├── lecture_8_matrices.ipynb ├── lecture_9_tensor_product_matrices.ipynb └── requirements.txt /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [The-Singularity-Research] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /Intro.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Jupyter Notebooks" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Jupyer notebooks and Jupyter Lab as free notebook environments for running Python code and writing markdown. They are available in the latest Anaconda distribution, which is also free. Jupyter is a standard tool used by the data science and machine learning community, and provides a very user friendly interactive interface. Jupyter cells such as this one can render markdown, which is similar to HTML, but more basic. To run a cell, simply click on the cell and press `shift+enter`. As an example, click on the following cell to run it and see its output. " 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": {}, 21 | "outputs": [ 22 | { 23 | "data": { 24 | "text/plain": [ 25 | "8" 26 | ] 27 | }, 28 | "execution_count": 1, 29 | "metadata": {}, 30 | "output_type": "execute_result" 31 | } 32 | ], 33 | "source": [ 34 | "5+3" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "The Jupyter notebook environment recognizes the cell's input as Python code, and returns an output of $8$. Other arithmetic operations can also be done. Click on the next cell and press `shift+enter` to run it. " 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 2, 47 | "metadata": {}, 48 | "outputs": [ 49 | { 50 | "data": { 51 | "text/plain": [ 52 | "15" 53 | ] 54 | }, 55 | "execution_count": 2, 56 | "metadata": {}, 57 | "output_type": "execute_result" 58 | } 59 | ], 60 | "source": [ 61 | "5*3" 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": {}, 67 | "source": [ 68 | "We can also print out \"strings\" of characters, by running something like the next cell." 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": 3, 74 | "metadata": {}, 75 | "outputs": [ 76 | { 77 | "name": "stdout", 78 | "output_type": "stream", 79 | "text": [ 80 | "this is a string of characters\n" 81 | ] 82 | } 83 | ], 84 | "source": [ 85 | "print('this is a string of characters')" 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "metadata": {}, 91 | "source": [ 92 | "## Installing QISKit\n", 93 | "\n", 94 | "Jupyer notebook also allows you to install various things using `pip`. For example, we can install IBM's QISKit using `pip` by running the next cell. The output is unimportant right now. The important thing to know is this allows us to bypass using terminal to install packages using `pip`. " 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": {}, 101 | "outputs": [ 102 | { 103 | "name": "stdout", 104 | "output_type": "stream", 105 | "text": [ 106 | "Requirement already satisfied: qiskit in /opt/anaconda3/lib/python3.7/site-packages (0.18.3)\n", 107 | "Requirement already satisfied: qiskit-aqua==0.6.6 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit) (0.6.6)\n", 108 | "Requirement already satisfied: qiskit-terra==0.13.0 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit) (0.13.0)\n", 109 | "Requirement already satisfied: qiskit-ibmq-provider==0.6.1 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit) (0.6.1)\n", 110 | "Requirement already satisfied: qiskit-ignis==0.3.0 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit) (0.3.0)\n", 111 | "Requirement already satisfied: qiskit-aer==0.5.1 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit) (0.5.1)\n", 112 | "Requirement already satisfied: sympy>=1.3 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aqua==0.6.6->qiskit) (1.5.1)\n", 113 | "Requirement already satisfied: scipy>=1.0 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aqua==0.6.6->qiskit) (1.4.1)\n", 114 | "Requirement already satisfied: psutil>=5 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aqua==0.6.6->qiskit) (5.6.7)\n", 115 | "Requirement already satisfied: h5py in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aqua==0.6.6->qiskit) (2.10.0)\n", 116 | "Requirement already satisfied: jsonschema>=2.6 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aqua==0.6.6->qiskit) (3.2.0)\n", 117 | "Requirement already satisfied: dlx in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aqua==0.6.6->qiskit) (1.0.4)\n", 118 | "Requirement already satisfied: setuptools>=40.1.0 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aqua==0.6.6->qiskit) (46.0.0.post20200309)\n", 119 | "Requirement already satisfied: scikit-learn>=0.20.0 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aqua==0.6.6->qiskit) (0.22.1)\n", 120 | "Requirement already satisfied: quandl in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aqua==0.6.6->qiskit) (3.5.0)\n", 121 | "Requirement already satisfied: networkx>=2.2 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aqua==0.6.6->qiskit) (2.4)\n", 122 | "Requirement already satisfied: pyscf; sys_platform == \"linux\" or (python_version < \"3.8\" and sys_platform != \"win32\") in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aqua==0.6.6->qiskit) (1.7.1.post1)\n", 123 | "Requirement already satisfied: fastdtw in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aqua==0.6.6->qiskit) (0.3.4)\n", 124 | "Requirement already satisfied: docplex in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aqua==0.6.6->qiskit) (2.13.184)\n", 125 | "Requirement already satisfied: numpy>=1.13 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aqua==0.6.6->qiskit) (1.18.1)\n", 126 | "Requirement already satisfied: marshmallow-polyfield<6,>=5.7 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-terra==0.13.0->qiskit) (5.9)\n", 127 | "Requirement already satisfied: ply>=3.10 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-terra==0.13.0->qiskit) (3.11)\n", 128 | "Requirement already satisfied: marshmallow<4,>=3 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-terra==0.13.0->qiskit) (3.5.1)\n", 129 | "Requirement already satisfied: fastjsonschema>=2.10 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-terra==0.13.0->qiskit) (2.14.4)\n", 130 | "Requirement already satisfied: retworkx>=0.3.2 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-terra==0.13.0->qiskit) (0.3.3)\n", 131 | "Requirement already satisfied: dill>=0.3 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-terra==0.13.0->qiskit) (0.3.1.1)\n", 132 | "Requirement already satisfied: python-constraint>=1.4 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-terra==0.13.0->qiskit) (1.4.0)\n", 133 | "Requirement already satisfied: requests>=2.19 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.6.1->qiskit) (2.22.0)\n", 134 | "Requirement already satisfied: nest-asyncio!=1.1.0,>=1.0.0 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.6.1->qiskit) (1.3.2)\n", 135 | "Requirement already satisfied: arrow>=0.15.5 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.6.1->qiskit) (0.15.5)\n", 136 | "Requirement already satisfied: requests-ntlm>=1.1.0 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.6.1->qiskit) (1.1.0)\n", 137 | "Requirement already satisfied: websockets<8,>=7 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-ibmq-provider==0.6.1->qiskit) (7.0)\n", 138 | "Requirement already satisfied: pybind11>=2.4 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aer==0.5.1->qiskit) (2.5.0)\n", 139 | "Requirement already satisfied: cython>=0.27.1 in /opt/anaconda3/lib/python3.7/site-packages (from qiskit-aer==0.5.1->qiskit) (0.29.15)\n", 140 | "Requirement already satisfied: mpmath>=0.19 in /opt/anaconda3/lib/python3.7/site-packages (from sympy>=1.3->qiskit-aqua==0.6.6->qiskit) (1.1.0)\n", 141 | "Requirement already satisfied: six in /opt/anaconda3/lib/python3.7/site-packages (from h5py->qiskit-aqua==0.6.6->qiskit) (1.14.0)\n", 142 | "Requirement already satisfied: pyrsistent>=0.14.0 in /opt/anaconda3/lib/python3.7/site-packages (from jsonschema>=2.6->qiskit-aqua==0.6.6->qiskit) (0.15.7)\n", 143 | "Requirement already satisfied: importlib-metadata; python_version < \"3.8\" in /opt/anaconda3/lib/python3.7/site-packages (from jsonschema>=2.6->qiskit-aqua==0.6.6->qiskit) (1.5.0)\n", 144 | "Requirement already satisfied: attrs>=17.4.0 in /opt/anaconda3/lib/python3.7/site-packages (from jsonschema>=2.6->qiskit-aqua==0.6.6->qiskit) (19.3.0)\n", 145 | "Requirement already satisfied: joblib>=0.11 in /opt/anaconda3/lib/python3.7/site-packages (from scikit-learn>=0.20.0->qiskit-aqua==0.6.6->qiskit) (0.14.1)\n", 146 | "Requirement already satisfied: python-dateutil in /opt/anaconda3/lib/python3.7/site-packages (from quandl->qiskit-aqua==0.6.6->qiskit) (2.8.1)\n", 147 | "Requirement already satisfied: more-itertools in /opt/anaconda3/lib/python3.7/site-packages (from quandl->qiskit-aqua==0.6.6->qiskit) (8.2.0)\n", 148 | "Requirement already satisfied: pandas>=0.14 in /opt/anaconda3/lib/python3.7/site-packages (from quandl->qiskit-aqua==0.6.6->qiskit) (1.0.1)\n", 149 | "Requirement already satisfied: inflection>=0.3.1 in /opt/anaconda3/lib/python3.7/site-packages (from quandl->qiskit-aqua==0.6.6->qiskit) (0.4.0)\n", 150 | "Requirement already satisfied: decorator>=4.3.0 in /opt/anaconda3/lib/python3.7/site-packages (from networkx>=2.2->qiskit-aqua==0.6.6->qiskit) (4.4.1)\n", 151 | "Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /opt/anaconda3/lib/python3.7/site-packages (from requests>=2.19->qiskit-ibmq-provider==0.6.1->qiskit) (1.25.8)\n", 152 | "Requirement already satisfied: idna<2.9,>=2.5 in /opt/anaconda3/lib/python3.7/site-packages (from requests>=2.19->qiskit-ibmq-provider==0.6.1->qiskit) (2.8)\n", 153 | "Requirement already satisfied: certifi>=2017.4.17 in /opt/anaconda3/lib/python3.7/site-packages (from requests>=2.19->qiskit-ibmq-provider==0.6.1->qiskit) (2019.11.28)\n", 154 | "Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /opt/anaconda3/lib/python3.7/site-packages (from requests>=2.19->qiskit-ibmq-provider==0.6.1->qiskit) (3.0.4)\n", 155 | "Requirement already satisfied: cryptography>=1.3 in /opt/anaconda3/lib/python3.7/site-packages (from requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.6.1->qiskit) (2.8)\n", 156 | "Requirement already satisfied: ntlm-auth>=1.0.2 in /opt/anaconda3/lib/python3.7/site-packages (from requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.6.1->qiskit) (1.4.0)\n", 157 | "Requirement already satisfied: zipp>=0.5 in /opt/anaconda3/lib/python3.7/site-packages (from importlib-metadata; python_version < \"3.8\"->jsonschema>=2.6->qiskit-aqua==0.6.6->qiskit) (2.2.0)\n", 158 | "Requirement already satisfied: pytz>=2017.2 in /opt/anaconda3/lib/python3.7/site-packages (from pandas>=0.14->quandl->qiskit-aqua==0.6.6->qiskit) (2019.3)\n", 159 | "Requirement already satisfied: cffi!=1.11.3,>=1.8 in /opt/anaconda3/lib/python3.7/site-packages (from cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.6.1->qiskit) (1.14.0)\n", 160 | "Requirement already satisfied: pycparser in /opt/anaconda3/lib/python3.7/site-packages (from cffi!=1.11.3,>=1.8->cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.6.1->qiskit) (2.19)\n" 161 | ] 162 | } 163 | ], 164 | "source": [ 165 | "pip install qiskit" 166 | ] 167 | }, 168 | { 169 | "cell_type": "markdown", 170 | "metadata": {}, 171 | "source": [ 172 | "## Installing PennyLane\n", 173 | "\n", 174 | "Now, let's install PennyLane" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": null, 180 | "metadata": {}, 181 | "outputs": [], 182 | "source": [ 183 | "pip install pennylane" 184 | ] 185 | }, 186 | { 187 | "cell_type": "markdown", 188 | "metadata": {}, 189 | "source": [ 190 | "We can also import packages to use by using the `import` command. For example, run the next cell to import NumPy. " 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": null, 196 | "metadata": {}, 197 | "outputs": [], 198 | "source": [ 199 | "import numpy as np" 200 | ] 201 | }, 202 | { 203 | "cell_type": "markdown", 204 | "metadata": {}, 205 | "source": [ 206 | "This imports NumPy as the \"alias\" `np`. When we use functionality from NumPy, we will use the prefix `np`. As an example, if we want to build a two-by-two matrix using NumPy, we would run the following code." 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": null, 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "np.matrix([[1, 2],\n", 216 | " [3, 4]])" 217 | ] 218 | }, 219 | { 220 | "cell_type": "markdown", 221 | "metadata": {}, 222 | "source": [ 223 | "We can set the symbol `A` equal to this matrix to store this data type as the more compact `A`, rather than writing out the all of the code in the definition of the matrix each time. " 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "metadata": {}, 230 | "outputs": [], 231 | "source": [ 232 | "A = np.matrix([[1, 2],\n", 233 | " [3, 4]])" 234 | ] 235 | }, 236 | { 237 | "cell_type": "markdown", 238 | "metadata": {}, 239 | "source": [ 240 | "Notice how nothing is printed. If we want to print `A`, we can use the following command. " 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": null, 246 | "metadata": {}, 247 | "outputs": [], 248 | "source": [ 249 | "print(A)" 250 | ] 251 | }, 252 | { 253 | "cell_type": "markdown", 254 | "metadata": {}, 255 | "source": [ 256 | "Many other things can be done with Jupyter notebooks. There are various plugins and widgets. We will be using Jupyer notebooks extensively throughout, so you will get much more practice running them and familiarizing yourself with Python code. Now, let's talk a little bit about what *Quantum Computing* is. " 257 | ] 258 | }, 259 | { 260 | "cell_type": "markdown", 261 | "metadata": {}, 262 | "source": [ 263 | "# Quantum Computing\n", 264 | "\n", 265 | "In the lectures and interactive notebooks, we will be using a lot of *linear algebra*, i.e. matrix and vector operations. " 266 | ] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": {}, 271 | "source": [ 272 | "## Why Linear Algebra?\n", 273 | "Linear algebra, the language of matrices and vectors, is the fundamental language of quantum computing and quantum information. Approaching quantum computing through linear algebra is the approach taken in the most cited textbook on the subject: [\"Quantum Computation and Quantum Information\"](https://www.amazon.com/gp/product/1107002176/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1107002176&linkCode=as2&tag=singularity07-20&linkId=10080ebd13739525bdfd76be97682775) by Nielsen & Chuang. This approach is also mathematically the most approachable for the complete beginner. If you have very little background in mathematics, physics, and computer programming, this approach is for you!\n", 274 | "\n", 275 | "## What Quantum Computing is, and is not\n", 276 | "Quantum computing is a way of processing information in a fundamentally different way from the way your laptop or smartphone processes data. The \"*classical*\" way of processing information is in terms of bits, binary values of zeros and ones. Quantum computing takes advantage of three different properties of quantum physics to process information. \n", 277 | "\n", 278 | "### Superposition\n", 279 | "**Superposition** is the fancy word used to describe how **qubits**, the quantum version of bits, can exist in a combination of both zero and one. Qubits can also be measured in different *bases*, allowing for things like *adaptive measurement* of states, and *measurement based quantum computation*. Since qubits can be in a state that is a mixture of both zero and one, this gives them computational properties that classical bits don't have. \n", 280 | "\n", 281 | "### Entanglement\n", 282 | "**Entanglement** is the word used to describe how qubits can have states that are correlated with other qubit states. Qubits interact with each other and become *entangled*, meaning their states are now statistically correlated and information about one can sometimes reveal information about the other. Multiple qubits can be entangled at once and complicated behavior can emerge. One area of research that scientists have shown much interest in is **multipartite entanglement**. This is a complex form of entanglement that exhibits highly non-classical behavior. This can be used in quantum teleportation and quantum cryptography protocols. It is also the source of a famous experiment named after Einstein, Podolsky, and Rosen, as well as John Bell's famous experiments showing that local hidden variables theories are highly unlikely to be true. \n", 283 | "\n", 284 | "### Interference\n", 285 | "**Interference**, like the interference we see in waves of light, sound, and water, is exhibited by quantum computers. Quantum computers can have *constructive* (additive) interference, or they can have *destructive* interference, where waves are out of sync and cancel. This interesteing and strange behavior is often the subject of debate when discussing *wave-particle duality* and the famous *measurement problem of quantum physics*. Interference can be used to enhance \"good information\" and to cancel \"bad information\" in computations. This allows us to set up computations in clever ways and use interference as an information processing technique to solve problems. \n", 286 | "\n", 287 | "### Quantum Computers as ASICs\n", 288 | "ASICs or **Application Specific Integrated Circuits** are specialized computer chips that are built for very specific kinds of computational tasks. A good example of this the GPU or *Graphics Processing Unit*, or Google's TPU (Tensor Processing Unit) which have enhanced machine learning drastically in recent years. Other examples might be the *neuromorphic computing chips* that Intel has designed. These were all designed to solve specific kinds of problems, such as enhancing machine learning tasks. Quantum computers can be thought of in a very similar way. There is a classical computer interface that delegates certain computational tasks to the quantum hardware. The quantum hardware then processes that computational task and then the classical computer receives the output data. There are many kinds of problems that quantum computers can solve. In fact, any computational task a classical computer can solve, a quantum computer can also solve. However, classical computers are still more efficient at certain things, so they will not be going anywhere. There are many algorithms and problems that can be solved exponentially faster on a quantum computer compared to all known classical methods. \n", 289 | "\n", 290 | "Finding out where this computational advantage comes from is one of the central tasks of quantum computing research. There is a [Quantum Algorithm Zoo](https://quantumalgorithmzoo.org/), which lists many of the known algorithms that give speedup over classical methods. Much of the focus of The Singularity is to research these various applications and algorithms and implement them in various quantum computing languages. Some applications include using Shor's algorithm to break RSA public key cryptography and other algorithms that break elliptic curve cryptography. If you are interested in discovering the inner workings of quantum algorithms and trying to understand where this quantum supremacy or advantage comes from, check out these lectures. \n", 291 | "\n", 292 | "### Quantikz\n", 293 | "If you are a [Donald Knuth](https://www-cs-faculty.stanford.edu/~knuth/) fan and love [LaTeX](https://www.latex-project.org/) too, check out the TikZ library [quantikz](https://ctan.org/pkg/quantikz?lang=en). It is a LaTeX library for constructing quantum circuit diagrams. It was used for rendering many of the quantum circuit diagrams in the lectures. " 294 | ] 295 | } 296 | ], 297 | "metadata": { 298 | "kernelspec": { 299 | "display_name": "Python 3", 300 | "language": "python", 301 | "name": "python3" 302 | }, 303 | "language_info": { 304 | "codemirror_mode": { 305 | "name": "ipython", 306 | "version": 3 307 | }, 308 | "file_extension": ".py", 309 | "mimetype": "text/x-python", 310 | "name": "python", 311 | "nbconvert_exporter": "python", 312 | "pygments_lexer": "ipython3", 313 | "version": "3.7.6" 314 | } 315 | }, 316 | "nbformat": 4, 317 | "nbformat_minor": 4 318 | } 319 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linear Algebra for Quantum Computing 2 | --- 3 | Interested in contributing to this project? 4 | - Reach out via email to: thesingularity.research@gmail.com 5 | - Be sure to include "Hacking the Universe" in the subject line, so that the email doesn't get overlooked. 6 | - Write a paragraph or two about how you would like to contribute. 7 | - Ask to Join the Discord server. 8 | - Ask to Join the Slack Channel. 9 | 10 | This is course material for a course on linear algebra and mathematical prerequisites for quantum computing. It contains Jupyter notebooks that can be downloaded as part of the course or opened in Binder as an online interactive notebook. 11 | 12 | [Become a Sponsor of The Singularity](https://github.com/sponsors/The-Singularity-Research) 13 | 14 | --- 15 | 16 | ## Why Linear Algebra? 17 | Linear algebra, the language of matrices and vectors, is the fundamental language of quantum computing and quantum information. Approaching quantum computing through linear algebra is the approach taken in the most cited textbook on the subject: ["Quantum Computation and Quantum Information"](https://www.amazon.com/gp/product/1107002176/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1107002176&linkCode=as2&tag=singularity07-20&linkId=10080ebd13739525bdfd76be97682775) by Nielsen & Chuang. This approach is also mathematically the most approachable for the complete beginner. If you have very little background in mathematics, physics, and computer programming, this approach is for you! 18 | 19 | ## What Quantum Computing is, and is not 20 | Quantum computing is a way of processing information in a fundamentally different way from the way your laptop or smartphone processes data. The "*classical*" way of processing information is in terms of bits, binary values of zeros and ones. Quantum computing takes advantage of three different properties of quantum physics to process information. 21 | 22 | ### Superposition 23 | **Superposition** is the fancy word used to describe how **qubits**, the quantum version of bits, can exist in a combination of both zero and one. Qubits can also be measured in different *bases*, allowing for things like *adaptive measurement* of states, and *measurement based quantum computation*. Since qubits can be in a state that is a mixture of both zero and one, this gives them computational properties that classical bits don't have. 24 | 25 | ### Entanglement 26 | **Entanglement** is the word used to describe how qubits can have states that are correlated with other qubit states. Qubits interact with each other and become *entangled*, meaning their states are now statistically correlated and information about one can sometimes reveal information about the other. Multiple qubits can be entangled at once and complicated behavior can emerge. One area of research that scientists have shown much interest in is **multipartite entanglement**. This is a complex form of entanglement that exhibits highly non-classical behavior. This can be used in quantum teleportation and quantum cryptography protocols. It is also the source of a famous experiment named after Einstein, Podolsky, and Rosen, as well as John Bell's famous experiments showing that local hidden variables theories are highly unlikely to be true. 27 | 28 | ### Interference 29 | **Interference**, like the interference we see in waves of light, sound, and water, is exhibited by quantum computers. Quantum computers can have *constructive* (additive) interference, or they can have *destructive* interference, where waves are out of sync and cancel. This interesteing and strange behavior is often the subject of debate when discussing *wave-particle duality* and the famous *measurement problem of quantum physics*. Interference can be used to enhance "good information" and to cancel "bad information" in computations. This allows us to set up computations in clever ways and use interference as an information processing technique to solve problems. 30 | 31 | ### Quantum Computers as ASICs 32 | ASICs or **Application Specific Integrated Circuits** are specialized computer chips that are built for very specific kinds of computational tasks. A good example of this the GPU or *Graphics Processing Unit*, or Google's TPU (Tensor Processing Unit) which have enhanced machine learning drastically in recent years. Other examples might be the *neuromorphic computing chips* that Intel has designed. These were all designed to solve specific kinds of problems, such as enhancing machine learning tasks. Quantum computers can be thought of in a very similar way. There is a classical computer interface that delegates certain computational tasks to the quantum hardware. The quantum hardware then processes that computational task and then the classical computer receives the output data. There are many kinds of problems that quantum computers can solve. In fact, any computational task a classical computer can solve, a quantum computer can also solve. However, classical computers are still more efficient at certain things, so they will not be going anywhere. There are many algorithms and problems that can be solved exponentially faster on a quantum computer compared to all known classical methods. 33 | 34 | Finding out where this computational advantage comes from is one of the central tasks of quantum computing research. There is a [Quantum Algorithm Zoo](https://quantumalgorithmzoo.org/), which lists many of the known algorithms that give speedup over classical methods. Much of the focus of The Singularity is to research these various applications and algorithms and implement them in various quantum computing languages. Some applications include using Shor's algorithm to break RSA public key cryptography and other algorithms that break elliptic curve cryptography. If you are interested in discovering the inner workings of quantum algorithms and trying to understand where this quantum supremacy or advantage comes from, check out these lectures. 35 | 36 | ### Quantikz 37 | If you are a Donald Knuth fan and love LaTeX too, check out the TikZ library [quantikz](https://ctan.org/pkg/quantikz?lang=en). It is a LaTeX library for constructing quantum circuit diagrams. It was used for rendering many of the quantum circuit diagrams in the following lectures. 38 | 39 | ## Lectures 40 | Below are lectures using Jupyter notebooks that fully develop all of the basic math and programming that is needed to start solving quantum computing problems. They start out with basic linear algebra, and explain the notion of quantum gates (similar to classical logical gates in classical computers). Click on the "launch binder" button to open the interactive version. 41 | 42 | - Lecture 1: [What is Quantum Computing?](https://github.com/The-Singularity-Research/linear_algebra_for_quantum_computing/blob/master/Intro.ipynb) 43 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/The-Singularity-Research/linear_algebra_for_quantum_computing/master?filepath=Intro.ipynb) 44 | - Lecture 2: [Complex Numbers in Python](https://github.com/The-Singularity-Research/linear_algebra_for_quantum_computing/blob/master/lecture_2_complex_numbers.ipynb) 45 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/The-Singularity-Research/linear_algebra_for_quantum_computing/master?filepath=lecture_2_complex_numbers.ipynb) 46 | - Lecture 3: [Vectors in Python](https://github.com/The-Singularity-Research/linear_algebra_for_quantum_computing/blob/master/lecture_3_vectors.ipynb) 47 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/The-Singularity-Research/linear_algebra_for_quantum_computing/master?filepath=lecture_3_vectors.ipynb) 48 | - Lecture 4: [Inner Products and Unit Vectors](https://github.com/The-Singularity-Research/linear_algebra_for_quantum_computing/blob/master/lecture_4_inner_products.ipynb) 49 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/The-Singularity-Research/linear_algebra_for_quantum_computing/master?filepath=lecture_4_inner_products.ipynb) 50 | - Lecture 5: [The Bloch Sphere and Representing Qubits](https://github.com/The-Singularity-Research/linear_algebra_for_quantum_computing/blob/master/lecture_5_bloch_sphere.ipynb) 51 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/The-Singularity-Research/linear_algebra_for_quantum_computing/master?filepath=lecture_5_bloch_sphere.ipynb) 52 | - Lecture 6: [Tensor Products of Vectors in Python](https://github.com/The-Singularity-Research/linear_algebra_for_quantum_computing/blob/master/lecture_6_tensor_products.ipynb) 53 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/The-Singularity-Research/linear_algebra_for_quantum_computing/master?filepath=lecture_6_tensor_products.ipynb) 54 | - Lecture 7: [Preparing Basis States in PennyLane](https://github.com/The-Singularity-Research/linear_algebra_for_quantum_computing/blob/master/lecture_7_preparing_basis_states.ipynb) 55 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/The-Singularity-Research/linear_algebra_for_quantum_computing/master?filepath=lecture_7_preparing_basis_states.ipynb) 56 | - Lecture 8: [Matrices in Python](https://github.com/The-Singularity-Research/linear_algebra_for_quantum_computing/blob/master/lecture_8_matrices.ipynb) 57 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/The-Singularity-Research/linear_algebra_for_quantum_computing/master?filepath=lecture_8_matrices.ipynb) 58 | - Lecture 9: [Tensor Products of Matrices in Python](https://github.com/The-Singularity-Research/linear_algebra_for_quantum_computing/blob/master/lecture_9_tensor_product_matrices.ipynb) 59 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/The-Singularity-Research/linear_algebra_for_quantum_computing/master?filepath=lecture_9_tensor_product_matrices.ipynb) 60 | - Lecture 10: [Quantum Logic Gates as Matrices](https://github.com/The-Singularity-Research/linear_algebra_for_quantum_computing/blob/master/lecture_10_quantum_gates.ipynb) 61 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/The-Singularity-Research/linear_algebra_for_quantum_computing/master?filepath=lecture_10_quantum_gates.ipynb) 62 | 63 | 64 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /lecture_2_complex_numbers.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Complex Numbers in Python" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Introduction" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "Complex numbers are fundamental to quantum computing so understanding them will be a good first step in understanding how quantum computers work. Quantum computers process information differently from classical computers like your laptop, smartphone, or a supercomputer used to train modern neural networks. They do this in part by using interference patterns of *amplitudes* which are complex numbers that describe the states of the qubits in a quantum computer. \n", 22 | "\n", 23 | "Complex numbers are always of the form\n", 24 | "\n", 25 | "\\begin{align}\n", 26 | "\\alpha = a + bi\n", 27 | "\\end{align}\n", 28 | "\n", 29 | "where $a$ and $b$ are real numbers and $i = \\sqrt{-1}$. Complex numbers can be used in Python by importing [NumPy](https://numpy.org/): \n", 30 | "\n", 31 | "> \"the fundamental package for scientific computing with Python. It contains among other things:\n", 32 | "> - a powerful N-dimensional array object\n", 33 | "> - sophisticated (broadcasting) functions\n", 34 | "> - tools for integrating C/C++ and Fortran code\n", 35 | "> - useful linear algebra, Fourier transform, and random number capabilities\n", 36 | "> Besides its obvious scientific uses, NumPy can also be used as an efficient multi-dimensional container of generic data. Arbitrary data-types can be defined. This allows NumPy to seamlessly and speedily integrate with a wide variety of databases.\"" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "## Importing NumPy\n", 44 | "\n", 45 | "We will primarily use NumPy for arithmetic with complex numbers as well as for linear algebra. To import NumPy we type the following code:" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": 1, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "import numpy as np" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "## Arithmetic of Complex Numbers" 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": {}, 67 | "source": [ 68 | "We will be working with complex numbers in this tutorial. If complex numbers are completely new and strange to you, consider watching the short video by [Khan Academy](https://www.youtube.com/watch?v=SP-YJe7Vldo&list=PLucNEDEq4z_AdqV1ii0vmH0clNhYPI4lK&index=2). In Python the imaginary unit $i$ is instead represented as $j$, and must always be written with a coefficient. So in Python, we must write $1j$ instead of $j$, and $-1j$ instead of just $-j$. Let's check that $j^2 = -1$. " 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": 2, 74 | "metadata": {}, 75 | "outputs": [ 76 | { 77 | "data": { 78 | "text/plain": [ 79 | "(-1+0j)" 80 | ] 81 | }, 82 | "execution_count": 2, 83 | "metadata": {}, 84 | "output_type": "execute_result" 85 | } 86 | ], 87 | "source": [ 88 | "(-1j)*(-1j)" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "As we can see, the output is:\n", 96 | "\n", 97 | "\\begin{align}\n", 98 | "-1 + 0j = -1. \n", 99 | "\\end{align}" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "Now, let's define two complex numbers \n", 107 | "\n", 108 | "\\begin{align}\n", 109 | "z &= 3+2j \\\\\n", 110 | "w &= -2-5j\n", 111 | "\\end{align}" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 3, 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "z = 3 + 2j\n", 121 | "w = -2 - 5j" 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "metadata": {}, 127 | "source": [ 128 | "If we wanted to print $z$ and $w$, we could simply type them into a Jupyter cell as follows (followed by `shift+enter`):" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 4, 134 | "metadata": {}, 135 | "outputs": [ 136 | { 137 | "data": { 138 | "text/plain": [ 139 | "(3+2j)" 140 | ] 141 | }, 142 | "execution_count": 4, 143 | "metadata": {}, 144 | "output_type": "execute_result" 145 | } 146 | ], 147 | "source": [ 148 | "z" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 5, 154 | "metadata": {}, 155 | "outputs": [ 156 | { 157 | "data": { 158 | "text/plain": [ 159 | "(-2-5j)" 160 | ] 161 | }, 162 | "execution_count": 5, 163 | "metadata": {}, 164 | "output_type": "execute_result" 165 | } 166 | ], 167 | "source": [ 168 | "w" 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "metadata": {}, 174 | "source": [ 175 | "We could also type the `print()` command (followed by `shift+enter`):" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": 6, 181 | "metadata": {}, 182 | "outputs": [ 183 | { 184 | "name": "stdout", 185 | "output_type": "stream", 186 | "text": [ 187 | "(3+2j)\n" 188 | ] 189 | } 190 | ], 191 | "source": [ 192 | "print(z)" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": 7, 198 | "metadata": {}, 199 | "outputs": [ 200 | { 201 | "name": "stdout", 202 | "output_type": "stream", 203 | "text": [ 204 | "(-2-5j)\n" 205 | ] 206 | } 207 | ], 208 | "source": [ 209 | "print(w)" 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "If we wanted to label what we are printing we can do the following:" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 8, 222 | "metadata": {}, 223 | "outputs": [ 224 | { 225 | "name": "stdout", 226 | "output_type": "stream", 227 | "text": [ 228 | "z= (3+2j)\n" 229 | ] 230 | } 231 | ], 232 | "source": [ 233 | "print(\"z=\", z)" 234 | ] 235 | }, 236 | { 237 | "cell_type": "code", 238 | "execution_count": 9, 239 | "metadata": {}, 240 | "outputs": [ 241 | { 242 | "name": "stdout", 243 | "output_type": "stream", 244 | "text": [ 245 | "the complex number w is: (-2-5j)\n" 246 | ] 247 | } 248 | ], 249 | "source": [ 250 | "print(\"the complex number w is:\", w)" 251 | ] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": {}, 256 | "source": [ 257 | "We can print the real and imaginary parts of complex numbers as follows for $z = 3+4i:" 258 | ] 259 | }, 260 | { 261 | "cell_type": "code", 262 | "execution_count": 10, 263 | "metadata": {}, 264 | "outputs": [ 265 | { 266 | "name": "stdout", 267 | "output_type": "stream", 268 | "text": [ 269 | "z= (3+2j)\n", 270 | "Real(z)= 3.0\n", 271 | "Imag(Z)= 2.0\n" 272 | ] 273 | } 274 | ], 275 | "source": [ 276 | "print('z=', z)\n", 277 | "print('Real(z)=', np.real(z))\n", 278 | "print('Imag(Z)=', np.imag(z))" 279 | ] 280 | }, 281 | { 282 | "cell_type": "code", 283 | "execution_count": 11, 284 | "metadata": {}, 285 | "outputs": [ 286 | { 287 | "name": "stdout", 288 | "output_type": "stream", 289 | "text": [ 290 | "w= (-2-5j)\n", 291 | "Real(w)= -2.0\n", 292 | "Imag(w)= -5.0\n" 293 | ] 294 | } 295 | ], 296 | "source": [ 297 | "print('w=', w)\n", 298 | "print('Real(w)=', np.real(w))\n", 299 | "print('Imag(w)=', np.imag(w))" 300 | ] 301 | }, 302 | { 303 | "cell_type": "markdown", 304 | "metadata": {}, 305 | "source": [ 306 | "## Complex Conjugates\n", 307 | "\n", 308 | "We can compute the **complex conjugates** $z^*$ and $w^*$, which just negates the imaginary part of the complex numbers as follows:" 309 | ] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "execution_count": 12, 314 | "metadata": {}, 315 | "outputs": [ 316 | { 317 | "data": { 318 | "text/plain": [ 319 | "(3-2j)" 320 | ] 321 | }, 322 | "execution_count": 12, 323 | "metadata": {}, 324 | "output_type": "execute_result" 325 | } 326 | ], 327 | "source": [ 328 | "np.conj(z)" 329 | ] 330 | }, 331 | { 332 | "cell_type": "code", 333 | "execution_count": 13, 334 | "metadata": {}, 335 | "outputs": [ 336 | { 337 | "data": { 338 | "text/plain": [ 339 | "(-2+5j)" 340 | ] 341 | }, 342 | "execution_count": 13, 343 | "metadata": {}, 344 | "output_type": "execute_result" 345 | } 346 | ], 347 | "source": [ 348 | "np.conj(w)" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "metadata": {}, 354 | "source": [ 355 | "As we can see, complex conjugation negates the imaginary part of the complex number. " 356 | ] 357 | }, 358 | { 359 | "cell_type": "markdown", 360 | "metadata": {}, 361 | "source": [ 362 | "## Norms/Absolute Values\n", 363 | "\n", 364 | "We can compute the **absolute value** of the complex numbers \n", 365 | "\n", 366 | "\\begin{align} ||z|| &= \\sqrt{zz^*} = \\sqrt{|z|^2},\\\\ ||w|| &= \\sqrt{ww^*} = \\sqrt{|w|^2}, \\end{align} " 367 | ] 368 | }, 369 | { 370 | "cell_type": "code", 371 | "execution_count": 14, 372 | "metadata": {}, 373 | "outputs": [ 374 | { 375 | "name": "stdout", 376 | "output_type": "stream", 377 | "text": [ 378 | "||z||= 3.605551275463989\n" 379 | ] 380 | } 381 | ], 382 | "source": [ 383 | "print('||z||=', np.abs(z))" 384 | ] 385 | }, 386 | { 387 | "cell_type": "code", 388 | "execution_count": 15, 389 | "metadata": {}, 390 | "outputs": [ 391 | { 392 | "name": "stdout", 393 | "output_type": "stream", 394 | "text": [ 395 | "||w||= 5.385164807134504\n" 396 | ] 397 | } 398 | ], 399 | "source": [ 400 | "print('||w||=', np.abs(w))" 401 | ] 402 | }, 403 | { 404 | "cell_type": "markdown", 405 | "metadata": {}, 406 | "source": [ 407 | "Now, there are many different arithmetic operations we can perform on complex numbers. Addition, subtraction, multiplication, and division are all defined for complex numbers and can be computer using NumPy. As an example, let's compute the following three values, \n", 408 | "\n", 409 | "- $z+w$\n", 410 | "- $z-w$\n", 411 | "- $w-z$" 412 | ] 413 | }, 414 | { 415 | "cell_type": "code", 416 | "execution_count": 16, 417 | "metadata": {}, 418 | "outputs": [ 419 | { 420 | "data": { 421 | "text/plain": [ 422 | "(1-3j)" 423 | ] 424 | }, 425 | "execution_count": 16, 426 | "metadata": {}, 427 | "output_type": "execute_result" 428 | } 429 | ], 430 | "source": [ 431 | "z+w" 432 | ] 433 | }, 434 | { 435 | "cell_type": "code", 436 | "execution_count": 17, 437 | "metadata": {}, 438 | "outputs": [ 439 | { 440 | "data": { 441 | "text/plain": [ 442 | "(5+7j)" 443 | ] 444 | }, 445 | "execution_count": 17, 446 | "metadata": {}, 447 | "output_type": "execute_result" 448 | } 449 | ], 450 | "source": [ 451 | "z-w" 452 | ] 453 | }, 454 | { 455 | "cell_type": "code", 456 | "execution_count": 18, 457 | "metadata": {}, 458 | "outputs": [ 459 | { 460 | "data": { 461 | "text/plain": [ 462 | "(-5-7j)" 463 | ] 464 | }, 465 | "execution_count": 18, 466 | "metadata": {}, 467 | "output_type": "execute_result" 468 | } 469 | ], 470 | "source": [ 471 | "w-z" 472 | ] 473 | }, 474 | { 475 | "cell_type": "markdown", 476 | "metadata": {}, 477 | "source": [ 478 | "Addition and subtraction of complex numbers is done *componentwise*, meaning the real parts and the complex parts are added or subtracted individually and do not effect each other. So, for example, \n", 479 | "\n", 480 | "\\begin{align}\n", 481 | "z+w = (3+2j) + (-2-5j) = (3-2) + (2-5)j = 1-3j. \n", 482 | "\\end{align}" 483 | ] 484 | }, 485 | { 486 | "cell_type": "markdown", 487 | "metadata": {}, 488 | "source": [ 489 | "We can also multiply complex numbers and divide them as well. This is also handled by NumPy. Let's compute the following three values, \n", 490 | "\n", 491 | "- $z \\cdot w$\n", 492 | "- $\\frac{z}{w}$\n", 493 | "- $\\frac{w}{z}$" 494 | ] 495 | }, 496 | { 497 | "cell_type": "code", 498 | "execution_count": 19, 499 | "metadata": {}, 500 | "outputs": [ 501 | { 502 | "data": { 503 | "text/plain": [ 504 | "(4-19j)" 505 | ] 506 | }, 507 | "execution_count": 19, 508 | "metadata": {}, 509 | "output_type": "execute_result" 510 | } 511 | ], 512 | "source": [ 513 | "z*w" 514 | ] 515 | }, 516 | { 517 | "cell_type": "code", 518 | "execution_count": 20, 519 | "metadata": {}, 520 | "outputs": [ 521 | { 522 | "data": { 523 | "text/plain": [ 524 | "(-0.5517241379310345+0.37931034482758624j)" 525 | ] 526 | }, 527 | "execution_count": 20, 528 | "metadata": {}, 529 | "output_type": "execute_result" 530 | } 531 | ], 532 | "source": [ 533 | "z/w" 534 | ] 535 | }, 536 | { 537 | "cell_type": "code", 538 | "execution_count": 21, 539 | "metadata": {}, 540 | "outputs": [ 541 | { 542 | "data": { 543 | "text/plain": [ 544 | "(-1.2307692307692308-0.8461538461538463j)" 545 | ] 546 | }, 547 | "execution_count": 21, 548 | "metadata": {}, 549 | "output_type": "execute_result" 550 | } 551 | ], 552 | "source": [ 553 | "w/z" 554 | ] 555 | }, 556 | { 557 | "cell_type": "markdown", 558 | "metadata": {}, 559 | "source": [ 560 | "## Exercises" 561 | ] 562 | }, 563 | { 564 | "cell_type": "markdown", 565 | "metadata": {}, 566 | "source": [ 567 | "1. Define two complex numbers $u = 1-3j$ and $v = 5+2j$ in Python.\n", 568 | "2. Compute $u+v$. \n", 569 | "3. Compute $u-v$. \n", 570 | "4. Compute $v-u$. \n", 571 | "5. Compute $u \\cdot v$. \n", 572 | "6. Compute $\\frac{u}{v}$. \n", 573 | "7. Compute $\\frac{v}{u}$. \n", 574 | "8. Compute the complex conjugate of the following complex numbers:\n", 575 | "\\begin{align}\n", 576 | "u = 2-i, \\quad z = 3+4i, \\quad w = -9i\n", 577 | "\\end{align}\n", 578 | "9. Compute the absolute value (or norm) of $u, z,$ and $w$.\n", 579 | "10. Compute the absolute values using Python to check that they match your answers. Don't forget to use \"$j$\" instead of \"$i$\" in Python. " 580 | ] 581 | } 582 | ], 583 | "metadata": { 584 | "kernelspec": { 585 | "display_name": "Python 3", 586 | "language": "python", 587 | "name": "python3" 588 | }, 589 | "language_info": { 590 | "codemirror_mode": { 591 | "name": "ipython", 592 | "version": 3 593 | }, 594 | "file_extension": ".py", 595 | "mimetype": "text/x-python", 596 | "name": "python", 597 | "nbconvert_exporter": "python", 598 | "pygments_lexer": "ipython3", 599 | "version": "3.7.3" 600 | } 601 | }, 602 | "nbformat": 4, 603 | "nbformat_minor": 4 604 | } 605 | -------------------------------------------------------------------------------- /lecture_3_vectors.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Vectors " 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Introduction\n", 15 | "\n", 16 | "Vectors will be fundamental in our study of quantum computing. The most basic unit of computation in a quantum computer is a \\emph{qubit}, which can be represented as a $2$-dimensional complex vector of length one. So understanding vectors will be foundational and necessary for most of what we will be doing in this book. Vectors can be thought of in many ways, one of the most basic is simply as an array of numbers, which we will often represent as a column of numbers called **column vectors**, but in some cases we will also need **row vectors**:\n", 17 | "\n", 18 | "\n", 19 | "\\begin{align} \\text{Column Vector:} \\ \\begin{pmatrix}\n", 20 | "a_1 \\\\ a_2 \\\\ \\vdots \\\\ a_n\n", 21 | "\\end{pmatrix}, \\quad \\quad \\text{Row Vector:} \\ \\begin{pmatrix}\n", 22 | "a_1, & a_2, & \\cdots, & a_n\n", 23 | "\\end{pmatrix} \\end{align}" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "## Importing NumPy\n", 31 | "\n", 32 | "For linear algebra, we need to make sure to import NumPy. " 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 1, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "import numpy as np" 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "## Row Vectors, Column Vectors, and Bra-Ket Notation" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "We can create a column vector and a row vector in Python:" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 2, 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [ 64 | "# Create a vector as a row\n", 65 | "row_vector = np.array([2-1j, 7j, -3])\n", 66 | "\n", 67 | "# Create a vector as a column\n", 68 | "column_vector = np.array([[2+1j],\n", 69 | " [-5],\n", 70 | " [2j]])" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "metadata": {}, 76 | "source": [ 77 | "Row vectors in quantum mechanics are also called **bra-vectors**, and are denoted as follows:\n", 78 | "\n", 79 | "\\begin{align} \\langle A| = \\begin{pmatrix}\n", 80 | "a_1, & a_2, \\cdots, & a_n\n", 81 | "\\end{pmatrix} \\end{align}\n", 82 | "\n", 83 | "Column vectors are also called **ket-vectors** in quantum mechanics denoted as follows:\n", 84 | "\n", 85 | "\\begin{align} |B\\rangle = \\begin{pmatrix}\n", 86 | "b_1 \\\\ b_2 \\\\ \\vdots \\\\ b_n\n", 87 | "\\end{pmatrix} \\end{align}\n", 88 | "\n", 89 | "In general, if we have a column vector, i.e. a ket-vector:\n", 90 | "\n", 91 | "\\begin{align} |A\\rangle = \\begin{pmatrix}\n", 92 | "a_1 \\\\ a_2 \\\\ \\vdots \\\\ a_n\n", 93 | "\\end{pmatrix} \\end{align}\n", 94 | "\n", 95 | "the corresponding bra-vector:\n", 96 | "\n", 97 | "\\begin{align} \\langle A| = \\begin{pmatrix}\n", 98 | "a_1^*, & a_2^*, & \\cdots, & a_n^*\n", 99 | "\\end{pmatrix} \\end{align}\n", 100 | "\n", 101 | "is the **complex-conjugate transpose** of the ket-vector $|A\\rangle$, and vice-versa. As an example, if we have the following ket-vector:\n", 102 | "\n", 103 | "\n", 104 | "\\begin{align} |A\\rangle = \\begin{pmatrix}\n", 105 | "2+i \\\\ 1-3i\n", 106 | "\\end{pmatrix} \\end{align}\n", 107 | "\n", 108 | "then the corresponding bra-vector is:\n", 109 | "\n", 110 | "\\begin{align} \\langle A| = \\begin{pmatrix}\n", 111 | "2-i, & 1+3i\n", 112 | "\\end{pmatrix} \\end{align}\n", 113 | "\n", 114 | "The notation $\\langle A|$ and $|A\\rangle$, for bra- and ket-vectors respectively, is a reference to \\emph{inner products} denoted by brackets and we will discuss it more in the next section when we define inner product. As another example, we might want to have a pair of $2$-dimensional complex column vectors (meaning the entries are complex numbers):\\\\\n", 115 | "\n", 116 | "\n", 117 | "\\begin{align} |A \\rangle = \\begin{pmatrix}\n", 118 | "2-i \\\\ 5\n", 119 | "\\end{pmatrix}, \\quad \\quad\n", 120 | "|B \\rangle = \\begin{pmatrix}\n", 121 | "7 \\\\ 3i\n", 122 | "\\end{pmatrix} \\end{align}\n", 123 | "\n", 124 | "Remember, $i^2 = -1$ and so $i = \\sqrt{-1}$ is imaginary and complex numbers are always of the form $a + bi$. The corresponding bra-vectors would then be:\n", 125 | "\n", 126 | "\\begin{align} \\langle A| = \\begin{pmatrix}\n", 127 | "2+i, & 5\n", 128 | "\\end{pmatrix}, \\quad \\quad \\langle B| = \\begin{pmatrix}\n", 129 | "7, & -3i\n", 130 | "\\end{pmatrix} \\end{align}\n", 131 | "\n", 132 | "The $2$-dimensional vectors we have listed above are not quite the kind of vectors we will be using most often in quantum computing, but until we introduce the norm of a vector and explain what unit vectors are, let us focus on the basic operations we can perform on these vectors. We can always add two vector of the same dimension:\n", 133 | "\n", 134 | "\\begin{align} |A\\rangle + |B\\rangle = \\begin{pmatrix}\n", 135 | "2-i \\\\ 5\n", 136 | "\\end{pmatrix} + \\begin{pmatrix}\n", 137 | "7 \\\\ 3i\n", 138 | "\\end{pmatrix} = \n", 139 | "\\begin{pmatrix}\n", 140 | "(2-i) + 7 \\\\ 5 + 3i\n", 141 | "\\end{pmatrix} = \n", 142 | "\\begin{pmatrix}\n", 143 | "9-i \\\\ 5 + 3i\n", 144 | "\\end{pmatrix} \\end{align}\n", 145 | "\n", 146 | "In Python this can be done as follows:" 147 | ] 148 | }, 149 | { 150 | "cell_type": "code", 151 | "execution_count": 3, 152 | "metadata": {}, 153 | "outputs": [ 154 | { 155 | "name": "stdout", 156 | "output_type": "stream", 157 | "text": [ 158 | "[[9.-1.j]\n", 159 | " [5.-3.j]]\n" 160 | ] 161 | } 162 | ], 163 | "source": [ 164 | "ket_A = np.array([[2-1j],\n", 165 | " [5]])\n", 166 | "ket_B = np.array([[7], \n", 167 | " [-3j]])\n", 168 | "print(ket_A + ket_B)" 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "metadata": {}, 174 | "source": [ 175 | "We can multiply bra-vectors and ket-vectors by scalars:\n", 176 | "\n", 177 | "$ 3|A\\rangle = 3 \\begin{pmatrix} 2-i\\\\5 \\end{pmatrix} = \\begin{pmatrix} 6-3i\\\\15 \\end{pmatrix} $" 178 | ] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "execution_count": 4, 183 | "metadata": {}, 184 | "outputs": [ 185 | { 186 | "name": "stdout", 187 | "output_type": "stream", 188 | "text": [ 189 | "[[ 6.-3.j]\n", 190 | " [15.+0.j]]\n" 191 | ] 192 | } 193 | ], 194 | "source": [ 195 | "print(3*ket_A)" 196 | ] 197 | }, 198 | { 199 | "cell_type": "markdown", 200 | "metadata": {}, 201 | "source": [ 202 | "Similarly for row vectors (bra-vectors):\n", 203 | "\n", 204 | "$ 5\\langle B| = 5 \\begin{pmatrix} 7, & 3i \\end{pmatrix} = \\begin{pmatrix} 35, & 15i \\end{pmatrix}$" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": 5, 210 | "metadata": {}, 211 | "outputs": [ 212 | { 213 | "name": "stdout", 214 | "output_type": "stream", 215 | "text": [ 216 | "[[35. +0.j 0.+15.j]]\n" 217 | ] 218 | } 219 | ], 220 | "source": [ 221 | "bra_B = np.array([[7, 3j]])\n", 222 | "print(5*bra_B)" 223 | ] 224 | }, 225 | { 226 | "cell_type": "markdown", 227 | "metadata": {}, 228 | "source": [ 229 | "## Exercises\n", 230 | "\n", 231 | "1. Define the following ket-vector as an array in Python:\n", 232 | "\n", 233 | "\\begin{align}\n", 234 | "|\\psi \\rangle = \\begin{pmatrix} 2, & 3i \\end{pmatrix}\n", 235 | "\\end{align}\n", 236 | "\n", 237 | "2. Define $|\\psi \\rangle$ from the previous problem as a (column) matrix and compute its Hermitian conjugate. \n", 238 | "3. Define two ket vectors (column vectors) in Python:\n", 239 | "\n", 240 | "\\begin{align}\n", 241 | "|A\\rangle = \\begin{pmatrix}\n", 242 | "2 \\\\ 5-5j\n", 243 | "\\end{pmatrix}, \\quad\n", 244 | "|B\\rangle = \\begin{pmatrix}\n", 245 | "2+3j \\\\ -3j\n", 246 | "\\end{pmatrix}\n", 247 | "\\end{align}\n", 248 | "\n", 249 | "4. Compute $|A\\rangle + |B\\rangle$\n", 250 | "5. Compute $3|A\\rangle$. " 251 | ] 252 | } 253 | ], 254 | "metadata": { 255 | "kernelspec": { 256 | "display_name": "Python 3", 257 | "language": "python", 258 | "name": "python3" 259 | }, 260 | "language_info": { 261 | "codemirror_mode": { 262 | "name": "ipython", 263 | "version": 3 264 | }, 265 | "file_extension": ".py", 266 | "mimetype": "text/x-python", 267 | "name": "python", 268 | "nbconvert_exporter": "python", 269 | "pygments_lexer": "ipython3", 270 | "version": "3.7.3" 271 | } 272 | }, 273 | "nbformat": 4, 274 | "nbformat_minor": 4 275 | } 276 | -------------------------------------------------------------------------------- /lecture_4_inner_products.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Inner Products and Norms of Vectors" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Importing NumPy\n", 15 | "\n", 16 | "As usual, we will need to import NumPy" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "import numpy as np" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## Introduction\n", 33 | "\n", 34 | "The most important vectors we will encounter will be $2$-dimensional complex vector which are of length $1$. In this section we will define the **inner product** of two vectors, and the **norm** (length) of a vector which is derived from the inner product of the vector with itself. The vectors of length $1$ are sometimes referred to as **unit vectors**. We will discuss complex unit vectors in $2$-dimensional space $\\mathbb{C}^2$ and how they relate to qubits. This will lead to a discussion of the **Bloch sphere** which will be a useful way of understanding qubits and how quantum gates act on them. \n", 35 | "\n", 36 | "\n", 37 | "### Inner Products\n", 38 | "**Inner Products** are a more general case of the **dot-product**, but for most of our purposes, they can be thought of as the same thing. The most basic case of an inner product and the one we will use the most often can be thought of as the product of a row vector and a column vector of the same dimension:\n", 39 | "\n", 40 | "\\begin{align} \\langle A| = \\begin{pmatrix}\n", 41 | "a_1, & a_2, & \\cdots, & a_n\n", 42 | "\\end{pmatrix}, \\quad \\quad\n", 43 | "|B\\rangle = \\begin{pmatrix}\n", 44 | "b_1 \\\\ b_2 \\\\ \\vdots \\\\ b_n\n", 45 | "\\end{pmatrix} \\end{align}\n", 46 | "\n", 47 | "Taking the inner product of $\\langle A|$ and $|B\\rangle$ gives the following:\n", 48 | "\n", 49 | "\\begin{align} \\langle A| B \\rangle &= \\begin{pmatrix} \n", 50 | "a_1, & a_2, & \\cdots, & a_n\n", 51 | "\\end{pmatrix}\n", 52 | "\\begin{pmatrix}\n", 53 | "b_1 \\\\ b_2 \\\\ \\vdots \\\\ b_n\n", 54 | "\\end{pmatrix}\\\\\n", 55 | "&= \n", 56 | "a_1b_1 + a_2b_2 + \\cdots + a_nb_n\\\\\n", 57 | "&= \\sum_{i=1}^n a_ib_i\n", 58 | "\\end{align}" 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "metadata": {}, 64 | "source": [ 65 | "As a basic example, take the inner product of the following $2$-dimensional vectors, \n", 66 | "\n", 67 | "\n", 68 | "\\begin{align} \\langle A| = \\begin{pmatrix}\n", 69 | "3i, & 2\n", 70 | "\\end{pmatrix}, \\quad \\quad |B\\rangle = \\begin{pmatrix}\n", 71 | "5+2i \\\\ 1-i\n", 72 | "\\end{pmatrix} \\end{align}\n", 73 | "\n", 74 | "as follows:\n", 75 | "\n", 76 | "\\begin{align}\n", 77 | "\\langle A| B \\rangle = \\begin{pmatrix}\n", 78 | "3i, & 2\n", 79 | "\\end{pmatrix}\n", 80 | "\\begin{pmatrix}\n", 81 | "5+2i \\\\ 1-i\n", 82 | "\\end{pmatrix} &= \n", 83 | "3i(5+2i) + 2(1-i)\\\\ \n", 84 | "&= 15i-6+2-2i\\\\ \n", 85 | "&= -4+13i\n", 86 | "\\end{align}" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "In order to define a complex vector so that we can take its conjugate transpose, also called the **Hermitian conjugate**, we must define it as a **matrix**. So, suppose we have the following column vector (ket-vector):\n", 94 | "\n", 95 | "\\begin{align}\n", 96 | "|A\\rangle = \\begin{pmatrix} 1-i \\\\ 3 \\\\ 2i \\\\ 5+i \\end{pmatrix}\n", 97 | "\\end{align}\n", 98 | "\n", 99 | "Then of course its **Hermitian conjugate** would be the bra-vector\n", 100 | "\n", 101 | "\\begin{align}\n", 102 | "\\langle A| = \\begin{pmatrix}1+i, & 3, & -2i, & 5-i \\end{pmatrix}\n", 103 | "\\end{align}\n", 104 | "\n", 105 | "Now, in Python, we must remember to use \"$j$\" instead of $\"i\"$ for the imaginary unit and we must define the $4 \\times 1$-matrix:" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": 2, 111 | "metadata": {}, 112 | "outputs": [ 113 | { 114 | "data": { 115 | "text/plain": [ 116 | "matrix([[1.+1.j, 3.-0.j, 0.-2.j, 5.-1.j]])" 117 | ] 118 | }, 119 | "execution_count": 2, 120 | "metadata": {}, 121 | "output_type": "execute_result" 122 | } 123 | ], 124 | "source": [ 125 | "# Define the 4x1 matrix version of a column vector (instead of using the np.array() version):\n", 126 | "A = np.matrix([[1-1j], \n", 127 | " [3], \n", 128 | " [2j], \n", 129 | " [5+1j]])\n", 130 | "\n", 131 | "# Compute the Hermitian Conjugate:\n", 132 | "A.H" 133 | ] 134 | }, 135 | { 136 | "cell_type": "markdown", 137 | "metadata": {}, 138 | "source": [ 139 | "Now, to compute the inner product $ \\langle A|A \\rangle $ we can simple multiply the two matrix versions just computed:" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": 3, 145 | "metadata": {}, 146 | "outputs": [ 147 | { 148 | "data": { 149 | "text/plain": [ 150 | "matrix([[41.+0.j]])" 151 | ] 152 | }, 153 | "execution_count": 3, 154 | "metadata": {}, 155 | "output_type": "execute_result" 156 | } 157 | ], 158 | "source": [ 159 | "np.dot(A.H, A)" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "Let's define another $4$-dimensional complex row vector $\\langle B|$:\n", 167 | "\n", 168 | "\\begin{align}\n", 169 | "\\langle B| = \\begin{pmatrix}\n", 170 | "-3i, & 2+2i, & -6i, & -7\n", 171 | "\\end{pmatrix}\n", 172 | "\\end{align}\n", 173 | "\n", 174 | "Once we have defined this bra-vector, let's compute the following inner products:\n", 175 | "\n", 176 | "\\begin{align}\n", 177 | "\\langle B|A \\rangle, \\quad \\langle B|B \\rangle, \\quad \\langle A|B \\rangle.\n", 178 | "\\end{align}" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": 4, 184 | "metadata": {}, 185 | "outputs": [ 186 | { 187 | "data": { 188 | "text/plain": [ 189 | "matrix([[-0.+3.j],\n", 190 | " [ 2.-2.j],\n", 191 | " [-0.+6.j],\n", 192 | " [-7.-0.j]])" 193 | ] 194 | }, 195 | "execution_count": 4, 196 | "metadata": {}, 197 | "output_type": "execute_result" 198 | } 199 | ], 200 | "source": [ 201 | "# Define B as a 1x4 matrix\n", 202 | "B = np.matrix([[-3j, 2+2j, -6j, -7]])\n", 203 | "\n", 204 | "#Compute the Hermitian Conjugate of B, which is a ket-vector\n", 205 | "B.H" 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": 5, 211 | "metadata": {}, 212 | "outputs": [ 213 | { 214 | "data": { 215 | "text/plain": [ 216 | "matrix([[-20.-4.j]])" 217 | ] 218 | }, 219 | "execution_count": 5, 220 | "metadata": {}, 221 | "output_type": "execute_result" 222 | } 223 | ], 224 | "source": [ 225 | "# Compute \n", 226 | "np.dot(B,A)" 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": 6, 232 | "metadata": {}, 233 | "outputs": [ 234 | { 235 | "data": { 236 | "text/plain": [ 237 | "matrix([[102.+0.j]])" 238 | ] 239 | }, 240 | "execution_count": 6, 241 | "metadata": {}, 242 | "output_type": "execute_result" 243 | } 244 | ], 245 | "source": [ 246 | "# Compute \n", 247 | "np.dot(B, B.H)" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": 7, 253 | "metadata": {}, 254 | "outputs": [ 255 | { 256 | "data": { 257 | "text/plain": [ 258 | "matrix([[-20.+4.j]])" 259 | ] 260 | }, 261 | "execution_count": 7, 262 | "metadata": {}, 263 | "output_type": "execute_result" 264 | } 265 | ], 266 | "source": [ 267 | "# Compute \n", 268 | "np.dot(A.H, B.H)" 269 | ] 270 | }, 271 | { 272 | "cell_type": "markdown", 273 | "metadata": {}, 274 | "source": [ 275 | "There is a different kind of product on two vectors which we will cover later called the **[outer product](https://en.wikipedia.org/wiki/Outer_product)**. The outer product is a specific case of the tensor product and is written in this case by $ |A\\rangle \\langle A|$. It is important to make this distinction now and note that the two **are not the same**. The **inner product** always yields a number, which can be complex valued if the two vectors are complex valued. The **outer product** in general gives a **matrix**. " 276 | ] 277 | }, 278 | { 279 | "cell_type": "code", 280 | "execution_count": 8, 281 | "metadata": {}, 282 | "outputs": [ 283 | { 284 | "data": { 285 | "text/plain": [ 286 | "matrix([[ 2. +0.j, 3. -3.j, -2. -2.j, 4. -6.j],\n", 287 | " [ 3. +3.j, 9. +0.j, 0. -6.j, 15. -3.j],\n", 288 | " [-2. +2.j, 0. +6.j, 4. +0.j, 2.+10.j],\n", 289 | " [ 4. +6.j, 15. +3.j, 2.-10.j, 26. +0.j]])" 290 | ] 291 | }, 292 | "execution_count": 8, 293 | "metadata": {}, 294 | "output_type": "execute_result" 295 | } 296 | ], 297 | "source": [ 298 | "np.dot(A, A.H)" 299 | ] 300 | }, 301 | { 302 | "cell_type": "markdown", 303 | "metadata": {}, 304 | "source": [ 305 | "For the more adventurous readers who understand how to multiply matrices, and to prepare for future computations where matrices are involved, we will compute a more general \"inner product\". If this does not make any sense to you and matrix multiplication is a foreign concept, we will discuss matrix multiplication in this chapter as a refresher, but now might be a good time to refresh your memory on how to multiply matrices if it is unfamiliar. When we discuss \\emph{measurements} and \\emph{expectation values} we will need to consider the case when there is a matrix in between the bra- and ket-vector. Take for example the Pauli-Z matrix, which we will discuss more later on:\n", 306 | "\n", 307 | "\n", 308 | "\\begin{align} Z = \\begin{pmatrix}\n", 309 | "1 & 0 \\\\\n", 310 | "0 & -1\n", 311 | "\\end{pmatrix} \\end{align}\n", 312 | "\n", 313 | "Now take the following bra-vector and ket-vector\n", 314 | "\n", 315 | "\n", 316 | "\\begin{align} \\langle A| = \\begin{pmatrix}\n", 317 | "i/\\sqrt{2}, & -i/\\sqrt{2}\n", 318 | "\\end{pmatrix}, \\quad \\quad |A\\rangle = \\begin{pmatrix}\n", 319 | "-i/\\sqrt{2} \\\\ i/\\sqrt{2}\n", 320 | "\\end{pmatrix} \\end{align}" 321 | ] 322 | }, 323 | { 324 | "cell_type": "markdown", 325 | "metadata": {}, 326 | "source": [ 327 | "Now, we can compute the following variation on the inner product\n", 328 | "\n", 329 | "\\begin{align}\n", 330 | "\\langle A| Z |A \\rangle &= \n", 331 | "\\begin{pmatrix}\n", 332 | "i/\\sqrt{2}, & -i/\\sqrt{2}\n", 333 | "\\end{pmatrix}\n", 334 | "\\begin{pmatrix}\n", 335 | "1 & 0 \\\\\n", 336 | "0 & -1\n", 337 | "\\end{pmatrix}\n", 338 | "\\begin{pmatrix}\n", 339 | "-i/\\sqrt{2} \\\\ i/\\sqrt{2}\n", 340 | "\\end{pmatrix} \\\\\n", 341 | "&= \\begin{pmatrix}\n", 342 | "i/\\sqrt{2}, & -i/\\sqrt{2}\n", 343 | "\\end{pmatrix}\n", 344 | "\\begin{pmatrix}\n", 345 | "-i/\\sqrt{2} \\\\ -i/\\sqrt{2}\n", 346 | "\\end{pmatrix} \\\\\n", 347 | "&= 1/2 - 1/2 \\\\\n", 348 | "&= 0\n", 349 | "\\end{align}\n", 350 | "\n", 351 | "We will encounter such calculations many more times when we discuss measurements and expectations. If this is a little confusing, it is not important to worry too much about it just yet. We will cover matrix multiplication shortly, and later we will discuss measurements and expectation values, at which point we should be comfortable computing more general version of this strange \"inner product\". " 352 | ] 353 | }, 354 | { 355 | "cell_type": "markdown", 356 | "metadata": {}, 357 | "source": [ 358 | "## Exercises\n", 359 | "\n", 360 | "1. Compute the following inner product by hand:\n", 361 | " \n", 362 | " $\\begin{pmatrix}\n", 363 | " 2, & 3i\n", 364 | " \\end{pmatrix}\n", 365 | " \\begin{pmatrix}\n", 366 | " 1+i \\\\ 4\n", 367 | " \\end{pmatrix}$ \n", 368 | " \n", 369 | "2. Write Python code to compute the previous inner product.\n", 370 | "3. Compute the following inner product by hand:\n", 371 | "\n", 372 | " $ \\begin{pmatrix}\n", 373 | " 3i, & 1+2i, & 4, & 2i\n", 374 | " \\end{pmatrix}\n", 375 | " \\begin{pmatrix}\n", 376 | " 5 \\\\ 1-2i \\\\ -3 \\\\ -i/2\n", 377 | " \\end{pmatrix} $\n", 378 | " \n", 379 | "5. Write Python code to compute the previous inner product. \n", 380 | "6. For the more adventurous who understand how to multiply matrices, and to prepare for future computations where matrices are involved, compute the following more general \"inner product\":\n", 381 | "\n", 382 | "\\begin{align}\n", 383 | "\\begin{pmatrix}1/\\sqrt{2}, & -1/\\sqrt{2} \\end{pmatrix}\n", 384 | "\\begin{pmatrix} 1&0\\\\0&-1 \\end{pmatrix}\n", 385 | "\\begin{pmatrix}1/\\sqrt{2}\\\\ -1/\\sqrt{2} \\end{pmatrix}\n", 386 | "\\end{align}" 387 | ] 388 | } 389 | ], 390 | "metadata": { 391 | "kernelspec": { 392 | "display_name": "Python 3", 393 | "language": "python", 394 | "name": "python3" 395 | }, 396 | "language_info": { 397 | "codemirror_mode": { 398 | "name": "ipython", 399 | "version": 3 400 | }, 401 | "file_extension": ".py", 402 | "mimetype": "text/x-python", 403 | "name": "python", 404 | "nbconvert_exporter": "python", 405 | "pygments_lexer": "ipython3", 406 | "version": "3.7.3" 407 | } 408 | }, 409 | "nbformat": 4, 410 | "nbformat_minor": 4 411 | } 412 | -------------------------------------------------------------------------------- /lecture_6_tensor_products.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Tensor Products of Vectors" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Importing NumPy\n", 15 | "\n", 16 | "We will need to import NumPy for tensor products. " 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "import numpy as np" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## Introduction" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "One of the most common tensor products we will encounter throughout the following chapters of the book will be the tensor product of two qubits, which are represented by $2$-dimensional vector which have length (or \"norm\") $1$.\n", 40 | "\n", 41 | "The general rule for the tensor product of two $2$-dimensional vectors is as follows:\n", 42 | "\n", 43 | "\\begin{align}\n", 44 | "\\begin{pmatrix}\n", 45 | "a_1 \\\\ a_2\n", 46 | "\\end{pmatrix} \\otimes\n", 47 | "\\begin{pmatrix}\n", 48 | "b_1 \\\\ b_2\n", 49 | "\\end{pmatrix} = \n", 50 | "\\begin{pmatrix}\n", 51 | "a_1 \\begin{pmatrix}\n", 52 | "b_1 \\\\ b_2\n", 53 | "\\end{pmatrix} \\\\ a_2 \\begin{pmatrix}\n", 54 | "b_1 \\\\ b_2\n", 55 | "\\end{pmatrix}\n", 56 | "\\end{pmatrix} = \n", 57 | "\\begin{pmatrix}\n", 58 | "a_1b_1 \\\\ a_1b_2 \\\\ a_2b_1 \\\\ a_2b_2\n", 59 | "\\end{pmatrix}\n", 60 | "\\end{align}" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": {}, 66 | "source": [ 67 | "Here is a numerical example:\n", 68 | "\n", 69 | "\\begin{align}\n", 70 | "\\begin{pmatrix}\n", 71 | "2 \\\\ 5\n", 72 | "\\end{pmatrix} \\otimes \n", 73 | "\\begin{pmatrix}\n", 74 | "3 \\\\ 1\n", 75 | "\\end{pmatrix} = \n", 76 | "\\begin{pmatrix}\n", 77 | "2 \\begin{pmatrix}\n", 78 | "3 \\\\ 1\n", 79 | "\\end{pmatrix} \\\\ 5 \\begin{pmatrix}\n", 80 | "3 \\\\ 1\n", 81 | "\\end{pmatrix}\n", 82 | "\\end{pmatrix} = \n", 83 | "\\begin{pmatrix}\n", 84 | "2 \\cdot 3 \\\\ 2 \\cdot 1 \\\\ 5 \\cdot 3 \\\\ 5 \\cdot 1\n", 85 | "\\end{pmatrix} = \n", 86 | "\\begin{pmatrix}\n", 87 | "6 \\\\ 2 \\\\ 15 \\\\ 5\n", 88 | "\\end{pmatrix}\n", 89 | "\\end{align}" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "metadata": {}, 95 | "source": [ 96 | "For an example using Python we will use the \"np.kron()\" function, which is the [Kronecker product](https://en.wikipedia.org/wiki/Kronecker_product), \n", 97 | "\n", 98 | "> \"In mathematics, the Kronecker product, sometimes denoted by ⊗, is an operation on two matrices of arbitrary size resulting in a block matrix. It is a generalization of the outer product (which is denoted by the same symbol) from vectors to matrices, and gives the matrix of the tensor product with respect to a standard choice of basis. The Kronecker product should not be confused with the usual matrix multiplication, which is an entirely different operation.\"" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": 2, 104 | "metadata": {}, 105 | "outputs": [ 106 | { 107 | "data": { 108 | "text/plain": [ 109 | "array([[ 6],\n", 110 | " [ 2],\n", 111 | " [15],\n", 112 | " [ 5]])" 113 | ] 114 | }, 115 | "execution_count": 2, 116 | "metadata": {}, 117 | "output_type": "execute_result" 118 | } 119 | ], 120 | "source": [ 121 | "# Define two ket-vectors\n", 122 | "\n", 123 | "A = np.array([[2], \n", 124 | " [5]])\n", 125 | "\n", 126 | "B = np.array([[3],\n", 127 | " [1]])\n", 128 | "\n", 129 | "# Take the Kronecker (tensor) product of the two column vectors\n", 130 | "np.kron(A,B)" 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": {}, 136 | "source": [ 137 | "We can also define these two vectors as matrices which will allow us to compute Hermitian conjugates. The Kronecker product function 'np.kron()' will work all the same:" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": 3, 143 | "metadata": {}, 144 | "outputs": [ 145 | { 146 | "data": { 147 | "text/plain": [ 148 | "matrix([[ 6],\n", 149 | " [ 2],\n", 150 | " [15],\n", 151 | " [ 5]])" 152 | ] 153 | }, 154 | "execution_count": 3, 155 | "metadata": {}, 156 | "output_type": "execute_result" 157 | } 158 | ], 159 | "source": [ 160 | "# Define the ket-vectors as 2x1 column matrices:\n", 161 | "ket_A = np.matrix([[2], \n", 162 | " [5]])\n", 163 | "\n", 164 | "ket_B = np.matrix([[3], \n", 165 | " [1]])\n", 166 | "\n", 167 | "# Compute their Kronecker product\n", 168 | "np.kron(ket_A, ket_B)" 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "metadata": {}, 174 | "source": [ 175 | "This can also be done with complex vectors:" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": 4, 181 | "metadata": {}, 182 | "outputs": [ 183 | { 184 | "data": { 185 | "text/plain": [ 186 | "matrix([[-6. +3.j],\n", 187 | " [ 6. -8.j],\n", 188 | " [-0. -9.j],\n", 189 | " [ 6.+12.j]])" 190 | ] 191 | }, 192 | "execution_count": 4, 193 | "metadata": {}, 194 | "output_type": "execute_result" 195 | } 196 | ], 197 | "source": [ 198 | "# Define the ket-vectors as 2x1 column matrices:\n", 199 | "ket_psi = np.matrix([[2-1j], \n", 200 | " [3j]])\n", 201 | "\n", 202 | "ket_phi = np.matrix([[-3], \n", 203 | " [4-2j]])\n", 204 | "\n", 205 | "# Compute their Kronecker product\n", 206 | "np.kron(ket_psi, ket_phi)" 207 | ] 208 | }, 209 | { 210 | "cell_type": "markdown", 211 | "metadata": {}, 212 | "source": [ 213 | "Here is a more complicated example with two vectors of different dimensions:\n", 214 | "\n", 215 | "\\begin{align}\n", 216 | "\\begin{pmatrix}\n", 217 | "a_1 \\\\ a_2\n", 218 | "\\end{pmatrix} \\otimes \n", 219 | "\\begin{pmatrix}\n", 220 | "b_1 \\\\ b_2 \\\\ b_3\n", 221 | "\\end{pmatrix} = \n", 222 | "\\begin{pmatrix}\n", 223 | "a_1 \\begin{pmatrix}\n", 224 | "b_1 \\\\ b_2 \\\\ b_3\n", 225 | "\\end{pmatrix} \\\\\n", 226 | "a_2 \\begin{pmatrix}\n", 227 | "b_1 \\\\ b_2 \\\\ b_3\n", 228 | "\\end{pmatrix}\n", 229 | "\\end{pmatrix} = \n", 230 | "\\begin{pmatrix}\n", 231 | "a_1b_1 \\\\ a_1b_2 \\\\ a_1b_3 \\\\ a_2b_1 \\\\ a_2b_2 \\\\ a_2b_3\n", 232 | "\\end{pmatrix}\n", 233 | "\\end{align}" 234 | ] 235 | }, 236 | { 237 | "cell_type": "markdown", 238 | "metadata": {}, 239 | "source": [ 240 | "Here is a corresponding numerical example:\n", 241 | "\n", 242 | "\\begin{align}\n", 243 | "\\begin{pmatrix}\n", 244 | "1 \\\\ 4\n", 245 | "\\end{pmatrix} \\otimes \n", 246 | "\\begin{pmatrix}\n", 247 | "5 \\\\ 2 \\\\ 4\n", 248 | "\\end{pmatrix} = \n", 249 | "\\begin{pmatrix}\n", 250 | "1 \\begin{pmatrix}\n", 251 | "5 \\\\ 2 \\\\ 4\n", 252 | "\\end{pmatrix} \\\\\n", 253 | "4 \\begin{pmatrix}\n", 254 | "5 \\\\ 2 \\\\ 4\n", 255 | "\\end{pmatrix}\n", 256 | "\\end{pmatrix} = \n", 257 | "\\begin{pmatrix}\n", 258 | "5 \\\\ 2 \\\\ 4 \\\\ 20 \\\\ 8 \\\\ 16\n", 259 | "\\end{pmatrix}\n", 260 | "\\end{align} " 261 | ] 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "metadata": {}, 266 | "source": [ 267 | "Here is the code for computing this tensor product:" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 5, 273 | "metadata": {}, 274 | "outputs": [ 275 | { 276 | "data": { 277 | "text/plain": [ 278 | "matrix([[ 5],\n", 279 | " [ 2],\n", 280 | " [ 4],\n", 281 | " [20],\n", 282 | " [ 8],\n", 283 | " [16]])" 284 | ] 285 | }, 286 | "execution_count": 5, 287 | "metadata": {}, 288 | "output_type": "execute_result" 289 | } 290 | ], 291 | "source": [ 292 | "ket_X = np.matrix([[1], \n", 293 | " [4]])\n", 294 | "\n", 295 | "ket_Y = np.matrix([[5], \n", 296 | " [2], \n", 297 | " [4]])\n", 298 | "\n", 299 | "np.kron(ket_X, ket_Y)" 300 | ] 301 | }, 302 | { 303 | "cell_type": "markdown", 304 | "metadata": {}, 305 | "source": [ 306 | "## Exercises" 307 | ] 308 | }, 309 | { 310 | "cell_type": "markdown", 311 | "metadata": {}, 312 | "source": [ 313 | "1. Using the following rule, \n", 314 | "\\begin{align}\n", 315 | "\\begin{pmatrix}\n", 316 | "a_1 \\\\ a_2 \\\\ a_3\n", 317 | "\\end{pmatrix} \\otimes\n", 318 | "\\begin{pmatrix}\n", 319 | "b_1 \\\\ b_2\n", 320 | "\\end{pmatrix} = \n", 321 | "\\begin{pmatrix}\n", 322 | "a_1 \\begin{pmatrix}\n", 323 | "b_1 \\\\ b_2\n", 324 | "\\end{pmatrix} \\\\ a_2\\begin{pmatrix}\n", 325 | "b_1 \\\\ b_2\n", 326 | "\\end{pmatrix} \\\\ a_3\\begin{pmatrix}\n", 327 | "b_1 \\\\ b_2\n", 328 | "\\end{pmatrix}\n", 329 | "\\end{pmatrix} = \n", 330 | "\\begin{pmatrix}\n", 331 | "a_1b_1 \\\\ a_1b_2 \\\\ a_2b_1 \\\\ a_2b_2 \\\\ a_3b_1 \\\\ a_3b_2\n", 332 | "\\end{pmatrix}\n", 333 | "\\end{align}\n", 334 | "\n", 335 | "compute the following tensor/Kroneckar product by hand:\n", 336 | "\n", 337 | "\\begin{align}\n", 338 | "\\begin{pmatrix}\n", 339 | "2 \\\\ 7 \\\\ 3\n", 340 | "\\end{pmatrix} \\otimes\n", 341 | "\\begin{pmatrix}\n", 342 | "5 \\\\ 9\n", 343 | "\\end{pmatrix}\n", 344 | "\\end{align}" 345 | ] 346 | }, 347 | { 348 | "cell_type": "markdown", 349 | "metadata": {}, 350 | "source": [ 351 | "2. Write Python code to compute the above computation using the 'np.kron()' function. Remember to define two column (ket) vectors as matrices first. " 352 | ] 353 | }, 354 | { 355 | "cell_type": "markdown", 356 | "metadata": {}, 357 | "source": [ 358 | "3. Derive a general rule for the following tensor product by performing the tensor product inside the parenthesis first:\n", 359 | "\n", 360 | "\\begin{align}\n", 361 | "\\left( \\begin{pmatrix}\n", 362 | "a_1 \\\\ a_2\n", 363 | "\\end{pmatrix} \\otimes \n", 364 | "\\begin{pmatrix}\n", 365 | "b_1 \\\\ b_2\n", 366 | "\\end{pmatrix}\\right) \\otimes \n", 367 | "\\begin{pmatrix}\n", 368 | "c_1 \\\\ c_2\n", 369 | "\\end{pmatrix}\n", 370 | "\\end{align}" 371 | ] 372 | }, 373 | { 374 | "cell_type": "markdown", 375 | "metadata": {}, 376 | "source": [ 377 | "4. Now derive a general rule for the following tensor product by performing the tensor product in the parenthesis first:\n", 378 | "\n", 379 | "\\begin{align}\n", 380 | "\\begin{pmatrix}\n", 381 | "a_1 \\\\ a_2\n", 382 | "\\end{pmatrix} \\otimes \n", 383 | "\\left( \\begin{pmatrix}\n", 384 | "b_1 \\\\ b_2\n", 385 | "\\end{pmatrix} \\otimes \n", 386 | "\\begin{pmatrix}\n", 387 | "c_1 \\\\ c_2\n", 388 | "\\end{pmatrix}\\right)\n", 389 | "\\end{align}\n", 390 | "\n", 391 | "Convince yourself that your results from the previous two computations are in fact equal." 392 | ] 393 | }, 394 | { 395 | "cell_type": "code", 396 | "execution_count": null, 397 | "metadata": {}, 398 | "outputs": [], 399 | "source": [ 400 | "5. Write Python code to compute $|0\\rangle \\otimes |0 \\rangle $. \n", 401 | "6. Write Python code to compute $|0\\rangle \\otimes |1 \\rangle $. \n", 402 | "7. Write Python code to compute $|1\\rangle \\otimes |0 \\rangle $.\n", 403 | "8. Write Python code to compute $|1\\rangle \\otimes |1 \\rangle $." 404 | ] 405 | } 406 | ], 407 | "metadata": { 408 | "kernelspec": { 409 | "display_name": "Python 3", 410 | "language": "python", 411 | "name": "python3" 412 | }, 413 | "language_info": { 414 | "codemirror_mode": { 415 | "name": "ipython", 416 | "version": 3 417 | }, 418 | "file_extension": ".py", 419 | "mimetype": "text/x-python", 420 | "name": "python", 421 | "nbconvert_exporter": "python", 422 | "pygments_lexer": "ipython3", 423 | "version": "3.7.3" 424 | } 425 | }, 426 | "nbformat": 4, 427 | "nbformat_minor": 4 428 | } 429 | -------------------------------------------------------------------------------- /lecture_7_preparing_basis_states.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Preparing Basis States in PennyLane" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Importing PennyLane and the wrapped version of NumPy\n", 15 | "\n", 16 | "PennyLane is an open source software for quantum machine learning and quantum computing algorithms. It integrates standard machine learning software and tools, such as TensorFlow and Pytorch, with quantum computing software like IBM's Qiskit, Microsoft's Q#, and Google's Cirq. It also has its own built in functions and models, and allows various hardware backends such as the quantum computers available from IBM via free cloud access. PennyLane has a wrapped version of NumPy that we import rather than the standard NumPy we have been using. " 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": {}, 23 | "outputs": [ 24 | { 25 | "name": "stdout", 26 | "output_type": "stream", 27 | "text": [ 28 | "Requirement already satisfied: pennylane in /Users/amelieschreiber/anaconda3/lib/python3.7/site-packages (0.8.1)\n", 29 | "Requirement already satisfied: networkx in /Users/amelieschreiber/anaconda3/lib/python3.7/site-packages (from pennylane) (2.3)\n", 30 | "Requirement already satisfied: scipy in /Users/amelieschreiber/anaconda3/lib/python3.7/site-packages (from pennylane) (1.4.1)\n", 31 | "Requirement already satisfied: numpy in /Users/amelieschreiber/anaconda3/lib/python3.7/site-packages (from pennylane) (1.18.0)\n", 32 | "Requirement already satisfied: semantic-version==2.6 in /Users/amelieschreiber/anaconda3/lib/python3.7/site-packages (from pennylane) (2.6.0)\n", 33 | "Requirement already satisfied: toml in /Users/amelieschreiber/anaconda3/lib/python3.7/site-packages (from pennylane) (0.10.0)\n", 34 | "Requirement already satisfied: autograd in /Users/amelieschreiber/anaconda3/lib/python3.7/site-packages (from pennylane) (1.3)\n", 35 | "Requirement already satisfied: appdirs in /Users/amelieschreiber/anaconda3/lib/python3.7/site-packages (from pennylane) (1.4.3)\n", 36 | "Requirement already satisfied: decorator>=4.3.0 in /Users/amelieschreiber/anaconda3/lib/python3.7/site-packages (from networkx->pennylane) (4.4.0)\n", 37 | "Requirement already satisfied: future>=0.15.2 in /Users/amelieschreiber/anaconda3/lib/python3.7/site-packages (from autograd->pennylane) (0.17.1)\n", 38 | "Note: you may need to restart the kernel to use updated packages.\n" 39 | ] 40 | } 41 | ], 42 | "source": [ 43 | "pip install pennylane" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 2, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "import pennylane as qml\n", 53 | "from pennylane import numpy as np" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "## Basis States\n", 61 | "\n", 62 | "In PennyLane, and other quantum computing software, we often want to prepare basis states. This is something we will return to again in the future when we study specific cases of quantum circuits, but having some basic introduction to state preparation now seems appropriate since the are simply tensor products of the basis states $|0\\rangle$ and $|1\\rangle$. In particular, we can prepare states like\n", 63 | "\n", 64 | "\\begin{align}\n", 65 | "|00 \\rangle &= |0\\rangle \\otimes |0\\rangle = \\begin{pmatrix} 1\\\\0 \\end{pmatrix} \\otimes \\begin{pmatrix} 1\\\\0 \\end{pmatrix} = \\begin{pmatrix} 1\\\\0\\\\0\\\\0 \\end{pmatrix} \\\\\n", 66 | "|01 \\rangle &= |0\\rangle \\otimes |1\\rangle = \\begin{pmatrix} 1\\\\0 \\end{pmatrix} \\otimes \\begin{pmatrix} 0\\\\1 \\end{pmatrix} = \\begin{pmatrix} 0\\\\1\\\\0\\\\0 \\end{pmatrix} \\\\\n", 67 | "|10 \\rangle &= |1\\rangle \\otimes |0\\rangle = \\begin{pmatrix} 0\\\\1 \\end{pmatrix} \\otimes \\begin{pmatrix} 1\\\\0 \\end{pmatrix} = \\begin{pmatrix} 0\\\\0\\\\1\\\\0 \\end{pmatrix} \\\\\n", 68 | "|11 \\rangle &= |1\\rangle \\otimes |1\\rangle = \\begin{pmatrix} 0\\\\1 \\end{pmatrix} \\otimes \\begin{pmatrix} 0\\\\1 \\end{pmatrix} = \\begin{pmatrix} 0\\\\0\\\\0\\\\1 \\end{pmatrix}\n", 69 | "\\end{align}" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": {}, 75 | "source": [ 76 | "Let's define these in Python" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": 3, 82 | "metadata": {}, 83 | "outputs": [], 84 | "source": [ 85 | "# Define single qubit states spin-up and spin-down\n", 86 | "u = np.matrix([[1],\n", 87 | " [0]])\n", 88 | "d = np.matrix([[0],\n", 89 | " [1]])\n", 90 | "\n", 91 | "# Define the basis states:\n", 92 | "uu = np.kron(u, u)\n", 93 | "ud = np.kron(u, d)\n", 94 | "du = np.kron(d, u)\n", 95 | "dd = np.kron(d, d)" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": 4, 101 | "metadata": {}, 102 | "outputs": [ 103 | { 104 | "name": "stdout", 105 | "output_type": "stream", 106 | "text": [ 107 | "|00> =\n", 108 | "[[1]\n", 109 | " [0]\n", 110 | " [0]\n", 111 | " [0]]\n" 112 | ] 113 | } 114 | ], 115 | "source": [ 116 | "print('|00> =')\n", 117 | "print(uu)" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": 5, 123 | "metadata": {}, 124 | "outputs": [ 125 | { 126 | "name": "stdout", 127 | "output_type": "stream", 128 | "text": [ 129 | "|01> =\n", 130 | "[[0]\n", 131 | " [1]\n", 132 | " [0]\n", 133 | " [0]]\n" 134 | ] 135 | } 136 | ], 137 | "source": [ 138 | "print('|01> =')\n", 139 | "print(ud)" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": 6, 145 | "metadata": {}, 146 | "outputs": [ 147 | { 148 | "name": "stdout", 149 | "output_type": "stream", 150 | "text": [ 151 | "|10> =\n", 152 | "[[0]\n", 153 | " [0]\n", 154 | " [1]\n", 155 | " [0]]\n" 156 | ] 157 | } 158 | ], 159 | "source": [ 160 | "print('|10> =')\n", 161 | "print(du)" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": 7, 167 | "metadata": {}, 168 | "outputs": [ 169 | { 170 | "name": "stdout", 171 | "output_type": "stream", 172 | "text": [ 173 | "|11> =\n", 174 | "[[0]\n", 175 | " [0]\n", 176 | " [0]\n", 177 | " [1]]\n" 178 | ] 179 | } 180 | ], 181 | "source": [ 182 | "print('|11> =')\n", 183 | "print(dd)" 184 | ] 185 | }, 186 | { 187 | "cell_type": "markdown", 188 | "metadata": {}, 189 | "source": [ 190 | "Taking this further, we can define states like:\n", 191 | " \n", 192 | "\\begin{align}\n", 193 | "|000\\rangle = |0\\rangle \\otimes |0\\rangle \\otimes |0\\rangle = \\begin{pmatrix} 1\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0 \\end{pmatrix}\n", 194 | "\\end{align}\n", 195 | "\n", 196 | "\\begin{align}\n", 197 | "|010\\rangle = |0\\rangle \\otimes |1\\rangle \\otimes |0\\rangle = \\begin{pmatrix} 0\\\\0\\\\1\\\\0\\\\0\\\\0\\\\0\\\\0 \\end{pmatrix}\n", 198 | "\\end{align}" 199 | ] 200 | }, 201 | { 202 | "cell_type": "markdown", 203 | "metadata": {}, 204 | "source": [ 205 | "In PennyLane the following code defines a device (quantum computer or simulator) on which to run code. We will use the default simulator for now since the code is very simple and running it on an actual quantum computer is unnecessary. The number of wires is equal to the number of qubits we are using. The number of shots is the number of times we want to run the circuit." 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": 8, 211 | "metadata": {}, 212 | "outputs": [], 213 | "source": [ 214 | "dev = qml.device(\"default.qubit\", wires=2, shots=1)" 215 | ] 216 | }, 217 | { 218 | "cell_type": "markdown", 219 | "metadata": {}, 220 | "source": [ 221 | "Now we can define an array that corresponds to the basis state $|00\\rangle$:" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": 9, 227 | "metadata": {}, 228 | "outputs": [], 229 | "source": [ 230 | "uu = np.array([0, 0])" 231 | ] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "metadata": {}, 236 | "source": [ 237 | "We can define a function `circuit()` that defines a quantum circuit. " 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": 10, 243 | "metadata": {}, 244 | "outputs": [], 245 | "source": [ 246 | "def circuit():\n", 247 | " qml.BasisState(uu, wires=[0, 1])\n", 248 | " return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1))" 249 | ] 250 | }, 251 | { 252 | "cell_type": "markdown", 253 | "metadata": {}, 254 | "source": [ 255 | "Now, we define a `qnode` class that runs on the device we have defined and samples the qubits and gives a $+1$ for spin-up and a $-1$ for spin down. This runs a quantum function (circuit) that we have just defined. " 256 | ] 257 | }, 258 | { 259 | "cell_type": "code", 260 | "execution_count": 11, 261 | "metadata": {}, 262 | "outputs": [ 263 | { 264 | "name": "stdout", 265 | "output_type": "stream", 266 | "text": [ 267 | "[[1]\n", 268 | " [1]]\n" 269 | ] 270 | } 271 | ], 272 | "source": [ 273 | "@qml.qnode(dev)\n", 274 | "def circuit():\n", 275 | " qml.BasisState(uu, wires=[0, 1])\n", 276 | " return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1))\n", 277 | "\n", 278 | "print(circuit())" 279 | ] 280 | }, 281 | { 282 | "cell_type": "markdown", 283 | "metadata": {}, 284 | "source": [ 285 | "Since qubits are by default in the spin-up state for any quantum circuit, and since we did not prepare any special initial state or perform any operations on the qubits, we get an expectation value of $+1$ for both when sampling the circuit. The vector above show a $+1$ in the first and second entry meaning it is measuring two spin-up states. Let's define a new state to prepare corresponding to $|01 \\rangle$:" 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": 12, 291 | "metadata": {}, 292 | "outputs": [], 293 | "source": [ 294 | "ud = np.array([0,1])" 295 | ] 296 | }, 297 | { 298 | "cell_type": "markdown", 299 | "metadata": {}, 300 | "source": [ 301 | "Next, let's define a new `qnode` to sample corresponding to this prepared state:" 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": 13, 307 | "metadata": {}, 308 | "outputs": [ 309 | { 310 | "name": "stdout", 311 | "output_type": "stream", 312 | "text": [ 313 | "[[ 1]\n", 314 | " [-1]]\n" 315 | ] 316 | } 317 | ], 318 | "source": [ 319 | "@qml.qnode(dev)\n", 320 | "def circuit():\n", 321 | " qml.BasisState(ud, wires=[0, 1])\n", 322 | " return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1))\n", 323 | "\n", 324 | "print(circuit())" 325 | ] 326 | }, 327 | { 328 | "cell_type": "markdown", 329 | "metadata": {}, 330 | "source": [ 331 | "As we can see, we get a $+1$ in the first component, corresponding to the spin-up state, and we get a $-1$ in the second component corresponding to the spin-down state. We can also tell PennyLane to perform multiple \"shots\" or samples by defining a new device:" 332 | ] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": 14, 337 | "metadata": {}, 338 | "outputs": [], 339 | "source": [ 340 | "dev2 = qml.device(\"default.qubit\", wires=2, shots=10)" 341 | ] 342 | }, 343 | { 344 | "cell_type": "code", 345 | "execution_count": 15, 346 | "metadata": {}, 347 | "outputs": [ 348 | { 349 | "name": "stdout", 350 | "output_type": "stream", 351 | "text": [ 352 | "[[ 1 1 1 1 1 1 1 1 1 1]\n", 353 | " [-1 -1 -1 -1 -1 -1 -1 -1 -1 -1]]\n" 354 | ] 355 | } 356 | ], 357 | "source": [ 358 | "@qml.qnode(dev2)\n", 359 | "def circuit():\n", 360 | " qml.BasisState(ud, wires=[0, 1])\n", 361 | " return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1))\n", 362 | "\n", 363 | "print(circuit())" 364 | ] 365 | }, 366 | { 367 | "cell_type": "markdown", 368 | "metadata": {}, 369 | "source": [ 370 | "This gives us 10 output samples instead of just one. If we define a new array corresponding to the state $|11\\rangle$," 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": 16, 376 | "metadata": {}, 377 | "outputs": [], 378 | "source": [ 379 | "dd = np.array([1,1])" 380 | ] 381 | }, 382 | { 383 | "cell_type": "markdown", 384 | "metadata": {}, 385 | "source": [ 386 | "We can define a new 'qnode' with the new device we just defined, and we should expect an output vector of \n", 387 | "\n", 388 | "\\begin{align}\n", 389 | "\\begin{pmatrix}\n", 390 | "-1\\\\-1\n", 391 | "\\end{pmatrix}\n", 392 | "\\end{align}\n", 393 | "\n", 394 | "ten times, corresponding to the ten samples:" 395 | ] 396 | }, 397 | { 398 | "cell_type": "code", 399 | "execution_count": 17, 400 | "metadata": {}, 401 | "outputs": [ 402 | { 403 | "name": "stdout", 404 | "output_type": "stream", 405 | "text": [ 406 | "[[-1 -1 -1 -1 -1 -1 -1 -1 -1 -1]\n", 407 | " [-1 -1 -1 -1 -1 -1 -1 -1 -1 -1]]\n" 408 | ] 409 | } 410 | ], 411 | "source": [ 412 | "@qml.qnode(dev2)\n", 413 | "def circuit():\n", 414 | " qml.BasisState(dd, wires=[0, 1])\n", 415 | " return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1))\n", 416 | "\n", 417 | "print(circuit())" 418 | ] 419 | }, 420 | { 421 | "cell_type": "markdown", 422 | "metadata": {}, 423 | "source": [ 424 | "We will return to this when we discuss measurements and expectation values. " 425 | ] 426 | }, 427 | { 428 | "cell_type": "markdown", 429 | "metadata": {}, 430 | "source": [ 431 | "## Exercises\n", 432 | "\n", 433 | "### Write code to compute the following basis states:\n", 434 | "\n", 435 | "1. $|000 \\rangle$\n", 436 | "2. $|001 \\rangle$\n", 437 | "3. $|010 \\rangle$\n", 438 | "4. $|011 \\rangle$\n", 439 | "5. $|100 \\rangle$\n", 440 | "6. $|101 \\rangle$\n", 441 | "7. $|110 \\rangle$\n", 442 | "8. $|111 \\rangle$\n", 443 | "\n", 444 | "Now, compute these by hand to verify your code is correct. For example, the first computation will be\n", 445 | "\n", 446 | "\\begin{align}\n", 447 | "\\begin{pmatrix} 1\\\\0 \\end{pmatrix} \\otimes\n", 448 | "\\begin{pmatrix} 1\\\\0 \\end{pmatrix} \\otimes\n", 449 | "\\begin{pmatrix} 0\\\\1 \\end{pmatrix}\n", 450 | "\\end{align}" 451 | ] 452 | }, 453 | { 454 | "cell_type": "markdown", 455 | "metadata": {}, 456 | "source": [ 457 | "9. Define a defaul device with \"3 wires\" and \"5 shots\".\n", 458 | "10. Define an array 'uud' for the basis state $|001\\rangle$.\n", 459 | "11. Now define a `qnode` that will print the samples: \n", 460 | "\n", 461 | "> `qml.sample(qml.PauliZ(0))`\n", 462 | "\n", 463 | "> `qml.sample(qml.PauliZ(1))` \n", 464 | "\n", 465 | "> `qml.sample(qml.PauliZ(2))`\n", 466 | "\n", 467 | "12. Run your 'qnode' and verify that you get the output vector\n", 468 | "\\begin{align}\n", 469 | "\\begin{pmatrix}\n", 470 | "1 \\\\ 1 \\\\ -1\n", 471 | "\\end{pmatrix}\n", 472 | "\\end{align}\n", 473 | "\n", 474 | "five times.\n", 475 | "\n", 476 | "13. Try doing this with the other basis states given in Exercises 1-8." 477 | ] 478 | } 479 | ], 480 | "metadata": { 481 | "kernelspec": { 482 | "display_name": "Python 3", 483 | "language": "python", 484 | "name": "python3" 485 | }, 486 | "language_info": { 487 | "codemirror_mode": { 488 | "name": "ipython", 489 | "version": 3 490 | }, 491 | "file_extension": ".py", 492 | "mimetype": "text/x-python", 493 | "name": "python", 494 | "nbconvert_exporter": "python", 495 | "pygments_lexer": "ipython3", 496 | "version": "3.7.3" 497 | } 498 | }, 499 | "nbformat": 4, 500 | "nbformat_minor": 4 501 | } 502 | -------------------------------------------------------------------------------- /lecture_8_matrices.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Matrices" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Imports and Dependencies\n", 15 | "\n", 16 | "We will primarily be using NumPy in this section. " 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "import numpy as np" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "Matrices form the foundation for quantum gates, which are just operations on qubits that move the points representing states around the Bloch sphere. Matrices are also called operators or quantum gates in the context of quantum computing. They can operate on a single qubit, or on many qubits simultaneously. The matrix operators (or gates) we will be using most are known as *unitary operators* or *unitary matrices*. In this section we will discuss basic matrix algebra and define unitary matrices. " 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "We have already seen two ways of realizing matrices in Python. Suppose we have the following $2 \\times 2$ matrix\n", 40 | "\n", 41 | "\\begin{align}\n", 42 | "M = \\begin{pmatrix}\n", 43 | "2-i & -3 \\\\\n", 44 | "-5i & 2\n", 45 | "\\end{pmatrix}\n", 46 | "\\end{align}\n", 47 | "\n", 48 | "We can define this matrix as a NumPy array, or as a matrix in Python." 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 2, 54 | "metadata": {}, 55 | "outputs": [ 56 | { 57 | "name": "stdout", 58 | "output_type": "stream", 59 | "text": [ 60 | "[[ 2.-1.j -3.+0.j]\n", 61 | " [-0.-5.j 2.+0.j]]\n" 62 | ] 63 | } 64 | ], 65 | "source": [ 66 | "M = np.array([[2-1j, -3],\n", 67 | " [-5j, 2]])\n", 68 | "\n", 69 | "print(M)" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": {}, 75 | "source": [ 76 | "Since we may want to compute Hermitian conjugates of matrices we will only be using the matrix data structure." 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": 3, 82 | "metadata": {}, 83 | "outputs": [ 84 | { 85 | "name": "stdout", 86 | "output_type": "stream", 87 | "text": [ 88 | "[[ 2.-1.j -3.+0.j]\n", 89 | " [-0.-5.j 2.+0.j]]\n" 90 | ] 91 | } 92 | ], 93 | "source": [ 94 | "M = np.matrix([[2-1j, -3],\n", 95 | " [-5j, 2]])\n", 96 | "\n", 97 | "print(M)" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "Remember, Hermitian conjugates are given by taking the conjugate transpose of the matrix. This means we compute the complex conjugate of each entry, and then transpose the matrix. Generally, the Hermitian conjugate is denoted by $M^{\\dagger}$. " 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 4, 110 | "metadata": {}, 111 | "outputs": [ 112 | { 113 | "name": "stdout", 114 | "output_type": "stream", 115 | "text": [ 116 | "[[ 2.+1.j -0.+5.j]\n", 117 | " [-3.-0.j 2.-0.j]]\n" 118 | ] 119 | } 120 | ], 121 | "source": [ 122 | "print(M.H)" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "Let's have a look at another example of a Hermitian conjugate. " 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": 5, 135 | "metadata": {}, 136 | "outputs": [ 137 | { 138 | "name": "stdout", 139 | "output_type": "stream", 140 | "text": [ 141 | "[[ 1.-0.j 1.+1.j]\n", 142 | " [-0.+3.j 1.-0.j]\n", 143 | " [ 5.-0.j 3.-0.j]\n", 144 | " [ 2.-0.j 0.-7.j]]\n" 145 | ] 146 | } 147 | ], 148 | "source": [ 149 | "B = np.matrix([[1, -3j, 5, 2], \n", 150 | " [1-1j, 1, 3, 7j]])\n", 151 | "\n", 152 | "print(B.H)" 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": {}, 158 | "source": [ 159 | "This is a $4 \\times 2$ matrix which came from the $2 \\times 4$ matrix $B$. Notice all of its entries have also been conjugated. We can also multiply matrices so long as the dimensions match appropriately. In particular, to multiply two matrices $A$ and $B$, the matrix must be an $n \\times p$ matrix and the matrix $B$ must be a $p \\times m$ matrix. The result is an $n \\times m$ matrix. For example:" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 6, 165 | "metadata": {}, 166 | "outputs": [ 167 | { 168 | "data": { 169 | "text/plain": [ 170 | "matrix([[ -5. +7.j, 1. +0.j, -15.+10.j, 0.-31.j],\n", 171 | " [ 4. -6.j, -14. -9.j, 18.-25.j, 6. -3.j],\n", 172 | " [ 9. +4.j, 0.-11.j, 25.+12.j, -18. +0.j]])" 173 | ] 174 | }, 175 | "execution_count": 6, 176 | "metadata": {}, 177 | "output_type": "execute_result" 178 | } 179 | ], 180 | "source": [ 181 | "# Define a 3x2 matrix:\n", 182 | "A = np.matrix([[2j, -5],\n", 183 | " [3-5j, 1], \n", 184 | " [5, 4j]])\n", 185 | "\n", 186 | "# Define a 2x4 matrix:\n", 187 | "B = np.matrix([[1, -3j, 5, 2], \n", 188 | " [1-1j, 1, 3, 7j]])\n", 189 | "\n", 190 | "#Taking the product of a (3x2) and (2x4) matrix will give a 3x4 matrix:\n", 191 | "np.dot(A, B)" 192 | ] 193 | }, 194 | { 195 | "cell_type": "markdown", 196 | "metadata": {}, 197 | "source": [ 198 | "If we are using the matrix definition instead of the array definition we can also multiply the two matrices as follows:" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": 7, 204 | "metadata": {}, 205 | "outputs": [ 206 | { 207 | "data": { 208 | "text/plain": [ 209 | "matrix([[ -5. +7.j, 1. +0.j, -15.+10.j, 0.-31.j],\n", 210 | " [ 4. -6.j, -14. -9.j, 18.-25.j, 6. -3.j],\n", 211 | " [ 9. +4.j, 0.-11.j, 25.+12.j, -18. +0.j]])" 212 | ] 213 | }, 214 | "execution_count": 7, 215 | "metadata": {}, 216 | "output_type": "execute_result" 217 | } 218 | ], 219 | "source": [ 220 | "A*B" 221 | ] 222 | }, 223 | { 224 | "cell_type": "markdown", 225 | "metadata": {}, 226 | "source": [ 227 | "If we define the two matrices as arrays, this will not work and will return an error. We won't often be adding and subtracting matrices in the context of quantum computing but these operations are well defined for matrices of the same size. The Python code is exactly what you would expect it to be. Let's define two matrices of the same size. " 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": 8, 233 | "metadata": {}, 234 | "outputs": [], 235 | "source": [ 236 | "M = np.matrix([[1, 2, 3], \n", 237 | " [1j, 2j, 3j]])\n", 238 | "\n", 239 | "N = np.matrix([[2, 4, 6],\n", 240 | " [-2j, -4j, -6j]])" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": 9, 246 | "metadata": {}, 247 | "outputs": [ 248 | { 249 | "data": { 250 | "text/plain": [ 251 | "matrix([[3.+0.j, 6.+0.j, 9.+0.j],\n", 252 | " [0.-1.j, 0.-2.j, 0.-3.j]])" 253 | ] 254 | }, 255 | "execution_count": 9, 256 | "metadata": {}, 257 | "output_type": "execute_result" 258 | } 259 | ], 260 | "source": [ 261 | "M+N" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": 10, 267 | "metadata": {}, 268 | "outputs": [ 269 | { 270 | "data": { 271 | "text/plain": [ 272 | "matrix([[-1.+0.j, -2.+0.j, -3.+0.j],\n", 273 | " [ 0.+3.j, 0.+6.j, 0.+9.j]])" 274 | ] 275 | }, 276 | "execution_count": 10, 277 | "metadata": {}, 278 | "output_type": "execute_result" 279 | } 280 | ], 281 | "source": [ 282 | "M-N" 283 | ] 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": {}, 288 | "source": [ 289 | "We can also multiply matrices by scalars." 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "execution_count": 11, 295 | "metadata": {}, 296 | "outputs": [ 297 | { 298 | "data": { 299 | "text/plain": [ 300 | "matrix([[3.+0.j, 6.+0.j, 9.+0.j],\n", 301 | " [0.+3.j, 0.+6.j, 0.+9.j]])" 302 | ] 303 | }, 304 | "execution_count": 11, 305 | "metadata": {}, 306 | "output_type": "execute_result" 307 | } 308 | ], 309 | "source": [ 310 | "3*M" 311 | ] 312 | }, 313 | { 314 | "cell_type": "markdown", 315 | "metadata": {}, 316 | "source": [ 317 | "## Exercises\n", 318 | "\n", 319 | "#### Define the following five matrices in Python:\n", 320 | "\n", 321 | "\\begin{align}\n", 322 | "I = \\begin{pmatrix} 1&0 \\\\ 0&1 \\end{pmatrix}, \\quad\n", 323 | "X = \\begin{pmatrix} 0&1 \\\\ 1&0 \\end{pmatrix}, \\quad\n", 324 | "Y = \\begin{pmatrix} 0&i \\\\ -i&0 \\end{pmatrix}, \\quad\n", 325 | "Z = \\begin{pmatrix} 1&0 \\\\ 0&-1 \\end{pmatrix}, \\quad\n", 326 | "H = \\frac{1}{\\sqrt{2}} \\begin{pmatrix} 1&1 \\\\ 1&-1 \\end{pmatrix}\n", 327 | "\\end{align}\n", 328 | "\n", 329 | "These are five of the most common single qubit gates used in quantum computing, and they will be used repeatedly. " 330 | ] 331 | }, 332 | { 333 | "cell_type": "markdown", 334 | "metadata": {}, 335 | "source": [ 336 | "#### Write Python code to compute the following matrix multiplications:\n", 337 | "\n", 338 | "1. $XY$\n", 339 | "2. $YX$\n", 340 | "3. $YZ$\n", 341 | "4. $ZY$\n", 342 | "5. $HX$\n", 343 | "6. $YH$\n", 344 | "7. $XYZ$\n", 345 | "8. $YZX$\n", 346 | "9. $ZXY$\n", 347 | "10. $XHY$" 348 | ] 349 | } 350 | ], 351 | "metadata": { 352 | "kernelspec": { 353 | "display_name": "Python 3", 354 | "language": "python", 355 | "name": "python3" 356 | }, 357 | "language_info": { 358 | "codemirror_mode": { 359 | "name": "ipython", 360 | "version": 3 361 | }, 362 | "file_extension": ".py", 363 | "mimetype": "text/x-python", 364 | "name": "python", 365 | "nbconvert_exporter": "python", 366 | "pygments_lexer": "ipython3", 367 | "version": "3.7.3" 368 | } 369 | }, 370 | "nbformat": 4, 371 | "nbformat_minor": 4 372 | } 373 | -------------------------------------------------------------------------------- /lecture_9_tensor_product_matrices.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Tensor Products of Matrices" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Imports\n", 15 | "\n", 16 | "As usual, we will need NumPy. So, let's import it now. " 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "import numpy as np" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "In this section we will discuss how to take tensor products of matrices in order to later build quantum gates that operate on many qubits at once. Understanding the basics of tensor products of matrices is fundamental to understanding quantum logic gates and quantum circuits. For the tensor product of two $2 \\times 2$ matrices, the general rule is as follows:" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "\\begin{align}\n", 40 | "\\begin{pmatrix}\n", 41 | "a & b \\\\\n", 42 | "c & d\n", 43 | "\\end{pmatrix} \\otimes \n", 44 | "\\begin{pmatrix}\n", 45 | "x & y \\\\\n", 46 | "z & w\n", 47 | "\\end{pmatrix} = \n", 48 | "\\begin{pmatrix}\n", 49 | "a \\begin{pmatrix}\n", 50 | "x & y \\\\\n", 51 | "z & w\n", 52 | "\\end{pmatrix} & b \\begin{pmatrix}\n", 53 | "x & y \\\\\n", 54 | "z & w\n", 55 | "\\end{pmatrix} \\\\\n", 56 | "c \\begin{pmatrix}\n", 57 | "x & y \\\\\n", 58 | "z & w\n", 59 | "\\end{pmatrix} & d \\begin{pmatrix}\n", 60 | "x & y \\\\\n", 61 | "z & w\n", 62 | "\\end{pmatrix}\n", 63 | "\\end{pmatrix} = \n", 64 | "\\begin{pmatrix}\n", 65 | "ax & ay & bx & by \\\\\n", 66 | "az & aw & bz & bw \\\\\n", 67 | "cx & cy & dx & dy \\\\\n", 68 | "cz & cw & dz & dw\n", 69 | "\\end{pmatrix}\n", 70 | "\\end{align}" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "metadata": {}, 76 | "source": [ 77 | "Here is a basic numerical example:\n", 78 | "\n", 79 | "\\begin{align}\n", 80 | "\\begin{pmatrix}\n", 81 | "2 & 3 \\\\\n", 82 | "6 & 1\n", 83 | "\\end{pmatrix} \\otimes\n", 84 | "\\begin{pmatrix}\n", 85 | "7 & 10 \\\\\n", 86 | "9 & 4\n", 87 | "\\end{pmatrix} = \n", 88 | "\\begin{pmatrix}\n", 89 | "2 \\begin{pmatrix}\n", 90 | "7 & 10 \\\\\n", 91 | "9 & 4\n", 92 | "\\end{pmatrix}& 3\\begin{pmatrix}\n", 93 | "7 & 10 \\\\\n", 94 | "9 & 4\n", 95 | "\\end{pmatrix} \\\\\n", 96 | "6\\begin{pmatrix}\n", 97 | "7 & 10 \\\\\n", 98 | "9 & 4\n", 99 | "\\end{pmatrix} & 1\\begin{pmatrix}\n", 100 | "7 & 10 \\\\\n", 101 | "9 & 4\n", 102 | "\\end{pmatrix}\n", 103 | "\\end{pmatrix} = \n", 104 | "\\begin{pmatrix}\n", 105 | "14 & 20 & 21 & 30 \\\\\n", 106 | "18 & 8 & 27 & 12 \\\\\n", 107 | "42 & 60 & 7 & 10 \\\\\n", 108 | "54 & 24 & 9 & 4\n", 109 | "\\end{pmatrix}\n", 110 | "\\end{align}" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "We can again use the 'np.kron()' function to perform this computation in Python, just as we did for qubit state vectors. " 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": 2, 123 | "metadata": {}, 124 | "outputs": [ 125 | { 126 | "data": { 127 | "text/plain": [ 128 | "matrix([[14, 20, 21, 30],\n", 129 | " [18, 8, 27, 12],\n", 130 | " [42, 60, 7, 10],\n", 131 | " [54, 24, 9, 4]])" 132 | ] 133 | }, 134 | "execution_count": 2, 135 | "metadata": {}, 136 | "output_type": "execute_result" 137 | } 138 | ], 139 | "source": [ 140 | "A = np.matrix([[2, 3],\n", 141 | " [6, 1]])\n", 142 | "\n", 143 | "B = np.matrix([[7, 10], \n", 144 | " [9, 4]])\n", 145 | "\n", 146 | "np.kron(A,B)" 147 | ] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "metadata": {}, 152 | "source": [ 153 | "In quantum computing, if we have basis states such as\n", 154 | "\n", 155 | "\\begin{align}\n", 156 | "|010\\rangle &= |0\\rangle \\otimes |1\\rangle \\otimes |0\\rangle \\\\\n", 157 | "&= \\begin{pmatrix}\n", 158 | "1\\\\0\n", 159 | "\\end{pmatrix} \\otimes \n", 160 | "\\begin{pmatrix}\n", 161 | "0\\\\1\n", 162 | "\\end{pmatrix} \\otimes \n", 163 | "\\begin{pmatrix}\n", 164 | "1\\\\0\n", 165 | "\\end{pmatrix} \\\\\n", 166 | "&= \\begin{pmatrix}\n", 167 | "0\\\\0\\\\1\\\\0\\\\0\\\\0\\\\0\\\\0\n", 168 | "\\end{pmatrix}\n", 169 | "\\end{align}\n", 170 | "\n", 171 | "we can operate on them using tensor product of operators. In particular, to operate on a three qubit basis state such at this, we need the tensor product of three $2 \\times 2$ matrix. For example, with the following four operators:\n", 172 | "\n", 173 | "\\begin{align}\n", 174 | "I = \\begin{pmatrix} 1&0 \\\\ 0&1 \\end{pmatrix}, \\quad\n", 175 | "X = \\begin{pmatrix} 0&1 \\\\ 1&0 \\end{pmatrix}, \\quad\n", 176 | "Y = \\begin{pmatrix} 0&i \\\\ -i&0 \\end{pmatrix}, \\quad\n", 177 | "Z = \\begin{pmatrix} 1&0 \\\\ 0&-1 \\end{pmatrix}, \\quad\n", 178 | "H = \\frac{1}{\\sqrt{2}} \\begin{pmatrix} 1&1 \\\\ 1&-1 \\end{pmatrix}\n", 179 | "\\end{align}\n", 180 | "\n", 181 | "We can form tensor products of matrices (gates) such as $H \\otimes X \\otimes I$ and $Z \\otimes Z \\otimes H$, and any other three matrix combination you might dream up. Let's define a basis state $|01\\rangle$ and compute a few operators that will operate on this basis state:" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": 3, 187 | "metadata": {}, 188 | "outputs": [ 189 | { 190 | "name": "stdout", 191 | "output_type": "stream", 192 | "text": [ 193 | "[[0]\n", 194 | " [1]\n", 195 | " [0]\n", 196 | " [0]]\n" 197 | ] 198 | } 199 | ], 200 | "source": [ 201 | "#Define the basis state |01>\n", 202 | "u = np.matrix([[1],\n", 203 | " [0]])\n", 204 | "\n", 205 | "d = np.matrix([[0],\n", 206 | " [1]])\n", 207 | "\n", 208 | "ud = np.kron(u,d)\n", 209 | "\n", 210 | "print(ud)" 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": 4, 216 | "metadata": {}, 217 | "outputs": [], 218 | "source": [ 219 | "# Define the matrices X, Y, Z, and H\n", 220 | "X = np.matrix([[0, 1],\n", 221 | " [1, 0]])\n", 222 | "\n", 223 | "Y = np.matrix([[0, -1j], \n", 224 | " [1j, 0]])\n", 225 | "\n", 226 | "Z = np.matrix([[1, 0],\n", 227 | " [0, -1]])\n", 228 | "\n", 229 | "H = (1/np.sqrt(2))*np.matrix([[1, 1], \n", 230 | " [1, -1]])" 231 | ] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "metadata": {}, 236 | "source": [ 237 | "Now let's compute the following four tensor products:\n", 238 | "\n", 239 | "\\begin{align}\n", 240 | "X \\otimes Y, \\quad X \\otimes Z, \\quad H \\otimes H\n", 241 | "\\end{align}" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": 5, 247 | "metadata": {}, 248 | "outputs": [ 249 | { 250 | "name": "stdout", 251 | "output_type": "stream", 252 | "text": [ 253 | "[[0.+0.j 0.-0.j 0.+0.j 0.-1.j]\n", 254 | " [0.+0.j 0.+0.j 0.+1.j 0.+0.j]\n", 255 | " [0.+0.j 0.-1.j 0.+0.j 0.-0.j]\n", 256 | " [0.+1.j 0.+0.j 0.+0.j 0.+0.j]]\n" 257 | ] 258 | } 259 | ], 260 | "source": [ 261 | "print(np.kron(X, Y))" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": 6, 267 | "metadata": {}, 268 | "outputs": [ 269 | { 270 | "name": "stdout", 271 | "output_type": "stream", 272 | "text": [ 273 | "[[ 0 0 1 0]\n", 274 | " [ 0 0 0 -1]\n", 275 | " [ 1 0 0 0]\n", 276 | " [ 0 -1 0 0]]\n" 277 | ] 278 | } 279 | ], 280 | "source": [ 281 | "print(np.kron(X, Z))" 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": 7, 287 | "metadata": {}, 288 | "outputs": [ 289 | { 290 | "name": "stdout", 291 | "output_type": "stream", 292 | "text": [ 293 | "[[ 0.5 0.5 0.5 0.5]\n", 294 | " [ 0.5 -0.5 0.5 -0.5]\n", 295 | " [ 0.5 0.5 -0.5 -0.5]\n", 296 | " [ 0.5 -0.5 -0.5 0.5]]\n" 297 | ] 298 | } 299 | ], 300 | "source": [ 301 | "print(np.kron(H, H))" 302 | ] 303 | }, 304 | { 305 | "cell_type": "markdown", 306 | "metadata": {}, 307 | "source": [ 308 | "Now, we can compute the action of these matrices on $|01\\rangle$ as:\n", 309 | "\n", 310 | "\\begin{align}\n", 311 | "(X \\otimes Y)(|01\\rangle) = (X \\otimes Y)(|0\\rangle \\otimes |1\\rangle) = X|0\\rangle \\otimes Y|1\\rangle \n", 312 | "\\end{align}\n", 313 | "\n", 314 | "\\begin{align}\n", 315 | "(X \\otimes Z)(|01\\rangle) = (X \\otimes Z)(|0\\rangle \\otimes |1\\rangle) = X|0\\rangle \\otimes Z|1\\rangle \n", 316 | "\\end{align}\n", 317 | "\n", 318 | "\\begin{align}\n", 319 | "(H \\otimes H)(|01\\rangle) = (H \\otimes H)(|0\\rangle \\otimes |1\\rangle) = H|0\\rangle \\otimes H|1\\rangle \n", 320 | "\\end{align}" 321 | ] 322 | }, 323 | { 324 | "cell_type": "code", 325 | "execution_count": 8, 326 | "metadata": {}, 327 | "outputs": [], 328 | "source": [ 329 | "XY = np.kron(X, Y)\n", 330 | "XZ = np.kron(X, Z)\n", 331 | "HH = np.kron(H, H)" 332 | ] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": 9, 337 | "metadata": {}, 338 | "outputs": [ 339 | { 340 | "name": "stdout", 341 | "output_type": "stream", 342 | "text": [ 343 | "[[0.+0.j]\n", 344 | " [0.+0.j]\n", 345 | " [0.-1.j]\n", 346 | " [0.+0.j]]\n" 347 | ] 348 | } 349 | ], 350 | "source": [ 351 | "print(XY * ud)" 352 | ] 353 | }, 354 | { 355 | "cell_type": "code", 356 | "execution_count": 10, 357 | "metadata": {}, 358 | "outputs": [ 359 | { 360 | "name": "stdout", 361 | "output_type": "stream", 362 | "text": [ 363 | "[[ 0]\n", 364 | " [ 0]\n", 365 | " [ 0]\n", 366 | " [-1]]\n" 367 | ] 368 | } 369 | ], 370 | "source": [ 371 | "print(XZ * ud)" 372 | ] 373 | }, 374 | { 375 | "cell_type": "code", 376 | "execution_count": 11, 377 | "metadata": {}, 378 | "outputs": [ 379 | { 380 | "name": "stdout", 381 | "output_type": "stream", 382 | "text": [ 383 | "[[ 0.5]\n", 384 | " [-0.5]\n", 385 | " [ 0.5]\n", 386 | " [-0.5]]\n" 387 | ] 388 | } 389 | ], 390 | "source": [ 391 | "print(HH * ud)" 392 | ] 393 | }, 394 | { 395 | "cell_type": "markdown", 396 | "metadata": {}, 397 | "source": [ 398 | "## Exercises\n", 399 | "\n", 400 | "1. Compute the following tensor product by hand:\n", 401 | "\n", 402 | "\\begin{align}\n", 403 | "\\begin{pmatrix}\n", 404 | "11 & 2 \\\\\n", 405 | "3 & 5\n", 406 | "\\end{pmatrix} \\otimes\n", 407 | "\\begin{pmatrix}\n", 408 | "7 & 8 \\\\ \n", 409 | "0 & 1\n", 410 | "\\end{pmatrix}\n", 411 | "\\end{align}" 412 | ] 413 | }, 414 | { 415 | "cell_type": "markdown", 416 | "metadata": {}, 417 | "source": [ 418 | "#### Compute the following tensor products by hand:\n", 419 | "\n", 420 | "2. $X \\otimes X$\n", 421 | "3. $Z \\otimes Y$\n", 422 | "4. $H \\otimes X$" 423 | ] 424 | }, 425 | { 426 | "cell_type": "markdown", 427 | "metadata": {}, 428 | "source": [ 429 | "#### Write Python code to compute the tensor products:\n", 430 | "\n", 431 | "5. $X \\otimes X$\n", 432 | "6. $Z \\otimes Y$\n", 433 | "7. $H \\otimes X$\n", 434 | "8. $H \\otimes H$" 435 | ] 436 | }, 437 | { 438 | "cell_type": "markdown", 439 | "metadata": {}, 440 | "source": [ 441 | "9. Write Python code to verify that $(H \\otimes H) \\otimes H = H \\otimes (H \\otimes H)$. " 442 | ] 443 | }, 444 | { 445 | "cell_type": "markdown", 446 | "metadata": {}, 447 | "source": [ 448 | "#### Use the following code defining the basis state $|010 \\rangle $ and the relevant tensor products to compute the following: \n", 449 | "\n", 450 | "10. $(X \\otimes X \\otimes Y)|010 \\rangle$\n", 451 | "11. $(X \\otimes Z \\otimes H)|010 \\rangle$\n", 452 | "12. $(H \\otimes H \\otimes H)|010 \\rangle$" 453 | ] 454 | }, 455 | { 456 | "cell_type": "code", 457 | "execution_count": 12, 458 | "metadata": {}, 459 | "outputs": [], 460 | "source": [ 461 | "# Define the matrices X, Y, Z, and H:\n", 462 | "X = np.matrix([[0, 1],\n", 463 | " [1, 0]])\n", 464 | "\n", 465 | "Y = np.matrix([[0, -1j], \n", 466 | " [1j, 0]])\n", 467 | "\n", 468 | "Z = np.matrix([[1, 0],\n", 469 | " [0, -1]])\n", 470 | "\n", 471 | "H = (1/np.sqrt(2))*np.matrix([[1, 1], \n", 472 | " [1, -1]])\n", 473 | "\n", 474 | "# Define the following tensor products:\n", 475 | "XY = np.kron(X, Y)\n", 476 | "XZ = np.kron(X, Z)\n", 477 | "HH = np.kron(H, H)\n", 478 | "\n", 479 | "# Define the following additional tensor products:\n", 480 | "XXY = np.kron(X, XY)\n", 481 | "XZH = np.kron(XZ, H)\n", 482 | "HHH = np.kron(HH, H)\n", 483 | "\n", 484 | "# spin-up and spin-down\n", 485 | "u = np.matrix([[1],\n", 486 | " [0]])\n", 487 | "\n", 488 | "d = np.matrix([[0],\n", 489 | " [1]])\n", 490 | "\n", 491 | "# Define the basis state |01\n", 492 | "ud = np.kron(u,d)\n", 493 | "\n", 494 | "# Define the basis state |010>:\n", 495 | "udu = np.kron(ud, u)" 496 | ] 497 | }, 498 | { 499 | "cell_type": "code", 500 | "execution_count": null, 501 | "metadata": {}, 502 | "outputs": [], 503 | "source": [] 504 | } 505 | ], 506 | "metadata": { 507 | "kernelspec": { 508 | "display_name": "Python 3", 509 | "language": "python", 510 | "name": "python3" 511 | }, 512 | "language_info": { 513 | "codemirror_mode": { 514 | "name": "ipython", 515 | "version": 3 516 | }, 517 | "file_extension": ".py", 518 | "mimetype": "text/x-python", 519 | "name": "python", 520 | "nbconvert_exporter": "python", 521 | "pygments_lexer": "ipython3", 522 | "version": "3.7.3" 523 | } 524 | }, 525 | "nbformat": 4, 526 | "nbformat_minor": 4 527 | } 528 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | qiskit 3 | --------------------------------------------------------------------------------