Created
March 23, 2016 13:13
-
-
Save cgranade/64d5e54c0b90fbe8bca1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "# Learning Quantum Information with QuTiP #\n", | |
| "Christopher Granade, University of Sydney\n", | |
| "$$\n", | |
| " \\newcommand{\\ket}[1]{\\left|#1\\right\\rangle}\n", | |
| " \\newcommand{\\bra}[1]{\\left\\langle#1\\right|}\n", | |
| " \\newcommand{\\braket}[2]{\\left\\langle#1\\mid#2\\right\\rangle}\n", | |
| " \\newcommand{\\id}{ \t𝟙 }\n", | |
| " \\newcommand{\\TT}{\\mathrm{T}}\n", | |
| " \\newcommand{\\ii}{\\mathrm{i}}\n", | |
| " \\newcommand{\\ee}{\\mathrm{e}}\n", | |
| "$$" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "## Introduction ##" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "In this tutorial, we'll learn a bit about quantum mechanics and quantum information, using the QuTiP library for Python 2 and 3. This library makes it easy to get a jump start in quantum information by providing classes for concepts central to quantum information, such as *states* and *operators*." | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "## Installing QuTiP ##\n", | |
| "\n", | |
| "### Linux and OS X ###\n", | |
| "\n", | |
| "> $ pip install qutip\n", | |
| "\n", | |
| "### Windows ###\n", | |
| "\n", | |
| "(TODO: conda-forge)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "## Notebook Setup ##" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "The last thing we'll need to do before jumping into learning quantum information is to do a little bit of setting up our notebook. Let's make sure all of our figures are inline, and that we import a few common NumPy functions that we'll need along the way." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 13, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stderr", | |
| "output_type": "stream", | |
| "text": [ | |
| "/home/cgranade/anaconda/envs/py3/lib/python3.5/site-packages/matplotlib/font_manager.py:273: UserWarning: Matplotlib is building the font cache using fc-list. This may take a moment.\n", | |
| " warnings.warn('Matplotlib is building the font cache using fc-list. This may take a moment.')\n", | |
| "/home/cgranade/anaconda/envs/py3/lib/python3.5/site-packages/matplotlib/font_manager.py:273: UserWarning: Matplotlib is building the font cache using fc-list. This may take a moment.\n", | |
| " warnings.warn('Matplotlib is building the font cache using fc-list. This may take a moment.')\n", | |
| "/home/cgranade/anaconda/envs/py3/lib/python3.5/site-packages/matplotlib/__init__.py:872: UserWarning: axes.color_cycle is deprecated and replaced with axes.prop_cycle; please use the latter.\n", | |
| " warnings.warn(self.msg_depr % (key, alt_key))\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "%matplotlib inline\n", | |
| "from numpy import abs, array" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "We'll also want the rich display support provided by IPython, so let's import it here." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 23, | |
| "metadata": { | |
| "collapsed": true | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "from IPython.display import display" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "## Rethinking Classical Information ##" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "We're used to thinking of classical information in terms of *bits*, but it can be helpful to dig a bit at what a bit means. A bit can be in one of two *states*, which we traditionally label as $0$ and $1$.\n", | |
| "\n", | |
| "(TODO: figure with two points.)\n", | |
| "\n", | |
| "Measuring a bit is pretty simple: if we initialize a bit to be in the $0$ state, and we measure it, we get a $0$ back. In this way, we can think of a bit as defining a particularly simple probability distribution. Suppose we *prepare* (quantum information parlance for \"initialize\") a state $x$. Then, the probability we *observe* a $y$ is given by\n", | |
| "$$\n", | |
| " \\Pr(y | x) = \\begin{cases}\n", | |
| " 1 & \\text{if } y = x \\\\\n", | |
| " 0 & \\text{if } y \\ne x\n", | |
| " \\end{cases}.\n", | |
| "$$\n", | |
| "We read $\\Pr(y | x)$ as \"the probability of $y$ given $x$.\"\n", | |
| "\n", | |
| "This way of thinking of bits also allows us to include randomness. A fair coin, for instance, can be thought of as a distribution over states of a bit,\n", | |
| "$$\n", | |
| " \\Pr(x) = \\frac12.\n", | |
| "$$\n", | |
| "More generally, we can consider preparing a bit based on the outcome of a biased coin, such that the probability of initializing in the $0$ state is some number $p$. We write this as\n", | |
| "$$\n", | |
| " \\Pr(x | p) = \\begin{cases}\n", | |
| " p & \\text{if } x = 0 \\\\\n", | |
| " 1 - p & \\text{if } x = 1\n", | |
| " \\end{cases}.\n", | |
| "$$\n", | |
| "Measurement with a biased coin gets a bit more interesting now, as we have to sum over everything that the bit could be (in statistical terminology, we have to *marginalize over* $x$):\n", | |
| "$$\n", | |
| " \\Pr(y | p) = \\sum_x \\Pr(y | x) \\Pr(x | p) = \\begin{cases}\n", | |
| " p & \\text{if } y = 0 \\\\\n", | |
| " 1 - p & \\text{if } y = 1\n", | |
| " \\end{cases}.\n", | |
| "$$" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "We'll make one more important observation before we move on to something more interesting. Writing out cases every time is rather annoying, since we only care about the probabilities for each possible state of our bit. Thankfully, this is precisely what the concept of a *vector* handles. Let's write out our coin as a vector\n", | |
| "$$\n", | |
| " \\vec{\\psi} = \\left(\\begin{matrix}\n", | |
| " p \\\\\n", | |
| " 1 - p\n", | |
| " \\end{matrix}\\right).\n", | |
| "$$\n", | |
| "Notice that the elements of $\\vec{\\psi}$ sum to one; this represents that our coin will always land either as $0$ or as $1$. Graphically, the vectors that sum to one in this way form a line between the $0$ and $1$ states.\n", | |
| "\n", | |
| "(TODO: figure with a line.)\n", | |
| "\n", | |
| "Measurement now looks like a dot product between a vector representing a possible outcome and the state. For instance, the outcome that we get a 0 can be written as the vector\n", | |
| "$$\n", | |
| " \\vec{\\phi} = \\left(\\begin{matrix}\n", | |
| " 1 \\\\ 0\n", | |
| " \\end{matrix}\\right).\n", | |
| "$$\n", | |
| "Putting it all together,\n", | |
| "$$\n", | |
| " \\Pr(\\phi | \\psi) = \\vec{\\phi} \\cdot \\vec{\\psi} = \\vec{\\phi}^\\TT \\vec{\\psi}.\n", | |
| "$$\n", | |
| "This notation will have a lot of advantages as we go on in this tutorial, including that we can now consider classical objects other than bits. Considering a *trit* (three states instead of two), for instance, just means replacing two-dimensional vectors by three-dimensional ones. For now, though, it's time to get to the heart of the matter, and meet the quantum state." | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "## Quantum States and Measurements ##" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "With this view of classical bits in mind, one way to think of quantum information is as a new kind of probability theory, where our states are vectors of complex numbers instead of positive real numbers. We write such vectors as $\\ket{\\psi}$ (read \"ket-psi\"). In QuTiP, quantum states are represented as instances of the ``Qobj`` class. Let's jump in and define particularly useful quantum state." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 1, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stderr", | |
| "output_type": "stream", | |
| "text": [ | |
| "/home/cgranade/anaconda/envs/py3/lib/python3.5/site-packages/qutip-3.2.0.dev_d803f82-py3.5-linux-x86_64.egg/qutip/__init__.py:203: UserWarning: matplotlib not found: Graphics will not work.\n", | |
| " warnings.warn(\"matplotlib not found: Graphics will not work.\")\n", | |
| "[2016-03-23 22:23:42] qutip.metrics[3008]: <module>: WARNING: CVXPY not found, semidefinite solving is disabled.\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "from qutip import Qobj" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 3, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2], [1]], shape = [2, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}1.0\\\\0.0\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2], [1]], shape = [2, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 1.]\n", | |
| " [ 0.]]" | |
| ] | |
| }, | |
| "execution_count": 3, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "ket0 = Qobj([[1], [0]])\n", | |
| "ket0" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "We'll get to what ``dims`` means later, but for now, note that ``ket0`` represents the vector $(1\\,0)^\\TT$ by a $2 \\times 1$ matrix. QuTiP has automatically identified this matrix as representing a \"ket\" vector, and stored that as the ``type`` attribute:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 9, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "ket\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "print(ket0.type)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "As in the classical case, if we prepare the quantum state represented by ``ket0`` and then measure it, we will always get the result labeled $0$. We therefore write this state $\\ket{0}$. Similarly, we can define a state $\\ket{1}$ that always returns a $1$." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 4, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2], [1]], shape = [2, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.0\\\\1.0\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2], [1]], shape = [2, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 0.]\n", | |
| " [ 1.]]" | |
| ] | |
| }, | |
| "execution_count": 4, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "ket1 = Qobj([[0], [1]])\n", | |
| "ket1" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Together, these two states form a *basis* for a quantum analogue to a classical bit, called a *qubit*. In particular, any other state of a qubit can be written in terms of these two states as\n", | |
| "$$\n", | |
| " \\ket{\\psi} = \\alpha \\ket{0} + \\beta\\ket{1}\n", | |
| "$$\n", | |
| "for some complex numbers $\\alpha$, $\\beta$. QuTiP thus provides us with a nice function to quickly make such basis states." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 8, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "from qutip import basis\n", | |
| "ket0, ket1 = [basis(2, n) for n in range(2)]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Now that we have states, we can quickly define measurement in a similar way as we did for classical bits. Let $\\ket{\\phi}$ be a particular state that we wish to know the probability of observing, given that we have prepared a state $\\ket{\\psi}$. Quantum mechanics then prescribes that\n", | |
| "$$\n", | |
| " \\Pr(\\phi | \\psi) = \\left| \\braket{\\phi}{\\psi} \\right|^2 = \\braket{\\phi}{\\psi} \\braket{\\psi}{\\phi},\n", | |
| "$$\n", | |
| "where $\\bra{\\phi}$ is the cojugate-transpose of $\\ket{\\phi}$, written $\\ket{\\phi}^\\dagger$. In QuTiP, the conjugate-transpose is written as the ``dag()`` method:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 12, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[1], [1]], shape = [1, 1], type = bra\\begin{equation*}\\left(\\begin{array}{*{11}c}1.0\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[1], [1]], shape = [1, 1], type = bra\n", | |
| "Qobj data =\n", | |
| "[[ 1.]]" | |
| ] | |
| }, | |
| "execution_count": 12, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "ket0.dag() * ket0" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "We now have everything we need to write quantum measurement as a function!" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 17, | |
| "metadata": { | |
| "collapsed": true | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "def measure(phi, psi):\n", | |
| " # Note that we have to get the (0, 0) element out, since phi.dag() * psi\n", | |
| " # isn't actually just a number, but a 1-by-1 matrix.\n", | |
| " return abs((phi.dag() * psi)[0, 0]) ** 2" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "To make sure it works, let's make sure that if we prepare a $0$, we always get a $0$ back:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 15, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "1.0\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "print(measure(ket0, ket0))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Similarly, if we know for sure that we have prepared a $0$, then we should never see a $1$:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 16, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "0.0\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "print(measure(ket1, ket0))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "As a final note before we dive into a cool example, just as we demanded that the elements of a classical state summed to 1, we demand that the squared modulus of each element ($|\\alpha|^2$) of a quantum state sums to 1. In QuTiP, we can quickly *renormalize* to ensure that this is the case by calling the ``unit()`` method." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 21, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2], [1]], shape = [2, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.707\\\\0.707\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2], [1]], shape = [2, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 0.70710678]\n", | |
| " [ 0.70710678]]" | |
| ] | |
| }, | |
| "execution_count": 21, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "psi = Qobj([[1], [1]]).unit()\n", | |
| "psi" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "By making this demand, the probability for each measurement outcome in a basis now sums to one, as we would expect." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 22, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "0.5\n", | |
| "0.5\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "print(measure(ket0, psi))\n", | |
| "print(measure(ket1, psi))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "## Dive Into Quantum: Superdense Coding ##" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Now to the thick of things, with an example of a very cool property of quantum information known as *superdense coding*. This example is covered in much more detail in the textbook *Quantum Information and Computation* by Nielsen and Chuang (TODO: link), and by [Wikipedia](https://en.wikipedia.org/wiki/Superdense_coding). Here, we'll focus on how QuTiP can help us make sense of superdense coding." | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "## Initial State ##" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "The superdense coding protocol starts off with a two-qubit state $\\ket{\\beta_{00}} = (\\ket{00} + \\ket{11}) / \\sqrt{2}$. Since we now have two qubits, they could be in one of four different possible basis states, each labeled by two classical bits ($00$, $01$, $10$ or $11$). Our quantum states are thus four-dimensional vectors now.\n", | |
| "\n", | |
| "We can construct the ``Qobj`` for $\\ket{\\beta_{00}}$ in a few different ways. Let's start with the most direct way, using lists to represent each of the elements of $\\ket{\\beta_{00}}$ explicitly:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 25, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.707\\\\0.0\\\\0.0\\\\0.707\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 0.70710678]\n", | |
| " [ 0. ]\n", | |
| " [ 0. ]\n", | |
| " [ 0.70710678]]" | |
| ] | |
| }, | |
| "execution_count": 25, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "beta_00 = Qobj([[1], [0], [0], [1]], dims=[[2, 2], [1, 1]]).unit()\n", | |
| "beta_00" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "In this call, ``dims`` tells QuTiP that our four-dimensional state vector can be broken down into the *tensor product* of two-dimensional state vectors. The [tensor product](https://en.wikipedia.org/wiki/Tensor_product) of two matrices $A$ and $B$ is written as $A \\otimes B$, and is computed as\n", | |
| "\\begin{align*}\n", | |
| " \\left(\\begin{matrix}\n", | |
| " a_{0, 0} & a_{0, 1} \\\\\n", | |
| " a_{1, 0} & a_{1, 1}\n", | |
| " \\end{matrix}\\right) \\otimes \\left(\\begin{matrix}\n", | |
| " b_{0, 0} & b_{0, 1} \\\\\n", | |
| " b_{1, 0} & b_{1, 1}\n", | |
| " \\end{matrix}\\right) & =\n", | |
| " \\left(\\begin{matrix}\n", | |
| " a_{0, 0} \\left(\\begin{matrix}\n", | |
| " b_{0, 0} & b_{0, 1} \\\\\n", | |
| " b_{1, 0} & b_{1, 1}\n", | |
| " \\end{matrix}\\right) & a_{0, 1} \\left(\\begin{matrix}\n", | |
| " b_{0, 0} & b_{0, 1} \\\\\n", | |
| " b_{1, 0} & b_{1, 1}\n", | |
| " \\end{matrix}\\right) \\\\\n", | |
| " a_{1, 0} \\left(\\begin{matrix}\n", | |
| " b_{0, 0} & b_{0, 1} \\\\\n", | |
| " b_{1, 0} & b_{1, 1}\n", | |
| " \\end{matrix}\\right) & a_{1, 1} \\left(\\begin{matrix}\n", | |
| " b_{0, 0} & b_{0, 1} \\\\\n", | |
| " b_{1, 0} & b_{1, 1}\n", | |
| " \\end{matrix}\\right)\n", | |
| " \\end{matrix}\\right) \\\\\n", | |
| " & =\n", | |
| " \\left(\\begin{matrix}\n", | |
| " a_{0, 0} b_{0, 0} & a_{0, 0} b_{0, 1} & a_{0, 1} b_{0, 0} & a_{0, 1} b_{0, 1} \\\\\n", | |
| " a_{0, 0} b_{1, 0} & a_{0, 0} b_{1, 1} & a_{0, 1} b_{1, 0} & a_{0, 1} b_{1, 1} \\\\\n", | |
| " a_{0, 1} b_{1, 0} & a_{0, 1} b_{1, 1} & a_{1, 1} b_{1, 0} & a_{1, 1} b_{1, 1} \\\\\n", | |
| " a_{0, 1} b_{1, 0} & a_{0, 1} b_{1, 1} & a_{1, 1} b_{1, 0} & a_{1, 1} b_{1, 1}\n", | |
| " \\end{matrix}\\right)\n", | |
| "\\end{align*}.\n", | |
| "For example, the two-qubit basis state $\\ket{00}$ is given by the tensor product $\\ket{0} \\otimes \\ket{0}$ of two single-qubit basis states. We can use this to provide a simpler way of writing out $\\ket{\\beta_{00}}$:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 26, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.707\\\\0.0\\\\0.0\\\\0.707\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 0.70710678]\n", | |
| " [ 0. ]\n", | |
| " [ 0. ]\n", | |
| " [ 0.70710678]]" | |
| ] | |
| }, | |
| "execution_count": 26, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "from qutip import tensor\n", | |
| "beta_00 = (\n", | |
| " tensor(ket0, ket0) +\n", | |
| " tensor(ket1, ket1)\n", | |
| ").unit()\n", | |
| "beta_00" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Unlike before, ``tensor()`` is able to figure out the correct ``dims`` attribute for us, making it simple to combine qubits together." | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Even though this is a lot better, it's still pretty verbose. Let's instead sum over the different basis states:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 27, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.707\\\\0.0\\\\0.0\\\\0.707\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 0.70710678]\n", | |
| " [ 0. ]\n", | |
| " [ 0. ]\n", | |
| " [ 0.70710678]]" | |
| ] | |
| }, | |
| "execution_count": 27, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "beta_00 = sum(tensor([basis(2, idx)] * 2) for idx in range(2)).unit()\n", | |
| "beta_00" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "This has the advantage of working for more than just qubits with a slight generalization, but is probably overkill for what we need right now." | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Before we get to the last and most useful way of writing out $\\ket{\\beta_{00}}$, let's pause for a moment and look at measurements on our shiny new state." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 30, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "0.5\n", | |
| "0.0\n", | |
| "0.0\n", | |
| "0.5\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "print(measure(tensor(ket0, ket0), beta_00))\n", | |
| "print(measure(tensor(ket0, ket1), beta_00))\n", | |
| "print(measure(tensor(ket1, ket0), beta_00))\n", | |
| "print(measure(tensor(ket1, ket1), beta_00))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "When we measure this state using states labeled by classical bits, we always get a result back with *even* parity. While we could have written down a classical state $\\vec{\\beta} = (1\\,0\\,0\\,1)^\\TT / 2$ with this property, our quantum state has a very important difference: if we measure instead with $\\bra{\\beta_{00}}$, we get a definite answer out." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 32, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "1.0\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "print(measure(beta_00, beta_00))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "That's all well and good, but what does it mean to measure $\\bra{\\beta_{00}}$? To answer that, we need to say how quantum states transform. As with classical bits, we can act logical gates on our quantum bits. For instance, the classical NOT gate takes $0$ to $1$ and vice-versa. We can write this gate down as a matrix that transforms classical states:\n", | |
| "$$\n", | |
| " \\text{NOT} \\left(\\begin{matrix}\n", | |
| " p \\\\ 1 - p\n", | |
| " \\end{matrix}\\right) =\n", | |
| " \\left(\\begin{matrix}\n", | |
| " 1 - p \\\\ p\n", | |
| " \\end{matrix}\\right) = \n", | |
| " \\left(\\begin{matrix}\n", | |
| " 0 & 1 \\\\ 1 & 0\n", | |
| " \\end{matrix}\\right)\n", | |
| " \\left(\\begin{matrix}\n", | |
| " p \\\\ 1 - p\n", | |
| " \\end{matrix}\\right).\n", | |
| "$$\n", | |
| "This can even be taken as the definition of the NOT-gate,\n", | |
| "$$\n", | |
| " \\text{NOT} =\n", | |
| " \\left(\\begin{matrix}\n", | |
| " 0 & 1 \\\\ 1 & 0\n", | |
| " \\end{matrix}\\right).\n", | |
| "$$" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Logic gates on quantum states can be thought of in the same way, as matrices $U$ that transform states. Quantum gates must satisfy the equation that $U U^\\dagger = \\id$, where $\\id$ is the identity matrix. This represents that quantum gates are reversible in time, and is of fundamental importance. Gates that are reversible in this way are called *unitary*." | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Any *reversible* classical logical gate can be written as a quantum gate in this way. For instance, consider a \"controlled-NOT\" gate that acts on two bits $i$ and $j$ by\n", | |
| "$$\n", | |
| " \\text{CNOT} (i, j) = \\begin{cases}\n", | |
| " (i, j) & i = 0 \\\\\n", | |
| " (i, \\text{NOT } j) & i = 1\n", | |
| " \\end{cases}.\n", | |
| "$$\n", | |
| "In matrix form:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 34, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2, 2], [2, 2]], shape = [4, 4], type = oper, isherm = True\\begin{equation*}\\left(\\begin{array}{*{11}c}1.0 & 0.0 & 0.0 & 0.0\\\\0.0 & 1.0 & 0.0 & 0.0\\\\0.0 & 0.0 & 0.0 & 1.0\\\\0.0 & 0.0 & 1.0 & 0.0\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2, 2], [2, 2]], shape = [4, 4], type = oper, isherm = True\n", | |
| "Qobj data =\n", | |
| "[[ 1. 0. 0. 0.]\n", | |
| " [ 0. 1. 0. 0.]\n", | |
| " [ 0. 0. 0. 1.]\n", | |
| " [ 0. 0. 1. 0.]]" | |
| ] | |
| }, | |
| "execution_count": 34, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "from qutip import cnot\n", | |
| "cnot()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "We can verify that $\\text{CNOT}$ acts as we expect on basis states. For instance, $\\text{CNOT}\\ket{10} = \\ket{11}$." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 36, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "True" | |
| ] | |
| }, | |
| "execution_count": 36, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "cnot() * tensor(ket1, ket0) == tensor(ket1, ket1)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "We can do much more than just write down classical logic gates, however. The *Hadamard gate*, for instance, is unique to quantum information, and does something quite interesting." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 38, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2], [2]], shape = [2, 2], type = oper, isherm = True\\begin{equation*}\\left(\\begin{array}{*{11}c}0.707 & 0.707\\\\0.707 & -0.707\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2], [2]], shape = [2, 2], type = oper, isherm = True\n", | |
| "Qobj data =\n", | |
| "[[ 0.70710678 0.70710678]\n", | |
| " [ 0.70710678 -0.70710678]]" | |
| ] | |
| }, | |
| "execution_count": 38, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "from qutip import hadamard_transform\n", | |
| "hadamard_transform()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 39, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2], [1]], shape = [2, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.707\\\\0.707\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2], [1]], shape = [2, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 0.70710678]\n", | |
| " [ 0.70710678]]" | |
| ] | |
| }, | |
| "execution_count": 39, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "hadamard_transform() * ket0" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 40, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2], [1]], shape = [2, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}1.0\\\\0.0\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2], [1]], shape = [2, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 1.]\n", | |
| " [ 0.]]" | |
| ] | |
| }, | |
| "execution_count": 40, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "hadamard_transform() * (hadamard_transform() * ket0)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "From these examples, we can see that the Hadamard gate takes $\\ket{0}$ to the state $(\\ket{0} + \\ket{1}) / \\sqrt{2}$ and back. When we combine this with the CNOT gate from above, we get a *circuit* that prepares $\\ket{beta_{00}}$ from the CNOT and Hadamard gates:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 41, | |
| "metadata": { | |
| "collapsed": true | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "from qutip import qeye" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 43, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.707\\\\0.0\\\\0.0\\\\0.707\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 0.70710678]\n", | |
| " [ 0. ]\n", | |
| " [ 0. ]\n", | |
| " [ 0.70710678]]" | |
| ] | |
| }, | |
| "execution_count": 43, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "beta_00 = cnot(2, 0, 1) * tensor(hadamard_transform(), qeye(2)) * tensor(ket0, ket0)\n", | |
| "beta_00" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "The tensor product in this expression indicates that we apply the Hadamard gate only to the first of the two qubits. The other qubit is acted upon by the identity matrix, represented in QuTiP by the ``qeye`` function. Here, two indicates the dimension." | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Finally, we can rely on QuTiP having a ``bell_state`` function built in. (For the curious, they implement this by using ``tensor`` and ``basis``--- [take a look](https://github.com/qutip/qutip/blob/master/qutip/states.py#L1098) if you like!)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 7, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.707\\\\0.0\\\\0.0\\\\0.707\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 0.70710678]\n", | |
| " [ 0. ]\n", | |
| " [ 0. ]\n", | |
| " [ 0.70710678]]" | |
| ] | |
| }, | |
| "execution_count": 7, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "beta_00 = qt.bell_state()\n", | |
| "beta_00" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "## Encoding ##" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "**TODO**: pick up here!!!!" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Next, we need Alice to encode her two classical bits (cbits) into the state she shares with Bob. Let's call them $i$ and $j$. As is usual for superdense coding, we suppose that Alice and Bob start far apart, such that Alice can only do things on her half of their state. That is, we need to evolve under a unitary $U = U_A(i, j) \\otimes \\id_B$, where $U_A(i, j)$ is a unitary on Alice's side and $\\id_B$ represents that what Bob does can't depend on $i$ and $j$, since he doesn't know those yet. That is, he might as well just do nothing, represented by an identity operator.\n", | |
| "\n", | |
| "In particular, Alice's gate is given by \n", | |
| "\\begin{equation}\n", | |
| " U_A(i, j) = \\begin{cases}\n", | |
| " \\id & (0, 0) \\\\\n", | |
| " \\sigma_x & (0, 1) \\\\\n", | |
| " \\sigma_z & (1, 0) \\\\\n", | |
| " i \\sigma_y & (1, 1)\n", | |
| " \\end{cases}.\n", | |
| "\\end{equation}\n", | |
| "Let's look at how we could implement this as a function." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 8, | |
| "metadata": { | |
| "collapsed": true | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "def alice_encode(i, j):\n", | |
| " if i == 0 and j == 0:\n", | |
| " return qt.qeye(2)\n", | |
| " elif i == 0 and j == 1:\n", | |
| " return qt.sigmax()\n", | |
| " elif i == 1 and j == 0:\n", | |
| " return qt.sigmaz()\n", | |
| " elif i == 1 and j == 1:\n", | |
| " return 1j * qt.sigmay()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 9, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2], [2]], shape = [2, 2], type = oper, isherm = False\\begin{equation*}\\left(\\begin{array}{*{11}c}0.0 & 1.0\\\\-1.0 & 0.0\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2], [2]], shape = [2, 2], type = oper, isherm = False\n", | |
| "Qobj data =\n", | |
| "[[ 0. 1.]\n", | |
| " [-1. 0.]]" | |
| ] | |
| }, | |
| "execution_count": 9, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "alice_encode(1, 1)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Phew, that was a lot of writing thanks to all those ``if`` statements! All we really wanted to do was to map the inputs $i$ and $j$ to an output we knew ahead of time. Thankfully, Python allows us to use a *dictionary* to express the same idea:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 10, | |
| "metadata": { | |
| "collapsed": true | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "def alice_encode(i, j):\n", | |
| " return {\n", | |
| " (0, 0): qt.qeye(2),\n", | |
| " (0, 1): qt.sigmax(),\n", | |
| " (1, 0): qt.sigmaz(),\n", | |
| " (1, 1): qt.sigmay()\n", | |
| " }[(i, j)]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Not that much shorter, but a lot less typing and it follows the math more closely. We can do still better, though, by exploiting that $i \\sigma_y = \\sigma_z \\sigma_x$:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 11, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "True" | |
| ] | |
| }, | |
| "execution_count": 11, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "1j * qt.sigmay() == qt.sigmaz() * qt.sigmax()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 12, | |
| "metadata": { | |
| "collapsed": true | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "def alice_encode(i, j):\n", | |
| " return qt.sigmaz() ** i * qt.sigmax() ** j" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 13, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2], [2]], shape = [2, 2], type = oper, isherm = False\\begin{equation*}\\left(\\begin{array}{*{11}c}0.0 & 1.0\\\\-1.0 & 0.0\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2], [2]], shape = [2, 2], type = oper, isherm = False\n", | |
| "Qobj data =\n", | |
| "[[ 0. 1.]\n", | |
| " [-1. 0.]]" | |
| ] | |
| }, | |
| "execution_count": 13, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "alice_encode(1, 1)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Now that we have a function that we like for $U_A$, let's use it to define the unitary for both Alice and Bob." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 14, | |
| "metadata": { | |
| "collapsed": true | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "def both_encode(i, j):\n", | |
| " return qt.tensor(alice_encode(i, j), qt.qeye(2))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Let's see what happens to their Bell state for each of Alice's inputs." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 15, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Alice's bits: 0, 0\n" | |
| ] | |
| }, | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.707\\\\0.0\\\\0.0\\\\0.707\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 0.70710678]\n", | |
| " [ 0. ]\n", | |
| " [ 0. ]\n", | |
| " [ 0.70710678]]" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| }, | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Alice's bits: 0, 1\n" | |
| ] | |
| }, | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.0\\\\0.707\\\\0.707\\\\0.0\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 0. ]\n", | |
| " [ 0.70710678]\n", | |
| " [ 0.70710678]\n", | |
| " [ 0. ]]" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| }, | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Alice's bits: 1, 0\n" | |
| ] | |
| }, | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.707\\\\0.0\\\\0.0\\\\-0.707\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 0.70710678]\n", | |
| " [ 0. ]\n", | |
| " [ 0. ]\n", | |
| " [-0.70710678]]" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| }, | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Alice's bits: 1, 1\n" | |
| ] | |
| }, | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.0\\\\0.707\\\\-0.707\\\\0.0\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 0. ]\n", | |
| " [ 0.70710678]\n", | |
| " [-0.70710678]\n", | |
| " [ 0. ]]" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| } | |
| ], | |
| "source": [ | |
| "for i in range(2):\n", | |
| " for j in range(2):\n", | |
| " print \"Alice's bits: {}, {}\".format(i, j)\n", | |
| " display(both_encode(i, j) * beta_00)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "## Decoding" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Next, Alice sends Bob her half of the state, so that Bob can now do a unitary on the entire state. To decode and figure out which bits Alice sent, let's undo how we made the state, using a Hadamard and a CNOT gate." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 16, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2, 2], [2, 2]], shape = [4, 4], type = oper, isherm = False\\begin{equation*}\\left(\\begin{array}{*{11}c}0.707 & 0.0 & 0.0 & 0.707\\\\0.0 & 0.707 & 0.707 & 0.0\\\\0.707 & 0.0 & 0.0 & -0.707\\\\0.0 & 0.707 & -0.707 & 0.0\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2, 2], [2, 2]], shape = [4, 4], type = oper, isherm = False\n", | |
| "Qobj data =\n", | |
| "[[ 0.70710678 0. 0. 0.70710678]\n", | |
| " [ 0. 0.70710678 0.70710678 0. ]\n", | |
| " [ 0.70710678 0. 0. -0.70710678]\n", | |
| " [ 0. 0.70710678 -0.70710678 0. ]]" | |
| ] | |
| }, | |
| "execution_count": 16, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "bob_decode = qt.tensor(qt.hadamard_transform(1), qt.qeye(2)) * qt.cnot(2, 0, 1)\n", | |
| "bob_decode" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Does it work? Let's look what it does for each state:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 17, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Alice's bits: 0, 0\n" | |
| ] | |
| }, | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}1.0\\\\0.0\\\\0.0\\\\0.0\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 1.]\n", | |
| " [ 0.]\n", | |
| " [ 0.]\n", | |
| " [ 0.]]" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| }, | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Alice's bits: 0, 1\n" | |
| ] | |
| }, | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.0\\\\1.0\\\\0.0\\\\0.0\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 0.]\n", | |
| " [ 1.]\n", | |
| " [ 0.]\n", | |
| " [ 0.]]" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| }, | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Alice's bits: 1, 0\n" | |
| ] | |
| }, | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.0\\\\0.0\\\\1.0\\\\0.0\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 0.]\n", | |
| " [ 0.]\n", | |
| " [ 1.]\n", | |
| " [ 0.]]" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| }, | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Alice's bits: 1, 1\n" | |
| ] | |
| }, | |
| { | |
| "data": { | |
| "text/latex": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}0.0\\\\0.0\\\\0.0\\\\1.0\\\\\\end{array}\\right)\\end{equation*}" | |
| ], | |
| "text/plain": [ | |
| "Quantum object: dims = [[2, 2], [1, 1]], shape = [4, 1], type = ket\n", | |
| "Qobj data =\n", | |
| "[[ 0.]\n", | |
| " [ 0.]\n", | |
| " [ 0.]\n", | |
| " [ 1.]]" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| } | |
| ], | |
| "source": [ | |
| "for i in range(2):\n", | |
| " for j in range(2):\n", | |
| " print \"Alice's bits: {}, {}\".format(i, j)\n", | |
| " display(bob_decode * both_encode(i, j) * beta_00)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Perfect! One last thing, though, we had to manually look at each of these to see that we got the right answer. We know what the answer should be in this case, though, so we can write a *test case* to check. A test case is a function that raises an error if another piece of code is malfunctioning, that way you can sometimes tell if you introduce new bugs when you change things." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 18, | |
| "metadata": { | |
| "collapsed": true | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "def test_superdense():\n", | |
| " for i in range(2):\n", | |
| " for j in range(2):\n", | |
| " expected = qt.tensor(qt.basis(2, i), qt.basis(2, j))\n", | |
| " assert expected == bob_decode * both_encode(i, j) * beta_00" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 19, | |
| "metadata": { | |
| "collapsed": true | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "test_superdense()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "The ``assert`` keyword raises an error if you give it something ``False``, and otherwise does nothing. Since the function returned without an error, that checks that we did it correctly." | |
| ] | |
| } | |
| ], | |
| "metadata": { | |
| "kernelspec": { | |
| "display_name": "Python 3", | |
| "language": "python", | |
| "name": "python3" | |
| }, | |
| "language_info": { | |
| "codemirror_mode": { | |
| "name": "ipython", | |
| "version": 3 | |
| }, | |
| "file_extension": ".py", | |
| "mimetype": "text/x-python", | |
| "name": "python", | |
| "nbconvert_exporter": "python", | |
| "pygments_lexer": "ipython3", | |
| "version": "3.5.1" | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 0 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment